Merge branch 'master' into customer-login
diff --git a/docs/dev/custom_script_examples/docs.dev.custom_script.calculate.md b/docs/dev/custom_script_examples/docs.dev.custom_script.calculate.md
new file mode 100644
index 0000000..6744e2b
--- /dev/null
+++ b/docs/dev/custom_script_examples/docs.dev.custom_script.calculate.md
@@ -0,0 +1,24 @@
+---
+{
+ "_label": "Calculate Incentive for Sales Team"
+}
+---
+Can be used in any Sales Transaction with **Sales Team** Table:
+
+
+ cur_frm.cscript.custom_validate = function(doc) {
+ // calculate incentives for each person on the deal
+ total_incentive = 0
+ $.each(wn.model.get("Sales Team", {parent:doc.name}), function(i, d) {
+
+ // calculate incentive
+ var incentive_percent = 2;
+ if(doc.grand_total > 400) incentive_percent = 4;
+
+ // actual incentive
+ d.incentives = flt(doc.grand_total) * incentive_percent / 100;
+ total_incentive += flt(d.incentives)
+ });
+
+ doc.total_incentive = total_incentive;
+ }
\ No newline at end of file
diff --git a/docs/dev/custom_script_examples/docs.dev.custom_script.fetch.md b/docs/dev/custom_script_examples/docs.dev.custom_script.fetch.md
new file mode 100644
index 0000000..d0414fb
--- /dev/null
+++ b/docs/dev/custom_script_examples/docs.dev.custom_script.fetch.md
@@ -0,0 +1,20 @@
+---
+{
+ "_label": "Custom Script: Fetch Values from Master"
+}
+---
+To pull a value of a link on selection, use the `add_fetch` method.
+
+ add_fetch(link_fieldname, source_fieldname, target_fieldname)
+
+### Example
+
+You create Custom Field **VAT ID** (`vat_id`) in **Customer** and **Sales Invoice** and want to make sure this value gets updated every time you select a Customer in a Sales Invoice.
+
+Then in the Sales Invoice Custom Script, add this line:
+
+ cur_frm.add_fetch('customer','vat_id','vat_id')
+
+
+---
+See: [How to create a custom script](!docs.dev.custom_script.html)
\ No newline at end of file
diff --git a/docs/dev/custom_script_examples/docs.dev.custom_script.item_code.md b/docs/dev/custom_script_examples/docs.dev.custom_script.item_code.md
new file mode 100644
index 0000000..22e75c9
--- /dev/null
+++ b/docs/dev/custom_script_examples/docs.dev.custom_script.item_code.md
@@ -0,0 +1,35 @@
+---
+{
+ "_label": "Generate Item Code based on Custom Logic"
+}
+---
+Add this in the Custom Script of **Item**, so that the new Item Code is generated just before the a new Item is saved.
+
+ cur_frm.cscript.custom_validate = function(doc) {
+ // clear item_code (name is from item_code)
+ doc.item_code = "";
+
+ // first 2 characters based on item_group
+ switch(doc.item_group) {
+ case "Test A":
+ doc.item_code = "TA";
+ break;
+ case "Test B":
+ doc.item_code = "TB";
+ break;
+ default:
+ doc.item_code = "XX";
+ }
+
+ // add next 2 characters based on brand
+ switch(doc.brand) {
+ case "Brand A":
+ doc.item_code += "BA";
+ break;
+ case "Brand B":
+ doc.item_code += "BB";
+ break;
+ default:
+ doc.item_code += "BX";
+ }
+ }
\ No newline at end of file
diff --git a/docs/dev/custom_script_examples/docs.dev.custom_script.read_only.md b/docs/dev/custom_script_examples/docs.dev.custom_script.read_only.md
new file mode 100644
index 0000000..9da3e85
--- /dev/null
+++ b/docs/dev/custom_script_examples/docs.dev.custom_script.read_only.md
@@ -0,0 +1,13 @@
+---
+{
+ "_label": "Make an Item read-only after Saving"
+}
+---
+Use the method `cur_frm.set_df_property` to update the field's display.
+
+In this script we also use the `__islocal` property of the doc to check if the document has been saved atleast once or is never saved. If `__islocal` is `1`, then the document has never been saved.
+
+ cur_frm.cscript.custom_refresh = function(doc) {
+ // use the __islocal value of doc, to check if the doc is saved or not
+ cur_frm.set_df_property("myfield", "read_only", doc.__islocal ? 0 : 1);
+ }
\ No newline at end of file
diff --git a/docs/dev/custom_script_examples/docs.dev.custom_script.validate.md b/docs/dev/custom_script_examples/docs.dev.custom_script.validate.md
new file mode 100644
index 0000000..a22851c
--- /dev/null
+++ b/docs/dev/custom_script_examples/docs.dev.custom_script.validate.md
@@ -0,0 +1,12 @@
+---
+{
+ "_label": "Date Validation: Do not allow past dates in a date field"
+}
+---
+
+ cur_frm.cscript.custom_validate = function(doc) {
+ if (doc.from_date < get_today()) {
+ msgprint("You can not select past date in From Date");
+ validated = false;
+ }
+ }
\ No newline at end of file
diff --git a/docs/dev/custom_script_examples/docs.dev.custom_script.validate1.md b/docs/dev/custom_script_examples/docs.dev.custom_script.validate1.md
new file mode 100644
index 0000000..b12c61f
--- /dev/null
+++ b/docs/dev/custom_script_examples/docs.dev.custom_script.validate1.md
@@ -0,0 +1,12 @@
+---
+{
+ "_label": "Restrict Purpose of Stock Entry"
+}
+---
+
+ cur_frm.cscript.custom_validate = function(doc) {
+ if(user=="user1@example.com" && doc.purpose!="Material Receipt") {
+ msgprint("You are only allowed Material Receipt");
+ validated = false;
+ }
+ }
\ No newline at end of file
diff --git a/docs/dev/custom_script_examples/docs.dev.custom_script.validate2.md b/docs/dev/custom_script_examples/docs.dev.custom_script.validate2.md
new file mode 100644
index 0000000..05d662e
--- /dev/null
+++ b/docs/dev/custom_script_examples/docs.dev.custom_script.validate2.md
@@ -0,0 +1,22 @@
+---
+{
+ "_label": "Restrict User Based on Child Record (Warehouse)"
+}
+---
+
+ // restrict certain warehouse to Material Manager
+ cur_frm.cscript.custom_validate = function(doc) {
+ if(user_roles.indexOf("Material Manager")==-1) {
+
+ var restricted_in_source = wn.model.get("Stock Entry Detail",
+ {parent:cur_frm.doc.name, s_warehouse:"Restricted"});
+
+ var restricted_in_target = wn.model.get("Stock Entry Detail",
+ {parent:cur_frm.doc.name, t_warehouse:"Restricted"})
+
+ if(restricted_in_source.length || restricted_in_target.length) {
+ msgprint("Only Material Manager can make entry in Restricted Warehouse");
+ validated = false;
+ }
+ }
+ }
\ No newline at end of file
diff --git a/docs/dev/custom_script_examples/docs.dev.custom_script.validate3.md b/docs/dev/custom_script_examples/docs.dev.custom_script.validate3.md
new file mode 100644
index 0000000..629f1c1
--- /dev/null
+++ b/docs/dev/custom_script_examples/docs.dev.custom_script.validate3.md
@@ -0,0 +1,17 @@
+---
+{
+ "_label": "Restrict Cancel Rights based on Certain Order Value"
+}
+---
+Add a handler to `custom_before_cancel` event:
+
+ cur_frm.cscript.custom_before_cancel = function(doc) {
+ if (user_roles.indexOf("Accounts User")!=-1 && user_roles.indexOf("Accounts Manager")==-1
+ && user_roles.indexOf("System Manager")==-1) {
+ if (flt(doc.grand_total) > 10000) {
+ msgprint("You can not cancel this transaction, because grand total \
+ is greater than 10000");
+ validated = false;
+ }
+ }
+ }
diff --git a/docs/dev/docs.dev.client_script.md b/docs/dev/docs.dev.client_script.md
deleted file mode 100644
index fc82f9d..0000000
--- a/docs/dev/docs.dev.client_script.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-{
- "_label": "Client Scripts: Custoimzing ERPNext"
-
-}
----
-
-
-
-
diff --git a/docs/dev/docs.dev.custom_script.md b/docs/dev/docs.dev.custom_script.md
new file mode 100644
index 0000000..ad50637
--- /dev/null
+++ b/docs/dev/docs.dev.custom_script.md
@@ -0,0 +1,31 @@
+---
+{
+ "_label": "Custom Script Examples",
+ "_toc": [
+ "docs.dev.custom_script.fetch",
+ "docs.dev.custom_script.validate",
+ "docs.dev.custom_script.validate1",
+ "docs.dev.custom_script.validate2",
+ "docs.dev.custom_script.validate3",
+ "docs.dev.custom_script.read_only",
+ "docs.dev.custom_script.calculate",
+ "docs.dev.custom_script.item_code"
+ ]
+}
+---
+### How to Create a Custom Script
+
+Create a Custom Script (you must have System Manager role for this):
+
+1. Got to: Setup > Custom Script > New Custom Script
+1. Select the DocType in which you want to add the Custom Script
+
+---
+### Notes
+
+1. Server Custom Scripts are only available for the Administrator.
+1. Client Custom Scripts are in Javascript and Server Custom Scripts are in Python.
+1. For testing, make sure to go to Tools > Clear Cache and refresh after updating a Custom Script.
+
+
+
diff --git a/docs/dev/docs.dev.md b/docs/dev/docs.dev.md
index 285f176..15afea8 100644
--- a/docs/dev/docs.dev.md
+++ b/docs/dev/docs.dev.md
@@ -6,7 +6,7 @@
"docs.dev.quickstart",
"docs.dev.framework",
"docs.dev.modules",
- "docs.dev.client_script",
+ "docs.dev.custom_script",
"docs.dev.api",
"docs.dev.translate",
"docs.dev.docs"
diff --git a/home/__init__.py b/home/__init__.py
index 9667efd..0861798 100644
--- a/home/__init__.py
+++ b/home/__init__.py
@@ -88,4 +88,5 @@
if method in ['on_update', 'on_submit']:
subject, color = feed_dict.get(doc.doctype, [None, None])
if subject:
- make_feed('', doc.doctype, doc.name, doc.owner, subject % doc.fields, color)
+ from webnotes.utils import encode_dict
+ make_feed('', doc.doctype, doc.name, doc.owner, subject % encode_dict(doc.fields.copy()), color)
diff --git a/hr/doctype/expense_claim/expense_claim.js b/hr/doctype/expense_claim/expense_claim.js
index ad3dace..65c5579 100644
--- a/hr/doctype/expense_claim/expense_claim.js
+++ b/hr/doctype/expense_claim/expense_claim.js
@@ -27,8 +27,8 @@
var d1 = wn.model.add_child(jv, 'Journal Voucher Detail', 'entries');
d1.credit = cur_frm.doc.total_sanctioned_amount;
if(r.message) {
- d1.account = r.message[0].account;
- d1.balance = r.message[0].balance;
+ d1.account = r.message.account;
+ d1.balance = r.message.balance;
}
loaddoc('Journal Voucher', jv.name);
diff --git a/patches/patch_list.py b/patches/patch_list.py
index 320614a..ff28031 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -259,4 +259,5 @@
"execute:webnotes.bean('Style Settings').save() #2013-08-20",
"patches.september_2013.p01_fix_buying_amount_gl_entries",
"patches.september_2013.p01_update_communication",
+ "patches.september_2013.p02_fix_serial_no_status",
]
\ No newline at end of file
diff --git a/patches/september_2013/p02_fix_serial_no_status.py b/patches/september_2013/p02_fix_serial_no_status.py
new file mode 100644
index 0000000..714cd7a
--- /dev/null
+++ b/patches/september_2013/p02_fix_serial_no_status.py
@@ -0,0 +1,30 @@
+# 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():
+ stock_entries = webnotes.conn.sql("""select ste_item.serial_no, ste.name
+ from `tabStock Entry Detail` ste_item, `tabStock Entry` ste
+ where ste.name = ste_item.parent
+ and ifnull(ste_item.serial_no, '') != ''
+ and ste.purpose='Material Transfer'
+ and ste.modified>='2013-08-14'
+ order by ste.posting_date desc, ste.posting_time desc, ste.name desc""", as_dict=1)
+
+ for d in stock_entries:
+ serial_nos = d.serial_no.split("\n")
+ for sr in serial_nos:
+ serial_no = sr.strip()
+ if serial_no:
+ serial_bean = webnotes.bean("Serial No", serial_no)
+ if serial_bean.doc.status == "Not Available":
+ latest_sle = webnotes.conn.sql("""select voucher_no from `tabStock Ledger Entry`
+ where item_code=%s and warehouse=%s and serial_no like %s
+ order by name desc limit 1""", (serial_bean.doc.item_code,
+ serial_bean.doc.warehouse, "%%%s%%" % serial_no))
+
+ if latest_sle and latest_sle[0][0] == d.name:
+ serial_bean.doc.status = "Available"
+ serial_bean.save()
\ No newline at end of file
diff --git a/startup/boot.py b/startup/boot.py
index 79f0a7b..ce446e9 100644
--- a/startup/boot.py
+++ b/startup/boot.py
@@ -16,7 +16,9 @@
if webnotes.session['user']!='Guest':
bootinfo['letter_heads'] = get_letter_heads()
-
+
+ load_country_and_currency(bootinfo)
+
import webnotes.model.doctype
bootinfo['notification_settings'] = webnotes.doc("Notification Control",
"Notification Control").get_values()
@@ -36,7 +38,15 @@
bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center
from `tabCompany`""", as_dict=1, update={"doctype":":Company"})
-
+
+def load_country_and_currency(bootinfo):
+ if bootinfo.control_panel.country and \
+ webnotes.conn.exists("Country", bootinfo.control_panel.country):
+ bootinfo["docs"] += [webnotes.doc("Country", bootinfo.control_panel.country)]
+
+ bootinfo["docs"] += webnotes.conn.sql("""select * from tabCurrency
+ where ifnull(enabled,0)=1""", as_dict=1, update={"doctype":":Currency"})
+
def get_letter_heads():
"""load letter heads with startup"""
import webnotes
diff --git a/startup/event_handlers.py b/startup/event_handlers.py
index e807797..fa08962 100644
--- a/startup/event_handlers.py
+++ b/startup/event_handlers.py
@@ -64,6 +64,12 @@
webnotes.response['message'] = 'Account Expired'
raise webnotes.AuthenticationError
+def on_build():
+ from website.helpers.make_web_include_files import make
+ make()
+
+ from home.page.latest_updates import latest_updates
+ latest_updates.make()
def comment_added(doc):
"""add comment to feed"""
diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py
index e7018a2..1feab02 100644
--- a/stock/doctype/serial_no/serial_no.py
+++ b/stock/doctype/serial_no/serial_no.py
@@ -52,9 +52,7 @@
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):
"""
diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py
index e3fc67e..6a61461 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -343,10 +343,14 @@
def update_stock_ledger(self, is_cancelled=0):
self.values = []
for d in getlist(self.doclist, 'mtn_details'):
- if cstr(d.s_warehouse):
+ if cstr(d.s_warehouse) and not is_cancelled:
self.add_to_values(d, cstr(d.s_warehouse), -flt(d.transfer_qty), is_cancelled)
+
if cstr(d.t_warehouse):
self.add_to_values(d, cstr(d.t_warehouse), flt(d.transfer_qty), is_cancelled)
+
+ if cstr(d.s_warehouse) and is_cancelled:
+ self.add_to_values(d, cstr(d.s_warehouse), -flt(d.transfer_qty), is_cancelled)
get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values,
self.doc.amended_from and 'Yes' or 'No')
diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py
index b9b3230..3beb61d 100644
--- a/stock/doctype/stock_entry/test_stock_entry.py
+++ b/stock/doctype/stock_entry/test_stock_entry.py
@@ -648,6 +648,9 @@
self.assertTrue(webnotes.conn.exists("Serial No", "ABCD"))
self.assertTrue(webnotes.conn.exists("Serial No", "EFGH"))
+ se.cancel()
+ self.assertFalse(webnotes.conn.get_value("Serial No", "ABCD", "warehouse"))
+
def test_serial_no_not_exists(self):
se = webnotes.bean(copy=test_records[0])
se.doc.purpose = "Material Issue"
@@ -699,6 +702,9 @@
se.insert()
se.submit()
self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse 1 - _TC")
+
+ se.cancel()
+ self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse - _TC")
def test_serial_warehouse_error(self):
make_serialized_item()
@@ -720,7 +726,6 @@
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])
diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 58fc828..2f36538 100644
--- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -119,13 +119,14 @@
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)
+ webnotes.throw(_("Serial No") + ": " + serial_no +
+ _(" does not belong to Warehouse") + ": " + self.doc.warehouse,
+ 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)
+ webnotes.throw(_("Serial No status must be 'Available' to Deliver")
+ + ": " + serial_no, SerialNoStatusError)
sr.doc.warehouse = None
diff --git a/website/helpers/make_web_include_files.py b/website/helpers/make_web_include_files.py
index 7ff3d6a..a9df23b 100644
--- a/website/helpers/make_web_include_files.py
+++ b/website/helpers/make_web_include_files.py
@@ -3,14 +3,14 @@
import os
import webnotes
-import webnotes.webutils
def make():
+ from startup.webutils import get_home_page
if not webnotes.conn:
webnotes.connect()
- home_page = webnotes.webutils.get_home_page()
+ home_page = get_home_page()
fname = 'js/wn-web.js'
if os.path.basename(os.path.abspath('.'))!='public':