Merge branch 'master' of github.com:webnotes/erpnext
diff --git a/accounts/Print Format/POS Invoice/POS Invoice.txt b/accounts/Print Format/POS Invoice/POS Invoice.txt
index edd5f11..672c107 100644
--- a/accounts/Print Format/POS Invoice/POS Invoice.txt
+++ b/accounts/Print Format/POS Invoice/POS Invoice.txt
@@ -1,21 +1,22 @@
[
{
- "owner": "Administrator",
- "docstatus": 0,
"creation": "2011-12-21 11:08:55",
+ "docstatus": 0,
+ "modified": "2013-08-16 16:15:46",
"modified_by": "Administrator",
- "modified": "2012-03-20 12:29:49"
+ "owner": "Administrator"
},
{
"doc_type": "Sales Invoice",
- "name": "__common__",
- "module": "Accounts",
"doctype": "Print Format",
- "html": "<html>\n<head>\n<!--Other charges function-->\n<script>\nvar make_row = function(title,val,bold){\nvar bstart = '<b>'; var bend = '</b>';\nreturn '<tr><td style=\"width:50%\">'+(bold?bstart:'')+title+(bold?bend:'')+'</td>'\n +'<td style=\"width:20%\">'+doc.currency+'</td>'\n +'<td style=\"width:30%;text-align:right\">'+(val?val:'0.00')+'</td>'\n +'</tr>'\n}\nvar make_row1 = function(title,val,bold){\n var bstart = '<b>'; var bend = '</b>';\n \n return '<tr style=\"font-family:courier new; line-height:150%\"><td style=\"width:50%\">'+(bold?bstart:'')+title+(bold?bend:'')+'</td>'\n +'<td style=\"width:50%;text-align:right\">'+(bold?bstart:'')+(val?val:'0.00')+(bold?bend:'')+'</td>'\n +'</tr>'\n}\n\nfunction get_letter_head() {\n // add letter head\n var cp = wn.boot.control_panel;\n if(doc.letter_head)\n var lh= cstr(_p.letter_heads[doc.letter_head]);\n else if(cp.letter_head)\n var lh= cp.letter_head;\n else \n var lh= '';\n \n return lh;\n}\n\nfunction get_tax_details(){\n var cl = getchildren('Sales Taxes and Charges',doc.name,'other_charges');\n var out = '';\n\n out += '<div><table style=\"float: right;\">';\n for(var i=0;i<cl.length;i++){\n if(cl[i].tax_amount) {\n out += make_row1(cl[i].description,fmt_money(convert_rate(cl[i].tax_amount)),0);\n }\n }\n out += make_row1('TOTAL',doc.currency + \" \" + fmt_money(convert_rate(doc.grand_total)),1);\n out +='</table></div>';\n return out;\n}\n\nfunction convert_rate(val){ \n var new_val = flt(val)/flt(doc.conversion_rate);\n return new_val;\n}\n</script>\n<style>\n table, td, tr, div, span {\n font-family: courier new;\n line-height: 200%;\n }\n</style>\n</head>\n\n<body>\n<table width=\"100%\" style=\"font-family: courier new; line-height:200%\">\n<tr>\n <td align=\"left\">NO: <script>doc.name</script></td>\n <td align=\"right\">DATE: <script>date.str_to_user(doc.posting_date)</script></td>\n</tr>\n<tr>\n <td>M/s <script>doc.contact_display</script></td>\n</tr>\n</table>\n<!--Item Table-->\n<div>\n<script>\nvar t = print_table('Sales Invoice', doc.name, 'entries', 'Sales Invoice Item',\n ['description','qty','export_rate','export_amount'], ['ITEM', 'QTY','RATE','AMOUNT'],\n ['35%','20%','20%','25%']);\nif(t.appendChild) { // single\n out = t.innerHTML.replace(/style=\"/gi,'style=\"font-family:courier new;line-height:150%;');\n} else { //multiple\n out = '<table class=\"None\" border=\"0px\" width=\"100%\" style=\"border:0px; font-family:courier-new\">';\n\n for(var i=0;i<t.length;i++) {\n if(i==0) {\n out += '<tr>' + t[i].childNodes[0].childNodes[0].childNodes[0].innerHTML\n .replace(/style=\"border: 1px solid rgb\\(0, 0, 0\\);/gi,'style=\"font-family:courier new;')\n + '</tr>';\n }\n out += '<tr>' + t[i].childNodes[0].childNodes[0].childNodes[1].innerHTML\n .replace(/style=\"border: 1px solid rgb\\(0, 0, 0\\);/gi,'style=\"font-family:courier new;')\n + '</tr>';\n }\n out += '</table>';\n}\nout;\n</script>\n</div>\n<!--Tax table-->\n<div><script>get_tax_details();</script></div><br />\n<table style=\"font-family:courier new;\">\n <tr><td><b><script>doc.terms</script></b></td></tr>\n <tr><td nowrap><b>For <script>doc.company</script></b></td></tr>\n <tr><td> </td></tr>\n <tr><td nowrap><b>Signatory</b></td></tr>\n</table>\n</body>\n</html>\n",
+ "html": "<html>\n<head>\n<!--Other charges function-->\n<script>\nvar make_row = function(title,val,bold){\nvar bstart = '<b>'; var bend = '</b>';\nreturn '<tr><td style=\"width:50%\">'+(bold?bstart:'')+title+(bold?bend:'')+'</td>'\n +'<td style=\"width:20%\">'+doc.currency+'</td>'\n +'<td style=\"width:30%;text-align:right\">'+(val?val:'0.00')+'</td>'\n +'</tr>'\n}\nvar make_row1 = function(title,val,bold){\n var bstart = '<b>'; var bend = '</b>';\n \n return '<tr style=\"font-family:courier new; line-height:150%\"><td style=\"width:50%\">'+(bold?bstart:'')+title+(bold?bend:'')+'</td>'\n +'<td style=\"width:50%;text-align:right\">'+(bold?bstart:'')+(val?val:'0.00')+(bold?bend:'')+'</td>'\n +'</tr>'\n}\n\nfunction get_letter_head() {\n // add letter head\n var cp = wn.boot.control_panel;\n if(doc.letter_head)\n var lh= cstr(_p.letter_heads[doc.letter_head]);\n else if(cp.letter_head)\n var lh= cp.letter_head;\n else \n var lh= '';\n \n return lh;\n}\n\nfunction get_tax_details(){\n var cl = getchildren('Sales Taxes and Charges',doc.name,'other_charges');\n var out = '';\n\n out += '<div><table style=\"float: right;\">';\n for(var i=0;i<cl.length;i++){\n if(cl[i].tax_amount) {\n out += make_row1(cl[i].description,fmt_money(convert_rate(cl[i].tax_amount)),0);\n }\n }\n out += make_row1('TOTAL',doc.currency + \" \" + fmt_money(convert_rate(doc.grand_total)),1);\n out +='</table></div>';\n return out;\n}\n\nfunction convert_rate(val){ \n var new_val = flt(val)/flt(doc.conversion_rate);\n return new_val;\n}\n</script>\n<style>\n table, td, tr, div, span {\n font-family: courier new;\n line-height: 200%;\n }\n</style>\n</head>\n\n<body>\n<table width=\"100%\" style=\"font-family: courier new; line-height:200%\">\n<tr>\n <td align=\"left\">NO: <script>doc.name</script></td>\n <td align=\"right\">DATE: <script>date.str_to_user(doc.posting_date)</script></td>\n</tr>\n<tr>\n <td>M/s <script>doc.contact_display</script></td>\n</tr>\n</table>\n<!--Item Table-->\n<div>\n<script>\nvar t = print_table(\n \t\t\t'Sales Invoice',\n\t\t\t\tdoc.name,\n\t\t\t\t'entries',\n\t\t\t\t'Sales Invoice Item',\n\t\t\t\t[// Here specify the table columns to be displayed\n\t\t\t\t\t'SR', 'description', 'qty', 'export_rate', 'export_amount'\n\t\t\t\t],\n\t\t\t\t[// Here specify the labels of column headings\n\t\t\t\t\t'Sr', 'Description', 'Qty', 'Rate', 'Amount'\n\t\t\t\t],\n\t\t\t\t[// Here specify the column widths\n\t\t\t\t\t'5%', '35%', '20%', '20%', '20%'\n\t\t\t\t],\n\t\t\t\tnull,\n\t\t\t\tnull\n\t\t\t);\nif(t.appendChild) { // single\n out = t.innerHTML.replace(/style=\"/gi,'style=\"font-family:courier new;line-height:150%;');\n} else { //multiple\n out = '<table class=\"None\" border=\"0px\" width=\"100%\" style=\"border:0px; font-family:courier-new\">';\n\n for(var i=0;i<t.length;i++) {\n if(i==0) {\n out += '<tr>' + t[i].childNodes[0].childNodes[0].childNodes[0].innerHTML\n .replace(/style=\"border: 1px solid rgb\\(0, 0, 0\\);/gi,'style=\"font-family:courier new;')\n + '</tr>';\n }\n out += '<tr>' + t[i].childNodes[0].childNodes[0].childNodes[1].innerHTML\n .replace(/style=\"border: 1px solid rgb\\(0, 0, 0\\);/gi,'style=\"font-family:courier new;')\n + '</tr>';\n }\n out += '</table>';\n}\nout;\n</script>\n</div>\n<!--Tax table-->\n<div><script>get_tax_details();</script></div><br />\n<table style=\"font-family:courier new;\">\n <tr><td><b><script>doc.terms</script></b></td></tr>\n <tr><td nowrap><b>For <script>doc.company</script></b></td></tr>\n <tr><td> </td></tr>\n <tr><td nowrap><b>Signatory</b></td></tr>\n</table>\n</body>\n</html>\n",
+ "module": "Accounts",
+ "name": "__common__",
+ "print_format_type": "Client",
"standard": "Yes"
},
{
- "name": "POS Invoice",
- "doctype": "Print Format"
+ "doctype": "Print Format",
+ "name": "POS Invoice"
}
]
\ No newline at end of file
diff --git a/accounts/doctype/journal_voucher/journal_voucher.py b/accounts/doctype/journal_voucher/journal_voucher.py
index 27b0518..0fb9747 100644
--- a/accounts/doctype/journal_voucher/journal_voucher.py
+++ b/accounts/doctype/journal_voucher/journal_voucher.py
@@ -231,11 +231,12 @@
for d in self.doclist.get({"parentfield": "entries"}):
if d.against_invoice and webnotes.conn.get_value("Sales Invoice",
d.against_invoice, "debit_to") != d.account:
- msgprint("Debit account is not matching with Sales Invoice", raise_exception=1)
+ webnotes.throw(_("Credited account (Customer) is not matching with Sales Invoice"))
if d.against_voucher and webnotes.conn.get_value("Purchase Invoice",
d.against_voucher, "credit_to") != d.account:
- msgprint("Credit account is not matching with Purchase Invoice", raise_exception=1)
+ webnotes.throw(_("Debited account (Supplier) is not matching with \
+ Purchase Invoice"))
def make_gl_entries(self, cancel=0, adv_adj=0):
from accounts.general_ledger import make_gl_entries
@@ -338,11 +339,72 @@
account = webnotes.conn.get_value("Company", company,
voucher_type=="Bank Voucher" and "default_bank_account" or "default_cash_account")
if account:
- return [{
+ return {
"account": account,
"balance": get_balance_on(account)
- }]
+ }
+
+@webnotes.whitelist()
+def get_payment_entry_from_sales_invoice(sales_invoice):
+ from accounts.utils import get_balance_on
+ si = webnotes.bean("Sales Invoice", sales_invoice)
+ jv = get_payment_entry(si.doc)
+ jv.doc.remark = 'Payment received against Sales Invoice %(name)s. %(remarks)s' % si.doc.fields
+ # credit customer
+ jv.doclist[1].account = si.doc.debit_to
+ jv.doclist[1].balance = get_balance_on(si.doc.debit_to)
+ jv.doclist[1].credit = si.doc.outstanding_amount
+ jv.doclist[1].against_invoice = si.doc.name
+
+ # debit bank
+ jv.doclist[2].debit = si.doc.outstanding_amount
+
+ return [d.fields for d in jv.doclist]
+
+@webnotes.whitelist()
+def get_payment_entry_from_purchase_invoice(purchase_invoice):
+ from accounts.utils import get_balance_on
+ pi = webnotes.bean("Purchase Invoice", purchase_invoice)
+ jv = get_payment_entry(pi.doc)
+ jv.doc.remark = 'Payment against Purchase Invoice %(name)s. %(remarks)s' % pi.doc.fields
+
+ # credit supplier
+ jv.doclist[1].account = pi.doc.credit_to
+ jv.doclist[1].balance = get_balance_on(pi.doc.credit_to)
+ jv.doclist[1].debit = pi.doc.outstanding_amount
+ jv.doclist[1].against_voucher = pi.doc.name
+
+ # credit bank
+ jv.doclist[2].credit = pi.doc.outstanding_amount
+
+ return [d.fields for d in jv.doclist]
+
+def get_payment_entry(doc):
+ bank_account = get_default_bank_cash_account(doc.company, "Bank Voucher")
+
+ jv = webnotes.new_bean('Journal Voucher')
+ jv.doc.voucher_type = 'Bank Voucher'
+
+ jv.doc.company = doc.company
+ jv.doc.fiscal_year = doc.fiscal_year
+
+ jv.doclist.append({
+ "doctype": "Journal Voucher Detail",
+ "parentfield": "entries"
+ })
+
+ jv.doclist.append({
+ "doctype": "Journal Voucher Detail",
+ "parentfield": "entries"
+ })
+
+ if bank_account:
+ jv.doclist[2].account = bank_account["account"]
+ jv.doclist[2].balance = bank_account["balance"]
+
+ return jv
+
@webnotes.whitelist()
def get_opening_accounts(company):
"""get all balance sheet accounts for opening entry"""
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js
index b630499..2428a7d 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -106,13 +106,13 @@
cur_frm.cscript.make_bank_voucher = function() {
return wn.call({
- method: "accounts.doctype.journal_voucher.journal_voucher.get_default_bank_cash_account",
+ method: "accounts.doctype.journal_voucher.journal_voucher.get_payment_entry_from_purchase_invoice",
args: {
- "company": cur_frm.doc.company,
- "voucher_type": "Bank Voucher"
+ "purchase_invoice": cur_frm.doc.name,
},
callback: function(r) {
- cur_frm.cscript.make_jv(cur_frm.doc, null, null, r.message);
+ var doclist = wn.model.sync(r.message);
+ wn.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
}
@@ -198,31 +198,6 @@
refresh_field('entries');
}
-cur_frm.cscript.make_jv = function(doc, dt, dn, bank_account) {
- var jv = wn.model.make_new_doc_and_get_name('Journal Voucher');
- jv = locals['Journal Voucher'][jv];
- jv.voucher_type = 'Bank Voucher';
- jv.remark = repl('Payment against voucher %(vn)s for %(rem)s', {vn:doc.name, rem:doc.remarks});
- jv.total_debit = doc.outstanding_amount;
- jv.total_credit = doc.outstanding_amount;
- jv.fiscal_year = doc.fiscal_year;
- jv.company = doc.company;
-
- // debit to creditor
- var d1 = wn.model.add_child(jv, 'Journal Voucher Detail', 'entries');
- d1.account = doc.credit_to;
- d1.debit = doc.outstanding_amount;
- d1.against_voucher = doc.name;
-
- // credit to bank
- var d1 = wn.model.add_child(jv, 'Journal Voucher Detail', 'entries');
- d1.account = bank_account.account;
- d1.credit = doc.outstanding_amount;
- d1.balance = bank_account.balance;
-
- loaddoc('Journal Voucher', jv.name);
-}
-
cur_frm.fields_dict['entries'].grid.get_field('project_name').get_query = function(doc, cdt, cdn) {
return{
filters:[
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py
index 516c014..0538323 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -127,9 +127,9 @@
if not self.doc.remarks and self.doc.bill_date:
self.doc.remarks = (self.doc.remarks or '') + "\n" + ("Against Bill %s dated %s"
% (self.doc.bill_no, formatdate(self.doc.bill_date)))
- else:
- if not self.doc.remarks:
- self.doc.remarks = "No Remarks"
+
+ if not self.doc.remarks:
+ self.doc.remarks = "No Remarks"
def validate_credit_acc(self):
acc = sql("select debit_or_credit, is_pl_account from tabAccount where name = %s",
@@ -261,13 +261,11 @@
if d.purchase_order:
submitted = sql("select name from `tabPurchase Order` where docstatus = 1 and name = '%s'" % d.purchase_order)
if not submitted:
- msgprint("Purchase Order : "+ cstr(d.purchase_order) +" is not submitted")
- raise Exception , "Validation Error."
+ webnotes.throw("Purchase Order : "+ cstr(d.purchase_order) +" is not submitted")
if d.purchase_receipt:
submitted = sql("select name from `tabPurchase Receipt` where docstatus = 1 and name = '%s'" % d.purchase_receipt)
if not submitted:
- msgprint("Purchase Receipt : "+ cstr(d.purchase_receipt) +" is not submitted")
- raise Exception , "Validation Error."
+ webnotes.throw("Purchase Receipt : "+ cstr(d.purchase_receipt) +" is not submitted")
def update_against_document_in_jv(self):
diff --git a/accounts/doctype/sales_invoice/pos.js b/accounts/doctype/sales_invoice/pos.js
index 5045530..1bd6de1 100644
--- a/accounts/doctype/sales_invoice/pos.js
+++ b/accounts/doctype/sales_invoice/pos.js
@@ -5,10 +5,73 @@
init: function(wrapper, frm) {
this.wrapper = wrapper;
this.frm = frm;
- this.wrapper.html('<div class="customer-area"></div>\
- <div class="item-area"></div>\
- <div><button class="btn btn-default btn-add">Add</button>');
-
+ this.wrapper.html('<div class="container">\
+ <div class="row">\
+ <div class="customer-area col-sm-3 col-xs-6"></div>\
+ <div class="barcode-area col-sm-3 col-xs-6"></div>\
+ <div class="search-area col-sm-3 col-xs-6"></div>\
+ <div class="item-group-area col-sm-3 col-xs-6"></div>\
+ </div>\
+ <div class="row">\
+ <div class="col-sm-6">\
+ <div class="pos-bill">\
+ <div class="item-cart">\
+ <table class="table table-condensed table-hover" id="cart" style="table-layout: fixed;">\
+ <thead>\
+ <tr>\
+ <th style="width: 50%">Item</th>\
+ <th style="width: 25%; text-align: right;">Qty</th>\
+ <th style="width: 25%; text-align: right;">Rate</th>\
+ </tr>\
+ </thead>\
+ <tbody>\
+ </tbody>\
+ </table>\
+ </div>\
+ <br>\
+ <div class="net-total-area" style="margin-left: 40%;">\
+ <table class="table table-condensed">\
+ <tr>\
+ <td><b>Net Total</b></td>\
+ <td style="text-align: right;" class="net-total"></td>\
+ </tr>\
+ </table>\
+ <div class="tax-table" style="display: none;">\
+ <table class="table table-condensed">\
+ <thead>\
+ <tr>\
+ <th style="width: 60%">Taxes</th>\
+ <th style="width: 40%; text-align: right;"></th>\
+ </tr>\
+ </thead>\
+ <tbody>\
+ </tbody>\
+ </table>\
+ </div>\
+ <table class="table table-condensed">\
+ <tr>\
+ <td style="vertical-align: middle;"><b>Grand Total</b></td>\
+ <td style="text-align: right; font-size: 200%; \
+ font-size: bold;" class="grand-total"></td>\
+ </tr>\
+ </table>\
+ </div>\
+ </div>\
+ <br><br>\
+ <button class="btn btn-success btn-lg make-payment">\
+ <i class="icon-money"></i> Make Payment</button>\
+ <button class="btn btn-default btn-lg delete-items pull-right" style="display: none;">\
+ <i class="icon-trash"></i> Del</button>\
+ <br><br>\
+ </div>\
+ <div class="col-sm-6">\
+ <div class="item-list-area">\
+ <div class="col-sm-12">\
+ <div class="row item-list"></div></div>\
+ </div>\
+ </div>\
+ </div></div>');
+
this.make();
var me = this;
@@ -16,10 +79,20 @@
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 @@
"fieldtype": "Link",
"options": "Customer",
"label": "Customer",
- "fieldname": "pos_customer"
+ "fieldname": "pos_customer",
+ "placeholder": "Customer"
},
parent: this.wrapper.find(".customer-area")
});
@@ -36,25 +110,302 @@
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 = "<img src='" + obj.image + "' class='img-responsive'>";
+ else
+ image = '<div class="missing-image"><i class="icon-camera"></i></div>';
+
+ $(repl('<div class="col-xs-3 pos-item" data-item_code="%(item_code)s">\
+ %(item_image)s\
+ <div class="small">%(item_code)s</div>\
+ <div class="small">%(item_name)s</div>\
+ <div class="small">%(item_price)s</div>\
+ </div>',
+ {
+ 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("<div>%(item_code)s</div>", d)).appendTo($items);
+ $(repl('<tr id="%(item_code)s" data-selected="false">\
+ <td>%(item_code)s%(item_name)s</td>\
+ <td><input type="text" value="%(qty)s" \
+ class="form-control qty" style="text-align: right;"></td>\
+ <td style="text-align: right;">%(rate)s<br><b>%(amount)s</b></td>\
+ </tr>',
+ {
+ item_code: d.item_code,
+ item_name: d.item_name===d.item_code ? "" : ("<br>" + 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('<tr>\
+ <td>%(description)s</td>\
+ <td style="text-align: right;">%(tax_amount)s</td>\
+ <tr>', {
+ 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 0000000..2f2ad6a
--- /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 0000000..e4b61b6
--- /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 8db8a72..109d9f8 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/accounts/doctype/sales_invoice/sales_invoice.js
@@ -12,7 +12,7 @@
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 @@
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 @@
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 @@
}
});
});
+
+ 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();
},
@@ -181,7 +210,12 @@
set_dynamic_labels: function() {
this._super();
this.hide_fields(this.frm.doc);
+ },
+
+ entries_on_form_rendered: function(doc, grid_row) {
+ erpnext.setup_serial_no(grid_row)
}
+
});
// for backward compatibility: combine new and previous states
@@ -250,13 +284,13 @@
cur_frm.cscript.make_bank_voucher = function() {
return wn.call({
- method: "accounts.doctype.journal_voucher.journal_voucher.get_default_bank_cash_account",
+ method: "accounts.doctype.journal_voucher.journal_voucher.get_payment_entry_from_sales_invoice",
args: {
- "company": cur_frm.doc.company,
- "voucher_type": "Bank Voucher"
+ "sales_invoice": cur_frm.doc.name
},
callback: function(r) {
- cur_frm.cscript.make_jv(cur_frm.doc, null, null, r.message);
+ var doclist = wn.model.sync(r.message);
+ wn.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
}
@@ -390,34 +424,6 @@
refresh_field(cur_frm.cscript.fname);
}
-// Make Journal Voucher
-// --------------------
-cur_frm.cscript.make_jv = function(doc, dt, dn, bank_account) {
- var jv = wn.model.make_new_doc_and_get_name('Journal Voucher');
- jv = locals['Journal Voucher'][jv];
- jv.voucher_type = 'Bank Voucher';
-
- jv.company = doc.company;
- jv.remark = repl('Payment received against invoice %(vn)s for %(rem)s', {vn:doc.name, rem:doc.remarks});
- jv.fiscal_year = doc.fiscal_year;
-
- // debit to creditor
- var d1 = wn.model.add_child(jv, 'Journal Voucher Detail', 'entries');
- d1.account = doc.debit_to;
- d1.credit = doc.outstanding_amount;
- d1.against_invoice = doc.name;
-
-
- // credit to bank
- var d1 = wn.model.add_child(jv, 'Journal Voucher Detail', 'entries');
- d1.account = bank_account.account;
- d1.debit = doc.outstanding_amount;
- d1.balance = bank_account.balance;
-
- loaddoc('Journal Voucher', jv.name);
-}
-
-
cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
if(cint(wn.boot.notification_settings.sales_invoice)) {
cur_frm.email_doc(wn.boot.notification_settings.sales_invoice_message);
diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py
index 87afd74..17ae216 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/accounts/doctype/sales_invoice/sales_invoice.py
@@ -65,9 +65,6 @@
self.validate_write_off_account()
if cint(self.doc.update_stock):
- sl = get_obj('Stock Ledger')
- sl.validate_serial_no(self, 'entries')
- sl.validate_serial_no(self, 'packing_details')
self.validate_item_code()
self.update_current_stock()
self.validate_delivery_note()
@@ -84,15 +81,9 @@
"delivery_note_details")
def on_submit(self):
- if cint(self.doc.update_stock) == 1:
- sl_obj = get_obj("Stock Ledger")
- sl_obj.validate_serial_no_warehouse(self, 'entries')
- sl_obj.validate_serial_no_warehouse(self, 'packing_details')
-
- sl_obj.update_serial_record(self, 'entries', is_submit = 1, is_incoming = 0)
- sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
-
+ if cint(self.doc.update_stock) == 1:
self.update_stock_ledger(update_stock=1)
+ self.update_serial_nos()
else:
# Check for Approving Authority
if not self.doc.recurring_id:
@@ -120,11 +111,8 @@
def on_cancel(self):
if cint(self.doc.update_stock) == 1:
- sl = get_obj('Stock Ledger')
- sl.update_serial_record(self, 'entries', is_submit = 0, is_incoming = 0)
- sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
-
self.update_stock_ledger(update_stock = -1)
+ self.update_serial_nos(cancel = True)
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self)
@@ -162,12 +150,22 @@
def set_missing_values(self, for_validate=False):
self.set_pos_fields(for_validate)
+
+ if not self.doc.debit_to:
+ self.doc.debit_to = self.get_customer_account()
+ if not self.doc.due_date:
+ self.doc.due_date = self.get_due_date()
+
super(DocType, self).set_missing_values(for_validate)
def set_customer_defaults(self):
# TODO cleanup these methods
- self.doc.fields.update(self.get_debit_to())
- self.get_cust_and_due_date()
+ if self.doc.customer:
+ self.doc.debit_to = self.get_customer_account()
+ elif self.doc.debit_to:
+ self.doc.customer = webnotes.conn.get_value('Account', self.doc.debit_to, 'master_name')
+
+ self.doc.due_date = self.get_due_date()
super(DocType, self).set_customer_defaults()
@@ -243,27 +241,24 @@
You must first create it from the Customer Master" %
(self.doc.customer, self.doc.company))
- def get_debit_to(self):
- acc_head = self.get_customer_account()
- return acc_head and {'debit_to' : acc_head} or {}
-
-
- def get_cust_and_due_date(self):
+ def get_due_date(self):
"""Set Due Date = Posting Date + Credit Days"""
+ due_date = None
if self.doc.posting_date:
credit_days = 0
if self.doc.debit_to:
credit_days = webnotes.conn.get_value("Account", self.doc.debit_to, "credit_days")
+ if self.doc.customer and not credit_days:
+ credit_days = webnotes.conn.get_value("Customer", self.doc.customer, "credit_days")
if self.doc.company and not credit_days:
credit_days = webnotes.conn.get_value("Company", self.doc.company, "credit_days")
if credit_days:
- self.doc.due_date = add_days(self.doc.posting_date, credit_days)
+ due_date = add_days(self.doc.posting_date, credit_days)
else:
- self.doc.due_date = self.doc.posting_date
-
- if self.doc.debit_to:
- self.doc.customer = webnotes.conn.get_value('Account',self.doc.debit_to,'master_name')
+ due_date = self.doc.posting_date
+
+ return due_date
def get_barcode_details(self, barcode):
return get_obj('Sales Common').get_barcode_details(barcode)
@@ -487,10 +482,6 @@
def make_packing_list(self):
get_obj('Sales Common').make_packing_list(self,'entries')
- sl = get_obj('Stock Ledger')
- sl.scrub_serial_nos(self)
- sl.scrub_serial_nos(self, 'packing_details')
-
def on_update(self):
if cint(self.doc.update_stock) == 1:
@@ -502,18 +493,20 @@
if not d.warehouse:
d.warehouse = cstr(w)
- if flt(self.doc.paid_amount) == 0:
- if self.doc.cash_bank_account:
- webnotes.conn.set(self.doc, 'paid_amount',
- (flt(self.doc.grand_total) - flt(self.doc.write_off_amount)))
- else:
- # show message that the amount is not paid
- webnotes.conn.set(self.doc,'paid_amount',0)
- webnotes.msgprint("Note: Payment Entry will not be created since 'Cash/Bank Account' was not specified.")
-
self.make_packing_list()
else:
self.doclist = self.doc.clear_table(self.doclist, 'packing_details')
+
+ if cint(self.doc.is_pos) == 1:
+ if flt(self.doc.paid_amount) == 0:
+ if self.doc.cash_bank_account:
+ webnotes.conn.set(self.doc, 'paid_amount',
+ (flt(self.doc.grand_total) - flt(self.doc.write_off_amount)))
+ else:
+ # show message that the amount is not paid
+ webnotes.conn.set(self.doc,'paid_amount',0)
+ webnotes.msgprint("Note: Payment Entry will not be created since 'Cash/Bank Account' was not specified.")
+ else:
webnotes.conn.set(self.doc,'paid_amount',0)
def check_prev_docstatus(self):
@@ -556,9 +549,7 @@
self.values = []
items = get_obj('Sales Common').get_item_list(self)
for d in items:
- stock_item = webnotes.conn.sql("SELECT is_stock_item, is_sample_item \
- FROM tabItem where name = '%s'"%(d['item_code']), as_dict = 1)
- if stock_item[0]['is_stock_item'] == "Yes":
+ if webnotes.conn.get_value("Item", d['item_code'], "is_stock_item") == "Yes":
if not d['warehouse']:
msgprint("Message: Please enter Warehouse for item %s as it is stock item." \
% d['item_code'], raise_exception=1)
diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt
index dbdf42f..2a2a2e1 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-08-09 14:45:42",
+ "modified": "2013-08-31 10:19:01",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -179,7 +179,6 @@
"search_index": 1
},
{
- "default": "Today",
"description": "Enter the date by which payments from customer is expected against this invoice.",
"doctype": "DocField",
"fieldname": "due_date",
diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py
index 05e4d92..5976ce4 100644
--- a/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -644,7 +644,61 @@
count = no_of_months == 12 and 3 or 13
for i in xrange(count):
base_si = _test(i)
+
+ def test_serialized(self):
+ from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
+ se = make_serialized_item()
+ serial_nos = get_serial_nos(se.doclist[1].serial_no)
+
+ si = webnotes.bean(copy=test_records[0])
+ si.doc.update_stock = 1
+ si.doclist[1].item_code = "_Test Serialized Item With Series"
+ si.doclist[1].qty = 1
+ si.doclist[1].serial_no = serial_nos[0]
+ si.insert()
+ si.submit()
+
+ self.assertEquals(webnotes.conn.get_value("Serial No", serial_nos[0], "status"), "Delivered")
+ self.assertFalse(webnotes.conn.get_value("Serial No", serial_nos[0], "warehouse"))
+ self.assertEquals(webnotes.conn.get_value("Serial No", serial_nos[0],
+ "delivery_document_no"), si.doc.name)
+
+ return si
+
+ def test_serialized_cancel(self):
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
+ si = self.test_serialized()
+ si.cancel()
+
+ serial_nos = get_serial_nos(si.doclist[1].serial_no)
+
+ self.assertEquals(webnotes.conn.get_value("Serial No", serial_nos[0], "status"), "Available")
+ self.assertEquals(webnotes.conn.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
+ self.assertFalse(webnotes.conn.get_value("Serial No", serial_nos[0],
+ "delivery_document_no"))
+
+ def test_serialize_status(self):
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import SerialNoStatusError, get_serial_nos
+ from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+
+ se = make_serialized_item()
+ serial_nos = get_serial_nos(se.doclist[1].serial_no)
+
+ sr = webnotes.bean("Serial No", serial_nos[0])
+ sr.doc.status = "Not Available"
+ sr.save()
+
+ si = webnotes.bean(copy=test_records[0])
+ si.doc.update_stock = 1
+ si.doclist[1].item_code = "_Test Serialized Item With Series"
+ si.doclist[1].qty = 1
+ si.doclist[1].serial_no = serial_nos[0]
+ si.insert()
+
+ self.assertRaises(SerialNoStatusError, si.submit)
+
test_dependencies = ["Journal Voucher", "POS Setting", "Contact", "Address"]
test_records = [
@@ -916,7 +970,6 @@
"item_name": "_Test Item Home Desktop 100",
"qty": 10,
"ref_rate": 62.5,
- "export_rate": 62.5,
"stock_uom": "_Test UOM",
"item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}),
"income_account": "Sales - _TC",
@@ -930,7 +983,6 @@
"item_name": "_Test Item Home Desktop 200",
"qty": 5,
"ref_rate": 190.66,
- "export_rate": 190.66,
"stock_uom": "_Test UOM",
"income_account": "Sales - _TC",
"cost_center": "_Test Cost Center - _TC",
diff --git a/accounts/page/financial_statements/financial_statements.txt b/accounts/page/financial_statements/financial_statements.txt
index 78c2c30..18f8904 100644
--- a/accounts/page/financial_statements/financial_statements.txt
+++ b/accounts/page/financial_statements/financial_statements.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-01-27 16:30:52",
"docstatus": 0,
- "modified": "2013-07-11 14:41:59",
+ "modified": "2013-08-14 12:47:45",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -19,14 +19,18 @@
"name": "__common__",
"parent": "Financial Statements",
"parentfield": "roles",
- "parenttype": "Page",
- "role": "Accounts Manager"
+ "parenttype": "Page"
},
{
"doctype": "Page",
"name": "Financial Statements"
},
{
- "doctype": "Page Role"
+ "doctype": "Page Role",
+ "role": "Accounts Manager"
+ },
+ {
+ "doctype": "Page Role",
+ "role": "Analytics"
}
]
\ No newline at end of file
diff --git a/accounts/page/general_ledger/general_ledger.js b/accounts/page/general_ledger/general_ledger.js
index 9ffec3a..da28eb3 100644
--- a/accounts/page/general_ledger/general_ledger.js
+++ b/accounts/page/general_ledger/general_ledger.js
@@ -141,13 +141,13 @@
toggle_group_by_checks: function() {
this.make_account_by_name();
- this.filter_inputs.group_by_ledger
- .parent().toggle(!!(this.account_by_name[this.account]
- && this.account_by_name[this.account].group_or_ledger==="Group"));
-
- this.filter_inputs.group_by_voucher
- .parent().toggle(!!(this.account_by_name[this.account]
- && this.account_by_name[this.account].group_or_ledger==="Ledger"));
+ // this.filter_inputs.group_by_ledger
+ // .parent().toggle(!!(this.account_by_name[this.account]
+ // && this.account_by_name[this.account].group_or_ledger==="Group"));
+ //
+ // this.filter_inputs.group_by_voucher
+ // .parent().toggle(!!(this.account_by_name[this.account]
+ // && this.account_by_name[this.account].group_or_ledger==="Ledger"));
},
prepare_data: function() {
var me = this;
@@ -389,7 +389,8 @@
grid: { hoverable: true, clickable: true },
xaxis: { mode: "time",
min: dateutil.str_to_obj(this.from_date).getTime(),
- max: dateutil.str_to_obj(this.to_date).getTime() }
+ max: dateutil.str_to_obj(this.to_date).getTime() },
+ series: { downsample: { threshold: 1000 } }
}
},
});
\ No newline at end of file
diff --git a/accounts/report/gross_profit/gross_profit.py b/accounts/report/gross_profit/gross_profit.py
index 3aba234..590babb 100644
--- a/accounts/report/gross_profit/gross_profit.py
+++ b/accounts/report/gross_profit/gross_profit.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
from webnotes.utils import flt
-from stock.utils import get_buying_amount
+from stock.utils import get_buying_amount, get_sales_bom_buying_amount
def execute(filters=None):
if not filters: filters = {}
@@ -21,10 +21,15 @@
data = []
for row in source:
selling_amount = flt(row.amount)
-
- buying_amount = get_buying_amount(row.item_code, row.parenttype, row.name, row.item_row,
- stock_ledger_entries.get((row.item_code, row.warehouse), []),
- item_sales_bom.get(row.parenttype, {}).get(row.name, webnotes._dict()))
+
+ item_sales_bom_map = item_sales_bom.get(row.parenttype, {}).get(row.name, webnotes._dict())
+
+ if item_sales_bom_map.get(row.item_code):
+ buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse,
+ row.parenttype, row.name, row.item_row, stock_ledger_entries, item_sales_bom_map)
+ else:
+ buying_amount = get_buying_amount(row.parenttype, row.name, row.item_row,
+ stock_ledger_entries.get((row.item_code, row.warehouse), []))
buying_amount = buying_amount > 0 and buying_amount or 0
diff --git a/buying/doctype/purchase_common/purchase_common.py b/buying/doctype/purchase_common/purchase_common.py
index 65bccaa..9b6dc6c 100644
--- a/buying/doctype/purchase_common/purchase_common.py
+++ b/buying/doctype/purchase_common/purchase_common.py
@@ -20,8 +20,7 @@
def is_item_table_empty(self, obj):
if not len(obj.doclist.get({"parentfield": obj.fname})):
- msgprint(_("Hey there! You need to put at least one item in \
- the item table."), raise_exception=True)
+ msgprint(_("You need to put at least one item in the item table."), raise_exception=True)
def get_supplier_details(self, name = ''):
details = sql("select supplier_name,address from `tabSupplier` where name = '%s' and docstatus != 2" %(name), as_dict = 1)
diff --git a/buying/doctype/quality_inspection/quality_inspection.js b/buying/doctype/quality_inspection/quality_inspection.js
index 0c865a5..8c7c932 100644
--- a/buying/doctype/quality_inspection/quality_inspection.js
+++ b/buying/doctype/quality_inspection/quality_inspection.js
@@ -46,10 +46,10 @@
if (doc.item_code) {
filter = {
'item_code': doc.item_code,
- 'status': "In Store"
+ 'status': "Available"
}
} else
- filter = { 'status': "In Store" }
+ filter = { 'status': "Available" }
return { filters: filter }
}
\ No newline at end of file
diff --git a/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js
index 939ff79..5db57f4 100644
--- a/buying/page/buying_home/buying_home.js
+++ b/buying/page/buying_home/buying_home.js
@@ -111,6 +111,11 @@
icon: "icon-list",
items: [
{
+ "label":wn._("Items To Be Requested"),
+ route: "query-report/Items To Be Requested",
+ doctype: "Item"
+ },
+ {
"label":wn._("Requested Items To Be Ordered"),
route: "query-report/Requested Items To Be Ordered",
doctype: "Material Request"
diff --git a/config.json b/config.json
index c9d1aa7..ef5a164 100644
--- a/config.json
+++ b/config.json
@@ -1,4 +1,5 @@
{
+ "app_name": "ERPNext",
"modules": {
"Selling": {
"link": "selling-home",
@@ -129,7 +130,7 @@
},
"writers": {
"template": "app/website/templates/pages/writers",
- "args_method": "website.helpers.blog.get_writers_args"
+ "args_method": "website.doctype.blogger.blogger.get_writers_args"
},
"profile": {
"no_cache": true,
diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py
index d6bf392..a3dae8e 100644
--- a/controllers/accounts_controller.py
+++ b/controllers/accounts_controller.py
@@ -75,7 +75,7 @@
self.doc.conversion_rate = self.doc.plc_conversion_rate
if self.meta.get_field("currency"):
- if self.doc.currency != company_currency:
+ if self.doc.currency and self.doc.currency != company_currency:
if not self.doc.conversion_rate:
exchange = self.doc.currency + "-" + company_currency
self.doc.conversion_rate = flt(webnotes.conn.get_value("Currency Exchange",
diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py
index 63070a5..043099a 100644
--- a/controllers/buying_controller.py
+++ b/controllers/buying_controller.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
from webnotes import _, msgprint
-from webnotes.utils import flt
+from webnotes.utils import flt, _round
from buying.utils import get_item_details
from setup.utils import get_company_currency
@@ -17,7 +17,6 @@
def onload_post_render(self):
# contact, address, item details
self.set_missing_values()
- self.set_taxes("purchase_tax_details", "purchase_other_charges")
def validate(self):
super(BuyingController, self).validate()
@@ -40,6 +39,8 @@
self.doc.fields[fieldname] = val
self.set_missing_item_details(get_item_details)
+ if self.doc.fields.get("__islocal"):
+ self.set_taxes("purchase_tax_details", "purchase_other_charges")
def set_supplier_from_item_default(self):
if self.meta.get_field("supplier") and not self.doc.supplier:
@@ -98,7 +99,7 @@
if item.discount_rate == 100.0:
item.import_rate = 0.0
- elif item.import_ref_rate:
+ elif not item.import_rate:
item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)),
self.precision("import_rate", item))
@@ -129,10 +130,10 @@
self.precision("total_tax"))
if self.meta.get_field("rounded_total"):
- self.doc.rounded_total = round(self.doc.grand_total)
+ self.doc.rounded_total = _round(self.doc.grand_total)
if self.meta.get_field("rounded_total_import"):
- self.doc.rounded_total_import = round(self.doc.grand_total_import)
+ self.doc.rounded_total_import = _round(self.doc.grand_total_import)
def calculate_outstanding_amount(self):
if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus < 2:
diff --git a/controllers/js/contact_address_common.js b/controllers/js/contact_address_common.js
index 82dad0e..7589947 100644
--- a/controllers/js/contact_address_common.js
+++ b/controllers/js/contact_address_common.js
@@ -16,11 +16,14 @@
if(doc.__islocal) {
var last_route = wn.route_history.slice(-2, -1)[0];
if(last_route && last_route[0]==="Form") {
+ var doctype = last_route[1],
+ docname = last_route.slice(2).join("/");
+
if(["Customer", "Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
"Installation Note", "Opportunity", "Customer Issue", "Maintenance Visit",
"Maintenance Schedule"]
- .indexOf(last_route[1])!==-1) {
- var refdoc = wn.model.get_doc(last_route[1], last_route[2]);
+ .indexOf(doctype)!==-1) {
+ var refdoc = wn.model.get_doc(doctype, docname);
if(refdoc.doctype == "Quotation" ? refdoc.quotation_to=="Customer" : true) {
cur_frm.set_value("customer", refdoc.customer || refdoc.name);
@@ -30,16 +33,16 @@
}
}
if(["Supplier", "Supplier Quotation", "Purchase Order", "Purchase Invoice", "Purchase Receipt"]
- .indexOf(last_route[1])!==-1) {
- var refdoc = wn.model.get_doc(last_route[1], last_route[2]);
+ .indexOf(doctype)!==-1) {
+ var refdoc = wn.model.get_doc(doctype, docname);
cur_frm.set_value("supplier", refdoc.supplier || refdoc.name);
cur_frm.set_value("supplier_name", refdoc.supplier_name);
if(cur_frm.doc.doctype==="Address")
cur_frm.set_value("address_title", cur_frm.doc.supplier_name);
}
if(["Lead", "Quotation"]
- .indexOf(last_route[1])!==-1) {
- var refdoc = wn.model.get_doc(last_route[1], last_route[2]);
+ .indexOf(doctype)!==-1) {
+ var refdoc = wn.model.get_doc(doctype, docname);
if(refdoc.doctype == "Quotation" ? refdoc.quotation_to=="Lead" : true) {
cur_frm.set_value("lead", refdoc.lead || refdoc.name);
diff --git a/controllers/queries.py b/controllers/queries.py
index 381d2c8..0e23d5c 100644
--- a/controllers/queries.py
+++ b/controllers/queries.py
@@ -207,4 +207,4 @@
"fcond": get_filters_cond(doctype, filters, []),
"mcond": get_match_cond(doctype),
"start": "%(start)s", "page_len": "%(page_len)s", "txt": "%(txt)s"
- }, { "start": start, "page_len": page_len, "txt": ("%%%s%%" % txt) }, debug=True)
\ No newline at end of file
+ }, { "start": start, "page_len": page_len, "txt": ("%%%s%%" % txt) })
\ No newline at end of file
diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py
index 033ac83..a13d747 100644
--- a/controllers/selling_controller.py
+++ b/controllers/selling_controller.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cint, flt, comma_or
+from webnotes.utils import cint, flt, comma_or, _round, add_days, cstr
from setup.utils import get_company_currency
from selling.utils import get_item_details
from webnotes import msgprint, _
@@ -15,18 +15,14 @@
# contact, address, item details and pos details (if applicable)
self.set_missing_values()
- self.set_taxes("other_charges", "charge")
-
- if self.meta.get_field("debit_to") and not self.doc.debit_to:
- self.doc.debit_to = self.get_debit_to().get("debit_to")
-
def set_missing_values(self, for_validate=False):
super(SellingController, self).set_missing_values(for_validate)
# set contact and address details for customer, if they are not mentioned
self.set_missing_lead_customer_details()
-
self.set_price_list_and_item_details()
+ if self.doc.fields.get("__islocal"):
+ self.set_taxes("other_charges", "charge")
def set_missing_lead_customer_details(self):
if self.doc.customer:
@@ -89,7 +85,7 @@
self.doc.grand_total_export or self.doc.rounded_total_export, self.doc.currency)
def set_buying_amount(self, stock_ledger_entries = None):
- from stock.utils import get_buying_amount
+ from stock.utils import get_buying_amount, get_sales_bom_buying_amount
if not stock_ledger_entries:
stock_ledger_entries = self.get_stock_ledger_entries()
@@ -103,13 +99,18 @@
for item in self.doclist.get({"parentfield": self.fname}):
if item.item_code in self.stock_items or \
(item_sales_bom and item_sales_bom.get(item.item_code)):
- buying_amount = get_buying_amount(item.item_code, self.doc.doctype, self.doc.name, item.name,
- stock_ledger_entries.get((item.item_code, item.warehouse), []),
- item_sales_bom)
+ if item.item_code in self.stock_items:
+ buying_amount = get_buying_amount(self.doc.doctype, self.doc.name,
+ item.name, stock_ledger_entries.get((item.item_code,
+ item.warehouse), []))
+ elif item_sales_bom and item_sales_bom.get(item.item_code):
+ buying_amount = get_sales_bom_buying_amount(item.item_code, item.warehouse,
+ self.doc.doctype, self.doc.name, item.name, stock_ledger_entries,
+ item_sales_bom)
- item.buying_amount = buying_amount >= 0.01 and buying_amount or 0
- webnotes.conn.set_value(item.doctype, item.name, "buying_amount",
- item.buying_amount)
+ item.buying_amount = buying_amount >= 0.01 and buying_amount or 0
+ webnotes.conn.set_value(item.doctype, item.name, "buying_amount",
+ item.buying_amount)
def check_expense_account(self, item):
if item.buying_amount and not item.expense_account:
@@ -191,7 +192,7 @@
if item.adj_rate == 100:
item.export_rate = 0
- elif item.ref_rate:
+ elif not item.export_rate:
item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)),
self.precision("export_rate", item))
@@ -222,8 +223,8 @@
self.doc.other_charges_total_export = flt(self.doc.grand_total_export - self.doc.net_total_export,
self.precision("other_charges_total_export"))
- self.doc.rounded_total = round(self.doc.grand_total)
- self.doc.rounded_total_export = round(self.doc.grand_total_export)
+ self.doc.rounded_total = _round(self.doc.grand_total)
+ self.doc.rounded_total_export = _round(self.doc.grand_total_export)
def calculate_outstanding_amount(self):
# NOTE:
@@ -271,3 +272,32 @@
msgprint(_(self.meta.get_label("order_type")) + " " +
_("must be one of") + ": " + comma_or(valid_types),
raise_exception=True)
+
+ def update_serial_nos(self, cancel=False):
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
+ update_serial_nos_after_submit(self, self.doc.doctype, self.fname)
+ update_serial_nos_after_submit(self, self.doc.doctype, "packing_details")
+
+ for table_fieldname in (self.fname, "packing_details"):
+ for d in self.doclist.get({"parentfield": table_fieldname}):
+ for serial_no in get_serial_nos(d.serial_no):
+ sr = webnotes.bean("Serial No", serial_no)
+ if cancel:
+ sr.doc.status = "Available"
+ for fieldname in ("warranty_expiry_date", "delivery_document_type",
+ "delivery_document_no", "delivery_date", "delivery_time", "customer",
+ "customer_name"):
+ sr.doc.fields[fieldname] = None
+ else:
+ sr.doc.delivery_document_type = self.doc.doctype
+ sr.doc.delivery_document_no = self.doc.name
+ sr.doc.delivery_date = self.doc.posting_date
+ sr.doc.delivery_time = self.doc.posting_time
+ sr.doc.customer = self.doc.customer
+ sr.doc.customer_name = self.doc.customer_name
+ if sr.doc.warranty_period:
+ sr.doc.warranty_expiry_date = add_days(cstr(self.doc.posting_date),
+ cint(sr.doc.warranty_period))
+ sr.doc.status = 'Delivered'
+
+ sr.save()
diff --git a/docs/docs.community.md b/docs/docs.community.md
deleted file mode 100644
index 121a692..0000000
--- a/docs/docs.community.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-{
- "_label": "Get Involved"
-}
----
-If you are an ERPNext user:
-
-[https://groups.google.com/group/erpnext-user-forum](https://groups.google.com/group/erpnext-user-forum)
-
-If you are an ERPNext developer:
-
-[https://groups.google.com/group/erpnext-developer-forum](https://groups.google.com/group/erpnext-developer-forum)
-
-
diff --git a/docs/docs.md b/docs/docs.md
index 378a0b8..3ed24b7 100644
--- a/docs/docs.md
+++ b/docs/docs.md
@@ -7,28 +7,25 @@
"docs.dev",
"docs.download",
"docs.community",
- "docs.blog",
- "docs.about"
+ "docs.blog"
],
"_no_toc": 1
}
---
-<div style="margin: 10px 0px">
- <h1 style="text-align: center">All-in-One Platform to Manage Your Organization.</h1>
- <h3 style="text-align: center; font-weight: normal; color: #888">100% Free and Open Source.</h1>
+<div class="text-center" style="margin: 10px 0px">
+ <h1>ERPNext Docs (beta)</h1>
+ <h3 class="text-muted">Open Source ERP Built for The Web.</h3>
+ <p>For the main site, go to <a href="https://erpnext.com/">https://erpnext.com</a></p>
</div>

