Merge branch 'master' into customer-login
This commit is contained in:
commit
36cd22bf71
@ -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;
|
||||||
|
}
|
||||||
@ -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)
|
||||||
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
{
|
|
||||||
"_label": "Client Scripts: Custoimzing ERPNext"
|
|
||||||
|
|
||||||
}
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
31
docs/dev/docs.dev.custom_script.md
Normal file
31
docs/dev/docs.dev.custom_script.md
Normal file
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
"docs.dev.quickstart",
|
"docs.dev.quickstart",
|
||||||
"docs.dev.framework",
|
"docs.dev.framework",
|
||||||
"docs.dev.modules",
|
"docs.dev.modules",
|
||||||
"docs.dev.client_script",
|
"docs.dev.custom_script",
|
||||||
"docs.dev.api",
|
"docs.dev.api",
|
||||||
"docs.dev.translate",
|
"docs.dev.translate",
|
||||||
"docs.dev.docs"
|
"docs.dev.docs"
|
||||||
|
|||||||
@ -88,4 +88,5 @@ def update_feed(controller, method=None):
|
|||||||
if method in ['on_update', 'on_submit']:
|
if method in ['on_update', 'on_submit']:
|
||||||
subject, color = feed_dict.get(doc.doctype, [None, None])
|
subject, color = feed_dict.get(doc.doctype, [None, None])
|
||||||
if subject:
|
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)
|
||||||
|
|||||||
@ -27,8 +27,8 @@ erpnext.hr.ExpenseClaimController = wn.ui.form.Controller.extend({
|
|||||||
var d1 = wn.model.add_child(jv, 'Journal Voucher Detail', 'entries');
|
var d1 = wn.model.add_child(jv, 'Journal Voucher Detail', 'entries');
|
||||||
d1.credit = cur_frm.doc.total_sanctioned_amount;
|
d1.credit = cur_frm.doc.total_sanctioned_amount;
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
d1.account = r.message[0].account;
|
d1.account = r.message.account;
|
||||||
d1.balance = r.message[0].balance;
|
d1.balance = r.message.balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
loaddoc('Journal Voucher', jv.name);
|
loaddoc('Journal Voucher', jv.name);
|
||||||
|
|||||||
@ -259,4 +259,5 @@ patch_list = [
|
|||||||
"execute:webnotes.bean('Style Settings').save() #2013-08-20",
|
"execute:webnotes.bean('Style Settings').save() #2013-08-20",
|
||||||
"patches.september_2013.p01_fix_buying_amount_gl_entries",
|
"patches.september_2013.p01_fix_buying_amount_gl_entries",
|
||||||
"patches.september_2013.p01_update_communication",
|
"patches.september_2013.p01_update_communication",
|
||||||
|
"patches.september_2013.p02_fix_serial_no_status",
|
||||||
]
|
]
|
||||||
30
patches/september_2013/p02_fix_serial_no_status.py
Normal file
30
patches/september_2013/p02_fix_serial_no_status.py
Normal file
@ -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()
|
||||||
@ -16,7 +16,9 @@ def boot_session(bootinfo):
|
|||||||
|
|
||||||
if webnotes.session['user']!='Guest':
|
if webnotes.session['user']!='Guest':
|
||||||
bootinfo['letter_heads'] = get_letter_heads()
|
bootinfo['letter_heads'] = get_letter_heads()
|
||||||
|
|
||||||
|
load_country_and_currency(bootinfo)
|
||||||
|
|
||||||
import webnotes.model.doctype
|
import webnotes.model.doctype
|
||||||
bootinfo['notification_settings'] = webnotes.doc("Notification Control",
|
bootinfo['notification_settings'] = webnotes.doc("Notification Control",
|
||||||
"Notification Control").get_values()
|
"Notification Control").get_values()
|
||||||
@ -36,7 +38,15 @@ def boot_session(bootinfo):
|
|||||||
|
|
||||||
bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center
|
bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center
|
||||||
from `tabCompany`""", as_dict=1, update={"doctype":":Company"})
|
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():
|
def get_letter_heads():
|
||||||
"""load letter heads with startup"""
|
"""load letter heads with startup"""
|
||||||
import webnotes
|
import webnotes
|
||||||
|
|||||||
@ -64,6 +64,12 @@ def check_if_expired():
|
|||||||
webnotes.response['message'] = 'Account Expired'
|
webnotes.response['message'] = 'Account Expired'
|
||||||
raise webnotes.AuthenticationError
|
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):
|
def comment_added(doc):
|
||||||
"""add comment to feed"""
|
"""add comment to feed"""
|
||||||
|
|||||||
@ -52,9 +52,7 @@ class DocType(StockController):
|
|||||||
webnotes.throw(_("Item Code cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
|
webnotes.throw(_("Item Code cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
|
||||||
if not self.via_stock_ledger and warehouse != self.doc.warehouse:
|
if not self.via_stock_ledger and warehouse != self.doc.warehouse:
|
||||||
webnotes.throw(_("Warehouse cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
|
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):
|
def validate_item(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -343,10 +343,14 @@ class DocType(StockController):
|
|||||||
def update_stock_ledger(self, is_cancelled=0):
|
def update_stock_ledger(self, is_cancelled=0):
|
||||||
self.values = []
|
self.values = []
|
||||||
for d in getlist(self.doclist, 'mtn_details'):
|
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)
|
self.add_to_values(d, cstr(d.s_warehouse), -flt(d.transfer_qty), is_cancelled)
|
||||||
|
|
||||||
if cstr(d.t_warehouse):
|
if cstr(d.t_warehouse):
|
||||||
self.add_to_values(d, cstr(d.t_warehouse), flt(d.transfer_qty), is_cancelled)
|
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,
|
get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values,
|
||||||
self.doc.amended_from and 'Yes' or 'No')
|
self.doc.amended_from and 'Yes' or 'No')
|
||||||
|
|||||||
@ -648,6 +648,9 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
self.assertTrue(webnotes.conn.exists("Serial No", "ABCD"))
|
self.assertTrue(webnotes.conn.exists("Serial No", "ABCD"))
|
||||||
self.assertTrue(webnotes.conn.exists("Serial No", "EFGH"))
|
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):
|
def test_serial_no_not_exists(self):
|
||||||
se = webnotes.bean(copy=test_records[0])
|
se = webnotes.bean(copy=test_records[0])
|
||||||
se.doc.purpose = "Material Issue"
|
se.doc.purpose = "Material Issue"
|
||||||
@ -699,6 +702,9 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
se.insert()
|
se.insert()
|
||||||
se.submit()
|
se.submit()
|
||||||
self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse 1 - _TC")
|
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):
|
def test_serial_warehouse_error(self):
|
||||||
make_serialized_item()
|
make_serialized_item()
|
||||||
@ -720,7 +726,6 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
|
|
||||||
serial_no = get_serial_nos(se.doclist[1].serial_no)[0]
|
serial_no = get_serial_nos(se.doclist[1].serial_no)[0]
|
||||||
self.assertFalse(webnotes.conn.get_value("Serial No", serial_no, "warehouse"))
|
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():
|
def make_serialized_item():
|
||||||
se = webnotes.bean(copy=test_records[0])
|
se = webnotes.bean(copy=test_records[0])
|
||||||
|
|||||||
@ -119,13 +119,14 @@ class DocType(DocListController):
|
|||||||
|
|
||||||
if self.doc.actual_qty < 0:
|
if self.doc.actual_qty < 0:
|
||||||
if sr.doc.warehouse!=self.doc.warehouse:
|
if sr.doc.warehouse!=self.doc.warehouse:
|
||||||
webnotes.throw(_("Warehouse does not belong to Item") + \
|
webnotes.throw(_("Serial No") + ": " + serial_no +
|
||||||
(": %s (%s)" % (self.doc.item_code, serial_no)), SerialNoWarehouseError)
|
_(" does not belong to Warehouse") + ": " + self.doc.warehouse,
|
||||||
|
SerialNoWarehouseError)
|
||||||
|
|
||||||
if self.doc.voucher_type in ("Delivery Note", "Sales Invoice") \
|
if self.doc.voucher_type in ("Delivery Note", "Sales Invoice") \
|
||||||
and sr.doc.status != "Available":
|
and sr.doc.status != "Available":
|
||||||
webnotes.throw(_("Serial No status must be 'Available' to Deliver") + \
|
webnotes.throw(_("Serial No status must be 'Available' to Deliver")
|
||||||
": " + serial_no, SerialNoStatusError)
|
+ ": " + serial_no, SerialNoStatusError)
|
||||||
|
|
||||||
|
|
||||||
sr.doc.warehouse = None
|
sr.doc.warehouse = None
|
||||||
|
|||||||
@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import webnotes
|
import webnotes
|
||||||
import webnotes.webutils
|
|
||||||
|
|
||||||
def make():
|
def make():
|
||||||
|
from startup.webutils import get_home_page
|
||||||
|
|
||||||
if not webnotes.conn:
|
if not webnotes.conn:
|
||||||
webnotes.connect()
|
webnotes.connect()
|
||||||
|
|
||||||
home_page = webnotes.webutils.get_home_page()
|
home_page = get_home_page()
|
||||||
|
|
||||||
fname = 'js/wn-web.js'
|
fname = 'js/wn-web.js'
|
||||||
if os.path.basename(os.path.abspath('.'))!='public':
|
if os.path.basename(os.path.abspath('.'))!='public':
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user