+Welcome to the ERPNext Documentation + Community Site
+
### What is ERPNext?
-ERPNext is an information system that links together an entire organization's operations. It is a software package that offers convenience of managing all the business functions from a single platform. No need of going to different applications to process different requests. No need of saving data in different functional packages. Under one ERP "roof" you can manage Accounting, Warehouse Management, CRM, Human Resources, Supply Chain Management, Sales Management, and Website Design.
+ERPNext is an Open Source integrated app (that manages Financial Accounting, Inventory, CRM) that is built grounds up for the web, using some of the latest web technologies and frameworks. ERPNext helps your organization manage Financial Accounting, Inventory, Sales, Purchase, Payroll, Customer Support, E-Commerce all in one platform. Learn more at [https://erpnext.com](https://erpnext.com)
-ERPNext is written by Web Notes Technologies keeping small and medium businesses in mind.
+### Site Contents
-- It gives better access to crucial information as a whole rather than in fragments of different versions.
-- It provides comparable financial reports.
-- It avoids duplication of reports and redundant data.
-- It allows better alignment across cross-functional departments.
-- It facilitates Website Design and provides shopping cart facility.
-- It gives better deployment on mobiles, tablets, desktops and large screens.
\ No newline at end of file
+This site contains the full User and Developer Documentation for ERPNext. This is still a work-in-progress. Please feel free to contribute issues and documentation.
diff --git a/hr/doctype/employee/employee.py b/hr/doctype/employee/employee.py
index f1646f7..e28e02d 100644
--- a/hr/doctype/employee/employee.py
+++ b/hr/doctype/employee/employee.py
@@ -156,20 +156,23 @@
raise_exception=InvalidLeaveApproverError)
def update_dob_event(self):
- if self.doc.date_of_birth:
- get_events = webnotes.conn.sql("""select name from `tabEvent` where repeat_on='Every Year'
+ if self.doc.status == "Active" and self.doc.date_of_birth:
+ birthday_event = webnotes.conn.sql("""select name from `tabEvent` where repeat_on='Every Year'
and ref_type='Employee' and ref_name=%s""", self.doc.name)
starts_on = self.doc.date_of_birth + " 00:00:00"
ends_on = self.doc.date_of_birth + " 00:15:00"
- if get_events:
- webnotes.conn.sql("""update `tabEvent` set starts_on=%s, ends_on=%s
- where name=%s""", (starts_on, ends_on, get_events[0][0]))
+ if birthday_event:
+ event = webnotes.bean("Event", birthday_event[0][0])
+ event.doc.starts_on = starts_on
+ event.doc.ends_on = ends_on
+ event.save()
else:
webnotes.bean({
"doctype": "Event",
"subject": _("Birthday") + ": " + self.doc.employee_name,
+ "description": _("Happy Birthday!") + " " + self.doc.employee_name,
"starts_on": starts_on,
"ends_on": ends_on,
"event_type": "Public",
diff --git a/hr/doctype/salary_slip/salary_slip.py b/hr/doctype/salary_slip/salary_slip.py
index 4ca1734..36d7ceb 100644
--- a/hr/doctype/salary_slip/salary_slip.py
+++ b/hr/doctype/salary_slip/salary_slip.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import add_days, cint, cstr, flt, getdate, nowdate
+from webnotes.utils import add_days, cint, cstr, flt, getdate, nowdate, _round
from webnotes.model.doc import make_autoname
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
@@ -164,7 +164,7 @@
self.doc.gross_pay = flt(self.doc.arrear_amount) + flt(self.doc.leave_encashment_amount)
for d in self.doclist.get({"parentfield": "earning_details"}):
if cint(d.e_depends_on_lwp) == 1:
- d.e_modified_amount = round(flt(d.e_amount) * flt(self.doc.payment_days)
+ d.e_modified_amount = _round(flt(d.e_amount) * flt(self.doc.payment_days)
/ cint(self.doc.total_days_in_month), 2)
elif not self.doc.payment_days:
d.e_modified_amount = 0
@@ -176,7 +176,7 @@
self.doc.total_deduction = 0
for d in getlist(self.doclist, 'deduction_details'):
if cint(d.d_depends_on_lwp) == 1:
- d.d_modified_amount = round(flt(d.d_amount) * flt(self.doc.payment_days)
+ d.d_modified_amount = _round(flt(d.d_amount) * flt(self.doc.payment_days)
/ cint(self.doc.total_days_in_month), 2)
elif not self.doc.payment_days:
d.d_modified_amount = 0
@@ -189,7 +189,7 @@
self.calculate_earning_total()
self.calculate_ded_total()
self.doc.net_pay = flt(self.doc.gross_pay) - flt(self.doc.total_deduction)
- self.doc.rounded_total = round(self.doc.net_pay)
+ self.doc.rounded_total = _round(self.doc.net_pay)
def on_submit(self):
if(self.doc.email_check == 1):
diff --git a/install_erpnext.py b/install_erpnext.py
index 1a802ae..c57a9e2 100644
--- a/install_erpnext.py
+++ b/install_erpnext.py
@@ -51,7 +51,7 @@
distribution = platform.linux_distribution()[0].lower().replace('"', '')
print "Distribution = ", distribution
is_redhat = distribution in ("redhat", "centos", "centos linux", "fedora")
- is_debian = distribution in ("debian", "ubuntu", "elementary os")
+ is_debian = distribution in ("debian", "ubuntu", "elementary os", "linuxmint")
if not (is_redhat or is_debian):
raise Exception, "Sorry! This installer works only with yum or apt-get package management"
@@ -59,7 +59,7 @@
return is_redhat, is_debian
def install_using_yum():
- packages = "python python-setuptools MySQL-python httpd git memcached ntp vim-enhanced screen"
+ packages = "python python-setuptools gcc python-devel MySQL-python httpd git memcached ntp vim-enhanced screen"
print "-"*80
print "Installing Packages: (This may take some time)"
@@ -108,7 +108,7 @@
def install_using_apt():
exec_in_shell("apt-get update")
- packages = "python python-setuptools python-mysqldb apache2 git memcached ntp vim screen htop"
+ packages = "python python-setuptools python-dev build-essential python-pip python-mysqldb apache2 git memcached ntp vim screen htop"
print "-"*80
print "Installing Packages: (This may take some time)"
print packages
@@ -145,7 +145,11 @@
print python_modules
print "-"*80
- exec_in_shell("easy_install pip")
+ if not exec_in_shell("which pip"):
+ exec_in_shell("easy_install pip")
+
+ exec_in_shell("pip install --upgrade pip")
+ exec_in_shell("pip install --upgrade virtualenv")
exec_in_shell("pip install -q %s" % python_modules)
def install_erpnext(install_path):
diff --git a/manufacturing/doctype/production_order/production_order.py b/manufacturing/doctype/production_order/production_order.py
index 90a74e9..6a133e7 100644
--- a/manufacturing/doctype/production_order/production_order.py
+++ b/manufacturing/doctype/production_order/production_order.py
@@ -143,11 +143,18 @@
def make_stock_entry(production_order_id, purpose):
production_order = webnotes.bean("Production Order", production_order_id)
+ # validate already existing
+ ste = webnotes.conn.get_value("Stock Entry", {
+ "production_order":production_order_id,
+ "purpose": purpose
+ }, "name")
+
stock_entry = webnotes.new_bean("Stock Entry")
stock_entry.doc.purpose = purpose
stock_entry.doc.production_order = production_order_id
stock_entry.doc.company = production_order.doc.company
stock_entry.doc.bom_no = production_order.doc.bom_no
+ stock_entry.doc.use_multi_level_bom = production_order.doc.use_multi_level_bom
stock_entry.doc.fg_completed_qty = flt(production_order.doc.qty) - flt(production_order.doc.produced_qty)
if purpose=="Material Transfer":
@@ -156,4 +163,5 @@
stock_entry.doc.from_warehouse = production_order.doc.wip_warehouse
stock_entry.doc.to_warehouse = production_order.doc.fg_warehouse
+ stock_entry.run_method("get_items")
return [d.fields for d in stock_entry.doclist]
diff --git a/patches/april_2012/serial_no_fixes.py b/patches/april_2012/serial_no_fixes.py
deleted file mode 100644
index 4fef016..0000000
--- a/patches/april_2012/serial_no_fixes.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-def execute():
- import webnotes
- from webnotes.modules import reload_doc
- reload_doc('stock', 'doctype', 'serial_no')
-
- webnotes.conn.sql("update `tabSerial No` set sle_exists = 1")
diff --git a/patches/august_2013/fix_fiscal_year.py b/patches/august_2013/fix_fiscal_year.py
new file mode 100644
index 0000000..67988c4
--- /dev/null
+++ b/patches/august_2013/fix_fiscal_year.py
@@ -0,0 +1,49 @@
+import webnotes
+
+def execute():
+ create_fiscal_years()
+
+ doctypes = webnotes.conn.sql_list("""select parent from tabDocField
+ where (fieldtype="Link" and options='Fiscal Year')
+ or (fieldtype="Select" and options='link:Fiscal Year')""")
+
+ for dt in doctypes:
+ date_fields = webnotes.conn.sql_list("""select fieldname from tabDocField
+ where parent=%s and fieldtype='Date'""", dt)
+
+ date_field = get_date_field(date_fields, dt)
+
+ if not date_field:
+ print dt, date_field
+ else:
+ webnotes.conn.sql("""update `tab%s` set fiscal_year =
+ if(%s<='2013-06-30', '2012-2013', '2013-2014')""" % (dt, date_field))
+
+def create_fiscal_years():
+ fiscal_years = {
+ "2012-2013": ["2012-07-01", "2013-06-30"],
+ "2013-2014": ["2013-07-01", "2014-06-30"]
+ }
+
+ for d in fiscal_years:
+ webnotes.bean({
+ "doctype": "Fiscal Year",
+ "year": d,
+ "year_start_date": fiscal_years[d][0],
+ "is_fiscal_year_closed": "No"
+ }).insert()
+
+
+def get_date_field(date_fields, dt):
+ date_field = None
+ if date_fields:
+ if "posting_date" in date_fields:
+ date_field = "posting_date"
+ elif "transaction_date" in date_fields:
+ date_field = 'transaction_date'
+ else:
+ date_field = date_fields[0]
+ # print dt, date_fields
+
+ return date_field
+
\ No newline at end of file
diff --git a/patches/august_2013/p02_rename_price_list.py b/patches/august_2013/p02_rename_price_list.py
index a66a0c2..0b1c4d0 100644
--- a/patches/august_2013/p02_rename_price_list.py
+++ b/patches/august_2013/p02_rename_price_list.py
@@ -5,6 +5,8 @@
import webnotes
def execute():
+ webnotes.reload_doc("website", "doctype", "shopping_cart_price_list")
+
for t in [
("Supplier Quotation", "price_list_name", "buying_price_list"),
("Purchase Order", "price_list_name", "buying_price_list"),
@@ -23,7 +25,7 @@
if t[2] in table_columns and t[1] in table_columns:
# already reloaded, so copy into new column and drop old column
webnotes.conn.sql("""update `tab%s` set `%s`=`%s`""" % (t[0], t[2], t[1]))
- webnotes.conn.sql("""alter table `tab%s` drop column `%s`""" % (t[0], t[1]))
+ webnotes.conn.sql_ddl("""alter table `tab%s` drop column `%s`""" % (t[0], t[1]))
elif t[1] in table_columns:
webnotes.conn.sql_ddl("alter table `tab%s` change `%s` `%s` varchar(180)" % t)
diff --git a/patches/august_2013/p04_employee_birthdays.py b/patches/august_2013/p04_employee_birthdays.py
deleted file mode 100644
index 6e8481d..0000000
--- a/patches/august_2013/p04_employee_birthdays.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# 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():
- for employee in webnotes.conn.sql_list("""select name from `tabEmployee` where ifnull(date_of_birth, '')!=''"""):
- obj = webnotes.get_obj("Employee", employee)
- obj.update_dob_event()
-
\ No newline at end of file
diff --git a/patches/august_2013/p05_employee_birthdays.py b/patches/august_2013/p05_employee_birthdays.py
new file mode 100644
index 0000000..0dc15cb
--- /dev/null
+++ b/patches/august_2013/p05_employee_birthdays.py
@@ -0,0 +1,14 @@
+# 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.reload_doc("core", "doctype", "event")
+ webnotes.conn.sql("""delete from `tabEvent` where repeat_on='Every Year' and ref_type='Employee'""")
+ for employee in webnotes.conn.sql_list("""select name from `tabEmployee` where status='Active' and
+ ifnull(date_of_birth, '')!=''"""):
+ obj = webnotes.get_obj("Employee", employee)
+ obj.update_dob_event()
+
\ No newline at end of file
diff --git a/patches/august_2013/p05_update_serial_no_status.py b/patches/august_2013/p05_update_serial_no_status.py
new file mode 100644
index 0000000..49ab411
--- /dev/null
+++ b/patches/august_2013/p05_update_serial_no_status.py
@@ -0,0 +1,5 @@
+import webnotes
+
+def execute():
+ webnotes.conn.sql("""update `tabSerial No` set status = 'Not Available' where status='Not In Store'""")
+ webnotes.conn.sql("""update `tabSerial No` set status = 'Available' where status='In Store'""")
\ No newline at end of file
diff --git a/patches/august_2013/p06_fix_sle_against_stock_entry.py b/patches/august_2013/p06_fix_sle_against_stock_entry.py
new file mode 100644
index 0000000..02588be
--- /dev/null
+++ b/patches/august_2013/p06_fix_sle_against_stock_entry.py
@@ -0,0 +1,117 @@
+import webnotes
+
+cancelled = []
+uncancelled = []
+
+def execute():
+ global cancelled, uncancelled
+ stock_entries = webnotes.conn.sql("""select * from `tabStock Entry`
+ where docstatus >= 1 and date(modified) >= "2013-08-16"
+ and ifnull(production_order, '') != '' and ifnull(bom_no, '') != ''
+ order by modified desc, name desc""", as_dict=True)
+
+ for entry in stock_entries:
+ if not webnotes.conn.sql("""select name from `tabStock Entry Detail`
+ where parent=%s""", entry.name):
+ res = webnotes.conn.sql("""select * from `tabStock Ledger Entry`
+ where voucher_type='Stock Entry' and voucher_no=%s
+ and is_cancelled='No'""", entry.name, as_dict=True)
+ if res:
+ make_stock_entry_detail(entry, res)
+
+ if cancelled or uncancelled:
+ send_email()
+
+def make_stock_entry_detail(entry, res):
+ global cancelled, uncancelled
+
+ fg_item = webnotes.conn.get_value("Production Order", entry.production_order,
+ "production_item")
+ voucher_detail_entries_map = {}
+ for sle in res:
+ voucher_detail_entries_map.setdefault(sle.voucher_detail_no, []).append(sle)
+
+ for i, voucher_detail_no in enumerate(sorted(voucher_detail_entries_map.keys())):
+ sl_entries = voucher_detail_entries_map[voucher_detail_no]
+ # create stock entry details back from stock ledger entries
+ stock_entry_detail = webnotes.doc({
+ "doctype": "Stock Entry Detail",
+ "parentfield": "mtn_details",
+ "parenttype": "Stock Entry",
+ "parent": entry.name,
+ "__islocal": 1,
+ "idx": i+1,
+ "docstatus": 1,
+ "owner": entry.owner,
+ "name": voucher_detail_no,
+ "transfer_qty": abs(sl_entries[0].actual_qty),
+ "qty": abs(sl_entries[0].actual_qty),
+ "stock_uom": sl_entries[0].stock_uom,
+ "uom": sl_entries[0].stock_uom,
+ "conversion_factor": 1,
+ "item_code": sl_entries[0].item_code,
+ "description": webnotes.conn.get_value("Item", sl_entries[0].item_code,
+ "description"),
+ "incoming_rate": sl_entries[0].incoming_rate,
+ "batch_no": sl_entries[0].batch_no,
+ "serial_no": sl_entries[0].serial_no
+ })
+
+ if sl_entries[0].item_code == fg_item:
+ stock_entry_detail.bom_no = entry.bom_no
+
+ for sle in sl_entries:
+ if sle.actual_qty < 0:
+ stock_entry_detail.s_warehouse = sle.warehouse
+ else:
+ stock_entry_detail.t_warehouse = sle.warehouse
+
+ stock_entry_detail.save()
+
+ if entry.docstatus == 2:
+ webnotes.conn.set_value("Stock Entry", entry.name, "docstatus", 1)
+
+ # call for cancelled ones
+ se = webnotes.bean("Stock Entry", entry.name)
+ controller = se.make_controller()
+ controller.update_production_order(1)
+
+ res = webnotes.conn.sql("""select name from `tabStock Entry`
+ where amended_from=%s""", entry.name)
+ if res:
+ cancelled.append(res[0][0])
+ if res[0][0] in uncancelled:
+ uncancelled.remove(res[0][0])
+
+ webnotes.bean("Stock Entry", res[0][0]).cancel()
+
+ uncancelled.append(se.doc.name)
+
+def send_email():
+ from webnotes.utils.email_lib import sendmail_to_system_managers
+ global cancelled, uncancelled
+ uncancelled = "we have undone the cancellation of the following Stock Entries through a patch:\n" + \
+ "\n".join(uncancelled) if uncancelled else ""
+ cancelled = "and cancelled the following Stock Entries:\n" + "\n".join(cancelled) \
+ if cancelled else ""
+
+ subject = "[ERPNext] [Important] Cancellation undone for some Stock Entries"
+ content = """Dear System Manager,
+
+An error got introduced into the code that cleared the item table in a Stock Entry associated to a Production Order.
+
+To undo its effect,
+%s
+
+%s
+
+You will have to edit them again.
+
+Sorry for the inconvenience this has caused.
+
+Regards,
+Team ERPNext.""" % (uncancelled, cancelled)
+
+ # print subject, content
+
+ sendmail_to_system_managers(subject, content)
\ No newline at end of file
diff --git a/patches/february_2013/gle_floating_point_issue_revisited.py b/patches/february_2013/gle_floating_point_issue_revisited.py
index a979dad..6d026eb 100644
--- a/patches/february_2013/gle_floating_point_issue_revisited.py
+++ b/patches/february_2013/gle_floating_point_issue_revisited.py
@@ -13,7 +13,7 @@
where account = %s and voucher_type = 'Sales Invoice' and voucher_no = %s
and ifnull(is_cancelled, 'No') = 'No' limit 1""", (r.debit_to, r.name), as_dict=1)
if gle:
- diff = round((flt(r.grand_total) - flt(gle[0]['debit'])), 2)
+ diff = flt((flt(r.grand_total) - flt(gle[0]['debit'])), 2)
if abs(diff) == 0.01:
# print r.name, r.grand_total, gle[0]['debit'], diff
diff --git a/patches/july_2012/packing_list_cleanup_and_serial_no.py b/patches/july_2012/packing_list_cleanup_and_serial_no.py
index ad3863e..b91ef81 100644
--- a/patches/july_2012/packing_list_cleanup_and_serial_no.py
+++ b/patches/july_2012/packing_list_cleanup_and_serial_no.py
@@ -39,7 +39,7 @@
status = 'Not in Use'
if sle and flt(sle[0]['actual_qty']) > 0:
- status = 'In Store'
+ status = 'Available'
elif sle and flt(sle[0]['actual_qty']) < 0:
status = 'Delivered'
diff --git a/patches/may_2013/p06_make_notes.py b/patches/may_2013/p06_make_notes.py
index b60642c..29bfe25 100644
--- a/patches/may_2013/p06_make_notes.py
+++ b/patches/may_2013/p06_make_notes.py
@@ -13,17 +13,27 @@
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": "<hr>".join([markdown2.markdown(c) for c in webnotes.conn.sql_list("""
- select answer from tabAnswer where question=%s""", question.name)]),
+ "content": "<hr>".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/patches/patch_list.py b/patches/patch_list.py
index 9e6938b..1d382a5 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -22,7 +22,6 @@
"patches.april_2012.update_role_in_address",
"patches.april_2012.update_permlevel_in_address",
"patches.april_2012.update_appraisal_permission",
- "patches.april_2012.serial_no_fixes",
"patches.april_2012.repost_stock_for_posting_time",
"patches.may_2012.cleanup_property_setter",
"patches.may_2012.rename_prev_doctype",
@@ -229,7 +228,6 @@
"execute:webnotes.delete_doc('Report', 'Received Items To Be Billed')",
"patches.july_2013.p02_copy_shipping_address",
"patches.july_2013.p03_cost_center_company",
- "execute:webnotes.bean('Style Settings').save() #2013-07-16",
"patches.july_2013.p04_merge_duplicate_leads",
"patches.july_2013.p05_custom_doctypes_in_list_view",
"patches.july_2013.p06_same_sales_rate",
@@ -254,5 +252,9 @@
"patches.august_2013.p01_hr_settings",
"patches.august_2013.p02_rename_price_list",
"patches.august_2013.p03_pos_setting_replace_customer_account",
- "patches.august_2013.p04_employee_birthdays",
+ "patches.august_2013.p05_update_serial_no_status",
+ "patches.august_2013.p05_employee_birthdays",
+ "execute:webnotes.reload_doc('accounts', 'Print Format', 'POS Invoice') # 2013-08-16",
+ "patches.august_2013.p06_fix_sle_against_stock_entry",
+ "execute:webnotes.bean('Style Settings').save() #2013-08-20",
]
\ No newline at end of file
diff --git a/public/js/transaction.js b/public/js/transaction.js
index 118594c..1d06a9f 100644
--- a/public/js/transaction.js
+++ b/public/js/transaction.js
@@ -6,9 +6,9 @@
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 @@
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 @@
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/public/js/utils.js b/public/js/utils.js
index aee55ba..61e613b 100644
--- a/public/js/utils.js
+++ b/public/js/utils.js
@@ -36,4 +36,47 @@
territory.territory = wn.defaults.get_default("territory");
}
},
+
+ setup_serial_no: function(grid_row) {
+ if(!grid_row.fields_dict.serial_no ||
+ grid_row.fields_dict.serial_no.get_status()!=="Write") return;
+
+ var $btn = $('<button class="btn btn-sm btn-default">Add Serial No</button>')
+ .appendTo($("<div>")
+ .css({"margin-bottom": "10px", "margin-top": "-10px"})
+ .appendTo(grid_row.fields_dict.serial_no.$wrapper));
+
+ $btn.on("click", function() {
+ var d = new wn.ui.Dialog({
+ title: "Add Serial No",
+ fields: [
+ {
+ "fieldtype": "Link",
+ "options": "Serial No",
+ "label": "Serial No",
+ "get_query": {
+ item_code: grid_row.doc.item_code,
+ warehouse: grid_row.doc.warehouse
+ }
+ },
+ {
+ "fieldtype": "Button",
+ "label": "Add"
+ }
+ ]
+ });
+
+ d.get_input("add").on("click", function() {
+ var serial_no = d.get_value("serial_no");
+ if(serial_no) {
+ var val = (grid_row.doc.serial_no || "").split("\n").concat([serial_no]).join("\n");
+ grid_row.fields_dict.serial_no.set_model_value(val.trim());
+ }
+ d.hide();
+ return false;
+ });
+
+ d.show();
+ });
+ }
});
\ No newline at end of file
diff --git a/selling/doctype/customer/customer.py b/selling/doctype/customer/customer.py
index f516ef2..2eabf12 100644
--- a/selling/doctype/customer/customer.py
+++ b/selling/doctype/customer/customer.py
@@ -77,9 +77,9 @@
msgprint("Please Select Company under which you want to create account head")
def update_credit_days_limit(self):
- sql("""update tabAccount set credit_days = %s, credit_limit = %s
- where name = %s""", (self.doc.credit_days or 0, self.doc.credit_limit or 0,
- self.doc.name + " - " + self.get_company_abbr()))
+ webnotes.conn.sql("""update tabAccount set credit_days = %s, credit_limit = %s
+ where master_type='Customer' and master_name = %s""",
+ (self.doc.credit_days or 0, self.doc.credit_limit or 0, self.doc.name))
def create_lead_address_contact(self):
if self.doc.lead_name:
diff --git a/selling/doctype/installation_note/installation_note.py b/selling/doctype/installation_note/installation_note.py
index 93a1bc1..ca47043 100644
--- a/selling/doctype/installation_note/installation_note.py
+++ b/selling/doctype/installation_note/installation_note.py
@@ -105,7 +105,6 @@
msgprint("Please fetch items from Delivery Note selected", raise_exception=1)
def on_update(self):
- get_obj("Stock Ledger").scrub_serial_nos(self, 'installed_item_details')
webnotes.conn.set(self.doc, 'status', 'Draft')
def on_submit(self):
diff --git a/selling/doctype/lead/get_leads.py b/selling/doctype/lead/get_leads.py
index c376f45..c63468e 100644
--- a/selling/doctype/lead/get_leads.py
+++ b/selling/doctype/lead/get_leads.py
@@ -9,17 +9,8 @@
def add_sales_communication(subject, content, sender, real_name, mail=None,
status="Open", date=None):
- def set_status(doctype, name):
- w = webnotes.bean(doctype, name)
- w.ignore_permissions = True
- w.doc.status = is_system_user and "Replied" or status
- w.doc.save()
- if mail:
- mail.save_attachments_in_doc(w.doc)
-
lead_name = webnotes.conn.get_value("Lead", {"email_id": sender})
contact_name = webnotes.conn.get_value("Contact", {"email_id": sender})
- is_system_user = webnotes.conn.get_value("Profile", sender)
if not (lead_name or contact_name):
# none, create a new Lead
@@ -34,14 +25,13 @@
lead.insert()
lead_name = lead.doc.name
- make(content=content, sender=sender, subject=subject,
+ message = make(content=content, sender=sender, subject=subject,
lead=lead_name, contact=contact_name, date=date)
- if contact_name:
- set_status("Contact", contact_name)
- elif lead_name:
- set_status("Lead", lead_name)
-
+ if mail:
+ # save attachments to parent if from mail
+ bean = webnotes.bean("Contact" if contact_name else "Lead", contact_name or lead_name)
+ mail.save_attachments_in_doc(bean.doc)
class SalesMailbox(POP3Mailbox):
def setup(self, args=None):
diff --git a/selling/doctype/lead/lead.py b/selling/doctype/lead/lead.py
index b2016aa..2bf1aa3 100644
--- a/selling/doctype/lead/lead.py
+++ b/selling/doctype/lead/lead.py
@@ -39,17 +39,14 @@
def validate(self):
if self.doc.status == 'Lead Lost' and not self.doc.order_lost_reason:
- msgprint("Please Enter Lost Reason under More Info section")
- raise Exception
+ webnotes.throw("Please Enter Lost Reason under More Info section")
if self.doc.source == 'Campaign' and not self.doc.campaign_name and session['user'] != 'Guest':
- msgprint("Please specify campaign name")
- raise Exception
+ webnotes.throw("Please specify campaign name")
if self.doc.email_id:
if not validate_email_add(self.doc.email_id):
- msgprint('Please enter valid email id.')
- raise Exception
+ webnotes.throw('Please enter valid email id.')
def on_update(self):
self.check_email_id_is_unique()
diff --git a/selling/doctype/sales_bom/sales_bom.py b/selling/doctype/sales_bom/sales_bom.py
index 8bcac17..15d8fd1 100644
--- a/selling/doctype/sales_bom/sales_bom.py
+++ b/selling/doctype/sales_bom/sales_bom.py
@@ -76,8 +76,8 @@
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_common/sales_common.js b/selling/doctype/sales_common/sales_common.js
index fe90b5b..5c0c96a 100644
--- a/selling/doctype/sales_common/sales_common.js
+++ b/selling/doctype/sales_common/sales_common.js
@@ -95,7 +95,7 @@
return{
query : "selling.doctype.sales_common.sales_common.get_batch_no",
filters: {
- 'item': item.item_code,
+ 'item_code': item.item_code,
'posting_date': me.frm.doc.posting_date
}
}
@@ -640,7 +640,7 @@
$(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true);
if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
- help_msg = "<div class='alert'> \
+ help_msg = "<div class='alert alert-warning'> \
For 'Sales BOM' items, warehouse, serial no and batch no \
will be considered from the 'Packing List' table. \
If warehouse and batch no are same for all packing items for any 'Sales BOM' item, \
diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py
index 3c43c1e..baa8850 100644
--- a/selling/doctype/sales_common/sales_common.py
+++ b/selling/doctype/sales_common/sales_common.py
@@ -340,7 +340,7 @@
and batch_no like '%(txt)s'
and exists(select * from `tabBatch`
where name = sle.batch_no
- and expiry_date >= '%(posting_date)s'
+ and (ifnull(expiry_date, '')='' or expiry_date >= '%(posting_date)s')
and docstatus != 2)
%(mcond)s
group by batch_no having sum(actual_qty) > 0
@@ -353,11 +353,11 @@
return webnotes.conn.sql("""select name from tabBatch
where docstatus != 2
and item = '%(item_code)s'
- and expiry_date >= '%(posting_date)s'
+ and (ifnull(expiry_date, '')='' or expiry_date >= '%(posting_date)s')
and name like '%(txt)s'
%(mcond)s
order by name desc
limit %(start)s, %(page_len)s""" % {'item_code': filters['item_code'],
'posting_date': filters['posting_date'], 'txt': "%%%s%%" % txt,
'mcond':get_match_cond(doctype, searchfield),'start': start,
- 'page_len': page_len})
\ No newline at end of file
+ 'page_len': page_len})
diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py
index a9bb7a2..8e3ed70 100644
--- a/selling/doctype/sales_order/sales_order.py
+++ b/selling/doctype/sales_order/sales_order.py
@@ -126,8 +126,9 @@
self.validate_mandatory()
self.validate_proj_cust()
self.validate_po()
- self.validate_uom_is_integer("stock_uom", "qty")
+ self.validate_uom_is_integer("stock_uom", "qty")
self.validate_for_items()
+ self.validate_warehouse_user()
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_active_sales_items(self)
sales_com_obj.check_conversion_rate(self)
@@ -147,6 +148,16 @@
if not self.doc.billing_status: self.doc.billing_status = 'Not Billed'
if not self.doc.delivery_status: self.doc.delivery_status = 'Not Delivered'
+
+ def validate_warehouse_user(self):
+ from stock.utils import validate_warehouse_user
+
+ warehouses = list(set([d.reserved_warehouse for d in
+ self.doclist.get({"doctype": self.tname}) if d.reserved_warehouse]))
+
+ for w in warehouses:
+ validate_warehouse_user(w)
+
def validate_with_previous_doc(self):
super(DocType, self).validate_with_previous_doc(self.tname, {
"Quotation": {
diff --git a/selling/doctype/sales_order/test_sales_order.py b/selling/doctype/sales_order/test_sales_order.py
index 9fd16e8..7b72271 100644
--- a/selling/doctype/sales_order/test_sales_order.py
+++ b/selling/doctype/sales_order/test_sales_order.py
@@ -272,6 +272,29 @@
self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
so.doclist[1].reserved_warehouse, 20.0)
+ def test_warehouse_user(self):
+ webnotes.session.user = "test@example.com"
+
+ webnotes.bean("Profile", "test@example.com").get_controller()\
+ .add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
+
+ webnotes.bean("Profile", "test2@example.com").get_controller()\
+ .add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
+
+
+ from stock.utils import UserNotAllowedForWarehouse
+ so = webnotes.bean(copy = test_records[0])
+ so.doc.company = "_Test Company 1"
+ so.doc.conversion_rate = 0.02
+ so.doc.plc_conversion_rate = 0.02
+ so.doclist[1].reserved_warehouse = "_Test Warehouse 2 - _TC1"
+ self.assertRaises(UserNotAllowedForWarehouse, so.insert)
+
+ webnotes.session.user = "test2@example.com"
+ so.insert()
+
+ webnotes.session.user = "Administrator"
+
test_dependencies = ["Sales BOM"]
test_records = [
diff --git a/selling/doctype/sales_order_item/sales_order_item.txt b/selling/doctype/sales_order_item/sales_order_item.txt
index eb4dec8..47b4d6b 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-07 14:44:50",
+ "modified": "2013-08-22 15:43:07",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -230,7 +230,7 @@
"fieldtype": "Link",
"in_list_view": 0,
"label": "Reserved Warehouse",
- "no_copy": 1,
+ "no_copy": 0,
"oldfieldname": "reserved_warehouse",
"oldfieldtype": "Link",
"options": "Warehouse",
diff --git a/selling/utils.py b/selling/utils.py
index 7ccad6a..53ebbee 100644
--- a/selling/utils.py
+++ b/selling/utils.py
@@ -69,10 +69,24 @@
if cint(args.is_pos):
pos_settings = get_pos_settings(args.company)
- out.update(apply_pos_settings(pos_settings, out))
-
+ if pos_settings:
+ out.update(apply_pos_settings(pos_settings, out))
+
+ if args.doctype in ("Sales Invoice", "Delivery Note"):
+ if item_bean.doc.has_serial_no and not args.serial_no:
+ out.serial_no = _get_serial_nos_by_fifo(args, item_bean)
+
return out
-
+
+def _get_serial_nos_by_fifo(args, item_bean):
+ return "\n".join(webnotes.conn.sql_list("""select name from `tabSerial No`
+ where item_code=%(item_code)s and warehouse=%(warehouse)s and status='Available'
+ order by timestamp(purchase_date, purchase_time) asc limit %(qty)s""", {
+ "item_code": args.item_code,
+ "warehouse": args.warehouse,
+ "qty": cint(args.qty)
+ }))
+
def _get_item_code(barcode):
item_code = webnotes.conn.sql_list("""select name from `tabItem` where barcode=%s""", barcode)
diff --git a/setup/doctype/features_setup/features_setup.txt b/setup/doctype/features_setup/features_setup.txt
index e1a4c08..89c9dd3 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"
},
@@ -185,6 +185,13 @@
"label": "Point of Sale"
},
{
+ "description": "To enable <b>Point of Sale</b> view",
+ "doctype": "DocField",
+ "fieldname": "fs_pos_view",
+ "fieldtype": "Check",
+ "label": "POS View"
+ },
+ {
"doctype": "DocField",
"fieldname": "production",
"fieldtype": "Section Break",
diff --git a/setup/page/setup/setup.js b/setup/page/setup/setup.js
index c9532e2..f396186 100644
--- a/setup/page/setup/setup.js
+++ b/setup/page/setup/setup.js
@@ -41,15 +41,15 @@
})
.appendTo(body);
- $('<div class="col col-lg-1"></div>').appendTo(row);
+ $('<div class="col-md-1"></div>').appendTo(row);
if(item.type==="Link") {
- var col = $('<div class="col col-lg-5"><b><a href="#'
+ var col = $('<div class="col-md-5"><b><a href="#'
+item.route+'"><i class="'+item.icon+'"></i> '
+item.title+'</a></b></div>').appendTo(row);
} else {
- var col = $(repl('<div class="col col-lg-5">\
+ var col = $(repl('<div class="col-md-5">\
<span class="badge view-link">%(count)s</span>\
<b><i class="%(icon)s"></i>\
<a class="data-link">%(title)s</a></b>\
@@ -73,9 +73,9 @@
}
if(dependency)
- col.addClass("col-offset-1");
+ col.addClass("col-md-offset-1");
else
- $('<div class="col col-lg-1"></div>').appendTo(row);
+ $('<div class="col-md-1"></div>').appendTo(row);
if(item.doctype) {
var badge = col.find(".badge, .data-link")
@@ -94,7 +94,7 @@
}
// tree
- $links = $('<div class="col col-lg-5">').appendTo(row);
+ $links = $('<div class="col-md-5">').appendTo(row);
if(item.tree) {
$('<a class="view-link"><i class="icon-sitemap"></i> Browse</a>\
diff --git a/startup/__init__.py b/startup/__init__.py
index 5bc86df..9246b5e 100644
--- a/startup/__init__.py
+++ b/startup/__init__.py
@@ -32,12 +32,13 @@
"nederlands": "nl",
"српски":"sr",
"தமிழ்": "ta",
- "Hrvatski": "hr",
+ "hrvatski": "hr",
+ "italiano": "it",
"ไทย": "th",
"العربية":"ar"
}
-lang_list = ["ar", "de", "en", "hi", "es", "fr", "pt-BR", "pt", "nl", "hr", "th"]
+lang_list = ["ar", "de", "en", "es", "fr", "hi", "hr", "it", "nl", "pt-BR", "pt", "th", "sr", "ta"]
product_name = "ERPNext"
profile_defaults = {
diff --git a/startup/install.py b/startup/install.py
index 7e9273c..ea281b8 100644
--- a/startup/install.py
+++ b/startup/install.py
@@ -72,7 +72,7 @@
'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/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js
index 6d601c2..063b258 100644
--- a/stock/doctype/delivery_note/delivery_note.js
+++ b/stock/doctype/delivery_note/delivery_note.js
@@ -74,6 +74,10 @@
tc_name: function() {
this.get_terms();
},
+
+ delivery_note_details_on_form_rendered: function(doc, grid_row) {
+ erpnext.setup_serial_no(grid_row)
+ }
});
diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py
index 679d743..7c839b6 100644
--- a/stock/doctype/delivery_note/delivery_note.py
+++ b/stock/doctype/delivery_note/delivery_note.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cstr, flt, cint
+from webnotes.utils import cstr, flt, cint, add_days
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
from webnotes import msgprint, _
@@ -175,9 +175,6 @@
def on_update(self):
self.doclist = get_obj('Sales Common').make_packing_list(self,'delivery_note_details')
- sl = get_obj('Stock Ledger')
- sl.scrub_serial_nos(self)
- sl.scrub_serial_nos(self, 'packing_details')
def on_submit(self):
self.validate_packed_qty()
@@ -185,22 +182,12 @@
# Check for Approving Authority
get_obj('Authorization Control').validate_approving_authority(self.doc.doctype, self.doc.company, self.doc.grand_total, self)
- # validate serial no for item table (non-sales-bom item) and packing list (sales-bom item)
- sl_obj = get_obj("Stock Ledger")
- sl_obj.validate_serial_no(self, 'delivery_note_details')
- sl_obj.validate_serial_no_warehouse(self, 'delivery_note_details')
- sl_obj.validate_serial_no(self, 'packing_details')
- sl_obj.validate_serial_no_warehouse(self, 'packing_details')
-
- # update delivery details in serial no
- sl_obj.update_serial_record(self, 'delivery_note_details', is_submit = 1, is_incoming = 0)
- sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
-
# update delivered qty in sales order
self.update_prevdoc_status()
# create stock ledger entry
self.update_stock_ledger(update_stock = 1)
+ self.update_serial_nos()
self.credit_limit()
@@ -211,6 +198,50 @@
webnotes.conn.set(self.doc, 'status', 'Submitted')
+ def on_cancel(self):
+ sales_com_obj = get_obj(dt = 'Sales Common')
+ sales_com_obj.check_stop_sales_order(self)
+ self.check_next_docstatus()
+
+ self.update_prevdoc_status()
+
+ self.update_stock_ledger(update_stock = -1)
+ self.update_serial_nos(cancel=True)
+
+ webnotes.conn.set(self.doc, 'status', 'Cancelled')
+ self.cancel_packing_slips()
+
+ self.make_cancel_gl_entries()
+
+ def update_serial_nos(self, cancel=False):
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
+ update_serial_nos_after_submit(self, "Delivery Note", "delivery_note_details")
+ update_serial_nos_after_submit(self, "Delivery Note", "packing_details")
+
+ for table_fieldname in ("delivery_note_details", "packing_details"):
+ for d in self.doclist.get({"parentfield": table_fieldname}):
+ for serial_no in get_serial_nos(d.serial_no):
+ sr = webnotes.bean("Serial No", serial_no)
+ if cancel:
+ sr.doc.status = "Available"
+ for fieldname in ("warranty_expiry_date", "delivery_document_type",
+ "delivery_document_no", "delivery_date", "delivery_time", "customer",
+ "customer_name"):
+ sr.doc.fields[fieldname] = None
+ else:
+ sr.doc.delivery_document_type = "Delivery Note"
+ sr.doc.delivery_document_no = self.doc.name
+ sr.doc.delivery_date = self.doc.posting_date
+ sr.doc.delivery_time = self.doc.posting_time
+ sr.doc.customer = self.doc.customer
+ sr.doc.customer_name = self.doc.customer_name
+ if sr.doc.warranty_period:
+ sr.doc.warranty_expiry_date = add_days(cstr(self.doc.posting_date),
+ cint(sr.doc.warranty_period))
+ sr.doc.status = 'Delivered'
+
+ sr.save()
+
def validate_packed_qty(self):
"""
Validate that if packed qty exists, it should be equal to qty
@@ -232,26 +263,6 @@
+ ", Packed: " + cstr(d[2])) for d in packing_error_list])
webnotes.msgprint("Packing Error:\n" + err_msg, raise_exception=1)
-
- def on_cancel(self):
- sales_com_obj = get_obj(dt = 'Sales Common')
- sales_com_obj.check_stop_sales_order(self)
- self.check_next_docstatus()
-
- # remove delivery details from serial no
- sl = get_obj('Stock Ledger')
- sl.update_serial_record(self, 'delivery_note_details', is_submit = 0, is_incoming = 0)
- sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
-
- self.update_prevdoc_status()
-
- self.update_stock_ledger(update_stock = -1)
- webnotes.conn.set(self.doc, 'status', 'Cancelled')
- self.cancel_packing_slips()
-
- self.make_cancel_gl_entries()
-
-
def check_next_docstatus(self):
submit_rv = sql("select t1.name from `tabSales Invoice` t1,`tabSales Invoice Item` t2 where t1.name = t2.parent and t2.delivery_note = '%s' and t1.docstatus = 1" % (self.doc.name))
if submit_rv:
@@ -263,7 +274,6 @@
msgprint("Installation Note : "+cstr(submit_in[0][0]) +" has already been submitted !")
raise Exception , "Validation Error."
-
def cancel_packing_slips(self):
"""
Cancel submitted packing slips related to this delivery note
diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py
index 2e3ab07..89690fe 100644
--- a/stock/doctype/delivery_note/test_delivery_note.py
+++ b/stock/doctype/delivery_note/test_delivery_note.py
@@ -33,7 +33,7 @@
self.assertEquals(len(si), len(dn.doclist))
# modify export_amount
- si[1].ref_rate = 200
+ si[1].export_rate = 200
self.assertRaises(webnotes.ValidationError, webnotes.bean(si).insert)
@@ -96,6 +96,59 @@
self.assertEquals(bal, prev_bal - 375.0)
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
+
+ def test_serialized(self):
+ from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
+
+ se = make_serialized_item()
+ serial_nos = get_serial_nos(se.doclist[1].serial_no)
+
+ dn = webnotes.bean(copy=test_records[0])
+ dn.doclist[1].item_code = "_Test Serialized Item With Series"
+ dn.doclist[1].qty = 1
+ dn.doclist[1].serial_no = serial_nos[0]
+ dn.insert()
+ dn.submit()
+
+ self.assertEquals(webnotes.conn.get_value("Serial No", serial_nos[0], "status"), "Delivered")
+ self.assertFalse(webnotes.conn.get_value("Serial No", serial_nos[0], "warehouse"))
+ self.assertEquals(webnotes.conn.get_value("Serial No", serial_nos[0],
+ "delivery_document_no"), dn.doc.name)
+
+ return dn
+
+ def test_serialized_cancel(self):
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
+ dn = self.test_serialized()
+ dn.cancel()
+
+ serial_nos = get_serial_nos(dn.doclist[1].serial_no)
+
+ self.assertEquals(webnotes.conn.get_value("Serial No", serial_nos[0], "status"), "Available")
+ self.assertEquals(webnotes.conn.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
+ self.assertFalse(webnotes.conn.get_value("Serial No", serial_nos[0],
+ "delivery_document_no"))
+
+ def test_serialize_status(self):
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import SerialNoStatusError, get_serial_nos
+ from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+
+ se = make_serialized_item()
+ serial_nos = get_serial_nos(se.doclist[1].serial_no)
+
+ sr = webnotes.bean("Serial No", serial_nos[0])
+ sr.doc.status = "Not Available"
+ sr.save()
+
+ dn = webnotes.bean(copy=test_records[0])
+ dn.doclist[1].item_code = "_Test Serialized Item With Series"
+ dn.doclist[1].qty = 1
+ dn.doclist[1].serial_no = serial_nos[0]
+ dn.insert()
+
+ self.assertRaises(SerialNoStatusError, dn.submit)
+
test_records = [
[
diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py
index 9ae1bfd..e6c277e 100644
--- a/stock/doctype/item/item.py
+++ b/stock/doctype/item/item.py
@@ -272,4 +272,4 @@
from stock.stock_ledger import update_entries_after
for wh in webnotes.conn.sql("""select warehouse from `tabBin`
where item_code=%s""", newdn):
- update_entries_after({"item_code": newdn, "warehouse": wh})
+ update_entries_after({"item_code": newdn, "warehouse": wh[0]})
diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt
index 7ceeb4b..eb05503 100644
--- a/stock/doctype/item/item.txt
+++ b/stock/doctype/item/item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-03 10:45:46",
"docstatus": 0,
- "modified": "2013-08-08 14:22:25",
+ "modified": "2013-08-30 16:21:38",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -35,7 +35,9 @@
"parentfield": "permissions",
"parenttype": "DocType",
"permlevel": 0,
- "read": 1
+ "read": 1,
+ "report": 1,
+ "submit": 0
},
{
"doctype": "DocType",
@@ -301,6 +303,14 @@
"reqd": 1
},
{
+ "depends_on": "eval: doc.has_serial_no===\"Yes\"",
+ "description": "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.",
+ "doctype": "DocField",
+ "fieldname": "serial_no_series",
+ "fieldtype": "Data",
+ "label": "Serial Number Series"
+ },
+ {
"depends_on": "eval:doc.is_stock_item==\"Yes\"",
"doctype": "DocField",
"fieldname": "warranty_period",
@@ -550,20 +560,6 @@
"reqd": 1
},
{
- "default": "No",
- "depends_on": "eval:doc.is_sales_item==\"Yes\"",
- "description": "Select \"Yes\" if this item is to be sent to a customer or received from a supplier as a sample. Delivery notes and Purchase Receipts will update stock levels but there will be no invoice against this item.",
- "doctype": "DocField",
- "fieldname": "is_sample_item",
- "fieldtype": "Select",
- "label": "Allow Samples",
- "oldfieldname": "is_sample_item",
- "oldfieldtype": "Select",
- "options": "Yes\nNo",
- "read_only": 0,
- "reqd": 1
- },
- {
"depends_on": "eval:doc.is_sales_item==\"Yes\"",
"doctype": "DocField",
"fieldname": "max_discount",
@@ -870,9 +866,7 @@
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
- "report": 1,
"role": "Material Master Manager",
- "submit": 0,
"write": 1
},
{
@@ -880,9 +874,7 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "report": 1,
"role": "Material Manager",
- "submit": 0,
"write": 0
},
{
@@ -890,21 +882,7 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "report": 1,
"role": "Material User",
- "submit": 0,
"write": 0
- },
- {
- "doctype": "DocPerm",
- "role": "Sales User"
- },
- {
- "doctype": "DocPerm",
- "role": "Purchase User"
- },
- {
- "doctype": "DocPerm",
- "role": "Accounts User"
}
]
\ No newline at end of file
diff --git a/stock/doctype/item/test_item.py b/stock/doctype/item/test_item.py
index a1d7852..7be6ea5 100644
--- a/stock/doctype/item/test_item.py
+++ b/stock/doctype/item/test_item.py
@@ -44,7 +44,6 @@
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
- "is_sample_item": "No",
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
@@ -82,7 +81,6 @@
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
- "is_sample_item": "No",
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
@@ -108,7 +106,6 @@
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
- "is_sample_item": "No",
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
@@ -128,7 +125,6 @@
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
- "is_sample_item": "No",
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
@@ -149,7 +145,6 @@
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
- "is_sample_item": "No",
"inspection_required": "No",
"is_pro_applicable": "Yes",
"is_sub_contracted_item": "Yes",
@@ -168,7 +163,6 @@
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
- "is_sample_item": "No",
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
@@ -188,7 +182,26 @@
"is_purchase_item": "Yes",
"is_sales_item": "Yes",
"is_service_item": "No",
- "is_sample_item": "No",
+ "inspection_required": "No",
+ "is_pro_applicable": "No",
+ "is_sub_contracted_item": "No",
+ "stock_uom": "_Test UOM"
+ }],
+ [{
+ "doctype": "Item",
+ "item_code": "_Test Serialized Item With Series",
+ "item_name": "_Test Serialized Item With Series",
+ "description": "_Test Serialized Item",
+ "item_group": "_Test Item Group Desktops",
+ "is_stock_item": "Yes",
+ "default_warehouse": "_Test Warehouse - _TC",
+ "is_asset_item": "No",
+ "has_batch_no": "No",
+ "has_serial_no": "Yes",
+ "serial_no_series": "ABCD.#####",
+ "is_purchase_item": "Yes",
+ "is_sales_item": "Yes",
+ "is_service_item": "No",
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
diff --git a/stock/doctype/material_request/material_request.py b/stock/doctype/material_request/material_request.py
index 4952834..9cb0bc5 100644
--- a/stock/doctype/material_request/material_request.py
+++ b/stock/doctype/material_request/material_request.py
@@ -51,10 +51,9 @@
msgprint("You can raise indent of maximum qty: %s for item: %s against sales order: %s\n Anyway, you can add more qty in new row for the same item." % (actual_so_qty - already_indented, item, so_no), raise_exception=1)
def validate_schedule_date(self):
- #:::::::: validate schedule date v/s indent date ::::::::::::
for d in getlist(self.doclist, 'indent_details'):
if d.schedule_date < self.doc.transaction_date:
- msgprint("Expected Schedule Date cannot be before Material Request Date")
+ msgprint("Expected Date cannot be before Material Request Date")
raise Exception
# Validate
diff --git a/stock/doctype/packing_slip/packing_slip.js b/stock/doctype/packing_slip/packing_slip.js
index f3634f6..eba20c5 100644
--- a/stock/doctype/packing_slip/packing_slip.js
+++ b/stock/doctype/packing_slip/packing_slip.js
@@ -103,7 +103,7 @@
net_weight_pkg += flt(item.net_weight) * flt(item.qty);
}
- doc.net_weight_pkg = roundNumber(net_weight_pkg, 2);
+ doc.net_weight_pkg = _round(net_weight_pkg, 2);
if(!flt(doc.gross_weight_pkg)) {
doc.gross_weight_pkg = doc.net_weight_pkg
}
diff --git a/stock/doctype/purchase_receipt/purchase_receipt.js b/stock/doctype/purchase_receipt/purchase_receipt.js
index bdf50ee..8775140 100644
--- a/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -23,24 +23,23 @@
this.show_stock_ledger();
this.show_general_ledger();
+ } else {
+ cur_frm.add_custom_button(wn._('From Purchase Order'),
+ function() {
+ wn.model.map_current_doc({
+ method: "buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
+ source_doctype: "Purchase Order",
+ get_query_filters: {
+ supplier: cur_frm.doc.supplier || undefined,
+ docstatus: 1,
+ status: ["!=", "Stopped"],
+ per_received: ["<", 99.99],
+ company: cur_frm.doc.company
+ }
+ })
+ });
}
- cur_frm.add_custom_button(wn._('From Purchase Order'),
- function() {
- wn.model.map_current_doc({
- method: "buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
- source_doctype: "Purchase Order",
- get_query_filters: {
- supplier: cur_frm.doc.supplier || undefined,
- docstatus: 1,
- status: ["!=", "Stopped"],
- per_received: ["<", 99.99],
- company: cur_frm.doc.company
- }
- })
- });
-
-
if(wn.boot.control_panel.country == 'India') {
unhide_field(['challan_no', 'challan_date']);
}
@@ -127,7 +126,7 @@
cur_frm.fields_dict['purchase_receipt_details'].grid.get_field('project_name').get_query = function(doc, cdt, cdn) {
return{
filters:[
- ['project', 'status', 'not in', 'Completed, Cancelled']
+ ['Project', 'status', 'not in', 'Completed, Cancelled']
]
}
}
diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py
index c78e759..f7cfcff 100644
--- a/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -127,8 +127,6 @@
self.validate_inspection()
self.validate_uom_is_integer("uom", ["qty", "received_qty"])
self.validate_uom_is_integer("stock_uom", "stock_qty")
-
- get_obj('Stock Ledger').validate_serial_no(self, 'purchase_receipt_details')
self.validate_challan_no()
pc_obj = get_obj(dt='Purchase Common')
@@ -147,16 +145,6 @@
for d in getlist(self.doclist,'purchase_receipt_details'):
d.rejected_warehouse = self.doc.rejected_warehouse
- get_obj('Stock Ledger').scrub_serial_nos(self)
- self.scrub_rejected_serial_nos()
-
-
- def scrub_rejected_serial_nos(self):
- for d in getlist(self.doclist, 'purchase_receipt_details'):
- if d.rejected_serial_no:
- d.rejected_serial_no = cstr(d.rejected_serial_no).strip().replace(',', '\n')
- d.save()
-
def update_stock(self, is_submit):
pc_obj = get_obj('Purchase Common')
self.values = []
@@ -207,11 +195,6 @@
# make Stock Entry
def make_sl_entry(self, d, wh, qty, in_value, is_submit, rejected = 0):
- if rejected:
- serial_no = cstr(d.rejected_serial_no).strip()
- else:
- serial_no = cstr(d.serial_no).strip()
-
self.values.append({
'item_code' : d.fields.has_key('item_code') and d.item_code or d.rm_item_code,
'warehouse' : wh,
@@ -227,7 +210,7 @@
'fiscal_year' : self.doc.fiscal_year,
'is_cancelled' : (is_submit==1) and 'No' or 'Yes',
'batch_no' : cstr(d.batch_no).strip(),
- 'serial_no' : serial_no,
+ 'serial_no' : d.serial_no,
"project" : d.project_name
})
@@ -257,20 +240,34 @@
get_obj('Authorization Control').validate_approving_authority(self.doc.doctype, self.doc.company, self.doc.grand_total)
# Set status as Submitted
- webnotes.conn.set(self.doc,'status', 'Submitted')
+ webnotes.conn.set(self.doc, 'status', 'Submitted')
self.update_prevdoc_status()
-
- # Update Serial Record
- get_obj('Stock Ledger').update_serial_record(self, 'purchase_receipt_details', is_submit = 1, is_incoming = 1)
-
+
# Update Stock
self.update_stock(is_submit = 1)
+ self.update_serial_nos()
+
# Update last purchase rate
purchase_controller.update_last_purchase_rate(self, 1)
self.make_gl_entries()
+
+ def update_serial_nos(self, cancel=False):
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
+ update_serial_nos_after_submit(self, "Purchase Receipt", "purchase_receipt_details")
+
+ for d in self.doclist.get({"parentfield": "purchase_receipt_details"}):
+ for serial_no in get_serial_nos(d.serial_no):
+ sr = webnotes.bean("Serial No", serial_no)
+ if cancel:
+ sr.doc.supplier = None
+ sr.doc.supplier_name = None
+ else:
+ sr.doc.supplier = self.doc.supplier
+ sr.doc.supplier_name = self.doc.supplier_name
+ sr.save()
def check_next_docstatus(self):
submit_rv = sql("select t1.name from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 where t1.name = t2.parent and t2.purchase_receipt = '%s' and t1.docstatus = 1" % (self.doc.name))
@@ -295,10 +292,10 @@
webnotes.conn.set(self.doc,'status','Cancelled')
# 3. Cancel Serial No
- get_obj('Stock Ledger').update_serial_record(self, 'purchase_receipt_details', is_submit = 0, is_incoming = 1)
# 4.Update Bin
self.update_stock(is_submit = 0)
+ self.update_serial_nos(cancel=True)
self.update_prevdoc_status()
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 bc0c9f6..0000000
--- 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("<a href=\"#Form/Purchase Order/%(name)s\">%(name)s</a>",
- {name: v}));
- });
- data.purchase_order_no = po_list.join(", ");
- }
- }
-};
diff --git a/stock/doctype/purchase_receipt/test_purchase_receipt.py b/stock/doctype/purchase_receipt/test_purchase_receipt.py
index c871b36..e303d79 100644
--- a/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -76,8 +76,31 @@
self.assertEquals(pr.doclist[1].rm_supp_cost, 70000.0)
self.assertEquals(len(pr.doclist.get({"parentfield": "pr_raw_material_details"})), 2)
+
+ def test_serial_no_supplier(self):
+ pr = webnotes.bean(copy=test_records[0])
+ pr.doclist[1].item_code = "_Test Serialized Item With Series"
+ pr.doclist[1].qty = 1
+ pr.doclist[1].received_qty = 1
+ pr.insert()
+ pr.submit()
+ self.assertEquals(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
+ "supplier"), pr.doc.supplier)
+
+ return pr
+
+ def test_serial_no_cancel(self):
+ pr = self.test_serial_no_supplier()
+ pr.cancel()
+ self.assertFalse(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
+ "warehouse"))
+ self.assertEqual(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
+ "status"), "Not Available")
+
+
+
test_dependencies = ["BOM"]
test_records = [
diff --git a/stock/doctype/serial_no/serial_no.js b/stock/doctype/serial_no/serial_no.js
index 8d2f210..83631cd 100644
--- a/stock/doctype/serial_no/serial_no.js
+++ b/stock/doctype/serial_no/serial_no.js
@@ -1,69 +1,10 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// License: GNU General Public License v3. See license.txt
-cur_frm.cscript.onload = function(doc, cdt, cdn) {
- if(!doc.status) set_multiple(cdt, cdn, {status:'In Store'});
- if(doc.__islocal) hide_field(['supplier_name','address_display'])
-}
+cur_frm.add_fetch("customer", "customer_name", "customer_name")
+cur_frm.add_fetch("supplier", "supplier_name", "supplier_name")
-
-cur_frm.cscript.refresh = function(doc, cdt, cdn) {
- flds = ['status', 'item_code', 'warehouse', 'purchase_document_type',
- 'purchase_document_no', 'purchase_date', 'purchase_time', 'purchase_rate',
- 'supplier']
- for(i=0;i<flds.length;i++) {
- cur_frm.set_df_property(flds[i], 'read_only', doc.__islocal ? 0 : 1);
- }
-}
-
-// item details
-// -------------
-cur_frm.add_fetch('item_code', 'item_name', 'item_name')
-cur_frm.add_fetch('item_code', 'item_group', 'item_group')
-cur_frm.add_fetch('item_code', 'brand', 'brand')
-cur_frm.add_fetch('item_code', 'description', 'description')
-cur_frm.add_fetch('item_code', 'warranty_period', 'warranty_period')
-
-// customer
-// ---------
-cur_frm.add_fetch('customer', 'customer_name', 'customer_name')
-cur_frm.add_fetch('customer', 'address', 'delivery_address')
-cur_frm.add_fetch('customer', 'territory', 'territory')
-
-// territory
-// ----------
-cur_frm.fields_dict['territory'].get_query = function(doc,cdt,cdn) {
- return{
- filters:{'is_group': "No"}
- }
-}
-
-// Supplier
-//-------------
-cur_frm.cscript.supplier = function(doc,dt,dn) {
- if(doc.supplier) return get_server_fields('get_default_supplier_address', JSON.stringify({supplier: doc.supplier}),'', doc, dt, dn, 1);
- if(doc.supplier) unhide_field(['supplier_name','address_display']);
-}
-
-//item code
-//----------
-cur_frm.fields_dict['item_code'].get_query = function(doc,cdt,cdn) {
- return{
- query:"controllers.queries.item_query",
- filters:{
- 'has_serial_no': 'Yes'
- }
- }
-}
-
-cur_frm.fields_dict.customer.get_query = function(doc,cdt,cdn) {
- return{
- query:"controllers.queries.customer_query"
- }
-}
-
-cur_frm.fields_dict.supplier.get_query = function(doc,cdt,cdn) {
- return{
- query:"controllers.queries.supplier_query"
- }
-}
\ No newline at end of file
+cur_frm.add_fetch("item_code", "item_name", "item_name")
+cur_frm.add_fetch("item_code", "description", "description")
+cur_frm.add_fetch("item_code", "item_group", "item_group")
+cur_frm.add_fetch("item_code", "brand", "brand")
diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py
index 7e59d64..e7018a2 100644
--- a/stock/doctype/serial_no/serial_no.py
+++ b/stock/doctype/serial_no/serial_no.py
@@ -7,13 +7,27 @@
from webnotes.utils import cint, getdate, nowdate
import datetime
from webnotes import msgprint, _
-
+
from controllers.stock_controller import StockController
+class SerialNoCannotCreateDirectError(webnotes.ValidationError): pass
+class SerialNoCannotCannotChangeError(webnotes.ValidationError): pass
+
class DocType(StockController):
def __init__(self, doc, doclist=[]):
self.doc = doc
self.doclist = doclist
+ self.via_stock_ledger = False
+
+ def validate(self):
+ if self.doc.fields.get("__islocal") and self.doc.warehouse:
+ webnotes.throw(_("New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"),
+ SerialNoCannotCreateDirectError)
+
+ self.validate_warranty_status()
+ self.validate_amc_status()
+ self.validate_warehouse()
+ self.validate_item()
def validate_amc_status(self):
"""
@@ -31,78 +45,40 @@
def validate_warehouse(self):
- if self.doc.status=='In Store' and not self.doc.warehouse:
- msgprint("Warehouse is mandatory if this Serial No is <b>In Store</b>", raise_exception=1)
+ if not self.doc.fields.get("__islocal"):
+ item_code, warehouse = webnotes.conn.get_value("Serial No",
+ self.doc.name, ["item_code", "warehouse"])
+ if item_code != self.doc.item_code:
+ webnotes.throw(_("Item Code cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
+ if not self.via_stock_ledger and warehouse != self.doc.warehouse:
+ webnotes.throw(_("Warehouse cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
+
+ if not self.doc.warehouse and self.doc.status=="Available":
+ self.doc.status = "Not Available"
def validate_item(self):
"""
Validate whether serial no is required for this item
"""
- item = webnotes.conn.sql("select name, has_serial_no from tabItem where name = '%s'" % self.doc.item_code)
- if not item:
- msgprint("Item is not exists in the system", raise_exception=1)
- elif item[0][1] == 'No':
- msgprint("To proceed please select 'Yes' in 'Has Serial No' in Item master: '%s'" % self.doc.item_code, raise_exception=1)
+ item = webnotes.doc("Item", self.doc.item_code)
+ if item.has_serial_no!="Yes":
+ webnotes.throw(_("Item must have 'Has Serial No' as 'Yes'") + ": " + self.doc.item_code)
-
- def validate(self):
- self.validate_warranty_status()
- self.validate_amc_status()
- self.validate_warehouse()
- self.validate_item()
-
- def on_update(self):
- if self.doc.warehouse and self.doc.status == 'In Store' \
- and cint(self.doc.sle_exists) == 0 and \
- not webnotes.conn.sql("""select name from `tabStock Ledger Entry`
- where serial_no = %s and ifnull(is_cancelled, 'No') = 'No'""", self.doc.name):
- self.make_stock_ledger_entry(1)
- webnotes.conn.set(self.doc, 'sle_exists', 1)
+ self.doc.item_group = item.item_group
+ self.doc.description = item.description
+ self.doc.item_name = item.item_name
+ self.doc.brand = item.brand
+ self.doc.warranty_period = item.warranty_period
- self.make_gl_entries()
-
- def make_stock_ledger_entry(self, qty):
- from webnotes.model.code import get_obj
- values = [{
- 'item_code' : self.doc.item_code,
- 'warehouse' : self.doc.warehouse,
- 'posting_date' : self.doc.purchase_date or (self.doc.creation and self.doc.creation.split(' ')[0]) or nowdate(),
- 'posting_time' : self.doc.purchase_time or '00:00',
- 'voucher_type' : 'Serial No',
- 'voucher_no' : self.doc.name,
- 'voucher_detail_no' : '',
- 'actual_qty' : qty,
- 'stock_uom' : webnotes.conn.get_value('Item', self.doc.item_code, 'stock_uom'),
- 'incoming_rate' : self.doc.purchase_rate,
- 'company' : self.doc.company,
- 'fiscal_year' : self.doc.fiscal_year,
- 'is_cancelled' : 'No', # is_cancelled is always 'No' because while deleted it can not find creation entry if it not created directly, voucher no != serial no
- 'batch_no' : '',
- 'serial_no' : self.doc.name
- }]
- get_obj('Stock Ledger').update_stock(values)
-
-
def on_trash(self):
if self.doc.status == 'Delivered':
msgprint("Cannot trash Serial No : %s as it is already Delivered" % (self.doc.name), raise_exception = 1)
- elif self.doc.status == 'In Store':
- webnotes.conn.set(self.doc, 'status', 'Not in Use')
- self.make_stock_ledger_entry(-1)
-
- if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \
- and webnotes.conn.sql("""select name from `tabGL Entry`
- where voucher_type=%s and voucher_no=%s and ifnull(is_cancelled, 'No')='No'""",
- (self.doc.doctype, self.doc.name)):
- self.make_gl_entries(cancel=True)
-
+ if self.doc.warehouse:
+ webnotes.throw(_("Cannot delete Serial No in warehouse. First remove from warehouse, then delete.") + \
+ ": " + self.doc.name)
def on_cancel(self):
self.on_trash()
-
- def on_restore(self):
- self.make_stock_ledger_entry(1)
- self.make_gl_entries()
def on_rename(self, new, old, merge=False):
"""rename serial_no text fields"""
@@ -119,18 +95,3 @@
webnotes.conn.sql("""update `tab%s` set serial_no = %s
where name=%s""" % (dt[0], '%s', '%s'),
('\n'.join(serial_nos), item[0]))
-
- def make_gl_entries(self, cancel=False):
- if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")):
- return
-
- from accounts.general_ledger import make_gl_entries
- against_stock_account = self.get_company_default("stock_adjustment_account")
- gl_entries = self.get_gl_entries_for_stock(against_stock_account, self.doc.purchase_rate)
-
- for entry in gl_entries:
- entry["posting_date"] = self.doc.purchase_date or (self.doc.creation and
- self.doc.creation.split(' ')[0]) or nowdate()
-
- if gl_entries:
- make_gl_entries(gl_entries, cancel)
\ No newline at end of file
diff --git a/stock/doctype/serial_no/serial_no.txt b/stock/doctype/serial_no/serial_no.txt
index efa35f5..2eba0f4 100644
--- a/stock/doctype/serial_no/serial_no.txt
+++ b/stock/doctype/serial_no/serial_no.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-16 10:59:15",
"docstatus": 0,
- "modified": "2013-07-22 15:29:43",
+ "modified": "2013-08-21 13:37:01",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -14,6 +14,7 @@
"doctype": "DocType",
"document_type": "Master",
"icon": "icon-barcode",
+ "in_create": 0,
"module": "Stock",
"name": "__common__",
"search_fields": "item_code,status"
@@ -34,8 +35,7 @@
"parenttype": "DocType",
"permlevel": 0,
"read": 1,
- "report": 1,
- "submit": 0
+ "write": 1
},
{
"doctype": "DocType",
@@ -57,6 +57,7 @@
},
{
"default": "In Store",
+ "description": "Only Serial Nos with status \"Available\" can be delivered.",
"doctype": "DocField",
"fieldname": "status",
"fieldtype": "Select",
@@ -66,8 +67,8 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
- "options": "\nIn Store\nDelivered\nNot in Use\nPurchase Returned",
- "read_only": 1,
+ "options": "\nAvailable\nNot Available\nDelivered\nPurchase Returned\nSales Returned",
+ "read_only": 0,
"reqd": 1,
"search_index": 1
},
@@ -99,6 +100,22 @@
"search_index": 0
},
{
+ "description": "Warehouse can only be changed via Stock Entry / Delivery Note / Purchase Receipt",
+ "doctype": "DocField",
+ "fieldname": "warehouse",
+ "fieldtype": "Link",
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "Warehouse",
+ "no_copy": 1,
+ "oldfieldname": "warehouse",
+ "oldfieldtype": "Link",
+ "options": "Warehouse",
+ "read_only": 1,
+ "reqd": 0,
+ "search_index": 1
+ },
+ {
"doctype": "DocField",
"fieldname": "column_break1",
"fieldtype": "Column Break",
@@ -134,7 +151,7 @@
"oldfieldtype": "Link",
"options": "Item Group",
"read_only": 1,
- "reqd": 1,
+ "reqd": 0,
"search_index": 0
},
{
@@ -154,7 +171,7 @@
"doctype": "DocField",
"fieldname": "purchase_details",
"fieldtype": "Section Break",
- "label": "Purchase Details",
+ "label": "Purchase / Manufacture Details",
"read_only": 0
},
{
@@ -168,30 +185,30 @@
"doctype": "DocField",
"fieldname": "purchase_document_type",
"fieldtype": "Select",
- "label": "Purchase Document Type",
+ "label": "Creation Document Type",
"no_copy": 1,
"options": "\nPurchase Receipt\nStock Entry",
- "read_only": 0
+ "read_only": 1
},
{
"doctype": "DocField",
"fieldname": "purchase_document_no",
"fieldtype": "Data",
"hidden": 0,
- "label": "Purchase Document No",
+ "label": "Creation Document No",
"no_copy": 1,
- "read_only": 0
+ "read_only": 1
},
{
"doctype": "DocField",
"fieldname": "purchase_date",
"fieldtype": "Date",
"in_filter": 1,
- "label": "Purchase Date",
+ "label": "Creation Date",
"no_copy": 1,
"oldfieldname": "purchase_date",
"oldfieldtype": "Date",
- "read_only": 0,
+ "read_only": 1,
"reqd": 0,
"search_index": 0
},
@@ -199,10 +216,10 @@
"doctype": "DocField",
"fieldname": "purchase_time",
"fieldtype": "Time",
- "label": "Incoming Time",
+ "label": "Creation Time",
"no_copy": 1,
- "read_only": 0,
- "reqd": 1
+ "read_only": 1,
+ "reqd": 0
},
{
"doctype": "DocField",
@@ -214,8 +231,8 @@
"oldfieldname": "purchase_rate",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "read_only": 0,
- "reqd": 1,
+ "read_only": 1,
+ "reqd": 0,
"search_index": 0
},
{
@@ -227,21 +244,6 @@
},
{
"doctype": "DocField",
- "fieldname": "warehouse",
- "fieldtype": "Link",
- "in_filter": 1,
- "in_list_view": 1,
- "label": "Warehouse",
- "no_copy": 1,
- "oldfieldname": "warehouse",
- "oldfieldtype": "Link",
- "options": "Warehouse",
- "read_only": 0,
- "reqd": 0,
- "search_index": 1
- },
- {
- "doctype": "DocField",
"fieldname": "supplier",
"fieldtype": "Link",
"in_filter": 1,
@@ -261,14 +263,6 @@
},
{
"doctype": "DocField",
- "fieldname": "address_display",
- "fieldtype": "Text",
- "label": "Supplier Address",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "doctype": "DocField",
"fieldname": "delivery_details",
"fieldtype": "Section Break",
"label": "Delivery Details",
@@ -277,13 +271,6 @@
},
{
"doctype": "DocField",
- "fieldname": "column_break4",
- "fieldtype": "Column Break",
- "read_only": 0,
- "width": "50%"
- },
- {
- "doctype": "DocField",
"fieldname": "delivery_document_type",
"fieldtype": "Select",
"in_filter": 1,
@@ -303,15 +290,6 @@
},
{
"doctype": "DocField",
- "fieldname": "customer_address",
- "fieldtype": "Text",
- "label": "Customer Address",
- "oldfieldname": "customer_address",
- "oldfieldtype": "Text",
- "read_only": 1
- },
- {
- "doctype": "DocField",
"fieldname": "delivery_date",
"fieldtype": "Date",
"label": "Delivery Date",
@@ -359,7 +337,7 @@
"oldfieldtype": "Link",
"options": "Customer",
"print_hide": 1,
- "read_only": 1,
+ "read_only": 0,
"search_index": 0
},
{
@@ -376,28 +354,6 @@
},
{
"doctype": "DocField",
- "fieldname": "delivery_address",
- "fieldtype": "Text",
- "label": "Delivery Address",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "doctype": "DocField",
- "fieldname": "territory",
- "fieldtype": "Link",
- "in_filter": 1,
- "label": "Territory",
- "no_copy": 1,
- "oldfieldname": "territory",
- "oldfieldtype": "Link",
- "options": "Territory",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 0
- },
- {
- "doctype": "DocField",
"fieldname": "warranty_amc_details",
"fieldtype": "Section Break",
"label": "Warranty / AMC Details",
@@ -485,62 +441,30 @@
"in_filter": 1,
"label": "Company",
"options": "link:Company",
- "read_only": 0,
- "reqd": 1,
- "search_index": 1
- },
- {
- "doctype": "DocField",
- "fieldname": "fiscal_year",
- "fieldtype": "Select",
- "in_filter": 1,
- "label": "Fiscal Year",
- "options": "link:Fiscal Year",
- "read_only": 0,
- "reqd": 1,
- "search_index": 1
- },
- {
- "doctype": "DocField",
- "fieldname": "trash_reason",
- "fieldtype": "Small Text",
- "label": "Trash Reason",
- "oldfieldname": "trash_reason",
- "oldfieldtype": "Small Text",
- "read_only": 1
- },
- {
- "doctype": "DocField",
- "fieldname": "sle_exists",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "SLE Exists",
- "no_copy": 1,
- "print_hide": 1,
"read_only": 1,
- "report_hide": 1
+ "reqd": 1,
+ "search_index": 1
},
{
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
- "role": "Material Master Manager",
- "write": 1
- },
- {
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "doctype": "DocPerm",
+ "report": 1,
"role": "Material Manager",
- "write": 0
+ "submit": 0
},
{
"amend": 0,
"cancel": 0,
+ "create": 1,
+ "doctype": "DocPerm",
+ "report": 1,
+ "role": "Material User",
+ "submit": 0
+ },
+ {
"create": 0,
"doctype": "DocPerm",
- "role": "Material User",
- "write": 0
+ "role": "Accounts User"
}
]
\ No newline at end of file
diff --git a/stock/doctype/serial_no/test_serial_no.py b/stock/doctype/serial_no/test_serial_no.py
index 1898b2f..8e5f775 100644
--- a/stock/doctype/serial_no/test_serial_no.py
+++ b/stock/doctype/serial_no/test_serial_no.py
@@ -7,99 +7,23 @@
from __future__ import unicode_literals
import webnotes, unittest
-class TestSerialNo(unittest.TestCase):
- def test_aii_gl_entries_for_serial_no_in_store(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
-
- sr = webnotes.bean(copy=test_records[0])
- sr.doc.serial_no = "_Test Serial No 1"
- sr.insert()
-
- stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company",
- "stock_in_hand_account")
- against_stock_account = webnotes.conn.get_value("Company", "_Test Company",
- "stock_adjustment_account")
-
- # check stock ledger entries
- sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry`
- where voucher_type = 'Serial No' and voucher_no = %s""", sr.doc.name, as_dict=1)[0]
- self.assertTrue(sle)
- self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty],
- ["_Test Serialized Item", "_Test Warehouse - _TC", 1.0])
-
- # check gl entries
- gl_entries = webnotes.conn.sql("""select account, debit, credit
- from `tabGL Entry` where voucher_type='Serial No' and voucher_no=%s
- order by account desc""", sr.doc.name, as_dict=1)
- self.assertTrue(gl_entries)
-
- expected_values = [
- [stock_in_hand_account, 1000.0, 0.0],
- [against_stock_account, 0.0, 1000.0]
- ]
-
- for i, gle in enumerate(gl_entries):
- self.assertEquals(expected_values[i][0], gle.account)
- self.assertEquals(expected_values[i][1], gle.debit)
- self.assertEquals(expected_values[i][2], gle.credit)
-
- sr.load_from_db()
- self.assertEquals(sr.doc.sle_exists, 1)
-
- # save again
- sr.save()
- gl_entries = webnotes.conn.sql("""select account, debit, credit
- from `tabGL Entry` where voucher_type='Serial No' and voucher_no=%s
- order by account desc""", sr.doc.name, as_dict=1)
-
- for i, gle in enumerate(gl_entries):
- self.assertEquals(expected_values[i][0], gle.account)
- self.assertEquals(expected_values[i][1], gle.debit)
- self.assertEquals(expected_values[i][2], gle.credit)
-
- # trash/cancel
- sr.submit()
- sr.cancel()
-
- gl_count = webnotes.conn.sql("""select count(name) from `tabGL Entry`
- where voucher_type='Serial No' and voucher_no=%s and ifnull(is_cancelled, 'No') = 'Yes'
- order by account asc, name asc""", sr.doc.name)
-
- self.assertEquals(gl_count[0][0], 4)
-
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
-
-
- def test_aii_gl_entries_for_serial_no_delivered(self):
- webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
-
- sr = webnotes.bean(copy=test_records[0])
- sr.doc.serial_no = "_Test Serial No 2"
- sr.doc.status = "Delivered"
- sr.insert()
-
- gl_entries = webnotes.conn.sql("""select account, debit, credit
- from `tabGL Entry` where voucher_type='Serial No' and voucher_no=%s
- order by account desc""", sr.doc.name, as_dict=1)
- self.assertFalse(gl_entries)
-
- webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
-
test_dependencies = ["Item"]
-test_records = [
- [
- {
- "company": "_Test Company",
- "doctype": "Serial No",
- "serial_no": "_Test Serial No",
- "status": "In Store",
- "item_code": "_Test Serialized Item",
- "item_group": "_Test Item Group",
- "warehouse": "_Test Warehouse - _TC",
- "purchase_rate": 1000.0,
- "purchase_time": "11:37:39",
- "purchase_date": "2013-02-26",
- 'fiscal_year': "_Test Fiscal Year 2013"
- }
- ]
-]
\ No newline at end of file
+test_records = []
+
+from stock.doctype.serial_no.serial_no import *
+
+class TestSerialNo(unittest.TestCase):
+ def test_cannot_create_direct(self):
+ sr = webnotes.new_bean("Serial No")
+ sr.doc.item_code = "_Test Serialized Item"
+ sr.doc.warehouse = "_Test Warehouse - _TC"
+ sr.doc.serial_no = "_TCSER0001"
+ sr.doc.purchase_rate = 10
+ self.assertRaises(SerialNoCannotCreateDirectError, sr.insert)
+
+ sr.doc.warehouse = None
+ sr.insert()
+ self.assertTrue(sr.doc.name)
+
+ sr.doc.warehouse = "_Test Warehouse - _TC"
+ self.assertTrue(SerialNoCannotCannotChangeError, sr.doc.save)
\ No newline at end of file
diff --git a/stock/doctype/stock_entry/stock_entry.js b/stock/doctype/stock_entry/stock_entry.js
index 53998f8..e369081 100644
--- a/stock/doctype/stock_entry/stock_entry.js
+++ b/stock/doctype/stock_entry/stock_entry.js
@@ -103,12 +103,8 @@
},
callback: function(r) {
if (!r.exc) me.frm.set_value("expense_adjustment_account", r.message);
-
- me.get_items();
}
});
- } else {
- me.get_items();
}
},
@@ -122,16 +118,15 @@
},
get_items: function() {
- if(this.frm.doc.__islocal && (this.frm.doc.production_order || this.frm.doc.bom_no)
- && !getchildren('Stock Entry Detail', this.frm.doc.name, 'mtn_details').length) {
- // if production order / bom is mentioned, get items
- return this.frm.call({
- doc: this.frm.doc,
- method: "get_items",
- callback: function(r) {
- if(!r.exc) refresh_field("mtn_details");
- }
- });
+ if(this.frm.doc.production_order || this.frm.doc.bom_no) {
+ // if production order / bom is mentioned, get items
+ return this.frm.call({
+ doc: this.frm.doc,
+ method: "get_items",
+ callback: function(r) {
+ if(!r.exc) refresh_field("mtn_details");
+ }
+ });
}
},
@@ -223,6 +218,10 @@
if(!row.s_warehouse) row.s_warehouse = this.frm.doc.from_warehouse;
if(!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
},
+
+ mtn_details_on_form_rendered: function(doc, grid_row) {
+ erpnext.setup_serial_no(grid_row)
+ }
});
cur_frm.script_manager.make(erpnext.stock.StockEntry);
diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py
index b702316..e3fc67e 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -20,6 +20,7 @@
class NotUpdateStockError(webnotes.ValidationError): pass
class StockOverReturnError(webnotes.ValidationError): pass
class IncorrectValuationRateError(webnotes.ValidationError): pass
+class DuplicateEntryForProductionOrderError(webnotes.ValidationError): pass
from controllers.stock_controller import StockController
@@ -32,7 +33,6 @@
def validate(self):
self.validate_posting_time()
self.validate_purpose()
- self.validate_serial_nos()
pro_obj = self.doc.production_order and \
get_obj('Production Order', self.doc.production_order) or None
@@ -52,14 +52,14 @@
self.set_total_amount()
def on_submit(self):
- self.update_serial_no(1)
self.update_stock_ledger(0)
+ self.update_serial_no(1)
self.update_production_order(1)
self.make_gl_entries()
def on_cancel(self):
- self.update_serial_no(0)
self.update_stock_ledger(1)
+ self.update_serial_no(0)
self.update_production_order(0)
self.make_cancel_gl_entries()
@@ -74,11 +74,6 @@
if self.doc.purpose not in valid_purposes:
msgprint(_("Purpose must be one of ") + comma_or(valid_purposes),
raise_exception=True)
-
- def validate_serial_nos(self):
- sl_obj = get_obj("Stock Ledger")
- sl_obj.scrub_serial_nos(self)
- sl_obj.validate_serial_no(self, 'mtn_details')
def validate_item(self):
for item in self.doclist.get({"parentfield": "mtn_details"}):
@@ -152,21 +147,33 @@
return
if self.doc.purpose == "Manufacture/Repack":
- if not flt(self.doc.fg_completed_qty):
- msgprint(_("Manufacturing Quantity") + _(" is mandatory"), raise_exception=1)
-
- if flt(pro_obj.doc.qty) < (flt(pro_obj.doc.produced_qty)
- + flt(self.doc.fg_completed_qty)):
- # do not allow manufacture of qty > production order qty
- msgprint(_("For Item ") + pro_obj.doc.production_item
- + _("Quantity already manufactured")
- + " = %s." % flt(pro_obj.doc.produced_qty)
- + _("Hence, maximum allowed Manufacturing Quantity")
- + " = %s." % (flt(pro_obj.doc.qty) - flt(pro_obj.doc.produced_qty)),
- raise_exception=1)
+ # check for double entry
+ self.check_duplicate_entry_for_production_order()
elif self.doc.purpose != "Material Transfer":
self.doc.production_order = None
+
+ def check_duplicate_entry_for_production_order(self):
+ other_ste = [t[0] for t in webnotes.conn.get_values("Stock Entry", {
+ "production_order": self.doc.production_order,
+ "purpose": self.doc.purpose,
+ "docstatus": ["!=", 2],
+ "name": ["!=", self.doc.name]
+ }, "name")]
+
+ if other_ste:
+ production_item, qty = webnotes.conn.get_value("Production Order",
+ self.doc.production_order, ["production_item", "qty"])
+ args = other_ste + [production_item]
+ fg_qty_already_entered = webnotes.conn.sql("""select sum(actual_qty)
+ from `tabStock Entry Detail`
+ where parent in (%s)
+ and item_code = %s
+ and ifnull(s_warehouse,'')='' """ % (", ".join(["%s" * len(other_ste)]), "%s"), args)[0][0]
+ if fg_qty_already_entered >= qty:
+ webnotes.throw(_("Stock Entries already created for Production Order ")
+ + self.doc.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
+
def set_total_amount(self):
self.doc.total_amount = sum([flt(item.amount) for item in self.doclist.get({"parentfield": "mtn_details"})])
@@ -206,7 +213,7 @@
"posting_date": self.doc.posting_date,
"posting_time": self.doc.posting_time,
"qty": d.s_warehouse and -1*d.transfer_qty or d.transfer_qty,
- "serial_no": cstr(d.serial_no).strip(),
+ "serial_no": d.serial_no,
"bom_no": d.bom_no,
})
# get actual stock at source warehouse
@@ -317,27 +324,21 @@
def update_serial_no(self, is_submit):
"""Create / Update Serial No"""
- from stock.utils import get_valid_serial_nos
-
- sl_obj = get_obj('Stock Ledger')
- if is_submit:
- sl_obj.validate_serial_no_warehouse(self, 'mtn_details')
+
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
+ update_serial_nos_after_submit(self, "Stock Entry", "mtn_details")
for d in getlist(self.doclist, 'mtn_details'):
- if cstr(d.serial_no).strip():
- for x in get_valid_serial_nos(d.serial_no):
- serial_no = x.strip()
- if d.s_warehouse:
- sl_obj.update_serial_delivery_details(self, d, serial_no, is_submit)
- if d.t_warehouse:
- sl_obj.update_serial_purchase_details(self, d, serial_no, is_submit,
- self.doc.purpose)
-
- if self.doc.purpose == 'Purchase Return':
- serial_doc = Document("Serial No", serial_no)
- serial_doc.status = is_submit and 'Purchase Returned' or 'In Store'
- serial_doc.docstatus = is_submit and 2 or 0
- serial_doc.save()
+ for serial_no in get_serial_nos(d.serial_no):
+ if self.doc.purpose == 'Purchase Return':
+ sr = webnotes.bean("Serial No", serial_no)
+ sr.doc.status = "Purchase Returned" if is_submit else "Available"
+ sr.save()
+
+ if self.doc.purpose == "Sales Return":
+ sr = webnotes.bean("Serial No", serial_no)
+ sr.doc.status = "Sales Returned" if is_submit else "Delivered"
+ sr.save()
def update_stock_ledger(self, is_cancelled=0):
self.values = []
@@ -571,7 +572,6 @@
for item in item_dict:
pending_to_issue = (max_qty * item_dict[item]["qty"]) - issued_item_qty.get(item, 0)
desire_to_transfer = flt(self.doc.fg_completed_qty) * item_dict[item]["qty"]
-
if desire_to_transfer <= pending_to_issue:
item_dict[item]["qty"] = desire_to_transfer
else:
diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py
index 6438116..b9b3230 100644
--- a/stock/doctype/stock_entry/test_stock_entry.py
+++ b/stock/doctype/stock_entry/test_stock_entry.py
@@ -7,6 +7,7 @@
from __future__ import unicode_literals
import webnotes, unittest
from webnotes.utils import flt
+from stock.doctype.stock_ledger_entry.stock_ledger_entry import *
class TestStockEntry(unittest.TestCase):
def tearDown(self):
@@ -40,11 +41,42 @@
webnotes.conn.set_default("company", self.old_default_company)
def test_warehouse_company_validation(self):
+ webnotes.session.user = "test2@example.com"
+ webnotes.bean("Profile", "test2@example.com").get_controller()\
+ .add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
+
from stock.doctype.stock_ledger_entry.stock_ledger_entry import InvalidWarehouseCompany
st1 = webnotes.bean(copy=test_records[0])
st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
st1.insert()
self.assertRaises(InvalidWarehouseCompany, st1.submit)
+
+ webnotes.session.user = "Administrator"
+
+ def test_warehouse_user(self):
+ from stock.utils import UserNotAllowedForWarehouse
+
+ webnotes.session.user = "test@example.com"
+ webnotes.bean("Profile", "test@example.com").get_controller()\
+ .add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
+
+ webnotes.bean("Profile", "test2@example.com").get_controller()\
+ .add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
+
+ st1 = webnotes.bean(copy=test_records[0])
+ st1.doc.company = "_Test Company 1"
+ st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
+ st1.insert()
+ self.assertRaises(UserNotAllowedForWarehouse, st1.submit)
+
+ webnotes.session.user = "test2@example.com"
+ st1 = webnotes.bean(copy=test_records[0])
+ st1.doc.company = "_Test Company 1"
+ st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
+ st1.insert()
+ st1.submit()
+
+ webnotes.session.user = "Administrator"
def test_material_receipt_gl_entry(self):
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
@@ -180,6 +212,7 @@
def _clear_stock(self):
webnotes.conn.sql("delete from `tabStock Ledger Entry`")
webnotes.conn.sql("""delete from `tabBin`""")
+ webnotes.conn.sql("""delete from `tabSerial No`""")
self.old_default_company = webnotes.conn.get_default("company")
webnotes.conn.set_default("company", "_Test Company")
@@ -571,12 +604,139 @@
return se, pr.doc.name
+ def test_serial_no_not_reqd(self):
+ se = webnotes.bean(copy=test_records[0])
+ se.doclist[1].serial_no = "ABCD"
+ se.insert()
+ self.assertRaises(SerialNoNotRequiredError, se.submit)
+
+ def test_serial_no_reqd(self):
+ se = webnotes.bean(copy=test_records[0])
+ se.doclist[1].item_code = "_Test Serialized Item"
+ se.doclist[1].qty = 2
+ se.doclist[1].transfer_qty = 2
+ se.insert()
+ self.assertRaises(SerialNoRequiredError, se.submit)
+
+ def test_serial_no_qty_more(self):
+ se = webnotes.bean(copy=test_records[0])
+ se.doclist[1].item_code = "_Test Serialized Item"
+ se.doclist[1].qty = 2
+ se.doclist[1].serial_no = "ABCD\nEFGH\nXYZ"
+ se.doclist[1].transfer_qty = 2
+ se.insert()
+ self.assertRaises(SerialNoQtyError, se.submit)
+
+ def test_serial_no_qty_less(self):
+ se = webnotes.bean(copy=test_records[0])
+ se.doclist[1].item_code = "_Test Serialized Item"
+ se.doclist[1].qty = 2
+ se.doclist[1].serial_no = "ABCD"
+ se.doclist[1].transfer_qty = 2
+ se.insert()
+ self.assertRaises(SerialNoQtyError, se.submit)
+
+ def test_serial_no_transfer_in(self):
+ se = webnotes.bean(copy=test_records[0])
+ se.doclist[1].item_code = "_Test Serialized Item"
+ se.doclist[1].qty = 2
+ se.doclist[1].serial_no = "ABCD\nEFGH"
+ se.doclist[1].transfer_qty = 2
+ se.insert()
+ se.submit()
+
+ self.assertTrue(webnotes.conn.exists("Serial No", "ABCD"))
+ self.assertTrue(webnotes.conn.exists("Serial No", "EFGH"))
+
+ def test_serial_no_not_exists(self):
+ se = webnotes.bean(copy=test_records[0])
+ se.doc.purpose = "Material Issue"
+ se.doclist[1].item_code = "_Test Serialized Item"
+ se.doclist[1].qty = 2
+ se.doclist[1].s_warehouse = "_Test Warehouse 1 - _TC"
+ se.doclist[1].t_warehouse = None
+ se.doclist[1].serial_no = "ABCD\nEFGH"
+ se.doclist[1].transfer_qty = 2
+ se.insert()
+ self.assertRaises(SerialNoNotExistsError, se.submit)
+
+ def test_serial_by_series(self):
+ se = make_serialized_item()
+
+ serial_nos = get_serial_nos(se.doclist[1].serial_no)
+
+ self.assertTrue(webnotes.conn.exists("Serial No", serial_nos[0]))
+ self.assertTrue(webnotes.conn.exists("Serial No", serial_nos[1]))
+
+ return se
+
+ def test_serial_item_error(self):
+ self.test_serial_by_series()
+
+ se = webnotes.bean(copy=test_records[0])
+ se.doc.purpose = "Material Transfer"
+ se.doclist[1].item_code = "_Test Serialized Item"
+ se.doclist[1].qty = 1
+ se.doclist[1].transfer_qty = 1
+ se.doclist[1].serial_no = "ABCD00001"
+ se.doclist[1].s_warehouse = "_Test Warehouse - _TC"
+ se.doclist[1].t_warehouse = "_Test Warehouse 1 - _TC"
+ se.insert()
+ self.assertRaises(SerialNoItemError, se.submit)
+
+ def test_serial_move(self):
+ se = make_serialized_item()
+ serial_no = get_serial_nos(se.doclist[1].serial_no)[0]
+
+ se = webnotes.bean(copy=test_records[0])
+ se.doc.purpose = "Material Transfer"
+ se.doclist[1].item_code = "_Test Serialized Item With Series"
+ se.doclist[1].qty = 1
+ se.doclist[1].transfer_qty = 1
+ se.doclist[1].serial_no = serial_no
+ se.doclist[1].s_warehouse = "_Test Warehouse - _TC"
+ se.doclist[1].t_warehouse = "_Test Warehouse 1 - _TC"
+ se.insert()
+ se.submit()
+ self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse 1 - _TC")
+
+ def test_serial_warehouse_error(self):
+ make_serialized_item()
+
+ se = webnotes.bean(copy=test_records[0])
+ se.doc.purpose = "Material Transfer"
+ se.doclist[1].item_code = "_Test Serialized Item With Series"
+ se.doclist[1].qty = 1
+ se.doclist[1].transfer_qty = 1
+ se.doclist[1].serial_no = "ABCD00001"
+ se.doclist[1].s_warehouse = "_Test Warehouse 1 - _TC"
+ se.doclist[1].t_warehouse = "_Test Warehouse - _TC"
+ se.insert()
+ self.assertRaises(SerialNoWarehouseError, se.submit)
+
+ def test_serial_cancel(self):
+ se = self.test_serial_by_series()
+ se.cancel()
+
+ serial_no = get_serial_nos(se.doclist[1].serial_no)[0]
+ self.assertFalse(webnotes.conn.get_value("Serial No", serial_no, "warehouse"))
+ self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "status"), "Not Available")
+
+def make_serialized_item():
+ se = webnotes.bean(copy=test_records[0])
+ se.doclist[1].item_code = "_Test Serialized Item With Series"
+ se.doclist[1].qty = 2
+ se.doclist[1].transfer_qty = 2
+ se.insert()
+ se.submit()
+ return se
+
test_records = [
[
{
"company": "_Test Company",
"doctype": "Stock Entry",
- "posting_date": "2013-01-25",
+ "posting_date": "2013-01-01",
"posting_time": "17:14:24",
"purpose": "Material Receipt",
"fiscal_year": "_Test Fiscal Year 2013",
diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py
index 0af18b9..10fb761 100644
--- a/stock/doctype/stock_ledger/stock_ledger.py
+++ b/stock/doctype/stock_ledger/stock_ledger.py
@@ -17,174 +17,10 @@
def __init__(self, doc, doclist=[]):
self.doc = doc
self.doclist = doclist
-
-
- def scrub_serial_nos(self, obj, table_name = ''):
- if not table_name:
- table_name = obj.fname
- for d in getlist(obj.doclist, table_name):
- if d.serial_no:
- serial_nos = cstr(d.serial_no).strip().replace(',', '\n').split('\n')
- d.serial_no = "\n".join(map(lambda x: x.strip(), serial_nos))
- d.save()
-
- def validate_serial_no_warehouse(self, obj, fname):
- for d in getlist(obj.doclist, fname):
- wh = d.warehouse or d.s_warehouse
- if cstr(d.serial_no).strip() and wh:
- serial_nos = get_valid_serial_nos(d.serial_no)
- for s in serial_nos:
- s = s.strip()
- sr_war = webnotes.conn.sql("select warehouse,name from `tabSerial No` where name = '%s'" % (s))
- if not sr_war:
- msgprint("Serial No %s does not exists"%s, raise_exception = 1)
- elif not sr_war[0][0]:
- msgprint("Warehouse not mentioned in the Serial No <b>%s</b>" % s, raise_exception = 1)
- elif sr_war[0][0] != wh:
- msgprint("Serial No : %s for Item : %s doesn't exists in Warehouse : %s" % (s, d.item_code, wh), raise_exception = 1)
-
-
- def validate_serial_no(self, obj, fname):
- """check whether serial no is required"""
- for d in getlist(obj.doclist, fname):
- is_stock_item = webnotes.conn.get_value('Item', d.item_code, 'is_stock_item')
- ar_required = webnotes.conn.get_value('Item', d.item_code, 'has_serial_no')
-
- # [bug fix] need to strip serial nos of all spaces and new lines for validation
- serial_no = cstr(d.serial_no).strip()
- if serial_no:
- if is_stock_item != 'Yes':
- msgprint("Serial No is not required for non-stock item: %s" % d.item_code, raise_exception=1)
- elif ar_required != 'Yes':
- msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :" + d.item_code + \
- ', otherwise please remove serial no', raise_exception=1)
- elif ar_required == 'Yes' and not serial_no and d.qty:
- msgprint("Serial no is mandatory for item: "+ d.item_code, raise_exception = 1)
-
- # validate rejected serial nos
- if fname == 'purchase_receipt_details' and flt(d.rejected_qty) > 0 and ar_required == 'Yes' and not d.rejected_serial_no:
- msgprint("Rejected serial no is mandatory for rejected qty of item: "+ d.item_code, raise_exception = 1)
-
-
- def set_pur_serial_no_values(self, obj, serial_no, d, s, new_rec, rejected=None):
- item_details = webnotes.conn.sql("""select item_group, warranty_period
- from `tabItem` where name = '%s' and (ifnull(end_of_life,'')='' or
- end_of_life = '0000-00-00' or end_of_life > now()) """ %(d.item_code), as_dict=1)
-
- s.purchase_document_type = obj.doc.doctype
- s.purchase_document_no = obj.doc.name
- s.purchase_date = obj.doc.posting_date
- s.purchase_time = obj.doc.posting_time
- s.purchase_rate = d.valuation_rate or d.incoming_rate
- s.item_code = d.item_code
- s.item_name = d.item_name
- s.brand = d.brand
- s.description = d.description
- s.item_group = item_details and item_details[0]['item_group'] or ''
- s.warranty_period = item_details and item_details[0]['warranty_period'] or 0
- s.supplier = obj.doc.supplier
- s.supplier_name = obj.doc.supplier_name
- s.address_display = obj.doc.address_display or obj.doc.supplier_address
- s.warehouse = rejected and obj.doc.rejected_warehouse \
- or d.warehouse or d.t_warehouse or ""
- s.docstatus = 0
- s.status = 'In Store'
- s.modified = nowdate()
- s.modified_by = session['user']
- s.serial_no = serial_no
- s.sle_exists = 1
- s.company = obj.doc.company
- s.save(new_rec)
-
-
- def update_serial_purchase_details(self, obj, d, serial_no, is_submit, purpose = '', rejected=None):
- exists = webnotes.conn.sql("select name, status, docstatus from `tabSerial No` where name = '%s'" % (serial_no))
- if is_submit:
- if exists and exists[0][2] != 2 and \
- purpose not in ['Material Transfer', "Material Receipt", 'Sales Return']:
- msgprint("Serial No: %s already %s" % (serial_no, exists and exists[0][1]), raise_exception = 1)
- elif exists:
- s = Document('Serial No', exists and exists[0][0])
- self.set_pur_serial_no_values(obj, serial_no, d, s, new_rec = 0, rejected=rejected)
- else:
- s = Document('Serial No')
- self.set_pur_serial_no_values(obj, serial_no, d, s, new_rec = 1, rejected=rejected)
- else:
- if exists and exists[0][1] == 'Delivered' and exists[0][2] != 2:
- msgprint("Serial No: %s is already delivered, you can not cancel the document." % serial_no, raise_exception=1)
- elif purpose == 'Material Transfer':
- webnotes.conn.sql("update `tabSerial No` set status = 'In Store', purchase_document_type = '', purchase_document_no = '', warehouse = '%s' where name = '%s'" % (d.s_warehouse, serial_no))
- elif purpose == 'Sales Return':
- webnotes.conn.sql("update `tabSerial No` set status = 'Delivered', purchase_document_type = '', purchase_document_no = '' where name = '%s'" % serial_no)
- else:
- webnotes.conn.sql("update `tabSerial No` set docstatus = 2, status = 'Not in Use', purchase_document_type = '', purchase_document_no = '', purchase_date = null, purchase_rate = 0, supplier = null, supplier_name = '', address_display = '', warehouse = '' where name = '%s'" % serial_no)
-
-
- def check_serial_no_exists(self, serial_no, item_code):
- chk = webnotes.conn.sql("select name, status, docstatus, item_code from `tabSerial No` where name = %s", (serial_no), as_dict=1)
- if not chk:
- msgprint("Serial No: %s does not exists in the system" % serial_no, raise_exception=1)
- elif chk and chk[0]['item_code'] != item_code:
- msgprint("Serial No: %s not belong to item: %s" % (serial_no, item_code), raise_exception=1)
- elif chk and chk[0]['docstatus'] == 2:
- msgprint("Serial No: %s of Item : %s is trashed in the system" % (serial_no, item_code), raise_exception = 1)
- elif chk and chk[0]['status'] == 'Delivered':
- msgprint("Serial No: %s of Item : %s is already delivered." % (serial_no, item_code), raise_exception = 1)
-
-
- def set_delivery_serial_no_values(self, obj, serial_no):
- s = Document('Serial No', serial_no)
- s.delivery_document_type = obj.doc.doctype
- s.delivery_document_no = obj.doc.name
- s.delivery_date = obj.doc.posting_date
- s.delivery_time = obj.doc.posting_time
- s.customer = obj.doc.customer
- s.customer_name = obj.doc.customer_name
- s.delivery_address = obj.doc.address_display
- s.territory = obj.doc.territory
- s.warranty_expiry_date = cint(s.warranty_period) and \
- add_days(cstr(obj.doc.posting_date), cint(s.warranty_period)) or s.warranty_expiry_date
- s.docstatus = 1
- s.status = 'Delivered'
- s.modified = nowdate()
- s.modified_by = session['user']
- s.save()
-
-
- def update_serial_delivery_details(self, obj, d, serial_no, is_submit):
- if is_submit:
- self.check_serial_no_exists(serial_no, d.item_code)
- self.set_delivery_serial_no_values(obj, serial_no)
- else:
- webnotes.conn.sql("update `tabSerial No` set docstatus = 0, status = 'In Store', delivery_document_type = '', delivery_document_no = '', delivery_date = null, customer = null, customer_name = '', delivery_address = '', territory = null where name = '%s'" % (serial_no))
-
-
- def update_serial_record(self, obj, fname, is_submit = 1, is_incoming = 0):
- for d in getlist(obj.doclist, fname):
- if d.serial_no:
- serial_nos = get_valid_serial_nos(d.serial_no)
- for a in serial_nos:
- serial_no = a.strip()
- if is_incoming:
- self.update_serial_purchase_details(obj, d, serial_no, is_submit)
- else:
- self.update_serial_delivery_details(obj, d, serial_no, is_submit)
-
- if fname == 'purchase_receipt_details' and d.rejected_qty and d.rejected_serial_no:
- serial_nos = get_valid_serial_nos(d.rejected_serial_no)
- for a in serial_nos:
- self.update_serial_purchase_details(obj, d, a, is_submit, rejected=True)
-
-
def update_stock(self, values, is_amended = 'No'):
for v in values:
- sle_id, valid_serial_nos = '', ''
- # get serial nos
- if v.get("serial_no", "").strip():
- valid_serial_nos = get_valid_serial_nos(v["serial_no"],
- v['actual_qty'], v['item_code'])
- v["serial_no"] = valid_serial_nos and "\n".join(valid_serial_nos) or ""
+ sle_id = ''
# reverse quantities for cancel
if v.get('is_cancelled') == 'Yes':
diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 3fda3ba..58fc828 100644
--- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -3,11 +3,21 @@
from __future__ import unicode_literals
import webnotes
-from webnotes import _, msgprint
-from webnotes.utils import cint, flt, getdate
+from webnotes import _, msgprint, ValidationError
+from webnotes.utils import cint, flt, getdate, cstr
from webnotes.model.controller import DocListController
-class InvalidWarehouseCompany(Exception): pass
+class InvalidWarehouseCompany(ValidationError): pass
+class SerialNoNotRequiredError(ValidationError): pass
+class SerialNoRequiredError(ValidationError): pass
+class SerialNoQtyError(ValidationError): pass
+class SerialNoItemError(ValidationError): pass
+class SerialNoWarehouseError(ValidationError): pass
+class SerialNoStatusError(ValidationError): pass
+class SerialNoNotExistsError(ValidationError): pass
+
+def get_serial_nos(serial_no):
+ return [s.strip() for s in cstr(serial_no).strip().replace(',', '\n').split('\n') if s.strip()]
class DocType(DocListController):
def __init__(self, doc, doclist=[]):
@@ -15,9 +25,14 @@
self.doclist = doclist
def validate(self):
+ from stock.utils import validate_warehouse_user
+ if not hasattr(webnotes, "new_stock_ledger_entries"):
+ webnotes.new_stock_ledger_entries = []
+
+ webnotes.new_stock_ledger_entries.append(self.doc)
self.validate_mandatory()
self.validate_item()
- self.validate_warehouse_user()
+ validate_warehouse_user(self.doc.warehouse)
self.validate_warehouse_company()
self.actual_amt_check()
self.check_stock_frozen_date()
@@ -39,16 +54,6 @@
self.doc.fields.pop('batch_bal')
- def validate_warehouse_user(self):
- if webnotes.session.user=="Administrator":
- return
- warehouse_users = [p[0] for p in webnotes.conn.sql("""select user from `tabWarehouse User`
- where parent=%s""", self.doc.warehouse)]
-
- if warehouse_users and not webnotes.session.user in warehouse_users:
- webnotes.msgprint(_("User not allowed entry in the Warehouse") \
- + ": " + webnotes.session.user + " / " + self.doc.warehouse, raise_exception = 1)
-
def validate_warehouse_company(self):
warehouse_company = webnotes.conn.get_value("Warehouse", self.doc.warehouse, "company")
if warehouse_company and warehouse_company != self.doc.company:
@@ -56,10 +61,10 @@
self.doc.warehouse + ", " + self.doc.company +")",
raise_exception=InvalidWarehouseCompany)
- def validate_mandatory(self):
+ def validate_mandatory(self):
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','actual_qty','company']
for k in mandatory:
- if self.doc.fields.get(k)==None:
+ if not self.doc.fields.get(k):
msgprint("Stock Ledger Entry: '%s' is mandatory" % k, raise_exception = 1)
elif k == 'warehouse':
if not webnotes.conn.sql("select name from tabWarehouse where name = '%s'" % self.doc.fields.get(k)):
@@ -67,35 +72,105 @@
def validate_item(self):
item_det = webnotes.conn.sql("""select name, has_batch_no, docstatus,
- ifnull(is_stock_item, 'No') from tabItem where name=%s""",
- self.doc.item_code)
+ is_stock_item, has_serial_no, serial_no_series
+ from tabItem where name=%s""",
+ self.doc.item_code, as_dict=True)[0]
- # check item exists
- if item_det:
- item_det = item_det and item_det[0]
- else:
- msgprint("Item: '%s' does not exist in the system. Please check." % self.doc.item_code, raise_exception = 1)
-
- if item_det[3]!='Yes':
- webnotes.msgprint("""Item: "%s" is not a Stock Item.""" % self.doc.item_code,
- raise_exception=1)
-
- # check if item is trashed
- if cint(item_det[2])==2:
- msgprint("Item: '%s' is trashed, cannot make a stock transaction against a trashed item" % self.doc.item_code, raise_exception = 1)
+ if item_det.is_stock_item != 'Yes':
+ webnotes.throw("""Item: "%s" is not a Stock Item.""" % self.doc.item_code)
# check if batch number is required
- if item_det[1]=='Yes' and self.doc.voucher_type != 'Stock Reconciliation':
+ if item_det.has_batch_no =='Yes' and self.doc.voucher_type != 'Stock Reconciliation':
if not self.doc.batch_no:
- msgprint("Batch number is mandatory for Item '%s'" % self.doc.item_code, raise_exception = 1)
- raise Exception
+ webnotes.throw("Batch number is mandatory for Item '%s'" % self.doc.item_code)
# check if batch belongs to item
- if not webnotes.conn.sql("select name from `tabBatch` where item='%s' and name ='%s' and docstatus != 2" % (self.doc.item_code, self.doc.batch_no)):
- msgprint("'%s' is not a valid Batch Number for Item '%s'" % (self.doc.batch_no, self.doc.item_code), raise_exception = 1)
+ if not webnotes.conn.sql("""select name from `tabBatch`
+ where item='%s' and name ='%s' and docstatus != 2""" % (self.doc.item_code, self.doc.batch_no)):
+ webnotes.throw("'%s' is not a valid Batch Number for Item '%s'" % (self.doc.batch_no, self.doc.item_code))
- # Nobody can do SL Entries where posting date is before freezing date except authorized person
- #----------------------------------------------------------------------------------------------
+ self.validate_serial_no(item_det)
+
+ def validate_serial_no(self, item_det):
+ if item_det.has_serial_no=="No":
+ if self.doc.serial_no:
+ webnotes.throw(_("Serial Number should be blank for Non Serialized Item" + ": " + self.doc.item),
+ SerialNoNotRequiredError)
+ else:
+ if self.doc.serial_no:
+ serial_nos = get_serial_nos(self.doc.serial_no)
+ if cint(self.doc.actual_qty) != flt(self.doc.actual_qty):
+ webnotes.throw(_("Serial No qty cannot be a fraction") + \
+ (": %s (%s)" % (self.doc.item_code, self.doc.actual_qty)))
+ if len(serial_nos) and len(serial_nos) != abs(cint(self.doc.actual_qty)):
+ webnotes.throw(_("Serial Nos do not match with qty") + \
+ (": %s (%s)" % (self.doc.item_code, self.doc.actual_qty)), SerialNoQtyError)
+
+ # check serial no exists, if yes then source
+ for serial_no in serial_nos:
+ if webnotes.conn.exists("Serial No", serial_no):
+ sr = webnotes.bean("Serial No", serial_no)
+
+ if sr.doc.item_code!=self.doc.item_code:
+ webnotes.throw(_("Serial No does not belong to Item") + \
+ (": %s (%s)" % (self.doc.item_code, serial_no)), SerialNoItemError)
+
+ sr.make_controller().via_stock_ledger = True
+
+ if self.doc.actual_qty < 0:
+ if sr.doc.warehouse!=self.doc.warehouse:
+ webnotes.throw(_("Warehouse does not belong to Item") + \
+ (": %s (%s)" % (self.doc.item_code, serial_no)), SerialNoWarehouseError)
+
+ if self.doc.voucher_type in ("Delivery Note", "Sales Invoice") \
+ and sr.doc.status != "Available":
+ webnotes.throw(_("Serial No status must be 'Available' to Deliver") + \
+ ": " + serial_no, SerialNoStatusError)
+
+
+ sr.doc.warehouse = None
+ sr.save()
+ else:
+ sr.doc.warehouse = self.doc.warehouse
+ sr.save()
+ else:
+ if self.doc.actual_qty < 0:
+ # transfer out
+ webnotes.throw(_("Serial No must exist to transfer out.") + \
+ ": " + serial_no, SerialNoNotExistsError)
+ else:
+ # transfer in
+ self.make_serial_no(serial_no)
+ else:
+ if item_det.serial_no_series:
+ from webnotes.model.doc import make_autoname
+ serial_nos = []
+ for i in xrange(cint(self.doc.actual_qty)):
+ serial_nos.append(self.make_serial_no(make_autoname(item_det.serial_no_series)))
+ self.doc.serial_no = "\n".join(serial_nos)
+ else:
+ webnotes.throw(_("Serial Number Required for Serialized Item" + ": " + self.doc.item),
+ SerialNoRequiredError)
+
+ def make_serial_no(self, serial_no):
+ sr = webnotes.new_bean("Serial No")
+ sr.doc.serial_no = serial_no
+ sr.doc.item_code = self.doc.item_code
+ sr.doc.purchase_rate = self.doc.incoming_rate
+ sr.doc.purchase_document_type = self.doc.voucher_type
+ sr.doc.purchase_document_no = self.doc.voucher_no
+ sr.doc.purchase_date = self.doc.posting_date
+ sr.doc.purchase_time = self.doc.posting_time
+ sr.make_controller().via_stock_ledger = True
+ sr.insert()
+
+ # set warehouse
+ sr.doc.warehouse = self.doc.warehouse
+ sr.doc.status = "Available"
+ sr.save()
+ webnotes.msgprint(_("Serial No created") + ": " + sr.doc.name)
+ return sr.doc.name
+
def check_stock_frozen_date(self):
stock_frozen_upto = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto') or ''
if stock_frozen_upto:
@@ -107,6 +182,21 @@
if not self.doc.posting_time or self.doc.posting_time == '00:0':
self.doc.posting_time = '00:00'
+def update_serial_nos_after_submit(controller, parenttype, parentfield):
+ if not hasattr(webnotes, "new_stock_ledger_entries"):
+ return
+
+ for d in controller.doclist.get({"parentfield": parentfield}):
+ serial_no = None
+ for sle in webnotes.new_stock_ledger_entries:
+ if sle.voucher_detail_no==d.name:
+ serial_no = sle.serial_no
+ break
+
+ if d.serial_no != serial_no:
+ d.serial_no = serial_no
+ webnotes.conn.set_value(d.doctype, d.name, "serial_no", serial_no)
+
def on_doctype_update():
if not webnotes.conn.sql("""show index from `tabStock Ledger Entry`
where Key_name="posting_sort_index" """):
diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.py b/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 617ec69..dbb0a34 100644
--- a/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -293,7 +293,7 @@
self.doc.stock_value_difference = 0.0
for d in self.entries:
- self.doc.stock_value_difference -= get_buying_amount(d.item_code, self.doc.doctype, self.doc.name,
+ self.doc.stock_value_difference -= get_buying_amount(self.doc.doctype, self.doc.name,
d.voucher_detail_no, stock_ledger_entries.get((d.item_code, d.warehouse), []))
webnotes.conn.set(self.doc, "stock_value_difference", self.doc.stock_value_difference)
diff --git a/stock/doctype/warehouse/test_warehouse.py b/stock/doctype/warehouse/test_warehouse.py
index 4e47d56..ed72562 100644
--- a/stock/doctype/warehouse/test_warehouse.py
+++ b/stock/doctype/warehouse/test_warehouse.py
@@ -16,5 +16,9 @@
"doctype": "Warehouse",
"warehouse_name": "_Test Warehouse 2",
"company": "_Test Company 1"
+ }, {
+ "doctype": "Warehouse User",
+ "parentfield": "warehouse_users",
+ "user": "test2@example.com"
}]
]
diff --git a/stock/page/stock_ageing/stock_ageing.js b/stock/page/stock_ageing/stock_ageing.js
index fe2c0d6..920ac84 100644
--- a/stock/page/stock_ageing/stock_ageing.js
+++ b/stock/page/stock_ageing/stock_ageing.js
@@ -176,7 +176,8 @@
xaxis: {
ticks: $.map(me.data, function(item, idx) { return [[idx+1, item.name]] }),
max: 20
- }
+ },
+ series: { downsample: { threshold: 1000 } }
}
}
});
\ No newline at end of file
diff --git a/stock/page/stock_home/stock_home.js b/stock/page/stock_home/stock_home.js
index 580c2dc..1cab754 100644
--- a/stock/page/stock_home/stock_home.js
+++ b/stock/page/stock_home/stock_home.js
@@ -181,6 +181,11 @@
doctype: "Purchase Receipt"
},
{
+ "label":wn._("Item Shortage Report"),
+ route: "Report/Bin/Item Shortage Report",
+ doctype: "Purchase Receipt"
+ },
+ {
"label":wn._("Serial No Service Contract Expiry"),
route: "Report/Serial No/Serial No Service Contract Expiry",
doctype: "Serial No"
diff --git a/stock/page/stock_ledger/stock_ledger.js b/stock/page/stock_ledger/stock_ledger.js
index 33bceeb..dacd78c 100644
--- a/stock/page/stock_ledger/stock_ledger.js
+++ b/stock/page/stock_ledger/stock_ledger.js
@@ -235,6 +235,7 @@
min: dateutil.str_to_obj(this.from_date).getTime(),
max: dateutil.str_to_obj(this.to_date).getTime(),
},
+ series: { downsample: { threshold: 1000 } }
}
},
get_tooltip_text: function(label, x, y) {
diff --git a/stock/report/item_shortage_report/__init__.py b/stock/report/item_shortage_report/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/stock/report/item_shortage_report/__init__.py
diff --git a/stock/report/item_shortage_report/item_shortage_report.txt b/stock/report/item_shortage_report/item_shortage_report.txt
new file mode 100644
index 0000000..7d54ace
--- /dev/null
+++ b/stock/report/item_shortage_report/item_shortage_report.txt
@@ -0,0 +1,22 @@
+[
+ {
+ "creation": "2013-08-20 13:43:30",
+ "docstatus": 0,
+ "modified": "2013-08-20 13:46:15",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "Report",
+ "is_standard": "Yes",
+ "json": "{\"filters\":[[\"Bin\",\"projected_qty\",\"<\",\"0\"]],\"columns\":[[\"warehouse\",\"Bin\"],[\"item_code\",\"Bin\"],[\"actual_qty\",\"Bin\"],[\"ordered_qty\",\"Bin\"],[\"planned_qty\",\"Bin\"],[\"reserved_qty\",\"Bin\"],[\"projected_qty\",\"Bin\"]],\"sort_by\":\"Bin.projected_qty\",\"sort_order\":\"asc\",\"sort_by_next\":\"\",\"sort_order_next\":\"desc\"}",
+ "name": "__common__",
+ "ref_doctype": "Bin",
+ "report_name": "Item Shortage Report",
+ "report_type": "Report Builder"
+ },
+ {
+ "doctype": "Report",
+ "name": "Item Shortage Report"
+ }
+]
\ No newline at end of file
diff --git a/stock/report/items_to_be_requested/__init__.py b/stock/report/items_to_be_requested/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/stock/report/items_to_be_requested/__init__.py
diff --git a/stock/report/items_to_be_requested/items_to_be_requested.txt b/stock/report/items_to_be_requested/items_to_be_requested.txt
new file mode 100644
index 0000000..91e8ca3
--- /dev/null
+++ b/stock/report/items_to_be_requested/items_to_be_requested.txt
@@ -0,0 +1,22 @@
+[
+ {
+ "creation": "2013-08-20 15:08:10",
+ "docstatus": 0,
+ "modified": "2013-08-20 15:10:45",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "Report",
+ "is_standard": "Yes",
+ "name": "__common__",
+ "query": "SELECT\n tabBin.item_code as \"Item:Link/Item:120\",\n tabBin.warehouse as \"Warehouse:Link/Warehouse:120\",\n tabBin.actual_qty as \"Actual:Float:90\",\n tabBin.indented_qty as \"Requested:Float:90\",\n tabBin.reserved_qty as \"Reserved:Float:90\",\n tabBin.ordered_qty as \"Ordered:Float:90\",\n tabBin.projected_qty as \"Projected:Float:90\"\nFROM\n tabBin, tabItem\nWHERE\n tabBin.item_code = tabItem.name\n AND tabItem.is_purchase_item = \"Yes\"\n AND tabBin.projected_qty < 0\nORDER BY\n tabBin.projected_qty ASC",
+ "ref_doctype": "Item",
+ "report_name": "Items To Be Requested",
+ "report_type": "Query Report"
+ },
+ {
+ "doctype": "Report",
+ "name": "Items To Be Requested"
+ }
+]
\ No newline at end of file
diff --git a/stock/utils.py b/stock/utils.py
index 848783b..52471e4 100644
--- a/stock/utils.py
+++ b/stock/utils.py
@@ -8,6 +8,8 @@
from webnotes.defaults import get_global_default
from webnotes.utils.email_lib import sendmail
+class UserNotAllowedForWarehouse(webnotes.ValidationError): pass
+
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
if not end_of_life:
end_of_life = webnotes.conn.get_value("Item", item_code, "end_of_life")
@@ -152,20 +154,28 @@
wlist.append([w])
return wlist
-def get_buying_amount(item_code, voucher_type, voucher_no, voucher_detail_no,
- stock_ledger_entries, item_sales_bom=None):
- if item_sales_bom and item_sales_bom.get(item_code):
- # sales bom item
- buying_amount = 0.0
- for bom_item in item_sales_bom[item_code]:
- if bom_item.get("parent_detail_docname")==voucher_detail_no:
- buying_amount += _get_buying_amount(voucher_type, voucher_no, voucher_detail_no, stock_ledger_entries)
- return buying_amount
- else:
- # doesn't have sales bom
- return _get_buying_amount(voucher_type, voucher_no, voucher_detail_no, stock_ledger_entries)
+def validate_warehouse_user(warehouse):
+ if webnotes.session.user=="Administrator":
+ return
+ warehouse_users = [p[0] for p in webnotes.conn.sql("""select user from `tabWarehouse User`
+ where parent=%s""", warehouse)]
+
+ if warehouse_users and not (webnotes.session.user in warehouse_users):
+ webnotes.throw(_("Not allowed entry in Warehouse") \
+ + ": " + warehouse, UserNotAllowedForWarehouse)
+
+def get_sales_bom_buying_amount(item_code, warehouse, voucher_type, voucher_no, voucher_detail_no,
+ stock_ledger_entries, item_sales_bom):
+ # sales bom item
+ buying_amount = 0.0
+ for bom_item in item_sales_bom[item_code]:
+ if bom_item.get("parent_detail_docname")==voucher_detail_no:
+ buying_amount += get_buying_amount(voucher_type, voucher_no, voucher_detail_no,
+ stock_ledger_entries.get((bom_item.item_code, warehouse), []))
+
+ return buying_amount
-def _get_buying_amount(voucher_type, voucher_no, item_row, stock_ledger_entries):
+def get_buying_amount(voucher_type, voucher_no, item_row, stock_ledger_entries):
# IMP NOTE
# stock_ledger_entries should already be filtered by item_code and warehouse and
# sorted by posting_date desc, posting_time desc
@@ -193,8 +203,7 @@
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",
diff --git a/support/doctype/maintenance_schedule/maintenance_schedule.py b/support/doctype/maintenance_schedule/maintenance_schedule.py
index 8659b9f..59d3f8e 100644
--- a/support/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/support/doctype/maintenance_schedule/maintenance_schedule.py
@@ -193,10 +193,6 @@
if not chk1:
msgprint("Serial no "+x+" does not exist in system.")
raise Exception
- else:
- if status=='In Store' or status=='Note in Use' or status=='Scrapped':
- msgprint("Serial no "+x+" is '"+status+"'")
- raise Exception
def validate(self):
self.validate_maintenance_detail()
diff --git a/support/doctype/support_ticket/support_ticket.txt b/support/doctype/support_ticket/support_ticket.txt
index 53d1c7c..dcb4520 100644
--- a/support/doctype/support_ticket/support_ticket.txt
+++ b/support/doctype/support_ticket/support_ticket.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-02-01 10:36:25",
"docstatus": 0,
- "modified": "2013-08-08 14:22:34",
+ "modified": "2013-08-28 18:29:06",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -225,17 +225,6 @@
"read_only": 1
},
{
- "depends_on": "eval:!doc.__islocal",
- "doctype": "DocField",
- "fieldname": "resolution_details",
- "fieldtype": "Small Text",
- "label": "Resolution Details",
- "no_copy": 1,
- "oldfieldname": "resolution_details",
- "oldfieldtype": "Text",
- "read_only": 1
- },
- {
"doctype": "DocField",
"fieldname": "first_responded_on",
"fieldtype": "Datetime",
@@ -255,6 +244,17 @@
"search_index": 0
},
{
+ "depends_on": "eval:!doc.__islocal",
+ "doctype": "DocField",
+ "fieldname": "resolution_details",
+ "fieldtype": "Small Text",
+ "label": "Resolution Details",
+ "no_copy": 1,
+ "oldfieldname": "resolution_details",
+ "oldfieldtype": "Text",
+ "read_only": 0
+ },
+ {
"doctype": "DocField",
"fieldname": "content_type",
"fieldtype": "Data",
diff --git a/translations/it.csv b/translations/it.csv
new file mode 100644
index 0000000..c91fc84
--- /dev/null
+++ b/translations/it.csv
Binary files differ
diff --git a/utilities/demo/__init__.py b/utilities/demo/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/utilities/demo/__init__.py
diff --git a/utilities/demo/demo-login.css b/utilities/demo/demo-login.css
new file mode 100644
index 0000000..f3464a3
--- /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 0000000..4595cb7
--- /dev/null
+++ b/utilities/demo/demo-login.html
@@ -0,0 +1,25 @@
+<div class="container">
+ <div class="row" style="margin-top: 100px;">
+ <div class="col-sm-offset-3 col-sm-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ Start ERPNext Demo
+ </div>
+ <div class="panel-body">
+ <p>
+ <input id="lead-email" type="email"
+ class="form-control" placeholder="Your Email Id (optional)">
+ </p>
+ <p>
+ <button type="submit" id="login_btn"
+ class="btn btn-primary btn-large">Launch Demo</button>
+ </p>
+ <hr>
+ <p class="text-muted small">Some functionality is disabled for the demo app. The demo data will be cleared regulary. To start your own ERPNext Trial, <a href="https://erpnext.com/pricing-and-signup">click here</a></p>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ </div>
+</div>
diff --git a/utilities/demo/demo-login.js b/utilities/demo/demo-login.js
new file mode 100644
index 0000000..229d169
--- /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 0000000..c70913e
--- /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/demo_docs/Item.csv b/utilities/demo/demo_docs/Item.csv
new file mode 100644
index 0000000..ff61c5f
--- /dev/null
+++ b/utilities/demo/demo_docs/Item.csv
@@ -0,0 +1,50 @@
+Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Table:,Item,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+DocType:,Item,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,-,Item Reorder,item_reorder,,,,-,UOM Conversion Detail,uom_conversion_details,,-,Item Supplier,item_supplier_details,,-,Item Customer Detail,item_customer_details,,-,Item Tax,item_tax,,-,Item Price,ref_rate_details,,,,-,Item Quality Inspection Parameter,item_specification_details,,-,Website Item Group,website_item_groups,-,Item Website Specification,item_website_specifications,
+Column Labels:,ID,Last Updated On,Item Name,Item Group,Default Unit of Measure,Description,Is Stock Item,Is Asset Item,Has Batch No,Has Serial No,Is Purchase Item,Is Sales Item,Is Service Item,Inspection Required,Allow Bill of Materials,Allow Production Order,Is Sub Contracted Item,Document Numbering Series,Item Code,Brand,Barcode,Image,Description HTML,Default Warehouse,Allowance Percent,Valuation Method,Minimum Order Qty,Serial Number Series,Warranty Period (in days),End of Life,Net Weight,Weight UOM,Re-Order Level,Re-Order Qty,Default Supplier,Lead Time Days,Default Expense Account,Default Cost Center,Last Purchase Rate,Standard Rate,Manufacturer,Manufacturer Part Number,Max Discount (%),Default Income Account,Cost Center,Default BOM,Show in Website,Page Name,Weightage,Slideshow,Image,Website Warehouse,Website Description,-,ID,Warehouse,Re-order Level,Material Request Type,Re-order Qty,-,ID,UOM,Conversion Factor,-,ID,Supplier,Supplier Part Number,-,ID,Customer Name,Ref Code,-,ID,Tax,Tax Rate,-,ID,Price List Name,Ref Rate,Valid for Buying or Selling?,Currency,-,ID,Parameter,Acceptance Criteria,-,ID,Item Group,-,ID,Label,Description
+Column Name:,name,modified,item_name,item_group,stock_uom,description,is_stock_item,is_asset_item,has_batch_no,has_serial_no,is_purchase_item,is_sales_item,is_service_item,inspection_required,is_manufactured_item,is_pro_applicable,is_sub_contracted_item,naming_series,item_code,brand,barcode,image,description_html,default_warehouse,tolerance,valuation_method,min_order_qty,serial_no_series,warranty_period,end_of_life,net_weight,weight_uom,re_order_level,re_order_qty,default_supplier,lead_time_days,purchase_account,cost_center,last_purchase_rate,standard_rate,manufacturer,manufacturer_part_no,max_discount,default_income_account,default_sales_cost_center,default_bom,show_in_website,page_name,weightage,slideshow,website_image,website_warehouse,web_long_description,-,name,warehouse,warehouse_reorder_level,material_request_type,warehouse_reorder_qty,-,name,uom,conversion_factor,-,name,supplier,supplier_part_no,-,name,customer_name,ref_code,-,name,tax_type,tax_rate,-,name,price_list,ref_rate,buying_or_selling,ref_currency,-,name,specification,value,-,name,item_group,-,name,label,description
+Mandatory:,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,-,Yes,Yes,Yes,Yes,No,-,Yes,No,No,-,Yes,No,No,-,Yes,Yes,Yes,-,Yes,Yes,No,-,Yes,Yes,Yes,Yes,No,-,Yes,Yes,No,-,Yes,No,-,Yes,No,No
+Type:,Data (text),Data,Data,Link,Link,Small Text,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Data,Link,Data,Select,Small Text,Link,Float,Select,Float,Data,Data,Date,Float,Link,Float,Float,Link,Int,Link,Link,Float,Float,Data,Data,Float,Link,Link,Link,Check,Data,Int,Link,Select,Link,Text Editor,-,Data,Link,Float,Select,Float,-,Data,Link,Float,-,Data,Link,Data,-,Data,Link,Data,-,Data,Link,Float,-,Data,Link,Currency,Select,Link,-,Data,Data,Data,-,Data,Link,-,Data,Data,Text
+Info:,,Don't change!,,Valid Item Group,Valid UOM,,"One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No",One of: ITEM,,Valid Brand,,One of: attach_files:,,Valid Warehouse,,"One of: FIFO, Moving Average",,,,,,Valid UOM,,,Valid Supplier,Integer,Valid Account,Valid Cost Center,,,,,,Valid Account,Valid Cost Center,Valid BOM,0 or 1,,Integer,Valid Website Slideshow,One of: attach_files:,Valid Warehouse,,-,Leave blank for new records,Valid Warehouse,,"One of: Purchase, Transfer",,-,Leave blank for new records,Valid UOM,,-,Leave blank for new records,Valid Supplier,,-,Leave blank for new records,Valid Customer,,-,Leave blank for new records,Valid Account,,-,Leave blank for new records,Valid Price List,,"One of: Buying, Selling",Valid Currency,-,Leave blank for new records,,,-,Leave blank for new records,Valid Item Group,-,Leave blank for new records,,
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,Base Bearing Plate,"""2013-08-20 11:11:53""",Base Bearing Plate,Raw Material,Nos,1/4 in. x 6 in. x 6 in. Mild Steel Plate,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Base Bearing Plate,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Eagle Hardware,0,,,15.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00001,Nos,1.0,,,,,,,,,,,,,,RFD/00001,Standard Buying,15.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00017,Standard Selling,21.0,Selling,USD,,,,,,,,,,,
+,Base Plate,"""2013-08-20 11:11:53""",Base Plate,Raw Material,Nos,3/4 in. x 2 ft. x 4 ft. Pine Plywood,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Base Plate,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,HomeBase,0,,,20.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00002,Nos,1.0,,,,,,,,,,,,,,RFD/00002,Standard Buying,20.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00018,Standard Selling,28.0,Selling,USD,,,,,,,,,,,
+,Bearing Assembly,"""2013-08-20 11:11:55""",Bearing Assembly,Sub Assemblies,Nos,Bearing Assembly,Yes,No,No,No,No,Yes,No,No,Yes,Yes,No,,Bearing Assembly,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Asiatic Solutions,0,,,0.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00003,Nos,1.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
+,Bearing Block,"""2013-08-20 11:11:54""",Bearing Block,Raw Material,Nos,"CAST IRON, MCMASTER PART NO. 3710T13",Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Bearing Block,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Nan Duskin,0,,,10.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00004,Nos,1.0,,,,,,,,,,,,,,RFD/00003,Standard Buying,10.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00019,Standard Selling,14.0,Selling,USD,,,,,,,,,,,
+,Bearing Collar,"""2013-08-20 11:11:54""",Bearing Collar,Raw Material,Nos,1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Bearing Collar,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Eagle Hardware,0,,,20.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00005,Nos,1.0,,,,,,,,,,,,,,RFD/00004,Standard Buying,20.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00020,Standard Selling,28.0,Selling,USD,,,,,,,,,,,
+,Bearing Pipe,"""2013-08-20 11:11:54""",Bearing Pipe,Raw Material,Nos,1.5 in. Diameter x 36 in. Mild Steel Tubing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Bearing Pipe,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,HomeBase,0,,,15.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00006,Nos,1.0,,,,,,,,,,,,,,RFD/00005,Standard Buying,15.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00021,Standard Selling,21.0,Selling,USD,,,,,,,,,,,
+,Blade Rib,"""2013-08-20 11:11:54""",Blade Rib,Raw Material,Nos,1/2 in. x 2 ft. x 4 ft. Pine Plywood,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Blade Rib,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Ks Merchandise,0,,,0.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00007,Nos,1.0,,,,,,,,,,,,,,RFD/00006,Standard Buying,10.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00022,Standard Selling,14.0,Selling,USD,,,,,,,,,,,
+,Disc Collars,"""2013-08-20 11:11:54""",Disc Collars,Raw Material,Nos,For Upper Bearing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Disc Collars,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Asiatic Solutions,0,,,0.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00008,Nos,1.0,,,,,,,,,,,,,,RFD/00007,Standard Buying,74.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00023,Standard Selling,103.6,Selling,USD,,,,,,,,,,,
+,External Disc,"""2013-08-20 11:11:54""",External Disc,Raw Material,Nos,15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,External Disc,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,HomeBase,0,,,45.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00009,Nos,1.0,,,,,,,,,,,,,,RFD/00008,Standard Buying,45.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00024,Standard Selling,63.0,Selling,USD,,,,,,,,,,,
+,Internal Disc,"""2013-08-20 11:11:54""",Internal Disc,Raw Material,Nos,For Bearing Collar,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Internal Disc,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,HomeBase,0,,,33.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00010,Nos,1.0,,,,,,,,,,,,,,RFD/00009,Standard Buying,33.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00025,Standard Selling,46.2,Selling,USD,,,,,,,,,,,
+,Shaft,"""2013-08-20 11:11:54""",Shaft,Raw Material,Nos,1.25 in. Diameter x 6 ft. Mild Steel Tubing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Shaft,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Eagle Hardware,0,,,30.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00011,Nos,1.0,,,,,,,,,,,,,,RFD/00010,Standard Buying,30.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00026,Standard Selling,42.0,Selling,USD,,,,,,,,,,,
+,Stand,"""2013-08-20 11:11:54""",Stand,Raw Material,Nos,N/A,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Stand,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Scott Ties,0,,,0.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00012,Nos,1.0,,,,,,,,,,,,,,RFD/00011,Standard Buying,40.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00027,Standard Selling,56.0,Selling,USD,,,,,,,,,,,
+,Upper Bearing Plate,"""2013-08-20 11:11:54""",Upper Bearing Plate,Raw Material,Nos,3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Upper Bearing Plate,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,Eagle Hardware,0,,,50.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00013,Nos,1.0,,,,,,,,,,,,,,RFD/00012,Standard Buying,50.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00028,Standard Selling,70.0,Selling,USD,,,,,,,,,,,
+,Wind Mill A Series,"""2013-08-20 11:27:46""",Wind Mill A Series,Products,Nos,Wind Mill A Series for Home Use 9ft,Yes,No,No,Yes,No,Yes,Yes,No,Yes,Yes,No,,Wind Mill A Series,,,,,Finished Goods - WP,0.0,,0.0,WMA,,,0.0,,0.0,0.0,,0,,,0.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00014,Nos,1.0,,,,,,,,,,,,,,RFD/00015,Standard Selling,340.0,Selling,USD,,,,,,,,,,,
+,Wind MIll C Series,"""2013-08-20 11:27:27""",Wind MIll C Series,Products,Nos,Wind Mill C Series for Commercial Use 18ft,Yes,No,No,Yes,No,Yes,Yes,No,Yes,Yes,No,,Wind MIll C Series,,,,,Finished Goods - WP,0.0,,0.0,WMC,,,0.0,,0.0,0.0,,0,,,0.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00015,Nos,1.0,,,,,,,,,,,,,,RFD/00016,Standard Selling,400.0,Selling,USD,,,,,,,,,,,
+,Wind Turbine,"""2013-08-20 11:29:26""",Wind Turbine,Products,Nos,Small Wind Turbine for Home Use,Yes,No,No,Yes,No,Yes,Yes,No,Yes,Yes,No,,Wind Turbine,,,,,Finished Goods - WP,0.0,,0.0,WTU,,,0.0,,0.0,0.0,,0,,,0.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00016,Nos,1.0,,,,,,,,,,,,,,RFD/00014,Standard Selling,300.0,Selling,USD,,,,,,,,,,,
+,Wing Sheet,"""2013-08-20 11:11:55""",Wing Sheet,Raw Material,Nos,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,,Wing Sheet,,,,,Stores - WP,0.0,,0.0,,,,0.0,,0.0,0.0,New World Realty,0,,,22.0,0.0,,,0.0,,,,0,,0,,,,,,,,,,,,UCDD/00017,Nos,1.0,,,,,,,,,,,,,,RFD/00013,Standard Buying,22.0,Buying,USD,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,RFD/00029,Standard Selling,30.8,Selling,USD,,,,,,,,,,,
\ No newline at end of file
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
Binary files differ
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
Binary files differ
diff --git a/utilities/demo/make_demo.py b/utilities/demo/make_demo.py
new file mode 100644
index 0000000..6f18f76
--- /dev/null
+++ b/utilities/demo/make_demo.py
@@ -0,0 +1,417 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+import webnotes, os, datetime
+import webnotes.utils
+from webnotes.utils import random_string
+from webnotes.widgets import query_report
+import random
+import json
+
+webnotes.session = webnotes._dict({"user":"Administrator"})
+from core.page.data_import_tool.data_import_tool import upload
+
+# fix price list
+# fix fiscal year
+
+company = "Wind Power LLC"
+company_abbr = "WP"
+country = "United States"
+currency = "USD"
+time_zone = "America/New York"
+start_date = '2013-01-01'
+bank_name = "Citibank"
+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, simulate=True):
+ webnotes.connect()
+ #webnotes.print_messages = True
+ webnotes.mute_emails = True
+ webnotes.rollback_on_exception = True
+
+ if reset:
+ setup()
+ if simulate:
+ simulate()
+
+def setup():
+ install()
+ complete_setup()
+ make_customers_suppliers_contacts()
+ make_items()
+ make_users_and_employees()
+ make_bank_account()
+ # make_opening_stock()
+ # make_opening_accounts()
+
+def simulate():
+ 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)
+ run_purchase(current_date)
+ 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"):
+ for i in xrange(how_many("Quotation")):
+ make_quotation(current_date)
+
+ if can_make("Sales Order"):
+ for i in xrange(how_many("Sales Order")):
+ make_sales_order(current_date)
+
+def run_accounts(current_date):
+ if can_make("Sales Invoice"):
+ from selling.doctype.sales_order.sales_order import make_sales_invoice
+ report = "Ordered Items to be Billed"
+ for so in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Sales Invoice")]:
+ si = webnotes.bean(make_sales_invoice(so))
+ si.doc.posting_date = current_date
+ si.insert()
+ si.submit()
+ webnotes.conn.commit()
+
+ if can_make("Purchase Invoice"):
+ from stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
+ report = "Received Items to be Billed"
+ for pr in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Purchase Invoice")]:
+ pi = webnotes.bean(make_purchase_invoice(pr))
+ pi.doc.posting_date = current_date
+ pi.doc.bill_no = random_string(6)
+ pi.insert()
+ pi.submit()
+ webnotes.conn.commit()
+
+ if can_make("Payment Received"):
+ from accounts.doctype.journal_voucher.journal_voucher import get_payment_entry_from_sales_invoice
+ report = "Accounts Receivable"
+ for si in list(set([r[4] for r in query_report.run(report, {"report_date": current_date })["result"] if r[3]=="Sales Invoice"]))[:how_many("Payment Received")]:
+ jv = webnotes.bean(get_payment_entry_from_sales_invoice(si))
+ jv.doc.posting_date = current_date
+ jv.doc.cheque_no = random_string(6)
+ jv.doc.cheque_date = current_date
+ jv.insert()
+ jv.submit()
+ webnotes.conn.commit()
+
+ if can_make("Payment Made"):
+ from accounts.doctype.journal_voucher.journal_voucher import get_payment_entry_from_purchase_invoice
+ report = "Accounts Payable"
+ for pi in list(set([r[4] for r in query_report.run(report, {"report_date": current_date })["result"] if r[3]=="Purchase Invoice"]))[:how_many("Payment Made")]:
+ jv = webnotes.bean(get_payment_entry_from_purchase_invoice(pi))
+ jv.doc.posting_date = current_date
+ jv.doc.cheque_no = random_string(6)
+ jv.doc.cheque_date = current_date
+ jv.insert()
+ jv.submit()
+ webnotes.conn.commit()
+
+def run_stock(current_date):
+ # make purchase requests
+ if can_make("Purchase Receipt"):
+ from buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+ report = "Purchase Order Items To Be Received"
+ 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 = "2013"
+ pr.insert()
+ pr.submit()
+ webnotes.conn.commit()
+
+ # make delivery notes (if possible)
+ if can_make("Delivery Note"):
+ from selling.doctype.sales_order.sales_order import make_delivery_note
+ from stock.stock_ledger import NegativeStockError
+ from stock.doctype.stock_ledger_entry.stock_ledger_entry import SerialNoRequiredError, SerialNoQtyError
+ report = "Ordered Items To Be Delivered"
+ 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 = "2013"
+ dn.insert()
+ try:
+ dn.submit()
+ webnotes.conn.commit()
+ except NegativeStockError: pass
+ except SerialNoRequiredError: pass
+ except SerialNoQtyError: pass
+
+ # try submitting existing
+ for dn in webnotes.conn.get_values("Delivery Note", {"docstatus": 0}, "name"):
+ b = webnotes.bean("Delivery Note", dn[0])
+ b.submit()
+ webnotes.conn.commit()
+
+def run_purchase(current_date):
+ # make material requests for purchase items that have negative projected qtys
+ if can_make("Material Request"):
+ report = "Items To Be Requested"
+ for row in query_report.run(report)["result"][:how_many("Material Request")]:
+ mr = webnotes.new_bean("Material Request")
+ mr.doc.material_request_type = "Purchase"
+ mr.doc.transaction_date = current_date
+ mr.doc.fiscal_year = "2013"
+ mr.doclist.append({
+ "doctype": "Material Request Item",
+ "parentfield": "indent_details",
+ "schedule_date": webnotes.utils.add_days(current_date, 7),
+ "item_code": row[0],
+ "qty": -row[-1]
+ })
+ mr.insert()
+ mr.submit()
+
+ # make supplier quotations
+ if can_make("Supplier Quotation"):
+ from stock.doctype.material_request.material_request import make_supplier_quotation
+ report = "Material Requests for which Supplier Quotations are not created"
+ for row in query_report.run(report)["result"][:how_many("Supplier Quotation")]:
+ if row[0] != "Total":
+ sq = webnotes.bean(make_supplier_quotation(row[0]))
+ sq.doc.transaction_date = current_date
+ sq.doc.fiscal_year = "2013"
+ sq.insert()
+ sq.submit()
+ webnotes.conn.commit()
+
+ # make purchase orders
+ if can_make("Purchase Order"):
+ from stock.doctype.material_request.material_request import make_purchase_order
+ report = "Requested Items To Be Ordered"
+ for row in query_report.run(report)["result"][:how_many("Purchase Order")]:
+ if row[0] != "Total":
+ po = webnotes.bean(make_purchase_order(row[0]))
+ po.doc.transaction_date = current_date
+ po.doc.fiscal_year = "2013"
+ po.insert()
+ po.submit()
+ webnotes.conn.commit()
+
+def run_manufacturing(current_date):
+ from stock.stock_ledger import NegativeStockError
+ from stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, DuplicateEntryForProductionOrderError
+
+ ppt = webnotes.bean("Production Planning Tool", "Production Planning Tool")
+ ppt.doc.company = company
+ ppt.doc.use_multi_level_bom = 1
+ ppt.doc.purchase_request_for_warehouse = "Stores - WP"
+ ppt.run_method("get_open_sales_orders")
+ ppt.run_method("get_items_from_so")
+ ppt.run_method("raise_production_order")
+ ppt.run_method("raise_purchase_request")
+ webnotes.conn.commit()
+
+ # submit production orders
+ for pro in webnotes.conn.get_values("Production Order", {"docstatus": 0}, "name"):
+ b = webnotes.bean("Production Order", pro[0])
+ b.doc.wip_warehouse = "Work in Progress - WP"
+ b.submit()
+ webnotes.conn.commit()
+
+ # submit material requests
+ for pro in webnotes.conn.get_values("Material Request", {"docstatus": 0}, "name"):
+ b = webnotes.bean("Material Request", pro[0])
+ b.submit()
+ webnotes.conn.commit()
+
+ # stores -> wip
+ if can_make("Stock Entry for WIP"):
+ for pro in query_report.run("Open Production Orders")["result"][:how_many("Stock Entry for WIP")]:
+ make_stock_entry_from_pro(pro[0], "Material Transfer", current_date)
+
+ # wip -> fg
+ if can_make("Stock Entry for FG"):
+ for pro in query_report.run("Production Orders in Progress")["result"][:how_many("Stock Entry for FG")]:
+ make_stock_entry_from_pro(pro[0], "Manufacture/Repack", current_date)
+
+ # try posting older drafts (if exists)
+ for st in webnotes.conn.get_values("Stock Entry", {"docstatus":0}, "name"):
+ try:
+ webnotes.bean("Stock Entry", st[0]).submit()
+ webnotes.conn.commit()
+ except NegativeStockError: pass
+ except IncorrectValuationRateError: pass
+ except DuplicateEntryForProductionOrderError: pass
+
+def make_stock_entry_from_pro(pro_id, purpose, current_date):
+ from manufacturing.doctype.production_order.production_order import make_stock_entry
+ from stock.stock_ledger import NegativeStockError
+ from stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, DuplicateEntryForProductionOrderError
+
+ try:
+ st = webnotes.bean(make_stock_entry(pro_id, purpose))
+ st.doc.posting_date = current_date
+ st.doc.fiscal_year = "2013"
+ st.doc.expense_adjustment_account = "Stock in Hand - WP"
+ st.insert()
+ webnotes.conn.commit()
+ st.submit()
+ webnotes.conn.commit()
+ except NegativeStockError: pass
+ except IncorrectValuationRateError: pass
+ except DuplicateEntryForProductionOrderError: pass
+
+def make_quotation(current_date):
+ b = webnotes.bean([{
+ "creation": current_date,
+ "doctype": "Quotation",
+ "quotation_to": "Customer",
+ "customer": get_random("Customer"),
+ "order_type": "Sales",
+ "transaction_date": current_date,
+ "fiscal_year": "2013"
+ }])
+
+ add_random_children(b, {
+ "doctype": "Quotation Item",
+ "parentfield": "quotation_details",
+ }, rows=3, randomize = {
+ "qty": (1, 5),
+ "item_code": ("Item", {"is_sales_item": "Yes"})
+ }, unique="item_code")
+
+ b.insert()
+ webnotes.conn.commit()
+ b.submit()
+ webnotes.conn.commit()
+
+def make_sales_order(current_date):
+ q = get_random("Quotation", {"status": "Submitted"})
+ if q:
+ from selling.doctype.quotation.quotation import make_sales_order
+ so = webnotes.bean(make_sales_order(q))
+ so.doc.transaction_date = current_date
+ so.doc.delivery_date = webnotes.utils.add_days(current_date, 10)
+ so.insert()
+ webnotes.conn.commit()
+ so.submit()
+ webnotes.conn.commit()
+
+def add_random_children(bean, template, rows, randomize, unique=None):
+ for i in xrange(random.randrange(1, rows)):
+ d = template.copy()
+ for key, val in randomize.items():
+ if isinstance(val[0], basestring):
+ d[key] = get_random(*val)
+ else:
+ d[key] = random.randrange(*val)
+
+ if unique:
+ if not bean.doclist.get({"doctype": d["doctype"], unique:d[unique]}):
+ bean.doclist.append(d)
+ else:
+ bean.doclist.append(d)
+
+def get_random(doctype, filters=None):
+ condition = []
+ if filters:
+ for key, val in filters.items():
+ condition.append("%s='%s'" % (key, val))
+ if condition:
+ condition = " where " + " and ".join(condition)
+ else:
+ condition = ""
+
+ out = webnotes.conn.sql("""select name from `tab%s` %s
+ order by RAND() limit 0,1""" % (doctype, condition))
+
+ return out and out[0][0] or None
+
+def can_make(doctype):
+ return random.random() < prob.get(doctype, prob["default"])["make"]
+
+def how_many(doctype):
+ return random.randrange(*prob.get(doctype, prob["default"])["qty"])
+
+def install():
+ print "Creating Fresh Database..."
+ from webnotes.install_lib.install import Installer
+ import conf
+ inst = Installer('root')
+ inst.import_from_db(conf.demo_db_name, verbose = 1)
+
+def complete_setup():
+ print "Complete Setup..."
+ webnotes.get_obj("Setup Control").setup_account({
+ "first_name": "Test",
+ "last_name": "User",
+ "fy_start": "1st Jan",
+ "industry": "Manufacturing",
+ "company_name": company,
+ "company_abbr": company_abbr,
+ "currency": currency,
+ "timezone": time_zone,
+ "country": country
+ })
+
+ import_data("Fiscal_Year")
+
+def make_items():
+ import_data("Item")
+ import_data("BOM", submit=True)
+
+def make_customers_suppliers_contacts():
+ import_data(["Customer", "Supplier", "Contact", "Address", "Lead"])
+
+def make_users_and_employees():
+ webnotes.conn.set_value("HR Settings", None, "emp_created_by", "Naming Series")
+ webnotes.conn.commit()
+
+ import_data(["Profile", "Employee", "Salary_Structure"])
+
+def make_bank_account():
+ ba = webnotes.bean({
+ "doctype": "Account",
+ "account_name": bank_name,
+ "account_type": "Bank or Cash",
+ "group_or_ledger": "Ledger",
+ "parent_account": "Bank Accounts - " + company_abbr,
+ "company": company
+ }).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)):
+ dt = [dt]
+
+ for doctype in dt:
+ print "Importing", doctype.replace("_", " "), "..."
+ webnotes.form_dict = webnotes._dict()
+ if submit:
+ webnotes.form_dict["params"] = json.dumps({"_submit": 1})
+ webnotes.uploaded_file = os.path.join(os.path.dirname(__file__), "demo_docs", doctype+".csv")
+ upload()
+
+if __name__=="__main__":
+ make()
\ No newline at end of file
diff --git a/utilities/demo/make_erpnext_demo.py b/utilities/demo/make_erpnext_demo.py
new file mode 100644
index 0000000..db1d25b
--- /dev/null
+++ b/utilities/demo/make_erpnext_demo.py
@@ -0,0 +1,121 @@
+if __name__=="__main__":
+ import sys
+ sys.path.extend([".", "lib", "app"])
+
+import webnotes, os
+import utilities.demo.make_demo
+
+def make_demo_app():
+
+
+def make_demo_user():
+ roles = ["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"]
+
+ def add_roles(bean):
+ for role in roles:
+ p.doclist.append({
+ "doctype": "UserRole",
+ "parentfield": "user_roles",
+ "role": role
+ })
+
+ # 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()
+ add_roles(p)
+ p.save()
+
+ # make system manager user
+ if webnotes.conn.exists("Profile", "admin@erpnext.com"):
+ webnotes.delete_doc("Profile", "admin@erpnext.com")
+
+ p = webnotes.new_bean("Profile")
+ p.doc.email = "admin@erpnext.com"
+ p.doc.first_name = "Admin"
+ p.doc.last_name = "User"
+ p.doc.enabled = 1
+ p.doc.user_type = "System User"
+ p.doc.send_invite_email = 0
+ p.doc.new_password = "admin010123"
+ p.insert()
+ roles.append("System Manager")
+ add_roles(p)
+ p.save()
+
+ # only read for newsletter
+ webnotes.conn.sql("""update `tabDocPerm` set `write`=0, `create`=0, `cancel`=0
+ where parent='Newsletter'""")
+ webnotes.conn.sql("""update `tabDocPerm` set `write`=0, `create`=0, `cancel`=0
+ where parent='Profile' and role='All'""")
+
+ 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()
+
+ website_settings = webnotes.bean("Website Settings", "Website Settings")
+ website_settings.doc.home_page = "demo-login"
+ website_settings.doc.disable_signup = 1
+ website_settings.save()
+
+ 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()
+ s.insert()
+
+ cp = webnotes.bean("Control Panel")
+ cp.doc.custom_startup_code = """wn.ui.toolbar.show_banner('You are using ERPNext Demo. To start your own ERPNext Trial, <a href="https://erpnext.com/pricing-and-signup" target="_blank">click here</a>')"""
+ cp.save()
+
+ webnotes.conn.commit()
+
+if __name__=="__main__":
+ # webnotes.connect()
+ webnotes.mute_emails = 1
+ utilities.demo.make_demo.make(reset=True, simulate=False)
+ # setup demo user etc so that the site it up faster, while the data loads
+ make_demo_user()
+ make_demo_login_page()
+ make_demo_on_login_script()
+ utilities.demo.make_demo.make(reset=False, simulate=True)
\ No newline at end of file
diff --git a/utilities/demo_docs/Item.csv b/utilities/demo_docs/Item.csv
deleted file mode 100644
index 3580748..0000000
--- a/utilities/demo_docs/Item.csv
+++ /dev/null
@@ -1,37 +0,0 @@
-Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Table:,Item,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Notes:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-DocType:,Item,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Column Labels:,ID,Item Name,Item Group,Default Unit of Measure,Description,Is Stock Item,Is Asset Item,Has Batch No,Has Serial No,Is Purchase Item,Is Sales Item,Is Service Item,Allow Samples,Inspection Required,Allow Bill of Materials,Allow Production Order,Is Sub Contracted Item,Document Numbering Series,Item Code,Brand,Barcode,Image,Description HTML,Default Warehouse,Allowance Percent,Valuation Method,Minimum Order Qty,Warranty Period (in days),End of Life,Net Weight,Weight UOM,Re-Order Level,Re-Order Qty,Default Supplier,Lead Time Days,Default Expense Account,Default Cost Center,Last Purchase Rate,Standard Rate,Manufacturer,Manufacturer Part Number,Max Discount (%),Default Income Account,Cost Center,Default BOM,Show in Website,Page Name,Weightage,Slideshow,Image,Website Warehouse,Website Description
-Column Name:,name,item_name,item_group,stock_uom,description,is_stock_item,is_asset_item,has_batch_no,has_serial_no,is_purchase_item,is_sales_item,is_service_item,is_sample_item,inspection_required,is_manufactured_item,is_pro_applicable,is_sub_contracted_item,naming_series,item_code,brand,barcode,image,description_html,default_warehouse,tolerance,valuation_method,min_order_qty,warranty_period,end_of_life,net_weight,weight_uom,re_order_level,re_order_qty,default_supplier,lead_time_days,purchase_account,cost_center,last_purchase_rate,standard_rate,manufacturer,manufacturer_part_no,max_discount,default_income_account,default_sales_cost_center,default_bom,show_in_website,page_name,weightage,slideshow,website_image,website_warehouse,web_long_description
-Mandatory:,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No
-Type:,Data (text),Data,Link,Link,Small Text,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Select,Data,Link,Data,Select,Small Text,Link,Float,Select,Float,Data,Date,Float,Link,Float,Float,Link,Int,Link,Link,Float,Float,Data,Data,Float,Link,Link,Link,Check,Data,Int,Link,Select,Link,Text Editor
-Info:,,,Valid Item Group,Valid UOM,,"One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No","One of: Yes, No",One of: ITEM,,Valid Brand,,One of: attach_files:,,Valid Warehouse,,"One of: FIFO, Moving Average",,,,,Valid UOM,,,Valid Supplier,Integer,Valid Account,Valid Cost Center,,,,,,Valid Account,Valid Cost Center,Valid BOM,0 or 1,,Integer,Valid Website Slideshow,One of: attach_files:,Valid Warehouse,
-Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,Base Bearing Plate,Base Bearing Plate,Raw Material,Nos,1/4 in. x 6 in. x 6 in. Mild Steel Plate,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Base Bearing Plate,,,,,Stores - WP,,,,,,,,,,Eagle Hardware,,,,,,,,,,,,,,,,,,
-,Base Plate,Base Plate,Raw Material,Nos,3/4 in. x 2 ft. x 4 ft. Pine Plywood,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Base Plate,,,,,Stores - WP,,,,,,,,,,HomeBase,,,,,,,,,,,,,,,,,,
-,Bearing Assembly,Bearing Assembly,Sub Assemblies,Nos,Bearing Assembly,Yes,No,No,No,No,Yes,No,No,No,Yes,Yes,No,,Bearing Assembly,,,,,Stores - WP,,,0.0,,,,,,,Asiatic Solutions,,,,,,,,,,,,,,,,,,
-,Bearing Block,Bearing Block,Raw Material,Nos,"CAST IRON, MCMASTER PART NO. 3710T13",Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Bearing Block,,,,,Stores - WP,,,,,,,,,,Nan Duskin,,,,,,,,,,,,,,,,,,
-,Bearing Collar,Bearing Collar,Raw Material,Nos,1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Bearing Collar,,,,,Stores - WP,,,,,,,,,,Eagle Hardware,,,,,,,,,,,,,,,,,,
-,Bearing Pipe,Bearing Pipe,Raw Material,Nos,1.5 in. Diameter x 36 in. Mild Steel Tubing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Bearing Pipe,,,,,Stores - WP,,,,,,,,,,HomeBase,,,,,,,,,,,,,,,,,,
-,Blade Rib,Blade Rib,Raw Material,Nos,1/2 in. x 2 ft. x 4 ft. Pine Plywood,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Blade Rib,,,,,Stores - WP,,,,,,,,,,Ks Merchandise,,,,,,,,,,,,,,,,,,
-,Disc Collars,Disc Collars,Raw Material,Nos,For Upper Bearing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Disc Collars,,,,,Stores - WP,,,,,,,,,,Asiatic Solutions,,,,,,,,,,,,,,,,,,
-,External Disc,External Disc,Raw Material,Nos,15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,External Disc,,,,,Stores - WP,,,,,,,,,,HomeBase,,,,,,,,,,,,,,,,,,
-,Internal Disc,Internal Disc,Raw Material,Nos,For Bearing Collar,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Internal Disc,,,,,Stores - WP,,,,,,,,,,HomeBase,,,,,,,,,,,,,,,,,,
-,Shaft,Shaft,Raw Material,Nos,1.25 in. Diameter x 6 ft. Mild Steel Tubing,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Shaft,,,,,Stores - WP,,,,,,,,,,Eagle Hardware,,,,,,,,,,,,,,,,,,
-,Stand,Stand,Raw Material,Nos,N/A,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Stand,,,,,Stores - WP,,,,,,,,,,Scott Ties,,,,,,,,,,,,,,,,,,
-,Upper Bearing Plate,Upper Bearing Plate,Raw Material,Nos,3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Upper Bearing Plate,,,,,Stores - WP,,,,,,,,,,Eagle Hardware,,,,,,,,,,,,,,,,,,
-,Wind Mill A Series,Wind Mill A Series,Products,Nos,Wind Mill A Series for Home Use 9ft,Yes,No,No,Yes,Yes,Yes,Yes,No,No,Yes,Yes,No,,Wind Mill A Series,,,,,Finished Goods - WP,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,Wind MIll C Series,Wind MIll C Series,Products,Nos,Wind Mill C Series for Commercial Use 18ft,Yes,No,No,Yes,Yes,Yes,Yes,No,No,Yes,Yes,No,,Wind MIll C Series,,,,,Finished Goods - WP,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,Wind Turbine,Wind Turbine,Products,Nos,Small Wind Turbine for Home Use,Yes,No,No,No,Yes,Yes,Yes,No,No,Yes,Yes,No,,Wind Turbine,,,,,Finished Goods - WP,,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,Wing Sheet,Wing Sheet,Raw Material,Nos,1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet,Yes,No,No,No,Yes,Yes,Yes,No,No,No,No,No,,Wing Sheet,,,,,Stores - WP,,,,,,,,,,New World Realty,,,,,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/utilities/doctype/contact/contact.py b/utilities/doctype/contact/contact.py
index 89cfc8e..961bb52 100644
--- a/utilities/doctype/contact/contact.py
+++ b/utilities/doctype/contact/contact.py
@@ -7,7 +7,7 @@
from utilities.transaction_base import TransactionBase
-class DocType(TransactionBase):
+class DocType(TransactionBase):
def __init__(self, doc, doclist=[]):
self.doc = doc
self.doclist = doclist
diff --git a/utilities/doctype/rename_tool/rename_tool.js b/utilities/doctype/rename_tool/rename_tool.js
index b339b85..0dc81a5 100644
--- a/utilities/doctype/rename_tool/rename_tool.js
+++ b/utilities/doctype/rename_tool/rename_tool.js
@@ -18,7 +18,7 @@
cur_frm.cscript.setup_upload = function() {
var me = this;
var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty()
- .html("<hr><div class='alert'>" +
+ .html("<hr><div class='alert alert-warning'>" +
wn._("Upload a .csv file with two columns: the old name and the new name. Max 500 rows.")
+ "</div>");
var $log = $(cur_frm.fields_dict.rename_log.wrapper).empty();
diff --git a/utilities/make_demo.py b/utilities/make_demo.py
deleted file mode 100644
index a522862..0000000
--- a/utilities/make_demo.py
+++ /dev/null
@@ -1,293 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
-# License: GNU General Public License v3. See license.txt
-
-import webnotes, os, datetime
-import webnotes.utils
-from webnotes.widgets import query_report
-import random
-import json
-
-webnotes.session = webnotes._dict({"user":"Administrator"})
-from core.page.data_import_tool.data_import_tool import upload
-
-# fix price list
-# fix fiscal year
-
-company = "Wind Power LLC"
-start_date = '2010-01-01'
-runs_for = 20
-prob = {
- "Quotation": { "make": 0.5, "qty": (1,5) },
- "Sales Order": { "make": 0.5, "qty": (1,4) },
- "Purchase Order": { "make": 0.7, "qty": (1,4) },
- "Purchase Receipt": { "make": 0.7, "qty": (1,4) },
- "Supplier Quotation": { "make": 0.5, "qty": (1, 3) }
-}
-
-def make(reset=False):
- webnotes.connect()
- webnotes.print_messages = True
- webnotes.mute_emails = True
-
- if reset:
- setup()
- simulate()
-
-def setup():
- install()
- complete_setup()
- make_customers_suppliers_contacts()
- make_items()
- make_users_and_employees()
- # make_opening_stock()
- # make_opening_accounts()
-
-def simulate():
- current_date = None
- for i in xrange(runs_for):
- print i
- if not current_date:
- current_date = webnotes.utils.getdate(start_date)
- else:
- current_date = webnotes.utils.add_days(current_date, 1)
-
- if current_date.weekday() in (5, 6):
- continue
-
- run_sales(current_date)
- run_purchase(current_date)
- run_manufacturing(current_date)
- run_stock(current_date)
-
- webnotes.conn.commit()
-
-def run_sales(current_date):
- if can_make("Quotation"):
- for i in xrange(how_many("Quotation")):
- make_quotation(current_date)
-
- if can_make("Sales Order"):
- for i in xrange(how_many("Sales Order")):
- make_sales_order(current_date)
-
-def run_stock(current_date):
- # make purchase requests
- if can_make("Purchase Receipt"):
- from buying.doctype.purchase_order.purchase_order import make_purchase_receipt
- report = "Purchase Order Items To Be Received"
- 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.insert()
- pr.submit()
-
- # make delivery notes (if possible)
- if can_make("Delivery Note"):
- from selling.doctype.sales_order.sales_order import make_delivery_note
- report = "Ordered Items To Be Delivered"
- 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.insert()
- dn.submit()
-
-
-def run_purchase(current_date):
- # make supplier quotations
- if can_make("Supplier Quotation"):
- from stock.doctype.material_request.material_request import make_supplier_quotation
- report = "Material Requests for which Supplier Quotations are not created"
- for row in query_report.run(report)["result"][:how_many("Supplier Quotation")]:
- if row[0] != "Total":
- sq = webnotes.bean(make_supplier_quotation(row[0]))
- sq.doc.transaction_date = current_date
- sq.doc.fiscal_year = "2010"
- sq.insert()
- sq.submit()
-
- # make purchase orders
- if can_make("Purchase Order"):
- from stock.doctype.material_request.material_request import make_purchase_order
- report = "Requested Items To Be Ordered"
- for row in query_report.run(report)["result"][:how_many("Purchase Order")]:
- if row[0] != "Total":
- po = webnotes.bean(make_purchase_order(row[0]))
- po.doc.transaction_date = current_date
- po.doc.fiscal_year = "2010"
- po.insert()
- po.submit()
-
-def run_manufacturing(current_date):
- ppt = webnotes.bean("Production Planning Tool", "Production Planning Tool")
- ppt.doc.company = company
- ppt.doc.use_multi_level_bom = 1
- ppt.doc.purchase_request_for_warehouse = "Stores - WP"
- ppt.run_method("get_open_sales_orders")
- ppt.run_method("get_items_from_so")
- ppt.run_method("raise_production_order")
- ppt.run_method("raise_purchase_request")
-
- # submit production orders
- for pro in webnotes.conn.get_values("Production Order", {"docstatus": 0}):
- b = webnotes.bean("Production Order", pro[0])
- b.doc.wip_warehouse = "Work in Progress - WP"
- b.submit()
-
- # submit material requests
- for pro in webnotes.conn.get_values("Material Request", {"docstatus": 0}):
- b = webnotes.bean("Material Request", pro[0])
- b.submit()
-
- # stores -> wip
- if can_make("Stock Entry for WIP"):
- for pro in query_report.run("Open Production Orders")["result"][:how_many("Stock Entry for WIP")]:
- make_stock_entry_from_pro(pro[0], "Material Transfer", current_date)
-
- # wip -> fg
- if can_make("Stock Entry for FG"):
- for pro in query_report.run("Production Orders in Progress")["result"][:how_many("Stock Entry for FG")]:
- make_stock_entry_from_pro(pro[0], "Manufacture/Repack", current_date)
-
- # try posting older drafts (if exists)
- for st in webnotes.conn.get_values("Stock Entry", {"docstatus":0}):
- try:
- webnotes.bean("Stock Entry", st[0]).submit()
- except NegativeStockError: pass
- except IncorrectValuationRateError: pass
-
-
-def make_stock_entry_from_pro(pro_id, purpose, current_date):
- from manufacturing.doctype.production_order.production_order import make_stock_entry
- from stock.stock_ledger import NegativeStockError
- from stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError
-
- st = webnotes.bean(make_stock_entry(pro_id, purpose))
- st.run_method("get_items")
- st.doc.posting_date = current_date
- st.doc.fiscal_year = "2010"
- st.doc.expense_adjustment_account = "Stock in Hand - WP"
- try:
- st.insert()
- st.submit()
- except NegativeStockError: pass
- except IncorrectValuationRateError: pass
-
-def make_quotation(current_date):
- b = webnotes.bean([{
- "creation": current_date,
- "doctype": "Quotation",
- "quotation_to": "Customer",
- "customer": get_random("Customer"),
- "order_type": "Sales",
- "transaction_date": current_date,
- "fiscal_year": "2010"
- }])
-
- add_random_children(b, {
- "doctype": "Quotation Item",
- "parentfield": "quotation_details",
- }, rows=3, randomize = {
- "qty": (1, 5),
- "item_code": ("Item", {"is_sales_item": "Yes"})
- }, unique="item_code")
-
- b.insert()
- b.submit()
-
-def make_sales_order(current_date):
- q = get_random("Quotation", {"status": "Submitted"})
- if q:
- from selling.doctype.quotation.quotation import make_sales_order
- so = webnotes.bean(make_sales_order(q))
- so.doc.transaction_date = current_date
- so.doc.delivery_date = webnotes.utils.add_days(current_date, 10)
- so.insert()
- so.submit()
-
-def add_random_children(bean, template, rows, randomize, unique=None):
- for i in xrange(random.randrange(1, rows)):
- d = template.copy()
- for key, val in randomize.items():
- if isinstance(val[0], basestring):
- d[key] = get_random(*val)
- else:
- d[key] = random.randrange(*val)
-
- if unique:
- if not bean.doclist.get({"doctype": d["doctype"], unique:d[unique]}):
- bean.doclist.append(d)
- else:
- bean.doclist.append(d)
-
-def get_random(doctype, filters=None):
- condition = []
- if filters:
- for key, val in filters.items():
- condition.append("%s='%s'" % (key, val))
- if condition:
- condition = " where " + " and ".join(condition)
- else:
- condition = ""
-
- out = webnotes.conn.sql("""select name from `tab%s` %s
- order by RAND() limit 0,1""" % (doctype, condition))
-
- return out and out[0][0] or None
-
-def can_make(doctype):
- return random.random() < prob.get(doctype, {"make": 0.5})["make"]
-
-def how_many(doctype):
- return random.randrange(*prob.get(doctype, {"qty": (1, 3)})["qty"])
-
-def install():
- print "Creating Fresh Database..."
- from webnotes.install_lib.install import Installer
- inst = Installer('root')
- inst.import_from_db("demo", verbose = 1)
-
-def complete_setup():
- print "Complete Setup..."
- webnotes.get_obj("Setup Control").setup_account({
- "first_name": "Test",
- "last_name": "User",
- "fy_start": "1st Jan",
- "industry": "Manufacturing",
- "company_name": company,
- "company_abbr": "WP",
- "currency": "USD",
- "timezone": "America/New York",
- "country": "United States"
- })
-
- import_data("Fiscal_Year")
-
-def make_items():
- import_data(["Item", "Item_Price"])
- import_data("BOM", submit=True)
-
-def make_customers_suppliers_contacts():
- import_data(["Customer", "Supplier", "Contact", "Address", "Lead"])
-
-def make_users_and_employees():
- webnotes.conn.set_value("HR Settings", None, "emp_created_by", "Naming Series")
- webnotes.conn.commit()
-
- import_data(["Profile", "Employee", "Salary_Structure"])
-
-def import_data(dt, submit=False):
- if not isinstance(dt, (tuple, list)):
- dt = [dt]
-
- for doctype in dt:
- print "Importing", doctype.replace("_", " "), "..."
- webnotes.form_dict = {}
- if submit:
- webnotes.form_dict["params"] = json.dumps({"_submit": 1})
- webnotes.uploaded_file = os.path.join(os.path.dirname(__file__), "demo_docs", doctype+".csv")
- upload()
-
-if __name__=="__main__":
- make()
\ No newline at end of file
diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py
index 47e35f1..aace0cb 100644
--- a/utilities/transaction_base.py
+++ b/utilities/transaction_base.py
@@ -55,6 +55,8 @@
return self._party_type_and_name
def get_customer_defaults(self):
+ if not self.doc.customer: return {}
+
out = self.get_default_address_and_contact("customer")
customer = webnotes.doc("Customer", self.doc.customer)
@@ -77,9 +79,9 @@
"""
customer_defaults = self.get_customer_defaults()
- customer_defaults["price_list_name"] = customer_defaults.get("price_list") or \
+ customer_defaults["selling_price_list"] = customer_defaults.get("price_list") or \
webnotes.conn.get_value("Customer Group", self.doc.customer_group, "default_price_list") or \
- self.doc.price_list
+ self.doc.selling_price_list
for fieldname, val in customer_defaults.items():
if self.meta.get_field(fieldname):
diff --git a/website/css/website.css b/website/css/website.css
index cdd277b..24553d9 100644
--- a/website/css/website.css
+++ b/website/css/website.css
@@ -18,14 +18,6 @@
padding-bottom: 30px;
}
-.outer {
- padding: 0px 20px;
-}
-
-.navbar {
- margin: 0px -20px;
-}
-
.banner {
margin-top: 20px;
padding: 0px 20px;
@@ -56,6 +48,7 @@
div.web-footer {
padding-top: 10px;
+ padding-bottom: 20px;
}
.web-footer-menu ul {
@@ -148,8 +141,15 @@
}
}
-.accordion-heading, .accordion-inner {
- padding-left: 10px;
+.panel-heading,
+.panel-body {
+ padding-left: 15px;
+}
+
+
+.breadcrumb {
+ margin: 0px -20px;
+ margin-bottom: 20px;
}
fieldset {
@@ -167,6 +167,7 @@
.btn-default:focus,
.btn-default:active,
.btn-default.active {
+ color: #ffffff;
background-color: #9a9c9d;
border-color: #8d9091;
}
diff --git a/website/doctype/blog_post/blog_post.py b/website/doctype/blog_post/blog_post.py
index ad8a0b8..48b3206 100644
--- a/website/doctype/blog_post/blog_post.py
+++ b/website/doctype/blog_post/blog_post.py
@@ -30,6 +30,7 @@
def prepare_template_args(self):
import webnotes.utils
+ import markdown2
# this is for double precaution. usually it wont reach this code if not published
if not webnotes.utils.cint(self.doc.published):
@@ -65,3 +66,4 @@
self.doc.comment_list = comment_list or []
for comment in self.doc.comment_list:
comment['comment_date'] = webnotes.utils.global_date_format(comment['creation'])
+ comment['comment'] = markdown2.markdown(comment['comment'])
diff --git a/website/doctype/blogger/blogger.py b/website/doctype/blogger/blogger.py
index b35b8ee..9cb4a44 100644
--- a/website/doctype/blogger/blogger.py
+++ b/website/doctype/blogger/blogger.py
@@ -5,6 +5,7 @@
from __future__ import unicode_literals
import webnotes
+from webnotes import _
class DocType:
def __init__(self, d, dl):
@@ -23,3 +24,18 @@
b.doc.blogger = self.doc.name
b.save()
+def get_writers_args():
+ bloggers = webnotes.conn.sql("""select * from `tabBlogger`
+ where ifnull(posts,0) > 0 and ifnull(disabled,0)=0
+ order by posts desc""", as_dict=1)
+
+ args = {
+ "bloggers": bloggers,
+ "texts": {
+ "all_posts_by": _("All posts by")
+ },
+ "categories": webnotes.conn.sql_list("select name from `tabBlog Category` order by name")
+ }
+
+ args.update(webnotes.doc("Blog Settings", "Blog Settings").fields)
+ return args
\ No newline at end of file
diff --git a/website/doctype/blogger/blogger.txt b/website/doctype/blogger/blogger.txt
index e2b4367..1486c01 100644
--- a/website/doctype/blogger/blogger.txt
+++ b/website/doctype/blogger/blogger.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-03-25 16:00:51",
"docstatus": 0,
- "modified": "2013-07-05 14:27:34",
+ "modified": "2013-08-30 16:35:24",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -40,6 +40,12 @@
"name": "Blogger"
},
{
+ "doctype": "DocField",
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "label": "Disabled"
+ },
+ {
"description": "Will be used in url (usually first name).",
"doctype": "DocField",
"fieldname": "short_name",
diff --git a/website/doctype/shopping_cart_settings/shopping_cart_settings.py b/website/doctype/shopping_cart_settings/shopping_cart_settings.py
index f06c1f7..74cc217 100644
--- a/website/doctype/shopping_cart_settings/shopping_cart_settings.py
+++ b/website/doctype/shopping_cart_settings/shopping_cart_settings.py
@@ -43,7 +43,7 @@
def validate_price_lists(self):
territory_name_map = self.validate_overlapping_territories("price_lists",
- "price_list")
+ "selling_price_list")
# validate that a Shopping Cart Price List exists for the root territory
# as a catch all!
@@ -92,9 +92,9 @@
raise_exception=ShoppingCartSetupError)
price_list_currency_map = webnotes.conn.get_values("Price List",
- [d.price_list for d in self.doclist.get({"parentfield": "price_lists"})],
+ [d.selling_price_list for d in self.doclist.get({"parentfield": "price_lists"})],
"currency")
-
+
expected_to_exist = [currency + "-" + company_currency
for currency in price_list_currency_map.values()
if currency != company_currency]
@@ -126,7 +126,7 @@
return name
def get_price_list(self, billing_territory):
- price_list = self.get_name_from_territory(billing_territory, "price_lists", "price_list")
+ price_list = self.get_name_from_territory(billing_territory, "price_lists", "selling_price_list")
return price_list and price_list[0] or None
def get_tax_master(self, billing_territory):
diff --git a/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py b/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py
index cbba566..3417cec 100644
--- a/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py
+++ b/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py
@@ -26,7 +26,7 @@
cart_settings.doclist.append({
"doctype": "Shopping Cart Price List",
"parentfield": "price_lists",
- "price_list": price_list
+ "selling_price_list": price_list
})
for price_list in ("_Test Price List Rest of the World", "_Test Price List India",
@@ -34,13 +34,13 @@
_add_price_list(price_list)
controller = cart_settings.make_controller()
- controller.validate_overlapping_territories("price_lists", "price_list")
+ controller.validate_overlapping_territories("price_lists", "selling_price_list")
_add_price_list("_Test Price List 2")
controller = cart_settings.make_controller()
self.assertRaises(ShoppingCartSetupError, controller.validate_overlapping_territories,
- "price_lists", "price_list")
+ "price_lists", "selling_price_list")
return cart_settings
diff --git a/website/doctype/style_settings/custom_template.css b/website/doctype/style_settings/custom_template.css
index b442989..7abdb4b 100644
--- a/website/doctype/style_settings/custom_template.css
+++ b/website/doctype/style_settings/custom_template.css
@@ -66,6 +66,10 @@
}
/* Bootstrap Navbar */
+.navbar-default {
+ border: 0px;
+}
+
.navbar {
box-shadow: none;
border-radius: 0px;
@@ -166,8 +170,8 @@
}
.navbar .nav li.dropdown > .dropdown-toggle:hover .caret {
- border-top-color: #{{ doc.top_bar_background }};
- border-bottom-color: #{{ doc.top_bar_background }};
+ border-top-color: #{{ doc.top_bar_foreground }};
+ border-bottom-color: #{{ doc.top_bar_foreground }};
}
.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
@@ -178,8 +182,12 @@
}
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #{{ doc.top_bar_foreground }};
+}
+
.breadcrumb {
- background-color: #{{ get_hex_shade(doc.page_background, 10) }};
+ background-color: #{{ get_hex_shade(doc.page_background, 5) }};
}
.breadcrumb > li {
diff --git a/website/doctype/style_settings/style_settings.js b/website/doctype/style_settings/style_settings.js
index 6ecdafa..f94246d 100644
--- a/website/doctype/style_settings/style_settings.js
+++ b/website/doctype/style_settings/style_settings.js
@@ -6,7 +6,7 @@
wn.require('lib/public/js/lib/jscolor/jscolor.js');
$.each(["background_color", "page_background", "page_text", "page_links",
"top_bar_background", "top_bar_foreground", "page_headings"], function(i, v) {
- cur_frm.fields_dict[v].input.className = 'color';
+ $(cur_frm.fields_dict[v].input).addClass('color');
})
jscolor.bind();
}
\ 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 0d0dab6..21b55be 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 3b27c7c..5d6c874 100644
--- a/website/doctype/website_settings/website_settings.py
+++ b/website/doctype/website_settings/website_settings.py
@@ -13,6 +13,26 @@
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"""
diff --git a/website/helpers/blog.py b/website/helpers/blog.py
index f8f7a42..3f6d94f 100644
--- a/website/helpers/blog.py
+++ b/website/helpers/blog.py
@@ -119,18 +119,3 @@
}
args.update(webnotes.doc("Blog Settings", "Blog Settings").fields)
return args
-
-def get_writers_args():
- bloggers = webnotes.conn.sql("""select * from `tabBlogger`
- order by posts desc""", as_dict=1)
-
- args = {
- "bloggers": bloggers,
- "texts": {
- "all_posts_by": _("All posts by")
- },
- "categories": webnotes.conn.sql_list("select name from `tabBlog Category` order by name")
- }
-
- args.update(webnotes.doc("Blog Settings", "Blog Settings").fields)
- return args
\ No newline at end of file
diff --git a/website/helpers/cart.py b/website/helpers/cart.py
index b6a7a01..fc7d062 100644
--- a/website/helpers/cart.py
+++ b/website/helpers/cart.py
@@ -228,6 +228,7 @@
"company": webnotes.defaults.get_user_default("company"),
"order_type": "Shopping Cart",
"status": "Draft",
+ "docstatus": 0,
"__islocal": 1,
(party.doctype.lower()): party.name
})
diff --git a/website/helpers/contact.py b/website/helpers/contact.py
index 05ae4f7..35446a3 100644
--- a/website/helpers/contact.py
+++ b/website/helpers/contact.py
@@ -20,7 +20,7 @@
# make lead / communication
from selling.doctype.lead.get_leads import add_sales_communication
- add_sales_communication(subject or "Website Query", message, sender, sender,
+ message = add_sales_communication(subject or "Website Query", message, sender, sender,
mail=None, status=status)
# guest method, cap max writes per hour
@@ -29,4 +29,4 @@
webnotes.response["message"] = "Sorry: we believe we have received an unreasonably high number of requests of this kind. Please try later"
return
- webnotes.response["message"] = 'Thank You'
\ No newline at end of file
+ webnotes.response.status = "okay"
diff --git a/website/templates/css/blog_page.css b/website/templates/css/blog_page.css
index ffa45b4..8f56cd2 100644
--- a/website/templates/css/blog_page.css
+++ b/website/templates/css/blog_page.css
@@ -1,8 +1,4 @@
<style>
- [itemprop="articleBody"] {
- font-size: 120%;
- }
-
.comment-title {
color:#777;
}
diff --git a/website/templates/html/blog_footer.html b/website/templates/html/blog_footer.html
index 8a84572..e71b3ec 100644
--- a/website/templates/html/blog_footer.html
+++ b/website/templates/html/blog_footer.html
@@ -1,8 +1,8 @@
-<div class="col col-lg-12">
+<div class="col-md-12">
<hr />
{% if categories %}
<h5>Explore posts by categories</h5>
- <ul class="breadcrumb" style="background-color: transparent; padding-left: 0px;">
+ <ul class="breadcrumb" style="background-color: transparent; padding-left: 20px;">
{% for category in categories %}
<li><a href="blog?category={{ category }}">{{ category }}</a>
{% endfor %}
diff --git a/website/templates/html/blog_page.html b/website/templates/html/blog_page.html
index ddb61d0..90f6a79 100644
--- a/website/templates/html/blog_page.html
+++ b/website/templates/html/blog_page.html
@@ -9,7 +9,7 @@
{% endblock %}
{% block content %}
-<div class="col col-lg-12" itemscope itemtype="http://schema.org/BlogPost">
+<div class="col-md-12" itemscope itemtype="http://schema.org/BlogPost">
<h2 itemprop="name headline">{{ title }}</h2>
<!-- begin blog content -->
@@ -38,14 +38,16 @@
{% include 'app/website/templates/html/comment.html' %}
</div>
<div><button class="btn btn-default add-comment">{{ texts.add_comment }}</button></div>
- <div style="display: none; margin-top: 10px;"
+ <div style="display: none; margin-top: 10px; max-width: 400px;"
id="comment-form">
<div class="alert" style="display:none;"></div>
<form>
<fieldset>
- <input name="comment_by_fullname" placeholder="Your Name" type="text"/><br>
- <input name="comment_by" placeholder="Your Email Id" type="text"/><br>
- <textarea name="comment" placeholder="Comment" style="width: 300px; height: 120px;"/>
+ <input class="form-control" name="comment_by_fullname" placeholder="Your Name" type="text"/><br>
+ <input class="form-control" name="comment_by"
+ placeholder="Your Email Id" type="text"/><br>
+ <textarea class="form-control" name="comment" rows=10
+ placeholder="Comment"/>
</textarea><br>
<button class="btn btn-info" id="submit-comment">{{ texts.submit }}</button>
</fieldset>
diff --git a/website/templates/html/blogger.html b/website/templates/html/blogger.html
index 738fd1a..90c3571 100644
--- a/website/templates/html/blogger.html
+++ b/website/templates/html/blogger.html
@@ -1,10 +1,10 @@
<div class="row">
- <div class="col col-lg-2">
+ <div class="col-md-2">
<div class="avatar avatar-large">
<img itemprop="thumbnailUrl" src="{{ blogger_info.avatar }}" />
</div>
</div>
- <div class="col col-lg-10">
+ <div class="col-md-10">
<h4>{{ blogger_info.full_name }}</h4>
<p style="color: #999">{{ blogger_info.bio }}</p>
<p><a href="blog?by={{ blogger_info.name }}&by_name={{ blogger_info.full_name }}">
diff --git a/website/templates/html/footer.html b/website/templates/html/footer.html
index 0e6cca7..e391794 100644
--- a/website/templates/html/footer.html
+++ b/website/templates/html/footer.html
@@ -1,6 +1,6 @@
<footer class="container">
<div class="web-footer row">
- <div class="col col-lg-12">
+ <div class="col-md-12">
{% if facebook_share or google_plus_one or twitter_share or linked_in_share %}
<div class="social-icons" style="">
<span style="font-size: 11px;">{{ share_text or "Share this page on: "}}</span>
@@ -40,8 +40,8 @@
{{ address }}
{% endif %}
<br>
- <div class="input-group col col-lg-6 col-offset-3">
- <input type="text" id="footer-subscribe-email" placeholder="Your email address...">
+ <div class="input-group col-md-6 col-md-offset-3">
+ <input class="form-control" type="text" id="footer-subscribe-email" placeholder="Your email address...">
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="footer-subscribe-button">Stay Updated</button>
</span>
diff --git a/website/templates/html/navbar.html b/website/templates/html/navbar.html
index 68706ec..9e827c4 100644
--- a/website/templates/html/navbar.html
+++ b/website/templates/html/navbar.html
@@ -1,13 +1,15 @@
-<div class="navbar">
- <a class="navbar-brand" href="index">{{ brand_html or "<i class='icon-home'></i>"}}</a>
+<div class="navbar navbar-default">
<div class="container">
- <button type="button" class="navbar-toggle" data-toggle="collapse"
- data-target=".navbar-responsive-collapse">
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- <div class="nav-collapse collapse navbar-responsive-collapse">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle" data-toggle="collapse"
+ data-target=".navbar-responsive-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="index">{{ brand_html or "<i class='icon-home'></i>"}}</a>
+ </div>
+ <div class="collapse navbar-collapse navbar-responsive-collapse">
<ul class="nav navbar-nav">
{%- for page in top_bar_items -%}
{% if not page.parent_label -%}
diff --git a/website/templates/html/outer.html b/website/templates/html/outer.html
index bac3515..462f4dd 100644
--- a/website/templates/html/outer.html
+++ b/website/templates/html/outer.html
@@ -1,6 +1,7 @@
{% extends "lib/templates/base.html" %}
{% block body %}
+ {% include "app/website/templates/html/navbar.html" %}
<div class="container">
<div class="pull-right" style="margin:4px;" id="user-tools">
{% if shopping_cart_enabled -%}
@@ -20,10 +21,9 @@
</div>
<div class="clearfix"></div>
{% if banner_html %}<div class="row banner">
- <div class="col col-lg-12">{{ banner_html }}</div>
+ <div class="col-md-12">{{ banner_html }}</div>
</div>{% endif %}
<div class="outer">
- {% include "app/website/templates/html/navbar.html" %}
<div class="content row" id="page-{{ name }}" style="display: block;">
{%- block content -%}
{%- endblock -%}
diff --git a/website/templates/html/partner_page.html b/website/templates/html/partner_page.html
index 37cf0b0..e5aac35 100644
--- a/website/templates/html/partner_page.html
+++ b/website/templates/html/partner_page.html
@@ -1,9 +1,9 @@
{% extends "app/website/templates/html/page.html" %}
{% block content %}
- <div class="col col-lg-12" itemscope itemtype="http://schema.org/Organization">
+ <div class="col-md-12" itemscope itemtype="http://schema.org/Organization">
<div class="row">
- <div class="col col-lg-4">
+ <div class="col-md-4">
{% if logo -%}
<img itemprop="brand" src="{{ logo }}" class="partner-logo"
alt="{{ partner_name }}" title="{{ partner_name }}" />
@@ -17,7 +17,7 @@
{% if email -%}<p itemprop="email"><span class="icon-envelope"></span> {{ email }}</p>{%- endif %}
</address>
</div>
- <div class="col col-lg-8">
+ <div class="col-md-8">
<h3 itemprop="name" style="margin-top: 0px;">{{ partner_name }}</h3>
<p>{{ description }}</p>
</div>
diff --git a/website/templates/html/product_breadcrumbs.html b/website/templates/html/product_breadcrumbs.html
index 3cd501e..6d26748 100644
--- a/website/templates/html/product_breadcrumbs.html
+++ b/website/templates/html/product_breadcrumbs.html
@@ -1,5 +1,5 @@
{% if obj.parent_groups and len(obj.parent_groups) > 1 %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<div class="clearfix">
<ul class="breadcrumb">
{% for ig in obj.parent_groups[:-1] %}
diff --git a/website/templates/html/product_group.html b/website/templates/html/product_group.html
index 54a55af..4bf52c5 100644
--- a/website/templates/html/product_group.html
+++ b/website/templates/html/product_group.html
@@ -3,7 +3,7 @@
{% block content %}
{% include 'app/website/templates/html/product_search_box.html' %}
{% include 'app/website/templates/html/product_breadcrumbs.html' %}
-<div class="col col-lg-12">
+<div class="col-md-12">
{% if slideshow %}<!-- slideshow -->
{% include "app/website/templates/html/slideshow.html" %}
{% endif %}
@@ -13,12 +13,12 @@
<h3>{{ name }}</h3>
{% endif %}
</div>
-<div class="col col-lg-12">
+<div class="col-md-12">
{% if sub_groups %}
<hr />
<div class="row">
{% for d in sub_groups %}
- <div class="col col-lg-4">
+ <div class="col-md-4">
<a href="{{ d.page_name }}">{{ d.name }} ({{ d.count }})</a>
</div>
{% endfor %}
diff --git a/website/templates/html/product_in_grid.html b/website/templates/html/product_in_grid.html
index e9752b3..adb2f74 100644
--- a/website/templates/html/product_in_grid.html
+++ b/website/templates/html/product_in_grid.html
@@ -1,4 +1,4 @@
-<div class="col col-lg-3">
+<div class="col-md-3">
<div style="height: 120px; overflow: hidden;">
<a href="{{ page_name }}">
{%- if website_image -%}
diff --git a/website/templates/html/product_in_list.html b/website/templates/html/product_in_list.html
index 14f020b..b3e5b86 100644
--- a/website/templates/html/product_in_list.html
+++ b/website/templates/html/product_in_list.html
@@ -1,5 +1,5 @@
<!-- TODO product listing -->
-<div class="col col-lg-12">
+<div class="col-md-12">
<div style="height: 120px; overflow: hidden;">
<a href="{{ page_name }}">
{%- if website_image -%}
diff --git a/website/templates/html/product_page.html b/website/templates/html/product_page.html
index 4f04cec..5ac0b48 100644
--- a/website/templates/html/product_page.html
+++ b/website/templates/html/product_page.html
@@ -11,9 +11,9 @@
{% block content %}
{% include 'app/website/templates/html/product_search_box.html' %}
{% include 'app/website/templates/html/product_breadcrumbs.html' %}
- <div class="col col-lg-12 product-page-content" itemscope itemtype="http://schema.org/Product">
+ <div class="col-md-12 product-page-content" itemscope itemtype="http://schema.org/Product">
<div class="row">
- <div class="col col-lg-6">
+ <div class="col-md-6">
{% if slideshow %}
{% include "app/website/templates/html/slideshow.html" %}
{% else %}
@@ -27,7 +27,7 @@
{% endif %}
{% endif %}
</div>
- <div class="col col-lg-6">
+ <div class="col-md-6">
<h3 itemprop="name" style="margin-top: 0px;">{{ item_name }}</h3>
<p class="help">Item Code: <span itemprop="productID">{{ name }}</span></p>
<h4>Product Description</h4>
@@ -42,9 +42,9 @@
<button class="btn btn-primary">
<i class="icon-shopping-cart"></i> Add to Cart</button>
</div>
- <div id="item-update-cart" class="input-group col-lg-4" style="display: none;
+ <div id="item-update-cart" class="input-group col-md-4" style="display: none;
padding-left: 0px; padding-right: 0px;">
- <input type="text">
+ <input class="form-control" type="text">
<div class="input-group-btn">
<button class="btn btn-primary">
<i class="icon-ok"></i></button>
@@ -56,7 +56,7 @@
</div>
{% if obj.doclist.get({"doctype":"Item Website Specification"}) -%}
<div class="row" style="margin-top: 20px">
- <div class="col col-lg-12">
+ <div class="col-md-12">
<h4>Specifications</h4>
<table class="table table-bordered" style="width: 100%">
{% for d in obj.doclist.get(
diff --git a/website/templates/html/product_search_box.html b/website/templates/html/product_search_box.html
index 29564f3..d808bf6 100644
--- a/website/templates/html/product_search_box.html
+++ b/website/templates/html/product_search_box.html
@@ -1,8 +1,8 @@
-<div class="col col-lg-12 clearfix">
+<div class="col-md-12 clearfix">
<div class="pull-right" style="margin-top: 15px; margin-bottom: 15px;">
<form class="form-search">
- <div class="input-group col col-lg-4 col-offset-8">
- <input type="text" id="product-search" placeholder="Product Search...">
+ <div class="input-group col-md-4 col-md-offset-8">
+ <input class="form-control" type="text" id="product-search" placeholder="Product Search...">
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="btn-product-search">
<i class="icon-search"></i></button>
diff --git a/website/templates/html/web_page.html b/website/templates/html/web_page.html
index 4711e58..c857263 100644
--- a/website/templates/html/web_page.html
+++ b/website/templates/html/web_page.html
@@ -7,7 +7,7 @@
{% endblock %}
{% block content %}
-<div class="col col-lg-12" style="margin-top: 15px;">
+<div class="col-md-12" style="margin-top: 15px;">
{% include "app/website/templates/html/slideshow.html" %}
{{ main_section }}
</div>
diff --git a/website/templates/js/blog.js b/website/templates/js/blog.js
index 252df64..8134b63 100644
--- a/website/templates/js/blog.js
+++ b/website/templates/js/blog.js
@@ -56,12 +56,12 @@
b.page_name = encodeURIComponent(b.page_name);
$(repl('<div class="row">\
- <div class="col col-lg-1">\
+ <div class="col-md-1">\
<div class="avatar avatar-medium" style="margin-top: 6px;">\
<img src="%(avatar)s" />\
</div>\
</div>\
- <div class="col col-lg-11">\
+ <div class="col-md-11">\
<h4><a href="%(page_name)s">%(title)s</a></h4>\
<p>%(content)s</p>\
<p style="color: #aaa; font-size: 90%">\
@@ -77,7 +77,7 @@
.parent().append("<div class='text-muted'>Nothing more to show.</div>");
} else {
$("#next-page").toggle(false)
- .parent().append("<div class='alert'>No blogs written yet.</div>");
+ .parent().append("<div class='alert alert-warning'>No blogs written yet.</div>");
}
} else {
$("#next-page").toggle(true);
diff --git a/website/templates/js/blog_page.js b/website/templates/js/blog_page.js
index 11656c8..ee56d90 100644
--- a/website/templates/js/blog_page.js
+++ b/website/templates/js/blog_page.js
@@ -11,7 +11,7 @@
}
if(n_comments > 50) {
$(".add-comment").toggle(false)
- .parent().append("<div class='alert'>Comments are closed.</div>")
+ .parent().append("<div class='alert alert-warning'>Comments are closed.</div>")
}
$(".add-comment").click(function() {
$("#comment-form").toggle();
@@ -54,7 +54,7 @@
$(".no_comment").toggle(false);
$(".add-comment").toggle(false);
$("#comment-form")
- .replaceWith("<div class='alert'>Thank you for your comment!</div>")
+ .replaceWith("<div class='alert alert-success'>Thank you for your comment!</div>")
}
}
})
diff --git a/website/templates/js/cart.js b/website/templates/js/cart.js
index 114f85b..20090ca 100644
--- a/website/templates/js/cart.js
+++ b/website/templates/js/cart.js
@@ -133,19 +133,19 @@
if(doc.description === doc.item_name) doc.description = "";
$(repl('<div class="row">\
- <div class="col col-lg-9 col-sm-9">\
+ <div class="col-md-9 col-sm-9">\
<div class="row">\
- <div class="col col-lg-3">%(image_html)s</div>\
- <div class="col col-lg-9">\
+ <div class="col-md-3">%(image_html)s</div>\
+ <div class="col-md-9">\
<h4><a href="%(page_name)s">%(item_name)s</a></h4>\
<p>%(description)s</p>\
</div>\
</div>\
</div>\
- <div class="col col-lg-3 col-sm-3 text-right">\
+ <div class="col-md-3 col-sm-3 text-right">\
<div class="input-group item-update-cart">\
<input type="text" placeholder="Qty" value="%(qty)s" \
- data-item-code="%(item_code)s" class="text-right">\
+ data-item-code="%(item_code)s" class="text-right form-control">\
<div class="input-group-btn">\
<button class="btn btn-primary" data-item-code="%(item_code)s">\
<i class="icon-ok"></i></button>\
@@ -160,20 +160,20 @@
render_tax_row: function($cart_taxes, doc, shipping_rules) {
var shipping_selector;
if(shipping_rules) {
- shipping_selector = '<select>' + $.map(shipping_rules, function(rule) {
+ shipping_selector = '<select class="form-control">' + $.map(shipping_rules, function(rule) {
return '<option value="' + rule[0] + '">' + rule[1] + '</option>' }).join("\n") +
'</select>';
}
var $tax_row = $(repl('<div class="row">\
- <div class="col col-lg-9 col-sm-9">\
+ <div class="col-md-9 col-sm-9">\
<div class="row">\
- <div class="col col-lg-9 col-offset-3">' +
+ <div class="col-md-9 col-md-offset-3">' +
(shipping_selector || '<p>%(description)s</p>') +
'</div>\
</div>\
</div>\
- <div class="col col-lg-3 col-sm-3 text-right">\
+ <div class="col-md-3 col-sm-3 text-right">\
<p' + (shipping_selector ? ' style="margin-top: 5px;"' : "") + '>%(formatted_tax_amount)s</p>\
</div>\
</div>', doc)).appendTo($cart_taxes);
@@ -206,32 +206,28 @@
render_address: function($address_wrapper, addresses, address_name) {
$.each(addresses, function(i, address) {
- $(repl('<div class="accordion-group"> \
- <div class="accordion-heading"> \
+ $(repl('<div class="panel panel-default"> \
+ <div class="panel-heading"> \
<div class="row"> \
- <div class="col col-lg-10 address-title" \
+ <div class="col-md-10 address-title" \
data-address-name="%(name)s"><strong>%(name)s</strong></div> \
- <div class="col col-lg-2"><input type="checkbox" \
+ <div class="col-md-2"><input type="checkbox" \
data-address-name="%(name)s"></div> \
</div> \
</div> \
- <div class="accordion-body collapse" data-address-name="%(name)s"> \
- <div class="accordion-inner">%(display)s</div> \
+ <div class="panel-collapse collapse" data-address-name="%(name)s"> \
+ <div class="panel-body">%(display)s</div> \
</div> \
</div>', address))
.css({"margin": "10px auto"})
.appendTo($address_wrapper);
});
- $address_wrapper.find(".accordion-heading")
- .css({
- "background-color": "#eee",
- "padding": "10px",
- })
+ $address_wrapper.find(".panel-heading")
.find(".address-title")
.css({"cursor": "pointer"})
.on("click", function() {
- $address_wrapper.find('.accordion-body[data-address-name="'
+ $address_wrapper.find('.panel-collapse[data-address-name="'
+$(this).attr("data-address-name")+'"]').collapse("toggle");
});
@@ -265,12 +261,12 @@
$address_wrapper.find('input[type="checkbox"][data-address-name="'+ address_name +'"]')
.prop("checked", true);
- $address_wrapper.find(".accordion-body").collapse({
+ $address_wrapper.find(".panel-collapse").collapse({
parent: $address_wrapper,
toggle: false
});
- $address_wrapper.find('.accordion-body[data-address-name="'+ address_name +'"]')
+ $address_wrapper.find('.panel-collapse[data-address-name="'+ address_name +'"]')
.collapse("show");
},
diff --git a/website/templates/js/contact.js b/website/templates/js/contact.js
index a28abba..dece898 100644
--- a/website/templates/js/contact.js
+++ b/website/templates/js/contact.js
@@ -26,7 +26,12 @@
sender: email,
message: message,
callback: function(r) {
- msgprint(r.message);
+ if(r.status==="okay") {
+ msgprint(r.message || "Thank you for your message.")
+ } else {
+ msgprint("There were errors");
+ console.log(r.exc);
+ }
$(':input').val('');
}
});
diff --git a/website/templates/js/product_list.js b/website/templates/js/product_list.js
index 801bc80..729499c 100644
--- a/website/templates/js/product_list.js
+++ b/website/templates/js/product_list.js
@@ -40,7 +40,7 @@
if(data.length < 10) {
if(!table) {
$(".more-btn")
- .replaceWith("<div class='alert'>No products found.</div>");
+ .replaceWith("<div class='alert alert-warning'>No products found.</div>");
} else {
$(".more-btn")
.replaceWith("<div class='text-muted'>Nothing more to show.</div>");
diff --git a/website/templates/pages/about.html b/website/templates/pages/about.html
index 2f4f6e4..277f790 100644
--- a/website/templates/pages/about.html
+++ b/website/templates/pages/about.html
@@ -3,14 +3,14 @@
{% set title="About Us" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
{{ obj.doc.company_introduction or "<h2>About Us</h2><p>Some Introduction about your company that you would like your website visitor to know. More people than you think will read your About page. People always like to know who the are doing business with. Be authentic and avoid using jargon like 'value added services' etc. Be sure to update your company history and list of key team members in Website > About Us Settings</p>" }}
{% if obj.doclist.get({"doctype":"Company History"}) %}
<h3>{{ obj.doc.company_history_heading or "Company History" }}</h3>
{% for d in obj.doclist.get({"doctype":"Company History"}) %}
<div class="row">
- <span class="col col-lg-2"><h4 style="margin:0px;">{{ d.year }}</h4></span>
- <span class="col col-lg-10"><p>{{ d.highlight }}</p></span>
+ <span class="col-md-2"><h4 style="margin:0px;">{{ d.year }}</h4></span>
+ <span class="col-md-10"><p>{{ d.highlight }}</p></span>
</div>
{% endfor %}
{% endif %}
@@ -18,12 +18,12 @@
<h3>{{ obj.doc.team_members_heading or "Team Members" }}</h3>
{% for d in obj.doclist.get({"doctype":"About Us Team Member"}) %}
<div class="row" itemscope itemtype="http://schema.org/Person">
- <span class="col col-lg-2">
+ <span class="col-md-2">
<div class="avatar avatar-large">
<img class="avatar" src="{{ d.image_link }}" style="" itemprop="image">
</div>
</span>
- <span class="col col-lg-10"><h4 itemprop="name">{{ d.full_name }}</h4>
+ <span class="col-md-10"><h4 itemprop="name">{{ d.full_name }}</h4>
<p itemprop="description">{{ d.bio }}</p>
</span>
</div>
diff --git a/website/templates/pages/account.html b/website/templates/pages/account.html
index b3fe5df..539e014 100644
--- a/website/templates/pages/account.html
+++ b/website/templates/pages/account.html
@@ -3,7 +3,7 @@
{% set title="My Account" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<ul class="breadcrumb">
<li><a href="index">Home</a></li>
<li class="active">My Account</li>
diff --git a/website/templates/pages/address.html b/website/templates/pages/address.html
index cf1fc4b..faf7bb0 100644
--- a/website/templates/pages/address.html
+++ b/website/templates/pages/address.html
@@ -8,7 +8,7 @@
{% if df.fieldtype in ["Data", "Link"] -%}
<fieldset>
<label>{{ df.label }}</label>
- <input type="text" placeholder="Type {{ df.label }}"
+ <input class="form-control" type="text" placeholder="Type {{ df.label }}"
data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}"
{% if doc and doc.fields.get(df.fieldname) -%} value="{{ doc.fields[df.fieldname] }}" {%- endif %}>
</fieldset>
@@ -22,7 +22,7 @@
{% elif df.fieldtype == "Select" -%}
<fieldset>
<label>{{ df.label }}</label>
- <select data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}">
+ <select class="form-control" data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}">
{% for value in df.options.split("\n") -%}
{% if doc and doc.fields.get(df.fieldname) == value -%}
<option selected="selected">{{ value }}</option>
@@ -37,7 +37,7 @@
{%- endmacro %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<ul class="breadcrumb">
<li><a href="index">Home</a></li>
<li><a href="account">My Account</a></li>
@@ -52,10 +52,10 @@
<div id="address-error" class="alert alert-danger" style="display: none;"></div>
<form autocomplete="on">
<div class="row">
- <section class="col col-lg-6">
+ <section class="col-md-6">
{{ render_fields(meta.left_fields) }}
</section>
- <section class="col col-lg-6">
+ <section class="col-md-6">
{{ render_fields(meta.right_fields) }}
</section>
</section>
diff --git a/website/templates/pages/addresses.html b/website/templates/pages/addresses.html
index 8526666..90d0d13 100644
--- a/website/templates/pages/addresses.html
+++ b/website/templates/pages/addresses.html
@@ -3,7 +3,7 @@
{% set title="My Addresses" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<ul class="breadcrumb">
<li><a href="index">Home</a></li>
<li><a href="account">My Account</a></li>
diff --git a/website/templates/pages/blog.html b/website/templates/pages/blog.html
index d7eaba4..9bd1abf 100644
--- a/website/templates/pages/blog.html
+++ b/website/templates/pages/blog.html
@@ -11,7 +11,7 @@
{% set title="Blog" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<h2 id="blog-title">{{ blog_title }}</h2>
{% if blog_introduction %}
<p>{{ blog_introduction }}</p>
diff --git a/website/templates/pages/cart.html b/website/templates/pages/cart.html
index 5bfca33..6708ad3 100644
--- a/website/templates/pages/cart.html
+++ b/website/templates/pages/cart.html
@@ -7,7 +7,7 @@
{% set title="Shopping Cart" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<h2><i class="icon-shopping-cart"></i> {{ title }}</h2>
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-info" style="width: 100%;"></div>
@@ -18,12 +18,12 @@
<div id="cart-error" class="alert alert-danger" style="display: none;"></div>
<hr>
<div class="row">
- <div class="col col-lg-9 col-sm-9">
+ <div class="col-md-9 col-sm-9">
<div class="row">
- <div class="col col-lg-9 col-offset-3"><h4>Item Details</h4></div>
+ <div class="col-md-9 col-md-offset-3"><h4>Item Details</h4></div>
</div>
</div>
- <div class="col col-lg-3 col-sm-3 text-right"><h4>Qty, Amount</h4></div>
+ <div class="col-md-3 col-sm-3 text-right"><h4>Qty, Amount</h4></div>
</div><hr>
<div id="cart-items">
</div>
@@ -34,16 +34,16 @@
<hr>
<div id="cart-addresses">
<div class="row">
- <div class="col col-lg-6">
+ <div class="col-md-6">
<h4>Shipping Address</h4>
- <div id="cart-shipping-address" class="accordion"
+ <div id="cart-shipping-address" class="panel-group"
data-fieldname="shipping_address_name"></div>
<button class="btn btn-default" type="button" id="cart-add-shipping-address">
<span class="icon icon-plus"></span> New Shipping Address</button>
</div>
- <div class="col col-lg-6">
+ <div class="col-md-6">
<h4>Billing Address</h4>
- <div id="cart-billing-address" class="accordion"
+ <div id="cart-billing-address" class="panel-group"
data-fieldname="customer_address"></div>
<button class="btn btn-default" type="button" id="cart-add-billing-address">
<span class="icon icon-plus"></span> New Billing Address</button>
diff --git a/website/templates/pages/contact.html b/website/templates/pages/contact.html
index 113b627..86be163 100644
--- a/website/templates/pages/contact.html
+++ b/website/templates/pages/contact.html
@@ -7,32 +7,32 @@
{% set title="Contact Us" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<h3>{{ obj.doc.heading or "Contact Us"}}</h3>
<div class="row">
- <div class="web-form col col-lg-8">
- <p id="contact-alert" class="alert"
+ <div class="web-form col-md-8">
+ <p id="contact-alert" class="alert alert-warning"
style="display: none;"> </p>
<p>
- <select name="subject">
+ <select name="subject" class="form-control">
{% for option in obj.query_options %}
<option value="{{ option }}">{{ option }}</option>
{% endfor %}
</select>
</p>
<p>
- <input name="email" type="text"
+ <input class="form-control" name="email" type="text"
placeholder="Your Email Address" />
</p>
<p>
- <textarea rows="10" name="message"></textarea>
+ <textarea rows="10" name="message" class="form-control"></textarea>
</p>
<p>
<button class="btn btn-primary btn-send">Send</button>
</p>
</div>
{% if obj.doc.address %}
- <div class="col col-lg-3 col-offset-1 alert" style="margin-top: 20px;" itemscope itemtype="http://schema.org/PostalAddress">
+ <div class="col-md-3 col-md-offset-1 alert alert-warning" style="margin-top: 20px;" itemscope itemtype="http://schema.org/PostalAddress">
<h4><i class="icon-map-marker"></i> {{ obj.address.address_title }}</h4>
{% if obj.address.address_line1 %}
<span itemprop="streetAddress">{{ obj.address.address_line1 }}</span><br>
diff --git a/website/templates/pages/order.html b/website/templates/pages/order.html
index 61afe32..1893575 100644
--- a/website/templates/pages/order.html
+++ b/website/templates/pages/order.html
@@ -3,7 +3,7 @@
{% set title=doc.name %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<ul class="breadcrumb">
<li><a href="index">Home</a></li>
<li><a href="account">My Account</a></li>
@@ -15,16 +15,16 @@
{%- if doc.status -%}
<div style="font-size: 13px;">
<div class="row">
- <div class="col col-lg-2">
+ <div class="col-md-2">
<div class="label">{{ doc.status }}</div>
</div>
- <div class="col col-lg-4">
+ <div class="col-md-4">
{{ utils.formatdate(doc.transaction_date) }}
</div>
</div>
<br>
<div class="row">
- <div class="col col-lg-12">
+ <div class="col-md-12">
<table class="table table-bordered">
<tbody>
<tr>
@@ -52,8 +52,8 @@
</div>
</div>
<div class="row">
- <div class="col col-lg-6"></div>
- <div class="col col-lg-6">
+ <div class="col-md-6"></div>
+ <div class="col-md-6">
<table cellspacing=0 width=100%>
<tbody>
<tr>
diff --git a/website/templates/pages/orders.html b/website/templates/pages/orders.html
index bb72fc3..e0bf4d7 100644
--- a/website/templates/pages/orders.html
+++ b/website/templates/pages/orders.html
@@ -8,7 +8,7 @@
currency = "{{ currency }}";
wn.currency_symbols = {{ currency_symbols }};
</script>
-<div class="col col-lg-12">
+<div class="col-md-12">
<ul class="breadcrumb">
<li><a href="index">Home</a> <span class="divider">/</span></li>
<li><a href="account">My Account</a> <span class="divider">/</span></li>
@@ -44,8 +44,8 @@
// parent
var $order = $(repl('<div class="row">\
- <div class="col col-lg-3"><a href="order?name=%(name)s">%(name)s</a></div>\
- <div class="col col-lg-9"></div>\
+ <div class="col-md-3"><a href="order?name=%(name)s">%(name)s</a></div>\
+ <div class="col-md-9"></div>\
</div>', order)).appendTo($list);
// items
@@ -53,12 +53,12 @@
item.export_rate = format_currency(item.export_rate, order.currency);
item.export_amount = format_currency(item.export_amount, order.currency);
var $item = $(repl('<div class="row">\
- <div class="col col-lg-3">%(item_name)s</div>\
- <div class="col col-lg-2" style="text-align: right;">%(export_rate)s</div>\
- <div class="col col-lg-2" style="text-align: right;">%(qty)s %(stock_uom)s</div>\
- <div class="col col-lg-2" style="text-align: right;">%(export_amount)s</div>\
+ <div class="col-md-3">%(item_name)s</div>\
+ <div class="col-md-2" style="text-align: right;">%(export_rate)s</div>\
+ <div class="col-md-2" style="text-align: right;">%(qty)s %(stock_uom)s</div>\
+ <div class="col-md-2" style="text-align: right;">%(export_amount)s</div>\
</div>\
- </div>', item)).appendTo($order.find(".col col-lg-9"));
+ </div>', item)).appendTo($order.find(".col-md-9"));
});
$("<hr>").appendTo($list);
diff --git a/website/templates/pages/partners.html b/website/templates/pages/partners.html
index c090227..50a095d 100644
--- a/website/templates/pages/partners.html
+++ b/website/templates/pages/partners.html
@@ -3,12 +3,12 @@
{% set title="Partners" %}
{% block content %}
- <div class="col col-lg-12">
+ <div class="col-md-12">
<h2 id="blog-title">{{ title }}</h2>
<hr>
{% for partner_info in partners %}
<div class="row">
- <div class="col col-lg-3">
+ <div class="col-md-3">
{% if partner_info.logo -%}
<a href="{{ partner_info.page_name }}">
<img itemprop="brand" src="{{ partner_info.logo }}" class="partner-logo"
@@ -16,7 +16,7 @@
</a>
{%- endif %}
</div>
- <div class="col col-lg-9">
+ <div class="col-md-9">
<a href="{{ partner_info.page_name }}">
<h4>{{ partner_info.partner_name }}</h4>
</a>
diff --git a/website/templates/pages/product_search.html b/website/templates/pages/product_search.html
index 4711408..e969f75 100644
--- a/website/templates/pages/product_search.html
+++ b/website/templates/pages/product_search.html
@@ -18,7 +18,7 @@
</script>
{% include 'app/website/templates/html/product_search_box.html' %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<h3 class="search-results">Search Results</h3>
<div id="search-list" class="row">
diff --git a/website/templates/pages/profile.html b/website/templates/pages/profile.html
index 61ae1b0..ea2433b 100644
--- a/website/templates/pages/profile.html
+++ b/website/templates/pages/profile.html
@@ -3,7 +3,7 @@
{% set title="My Profile" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<ul class="breadcrumb">
<li><a href="index">Home</a> <span class="divider">/</span></li>
<li><a href="account">My Account</a> <span class="divider">/</span></li>
@@ -15,23 +15,23 @@
<form>
<fieldset>
<label>Full Name</label>
- <input type="text" id="fullname" placeholder="Your Name">
+ <input class="form-control" type="text" id="fullname" placeholder="Your Name">
</fieldset>
<fieldset>
<label>Password</label>
- <input type="password" id="password" placeholder="Password">
+ <input class="form-control" type="password" id="password" placeholder="Password">
</fieldset>
<fieldset>
<label>Company Name</label>
- <input type="text" id="company_name" placeholder="Company Name" value="{{ company_name }}">
+ <input class="form-control" type="text" id="company_name" placeholder="Company Name" value="{{ company_name }}">
</fieldset>
<fieldset>
<label>Mobile No</label>
- <input type="text" id="mobile_no" placeholder="Mobile No" value="{{ mobile_no }}">
+ <input class="form-control" type="text" id="mobile_no" placeholder="Mobile No" value="{{ mobile_no }}">
</fieldset>
<fieldset>
<label>Phone</label>
- <input type="text" id="phone" placeholder="Phone" value="{{ phone }}">
+ <input class="form-control" type="text" id="phone" placeholder="Phone" value="{{ phone }}">
</fieldset>
<button id="update_profile" type="submit" class="btn btn-default">Update</button>
</form>
diff --git a/website/templates/pages/ticket.html b/website/templates/pages/ticket.html
index 5835246..1ae2e88 100644
--- a/website/templates/pages/ticket.html
+++ b/website/templates/pages/ticket.html
@@ -3,7 +3,7 @@
{% set title=doc.name %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<ul class="breadcrumb">
<li><a href="index">Home</a> <span class="divider">/</span></li>
<li><a href="account">My Account</a> <span class="divider">/</span></li>
@@ -14,13 +14,13 @@
<hr>
{%- if doc.status -%}
<div class="row">
- <div class="col col-lg-2">
+ <div class="col-md-2">
<div class="label">{{ doc.status }}</div>
</div>
- <div class="col col-lg-7">
+ <div class="col-md-7">
{{ doc.subject }}
</div>
- <div class="col col-lg-3">
+ <div class="col-md-3">
{{ utils.formatdate(doc.transaction_date) }}
</div>
</div>
diff --git a/website/templates/pages/tickets.html b/website/templates/pages/tickets.html
index 901453d..3d5cf21 100644
--- a/website/templates/pages/tickets.html
+++ b/website/templates/pages/tickets.html
@@ -3,7 +3,7 @@
{% set title="My Tickets" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<ul class="breadcrumb">
<li><a href="index">Home</a></li>
<li><a href="account">My Account</a></li>
@@ -39,9 +39,9 @@
// parent
var $ticket = $(repl('<div class="row">\
- <div class="col col-lg-2"><span class="label">%(status)s</span></div>\
- <div class="col col-lg-3"><a href="ticket?name=%(name)s">%(name)s</a></div>\
- <div class="col col-lg-7">%(subject)s</div>\
+ <div class="col-md-2"><span class="label">%(status)s</span></div>\
+ <div class="col-md-3"><a href="ticket?name=%(name)s">%(name)s</a></div>\
+ <div class="col-md-7">%(subject)s</div>\
</div>', ticket)).appendTo($list);
$("<hr>").appendTo($list);
diff --git a/website/templates/pages/writers.html b/website/templates/pages/writers.html
index 9d660b8..5b254b0 100644
--- a/website/templates/pages/writers.html
+++ b/website/templates/pages/writers.html
@@ -3,7 +3,7 @@
{% set title="Blog Writers" %}
{% block content %}
-<div class="col col-lg-12">
+<div class="col-md-12">
<h2 id="blog-title">Blog Writers</h2>
{% if writers_introduction %}
<p>{{ writers_introduction }}</p>