Merge branch 'master' of github.com:webnotes/erpnext

This commit is contained in:
Rushabh Mehta 2012-12-06 07:17:58 +01:00
commit 989ca93b17
31 changed files with 731 additions and 1680 deletions

View File

@ -20,7 +20,7 @@ def get_companies():
else: else:
return [r[0] for r in webnotes.conn.sql("""select name from tabCompany return [r[0] for r in webnotes.conn.sql("""select name from tabCompany
where docstatus!=2""")] where docstatus!=2""")]
@webnotes.whitelist() @webnotes.whitelist()
def get_children(): def get_children():
args = webnotes.form_dict args = webnotes.form_dict

View File

@ -1,31 +1,27 @@
# Search Criteria, creditors_ledger
[ [
{
# These values are common in all dictionaries "owner": "nabin@erpnext.com",
{ "docstatus": 0,
'creation': '2012-04-03 12:49:51', "creation": "2012-05-14 18:05:41",
'docstatus': 0, "modified_by": "nabin@erpnext.com",
'modified': '2012-04-03 12:49:51', "modified": "2012-12-06 11:36:09"
'modified_by': u'Administrator', },
'owner': u'nabin@webnotestech.com' {
}, "custom_query": null,
"report_script": null,
# These values are common for all Search Criteria "page_len": 50,
{ "module": "Accounts",
'criteria_name': u"Creditor's Ledger", "standard": "Yes",
'doc_type': u'GL Entry', "sort_order": "DESC",
'doctype': 'Search Criteria', "filters": "{\"GL Entry\\u0001Voucher Type\":[],\"GL Entry\\u0001Is Cancelled\":[\"No\"],\"GL Entry\\u0001Is Opening\":[\"\"],\"GL Entry\\u0001Fiscal Year\":[\"\"]}",
'filters': u"{'GL Entry\x01Voucher Type':'','GL Entry\x01Is Cancelled':'','GL Entry\x01Is Opening':'','GL Entry\x01Fiscal Year':''}", "doc_type": "GL Entry",
'module': u'Accounts', "name": "__common__",
'name': '__common__', "doctype": "Search Criteria",
'page_len': 50, "sort_by": "`tabGL Entry`.`name`",
'sort_order': u'DESC', "criteria_name": "Creditor's Ledger"
'standard': u'Yes' },
}, {
"name": "creditors_ledger",
# Search Criteria, creditors_ledger "doctype": "Search Criteria"
{ }
'doctype': 'Search Criteria',
'name': u'creditors_ledger'
}
] ]

View File

@ -1,31 +1,27 @@
# Search Criteria, debtors_ledger
[ [
{
# These values are common in all dictionaries "owner": "nabin@erpnext.com",
{ "docstatus": 0,
'creation': '2012-04-03 12:49:51', "creation": "2012-05-14 18:05:42",
'docstatus': 0, "modified_by": "nabin@erpnext.com",
'modified': '2012-04-03 12:49:51', "modified": "2012-12-06 11:37:15"
'modified_by': u'Administrator', },
'owner': u'nabin@webnotestech.com' {
}, "custom_query": null,
"report_script": null,
# These values are common for all Search Criteria "page_len": 50,
{ "module": "Accounts",
'criteria_name': u"Debtor's Ledger", "standard": "Yes",
'doc_type': u'GL Entry', "sort_order": "DESC",
'doctype': 'Search Criteria', "filters": "{\"GL Entry\\u0001Voucher Type\":[],\"GL Entry\\u0001Is Cancelled\":[\"No\"],\"GL Entry\\u0001Is Opening\":[],\"GL Entry\\u0001Fiscal Year\":[]}",
'filters': u"{'GL Entry\x01Voucher Type':'','GL Entry\x01Is Cancelled':'No','GL Entry\x01Is Opening':'','GL Entry\x01Fiscal Year':''}", "doc_type": "GL Entry",
'module': u'Accounts', "name": "__common__",
'name': '__common__', "doctype": "Search Criteria",
'page_len': 50, "sort_by": "`tabGL Entry`.`name`",
'sort_order': u'DESC', "criteria_name": "Debtor's Ledger"
'standard': u'Yes' },
}, {
"name": "debtors_ledger",
# Search Criteria, debtors_ledger "doctype": "Search Criteria"
{ }
'doctype': 'Search Criteria',
'name': u'debtors_ledger'
}
] ]

View File

@ -199,23 +199,35 @@ cur_frm.cscript.update_stock_qty = function(doc,cdt,cdn){
} }
//==================== UOM ====================================================================== //==================== UOM ======================================================================
cur_frm.cscript.uom = function(doc, cdt, cdn) { cur_frm.cscript.uom = function(doc, cdt, cdn, args) {
if(!args) args = {};
// args passed can contain conversion_factor
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
if (d.item_code && d.uom) { $.extend(args, {
call_back = function(doc, cdt, cdn){ item_code: d.item_code,
cur_frm.cscript.calc_amount(doc, 2); uom: d.uom,
} stock_qty: flt(d.stock_qty),
str_arg = {'item_code':d.item_code, 'uom':d.uom, 'stock_qty':flt(d.stock_qty), 'qty': flt(d.qty), 'conversion_rate':doc.conversion_rate, 'doc_name': doc.name} });
// Updates Conversion Factor, Qty and Purchase Rate
get_server_fields('get_uom_details',JSON.stringify(str_arg), fname, doc,cdt,cdn,1, call_back); if(d.item_code && d.uom) {
// don't make mistake of calling update_stock_qty() the get_uom_details returns stock_qty as per conversion factor properly cur_frm.call({
method: "buying.doctype.purchase_common.purchase_common.get_uom_details",
args: { args: args },
child: d,
callback: function(r) {
cur_frm.cscript.calc_amount(doc, 2);
}
});
} }
} }
//==================== Conversion factor ========================================================= //==================== Conversion factor =========================================================
cur_frm.cscript.conversion_factor = function(doc, cdt, cdn) { cur_frm.cscript.conversion_factor = function(doc, cdt, cdn) {
cur_frm.cscript.uom(doc, cdt, cdn); var item = locals[cdt][cdn];
cur_frm.cscript.uom(doc, cdt, cdn, { conversion_factor: item.conversion_factor });
} }
//==================== stock qty ====================================================================== //==================== stock qty ======================================================================

View File

@ -182,39 +182,6 @@ class DocType(TransactionBase):
ret = { 'projected_qty' : bin and flt(bin[0]['projected_qty']) or 0 } ret = { 'projected_qty' : bin and flt(bin[0]['projected_qty']) or 0 }
return ret return ret
def get_uom_details(self, arg = ''):
"""fetches details on change of UOM"""
import json
arg, ret = json.loads(arg), {}
uom = webnotes.conn.sql("""\
select conversion_factor
from `tabUOM Conversion Detail`
where parent = %s and uom = %s""", (arg['item_code'],arg['uom']), as_dict = 1)
if not uom: return ret
last_purchase_details, last_purchase_date = self.get_last_purchase_details(arg['item_code'], arg['doc_name'])
conversion_factor = flt(uom[0]['conversion_factor'])
conversion_rate = flt(arg['conversion_rate'])
purchase_ref_rate = last_purchase_details and \
(last_purchase_details['purchase_ref_rate'] * conversion_factor) or 0
purchase_rate = last_purchase_details and \
(last_purchase_details['purchase_rate'] * conversion_factor) or 0
ret = {
'conversion_factor': conversion_factor,
'qty': flt(arg['stock_qty']) / conversion_factor,
'purchase_ref_rate': purchase_ref_rate,
'purchase_rate': purchase_rate,
'import_ref_rate': purchase_ref_rate / conversion_rate,
'import_rate': purchase_rate / conversion_rate,
}
return ret
# --- Last Purchase Rate related methods --- # --- Last Purchase Rate related methods ---
def update_last_purchase_rate(self, obj, is_submit): def update_last_purchase_rate(self, obj, is_submit):
@ -683,3 +650,27 @@ class DocType(TransactionBase):
if d.prevdoc_doctype and d.prevdoc_docname: if d.prevdoc_doctype and d.prevdoc_docname:
dt = sql("select transaction_date from `tab%s` where name = '%s'" % (d.prevdoc_doctype, d.prevdoc_docname)) dt = sql("select transaction_date from `tab%s` where name = '%s'" % (d.prevdoc_doctype, d.prevdoc_docname))
d.prevdoc_date = dt and dt[0][0].strftime('%Y-%m-%d') or '' d.prevdoc_date = dt and dt[0][0].strftime('%Y-%m-%d') or ''
@webnotes.whitelist()
def get_uom_details(args=None):
"""fetches details on change of UOM"""
if not args:
return {}
if isinstance(args, basestring):
import json
args = json.loads(args)
uom = webnotes.conn.sql("""select conversion_factor
from `tabUOM Conversion Detail` where parent = %s and uom = %s""",
(args['item_code'], args['uom']), as_dict=1)
if not uom: return {}
conversion_factor = args.get("conversion_factor") or \
flt(uom[0]["conversion_factor"])
return {
"conversion_factor": conversion_factor,
"qty": flt(args["stock_qty"]) / conversion_factor,
}

View File

@ -69,10 +69,6 @@ class DocType(TransactionBase):
# Get UOM Details
def get_uom_details(self, arg = ''):
return get_obj('Purchase Common').get_uom_details(arg)
# get available qty at warehouse # get available qty at warehouse
def get_bin_details(self, arg = ''): def get_bin_details(self, arg = ''):
return get_obj(dt='Purchase Common').get_bin_details(arg) return get_obj(dt='Purchase Common').get_bin_details(arg)

View File

@ -27,7 +27,7 @@ wn.doclistviews['Purchase Order'] = wn.views.ListView.extend({
}, },
css: {'text-align':'right'} css: {'text-align':'right'}
}, },
{width: '8%', content: 'per_received', type:'bar-graph', label:'Delivered'}, {width: '8%', content: 'per_received', type:'bar-graph', label:'Received'},
{width: '8%', content: 'per_billed', type:'bar-graph', label:'Billed'}, {width: '8%', content: 'per_billed', type:'bar-graph', label:'Billed'},
{width: '12%', content:'transaction_date', {width: '12%', content:'transaction_date',
css: {'text-align': 'right', 'color':'#777'}, css: {'text-align': 'right', 'color':'#777'},

View File

@ -121,11 +121,6 @@ class DocType:
doc.fields[r] = ret[r] doc.fields[r] = ret[r]
# Get UOM Details
# ---------------------------------
def get_uom_details(self, arg = ''):
return get_obj(dt='Purchase Common').get_uom_details(arg)
# GET TERMS & CONDITIONS # GET TERMS & CONDITIONS
#----------------------------- #-----------------------------
def get_tc_details(self): def get_tc_details(self):
@ -219,4 +214,4 @@ class DocType:
self.update_bin(is_submit = 0, is_stopped = (cstr(self.doc.status) == 'Stopped') and 1 or 0) self.update_bin(is_submit = 0, is_stopped = (cstr(self.doc.status) == 'Stopped') and 1 or 0)
# Step 5:=> Set Status # Step 5:=> Set Status
webnotes.conn.set(self.doc,'status','Cancelled') webnotes.conn.set(self.doc,'status','Cancelled')

View File

@ -140,7 +140,7 @@ class TaxController(TransactionController):
self.doc.net_total = flt(self.doc.net_total, self.precision.main.net_total) self.doc.net_total = flt(self.doc.net_total, self.precision.main.net_total)
self.doc.fields[self.fmap.net_total_print] = \ self.doc.fields[self.fmap.net_total_print] = \
flt(self.doc.fields.get(self.fmap.net_total_print), flt(self.doc.fields.get(self.fmap.net_total_print),
self.precision.main[self.fmap.net_total_print]) self.precision.main.get(self.fmap.net_total_print))
def calculate_taxes(self): def calculate_taxes(self):
for item in self.item_doclist: for item in self.item_doclist:

View File

@ -1,5 +1,7 @@
erpnext.updates = [ erpnext.updates = [
["5th December 2012", [ ["5th December 2012", [
"Leave Application: Now can set approver.",
"New Roles Added: Leave Approver and Expense Approver.",
"Production Order is now linked with sales order.", "Production Order is now linked with sales order.",
"Production Planning Tool: The field 'Allow SA items as raw material' has been renamed to 'Use multi-level BOM', 'Include in plan' column from SO table has been deleted", "Production Planning Tool: The field 'Allow SA items as raw material' has been renamed to 'Use multi-level BOM', 'Include in plan' column from SO table has been deleted",
"Batch nos are now filtered with item and available qty", "Batch nos are now filtered with item and available qty",

View File

@ -3,4 +3,6 @@ install_docs = [
{"doctype":"Role", "role_name":"Employee", "name":"Employee"}, {"doctype":"Role", "role_name":"Employee", "name":"Employee"},
{"doctype":"Role", "role_name":"HR Manager", "name":"HR Manager"}, {"doctype":"Role", "role_name":"HR Manager", "name":"HR Manager"},
{"doctype":"Role", "role_name":"HR User", "name":"HR User"}, {"doctype":"Role", "role_name":"HR User", "name":"HR User"},
{"doctype":"Role", "role_name":"Leave Approver", "name":"Leave Approver"},
{"doctype":"Role", "role_name":"Expense Approver", "name":"Expense Approver"},
] ]

View File

@ -15,79 +15,84 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
cur_frm.add_fetch('employee', 'company', 'company'); cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.cscript.onload = function(doc,cdt,cdn){ cur_frm.cscript.onload = function(doc,cdt,cdn){
// if(!doc.approval_status)
if(!doc.approval_status) set_multiple(cdt,cdn,{approval_status:'Draft'}); cur_frm.set_value("approval_status", "Draft")
if(doc.employee) cur_frm.cscript.employee(doc,cdt,cdn);
if (doc.__islocal) { if (doc.__islocal) {
if(doc.amended_from) set_multiple(cdt,cdn,{approval_status:'Draft'}); cur_frm.set_value("posting_date", dateutil.get_today());
var val = getchildren('Expense Claim Detail', doc.name, 'expense_voucher_details', doc.doctype); if(doc.amended_from)
for(var i = 0; i<val.length; i++){ cur_frm.set_value("approval_status", "Draft");
val[i].sanctioned_amount =''; cur_frm.cscript.clear_sanctioned(doc);
}
doc.total_sanctioned_amount = '';
refresh_many(['sanctioned_amount', 'total_sanctioned_amount']);
} }
cur_frm.call({
method:"get_approver_list",
callback: function(r) {
cur_frm.set_df_property("exp_approver", "options", r.message);
}
});
}
cur_frm.cscript.clear_sanctioned = function(doc) {
var val = getchildren('Expense Claim Detail', doc.name,
'expense_voucher_details', doc.doctype);
for(var i = 0; i<val.length; i++){
val[i].sanctioned_amount ='';
}
doc.total_sanctioned_amount = '';
refresh_many(['sanctioned_amount', 'total_sanctioned_amount']);
} }
cur_frm.cscript.refresh = function(doc,cdt,cdn){ cur_frm.cscript.refresh = function(doc,cdt,cdn){
hide_field('calculate_total_amount'); cur_frm.set_intro("");
if(user == doc.exp_approver && doc.approval_status == 'Submitted'){ if(doc.__islocal && !in_list(user_roles, "HR User")) {
unhide_field(['update_voucher', 'approve', 'reject', 'calculate_total_amount']); cur_frm.set_intro("Fill the form and save it")
cur_frm.fields_dict['expense_voucher_details'].grid.set_column_disp('sanctioned_amount', true);
set_field_permlevel('remark', 0);
} else { } else {
hide_field(['update_voucher', 'approve', 'reject']); if(doc.approval_status=="Draft") {
cur_frm.fields_dict['expense_voucher_details'].grid.set_column_disp('sanctioned_amount', false); if(user==doc.exp_approver) {
set_field_permlevel('remark', 1); if(doc.approval_status=="Draft") {
cur_frm.set_intro("You are the Expense Approver for this record. Please Update the 'Status' and Save");
cur_frm.toggle_enable("approval_status", true);
}
} else {
cur_frm.set_intro("Expense Claim is pending approval. Only the Expense Approver can update status.");
cur_frm.toggle_enable("approval_status", false);
}
} else {
if(doc.approval_status=="Approved") {
cur_frm.set_intro("Expense Claim has been approved.");
} else if(doc.approval_status=="Rejected") {
cur_frm.set_intro("Expense Claim has been rejected.");
}
}
} }
if (doc.docstatus == 0) unhide_field('calculate_total_amount');
} if(doc.approval_status=="Approved" && doc.docstatus==0) {
cur_frm.savesubmit()
}}
cur_frm.cscript.validate = function(doc) { cur_frm.cscript.validate = function(doc) {
if(cint(doc.docstatus) == 0) {
doc.approval_status = "Draft";
}
cur_frm.cscript.calculate_total(doc); cur_frm.cscript.calculate_total(doc);
} }
cur_frm.cscript.employee = function(doc,cdt,cdn){
if(doc.employee){
$c_obj(make_doclist(doc.doctype, doc.name),'set_approver','', function(r,rt){
if(r.message){
doc.employee_name = r.message['emp_nm'];
wn.meta.get_docfield(doc.doctype, 'exp_approver' , doc.name).options = r.message['app_lst'];
refresh_many(['exp_approver','employee_name']);
}
});
}
}
cur_frm.cscript.calculate_total = function(doc,cdt,cdn){ cur_frm.cscript.calculate_total = function(doc,cdt,cdn){
if(doc.approval_status == 'Draft'){ doc.total_claimed_amount = 0;
var val = getchildren('Expense Claim Detail', doc.name, 'expense_voucher_details', doc.doctype); doc.total_sanctioned_amount = 0;
var total_claim =0; $.each(wn.model.get("Expense Claim Detail", {parent:doc.name}), function(i, d) {
for(var i = 0; i<val.length; i++){ doc.total_claimed_amount += d.claim_amount;
val[i].sanctioned_amount = val[i].claim_amount; if(d.sanctioned_amount==null) {
total_claim = flt(total_claim)+flt(val[i].claim_amount); d.sanctioned_amount = d.claim_amount;
refresh_field('sactioned_amount', val[i].name, 'expense_voucher_details');
} }
doc.total_claimed_amount = flt(total_claim); doc.total_sanctioned_amount += d.sanctioned_amount;
refresh_field('total_claimed_amount'); });
}
else if(doc.approval_status == 'Submitted'){ refresh_field("total_claimed_amount");
var val = getchildren('Expense Claim Detail', doc.name, 'expense_voucher_details', doc.doctype); refresh_field('total_sanctioned_amount');
var total_sanctioned = 0;
for(var i = 0; i<val.length; i++){
total_sanctioned = flt(total_sanctioned)+flt(val[i].sanctioned_amount);
refresh_field('sactioned_amount', val[i].name, 'expense_voucher_details');
}
doc.total_sanctioned_amount = flt(total_sanctioned);
refresh_field('total_sanctioned_amount');
}
} }
cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){ cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){
@ -100,155 +105,8 @@ cur_frm.cscript.sanctioned_amount = function(doc,cdt,cdn){
cur_frm.cscript.calculate_total(doc,cdt,cdn); cur_frm.cscript.calculate_total(doc,cdt,cdn);
} }
cur_frm.cscript.approve = function(doc,cdt,cdn){
cur_frm.cscript.calculate_total(doc,cdt,cdn);
if(user == doc.exp_approver){
var approve_voucher_dialog;
set_approve_voucher_dialog = function() {
approve_voucher_dialog = new Dialog(400, 200, 'Approve Voucher');
approve_voucher_dialog.make_body([
['HTML', 'Message', '<div class = "comment">You wont be able to do any changes after approving this expense voucher. Are you sure, you want to approve it ?</div>'],
['HTML', 'Response', '<div class = "comment" id="approve_voucher_dialog_response"></div>'],
['HTML', 'Approve Voucher', '<div></div>']
]);
var approve_voucher_btn1 = $a($i(approve_voucher_dialog.widgets['Approve Voucher']), 'button', 'button');
approve_voucher_btn1.innerHTML = 'Yes';
approve_voucher_btn1.onclick = function(){ approve_voucher_dialog.add(); }
var approve_voucher_btn2 = $a($i(approve_voucher_dialog.widgets['Approve Voucher']), 'button', 'button');
approve_voucher_btn2.innerHTML = 'No';
$y(approve_voucher_btn2,{marginLeft:'4px'});
approve_voucher_btn2.onclick = function(){ approve_voucher_dialog.hide();}
approve_voucher_dialog.onshow = function() {
$i('approve_voucher_dialog_response').innerHTML = '';
}
approve_voucher_dialog.add = function() {
// sending...
$i('approve_voucher_dialog_response').innerHTML = 'Processing...';
$c_obj(make_doclist(this.doc.doctype, this.doc.name),'approve_voucher','', function(r,rt){
if(r.message == 'Approved'){
$i('approve_voucher_dialog_response').innerHTML = 'Approved';
refresh_field('approval_status');
hide_field(['update_voucher', 'approve', 'reject', 'calculate_total_amount']);
approve_voucher_dialog.hide();
var args = {
type: 'Expense Claim Approved',
doctype: 'Expense Claim',
contact_name: doc.employee_name,
send_to: doc.email_id
}
cur_frm.cscript.notify(doc, args);
}
else if(r.message == 'Incomplete'){
$i('approve_voucher_dialog_response').innerHTML = 'Incomplete Voucher';
}
else if(r.message == 'No Amount'){
$i('approve_voucher_dialog_response').innerHTML = 'Calculate total amount';
}
});
}
}
if(!approve_voucher_dialog){
set_approve_voucher_dialog();
}
approve_voucher_dialog.doc = doc;
approve_voucher_dialog.cdt = cdt;
approve_voucher_dialog.cdn = cdn;
approve_voucher_dialog.show();
refresh_field('expense_voucher_details');
doc.__unsaved = 0;
cur_frm.refresh_header();
}else{
msgprint("Expense Claim can be approved by Approver only");
}
}
cur_frm.cscript.reject = function(doc,cdt,cdn){
cur_frm.cscript.calculate_total(doc,cdt,cdn);
if(user == doc.exp_approver){
var reject_voucher_dialog;
set_reject_voucher_dialog = function() {
reject_voucher_dialog = new Dialog(400, 200, 'Reject Voucher');
reject_voucher_dialog.make_body([
['HTML', 'Message', '<div class = "comment">You wont be able to do any changes after rejecting this expense voucher. Are you sure, you want to reject it ?</div>'],
['HTML', 'Response', '<div class = "comment" id="reject_voucher_dialog_response"></div>'],
['HTML', 'Reject Voucher', '<div></div>']
]);
var reject_voucher_btn1 = $a($i(reject_voucher_dialog.widgets['Reject Voucher']), 'button', 'button');
reject_voucher_btn1.innerHTML = 'Yes';
reject_voucher_btn1.onclick = function(){ reject_voucher_dialog.add(); }
var reject_voucher_btn2 = $a($i(reject_voucher_dialog.widgets['Reject Voucher']), 'button', 'button');
reject_voucher_btn2.innerHTML = 'No';
$y(reject_voucher_btn2,{marginLeft:'4px'});
reject_voucher_btn2.onclick = function(){ reject_voucher_dialog.hide();}
reject_voucher_dialog.onshow = function() {
$i('reject_voucher_dialog_response').innerHTML = '';
}
reject_voucher_dialog.add = function() {
// sending...
$i('reject_voucher_dialog_response').innerHTML = 'Processing...';
$c_obj(make_doclist(this.doc.doctype, this.doc.name),'reject_voucher','', function(r,rt){
if(r.message == 'Rejected'){
$i('reject_voucher_dialog_response').innerHTML = 'Rejected';
refresh_field('approval_status');
hide_field(['update_voucher', 'approve', 'reject', 'calculate_total_amount']);
reject_voucher_dialog.hide();
var args = {
type: 'Expense Claim Rejected',
doctype: 'Expense Claim',
contact_name: doc.employee_name,
send_to: doc.email_id
}
cur_frm.cscript.notify(doc, args);
}
});
}
}
if(!reject_voucher_dialog){
set_reject_voucher_dialog();
}
reject_voucher_dialog.doc = doc;
reject_voucher_dialog.cdt = cdt;
reject_voucher_dialog.cdn = cdn;
reject_voucher_dialog.show();
refresh_field('expense_voucher_details');
doc.__unsaved = 0;
cur_frm.refresh_header();
}else{
msgprint("Expense Claim can be rejected by Approver only");
}
}
//update follow up
//=================================================================================
cur_frm.cscript.update_voucher = function(doc){
$c_obj(make_doclist(doc.doctype, doc.name),'update_voucher','',function(r, rt){
refresh_field('expense_voucher_details');
doc.__unsaved = 0;
cur_frm.refresh_header();
});
}
cur_frm.cscript.on_submit = function(doc, cdt, cdn) { cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
if(cint(wn.boot.notification_settings.expense_claim)) { if(cint(wn.boot.notification_settings && wn.boot.notification_settings.expense_claim)) {
cur_frm.email_doc(wn.boot.notification_settings.expense_claim_message); cur_frm.email_doc(wn.boot.notification_settings.expense_claim_message);
} }
} }
cur_frm.fields_dict.employee.get_query = erpnext.utils.employee_query;

View File

@ -17,86 +17,27 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import add_days, cstr from webnotes.utils import add_days
from webnotes.model import db_exists from webnotes.model.wrapper import getlist
from webnotes.model.wrapper import getlist, copy_doclist
from webnotes.model.code import get_obj
from webnotes import form, msgprint from webnotes import form, msgprint
sql = webnotes.conn.sql sql = webnotes.conn.sql
class DocType: class DocType:
def __init__(self, doc, doclist=[]): def __init__(self, doc, doclist=[]):
self.doc = doc self.doc = doc
self.doclist = doclist self.doclist = doclist
def get_employee_name(self): def validate(self):
emp_dtl = sql("select employee_name,company_email from `tabEmployee` where name=%s", self.doc.employee) if self.doc.exp_approver == self.doc.owner:
emp_nm = emp_dtl and emp_dtl[0][0] or '' webnotes.msgprint("""Self Approval is not allowed.""", raise_exception=1)
self.doc.employee_name = emp_nm self.validate_fiscal_year()
self.doc.email_id = emp_dtl and emp_dtl[0][1] or '' self.validate_exp_details()
return cstr(emp_nm) def on_submit(self):
if self.doc.approval_status=="Draft":
def get_approver_lst(self): webnotes.msgprint("""Please set Approval Status to 'Approved' or 'Rejected' before submitting""",
approver_lst =[] raise_exception=1)
approver_lst1 = get_obj('Authorization Control').get_approver_name(self.doc.doctype,0,self)
if approver_lst1:
approver_lst=approver_lst1
else:
approver_lst = [x[0] for x in sql("select distinct name from `tabProfile` where enabled=1 and name!='Administrator' and name!='Guest' and docstatus!=2")]
return approver_lst
def set_approver(self):
ret={}
approver_lst =[]
emp_nm = self.get_employee_name()
approver_lst = self.get_approver_lst()
ret = {'app_lst':"\n" + "\n".join(approver_lst), 'emp_nm':cstr(emp_nm)}
return ret
def update_voucher(self):
sql("delete from `tabExpense Claim Detail` where parent = '%s'"%self.doc.name)
for d in getlist(self.doclist, 'expense_voucher_details'):
if not d.expense_type or not d.claim_amount:
msgprint("Please remove the extra blank row added")
raise Exception
d.save(1)
if self.doc.total_sanctioned_amount:
webnotes.conn.set(self.doc,'total_sanctioned_amount',self.doc.total_sanctioned_amount)
if self.doc.remark:
webnotes.conn.set(self.doc, 'remark', self.doc.remark)
def approve_voucher(self):
missing_count = 0
for d in getlist(self.doclist, 'expense_voucher_details'):
if not d.sanctioned_amount:
missing_count += 1
if missing_count == len(getlist(self.doclist, 'expense_voucher_details')):
msgprint("Please add 'Sanctioned Amount' for atleast one expense")
return cstr('Incomplete')
if not self.doc.total_sanctioned_amount:
msgprint("Please calculate total sanctioned amount using button 'Calculate Total Amount'")
return cstr('No Amount')
self.update_voucher()
webnotes.conn.set(self.doc, 'approval_status', 'Approved')
# on approval notification
#get_obj('Notification Control').notify_contact('Expense Claim Approved', self.doc.doctype, self.doc.name, self.doc.email_id, self.doc.employee_name)
return cstr('Approved')
def reject_voucher(self):
if self.doc.remark:
webnotes.conn.set(self.doc, 'remark', self.doc.remark)
webnotes.conn.set(self.doc, 'approval_status', 'Rejected')
return cstr('Rejected')
def validate_fiscal_year(self): def validate_fiscal_year(self):
fy=sql("select year_start_date from `tabFiscal Year` where name='%s'"%self.doc.fiscal_year) fy=sql("select year_start_date from `tabFiscal Year` where name='%s'"%self.doc.fiscal_year)
@ -105,33 +46,16 @@ class DocType:
if str(self.doc.posting_date) < str(ysd) or str(self.doc.posting_date) > str(yed): if str(self.doc.posting_date) < str(ysd) or str(self.doc.posting_date) > str(yed):
msgprint("Posting Date is not within the Fiscal Year selected") msgprint("Posting Date is not within the Fiscal Year selected")
raise Exception raise Exception
def validate(self):
self.validate_fiscal_year()
def on_update(self):
webnotes.conn.set(self.doc, 'approval_status', 'Draft')
def validate_exp_details(self): def validate_exp_details(self):
if not getlist(self.doclist, 'expense_voucher_details'): if not getlist(self.doclist, 'expense_voucher_details'):
msgprint("Please add expense voucher details") msgprint("Please add expense voucher details")
raise Exception raise Exception
if not self.doc.total_claimed_amount: @webnotes.whitelist()
msgprint("Please calculate Total Claimed Amount") def get_approver_list():
raise Exception roles = [r[0] for r in webnotes.conn.sql("""select distinct parent from `tabUserRole`
where role='Expense Approver'""")]
if not self.doc.exp_approver: if not roles:
msgprint("Please select Expense Claim approver") webnotes.msgprint("No Expense Approvers. Please assign 'Expense Approver' Role to atleast one user.")
raise Exception return roles
def on_submit(self):
self.validate_exp_details()
webnotes.conn.set(self.doc, 'approval_status', 'Submitted')
def on_cancel(self):
webnotes.conn.set(self.doc, 'approval_status', 'Cancelled')
def get_formatted_message(self, args):
""" get formatted message for auto notification"""
return get_obj('Notification Control').get_formatted_message(args)

View File

@ -1,390 +1,271 @@
# DocType, Expense Claim
[ [
{
# These values are common in all dictionaries "owner": "harshada@webnotestech.com",
{ "docstatus": 0,
'creation': '2012-03-27 14:35:56', "creation": "2012-12-05 14:11:53",
'docstatus': 0, "modified_by": "Administrator",
'modified': '2012-03-27 14:45:48', "modified": "2012-12-05 14:22:27"
'modified_by': u'Administrator', },
'owner': u'harshada@webnotestech.com' {
}, "is_submittable": 1,
"autoname": "EXP.######",
# These values are common for all DocType "name": "__common__",
{ "default_print_format": "Standard",
'_last_update': u'1308808105', "search_fields": "approval_status,employee,employee_name",
'autoname': u'EXP.######', "module": "HR",
'colour': u'White:FFF', "doctype": "DocType"
'default_print_format': u'Standard', },
'doctype': 'DocType', {
'is_submittable': 1, "name": "__common__",
'module': u'HR', "parent": "Expense Claim",
'name': '__common__', "doctype": "DocField",
'search_fields': u'approval_status,employee,employee_name', "parenttype": "DocType",
'section_style': u'Simple', "parentfield": "fields"
'server_code_error': u' ', },
'show_in_menu': 0, {
'subject': u'From %(employee_name)s for %(total_claimed_amount)s (claimed)', "name": "__common__",
'tag_fields': u'approval_status', "parent": "Expense Claim",
'version': 135 "read": 1,
}, "doctype": "DocPerm",
"parenttype": "DocType",
# These values are common for all DocField "parentfield": "permissions"
{ },
'doctype': u'DocField', {
'name': '__common__', "name": "Expense Claim",
'parent': u'Expense Claim', "doctype": "DocType"
'parentfield': u'fields', },
'parenttype': u'DocType' {
}, "oldfieldtype": "Section Break",
"doctype": "DocField",
# These values are common for all DocPerm "label": "Details",
{ "fieldname": "details",
'doctype': u'DocPerm', "fieldtype": "Section Break",
'name': '__common__', "permlevel": 0
'parent': u'Expense Claim', },
'parentfield': u'permissions', {
'parenttype': u'DocType', "permlevel": 1,
'read': 1 "no_copy": 1,
}, "oldfieldtype": "Select",
"colour": "White:FFF",
# DocType, Expense Claim "doctype": "DocField",
{ "label": "Approval Status",
'doctype': 'DocType', "oldfieldname": "approval_status",
'name': u'Expense Claim' "default": "Draft",
}, "fieldname": "approval_status",
"fieldtype": "Select",
# DocPerm "search_index": 1,
{ "options": "\nDraft\nApproved\nRejected",
'doctype': u'DocPerm', "in_filter": 1
'permlevel': 1, },
'role': u'All' {
}, "oldfieldtype": "Select",
"doctype": "DocField",
# DocPerm "label": "Approver",
{ "oldfieldname": "exp_approver",
'amend': 1, "width": "160px",
'cancel': 1, "fieldname": "exp_approver",
'create': 1, "fieldtype": "Select",
'doctype': u'DocPerm', "permlevel": 0
'match': u'owner', },
'permlevel': 0, {
'submit': 1, "oldfieldtype": "Date",
'write': 1 "doctype": "DocField",
}, "label": "Posting Date",
"oldfieldname": "posting_date",
# DocPerm "fieldname": "posting_date",
{ "fieldtype": "Date",
'amend': 1, "reqd": 1,
'cancel': 1, "permlevel": 0,
'create': 1, "in_filter": 1
'doctype': u'DocPerm', },
'permlevel': 0, {
'role': u'HR Manager', "oldfieldtype": "Column Break",
'submit': 1, "doctype": "DocField",
'write': 1 "width": "50%",
}, "fieldname": "column_break0",
"fieldtype": "Column Break",
# DocPerm "permlevel": 0
{ },
'amend': 1, {
'cancel': 1, "oldfieldtype": "Link",
'create': 1, "colour": "White:FFF",
'doctype': u'DocPerm', "doctype": "DocField",
'permlevel': 0, "label": "From Employee",
'role': u'HR User', "oldfieldname": "employee",
'submit': 1, "permlevel": 0,
'write': 1 "trigger": "Client",
}, "fieldname": "employee",
"fieldtype": "Link",
# DocField "search_index": 1,
{ "reqd": 1,
'doctype': u'DocField', "options": "Employee",
'fieldname': u'details', "in_filter": 1
'fieldtype': u'Section Break', },
'label': u'Details', {
'oldfieldtype': u'Section Break', "oldfieldtype": "Data",
'permlevel': 0 "doctype": "DocField",
}, "label": "Employee Name",
"oldfieldname": "employee_name",
# DocField "width": "150px",
{ "fieldname": "employee_name",
'colour': u'White:FFF', "fieldtype": "Data",
'default': u'Draft', "search_index": 0,
'doctype': u'DocField', "permlevel": 1,
'fieldname': u'approval_status', "in_filter": 1
'fieldtype': u'Select', },
'in_filter': 1, {
'label': u'Approval Status', "no_copy": 1,
'no_copy': 1, "oldfieldtype": "Small Text",
'oldfieldname': u'approval_status', "colour": "White:FFF",
'oldfieldtype': u'Select', "allow_on_submit": 0,
'options': u'\nDraft\nSubmitted\nApproved \nRejected\nCancelled', "doctype": "DocField",
'permlevel': 1, "label": "Remark",
'search_index': 1 "oldfieldname": "remark",
}, "fieldname": "remark",
"fieldtype": "Small Text",
# DocField "permlevel": 0
{ },
'colour': u'White:FFF', {
'doctype': u'DocField', "print_hide": 1,
'fieldname': u'employee', "no_copy": 1,
'fieldtype': u'Link', "oldfieldtype": "Data",
'in_filter': 1, "colour": "White:FFF",
'label': u'From Employee', "doctype": "DocField",
'oldfieldname': u'employee', "label": "Amended From",
'oldfieldtype': u'Link', "oldfieldname": "amended_from",
'options': u'Employee', "width": "160px",
'permlevel': 0, "fieldname": "amended_from",
'reqd': 1, "fieldtype": "Data",
'search_index': 1, "permlevel": 1,
'trigger': u'Client' "report_hide": 1
}, },
{
# DocField "print_hide": 1,
{ "no_copy": 1,
'doctype': u'DocField', "oldfieldtype": "Date",
'fieldname': u'employee_name', "colour": "White:FFF",
'fieldtype': u'Data', "doctype": "DocField",
'in_filter': 1, "label": "Amendment Date",
'label': u'Employee Name', "oldfieldname": "amendment_date",
'oldfieldname': u'employee_name', "width": "160px",
'oldfieldtype': u'Data', "fieldname": "amendment_date",
'permlevel': 1, "fieldtype": "Date",
'search_index': 0, "permlevel": 1,
'width': u'150px' "report_hide": 1
}, },
{
# DocField "oldfieldtype": "Section Break",
{ "doctype": "DocField",
'doctype': u'DocField', "label": "Expense Details",
'fieldname': u'fiscal_year', "fieldname": "expense_details",
'fieldtype': u'Select', "fieldtype": "Section Break",
'in_filter': 1, "permlevel": 0
'label': u'Fiscal Year', },
'oldfieldname': u'fiscal_year', {
'oldfieldtype': u'Select', "oldfieldtype": "Table",
'options': u'link:Fiscal Year', "allow_on_submit": 0,
'permlevel': 0, "doctype": "DocField",
'reqd': 1 "label": "Expense Claim Details",
}, "oldfieldname": "expense_voucher_details",
"options": "Expense Claim Detail",
# DocField "fieldname": "expense_voucher_details",
{ "fieldtype": "Table",
'doctype': u'DocField', "permlevel": 0
'fieldname': u'company', },
'fieldtype': u'Select', {
'in_filter': 1, "no_copy": 1,
'label': u'Company', "oldfieldtype": "Currency",
'oldfieldname': u'company', "colour": "White:FFF",
'oldfieldtype': u'Link', "doctype": "DocField",
'options': u'link:Company', "label": "Total Claimed Amount",
'permlevel': 0, "oldfieldname": "total_claimed_amount",
'reqd': 1 "width": "160px",
}, "fieldname": "total_claimed_amount",
"fieldtype": "Currency",
# DocField "reqd": 0,
{ "permlevel": 1,
'doctype': u'DocField', "in_filter": 0
'fieldname': u'column_break0', },
'fieldtype': u'Column Break', {
'oldfieldtype': u'Column Break', "no_copy": 1,
'permlevel': 0, "oldfieldtype": "Currency",
'width': u'50%' "colour": "White:FFF",
}, "doctype": "DocField",
"label": "Total Sanctioned Amount",
# DocField "oldfieldname": "total_sanctioned_amount",
{ "width": "160px",
'doctype': u'DocField', "fieldname": "total_sanctioned_amount",
'fieldname': u'posting_date', "fieldtype": "Currency",
'fieldtype': u'Date', "permlevel": 1,
'in_filter': 1, "in_filter": 0
'label': u'Posting Date', },
'oldfieldname': u'posting_date', {
'oldfieldtype': u'Date', "print_hide": 1,
'permlevel': 0, "oldfieldtype": "Data",
'reqd': 1 "doctype": "DocField",
}, "label": "Employees Email Id",
"oldfieldname": "email_id",
# DocField "fieldname": "email_id",
{ "fieldtype": "Data",
'doctype': u'DocField', "hidden": 1,
'fieldname': u'exp_approver', "permlevel": 0
'fieldtype': u'Select', },
'label': u'Approver', {
'oldfieldname': u'exp_approver', "oldfieldtype": "Select",
'oldfieldtype': u'Select', "doctype": "DocField",
'permlevel': 0, "label": "Fiscal Year",
'width': u'160px' "oldfieldname": "fiscal_year",
}, "options": "link:Fiscal Year",
"fieldname": "fiscal_year",
# DocField "fieldtype": "Select",
{ "reqd": 1,
'allow_on_submit': 1, "permlevel": 0,
'colour': u'White:FFF', "in_filter": 1
'doctype': u'DocField', },
'fieldname': u'remark', {
'fieldtype': u'Small Text', "oldfieldtype": "Link",
'label': u'Remark', "doctype": "DocField",
'no_copy': 1, "label": "Company",
'oldfieldname': u'remark', "oldfieldname": "company",
'oldfieldtype': u'Small Text', "options": "link:Company",
'permlevel': 0 "fieldname": "company",
}, "fieldtype": "Select",
"reqd": 1,
# DocField "permlevel": 0,
{ "in_filter": 1
'colour': u'White:FFF', },
'doctype': u'DocField', {
'fieldname': u'amended_from', "create": 1,
'fieldtype': u'Data', "doctype": "DocPerm",
'label': u'Amended From', "write": 1,
'no_copy': 1, "role": "Employee",
'oldfieldname': u'amended_from', "permlevel": 0,
'oldfieldtype': u'Data', "match": "owner"
'permlevel': 1, },
'print_hide': 1, {
'report_hide': 1, "amend": 1,
'width': u'160px' "create": 1,
}, "doctype": "DocPerm",
"submit": 1,
# DocField "write": 1,
{ "cancel": 1,
'colour': u'White:FFF', "role": "Expense Approver",
'doctype': u'DocField', "permlevel": 0,
'fieldname': u'amendment_date', "match": "exp_approver:user"
'fieldtype': u'Date', },
'label': u'Amendment Date', {
'no_copy': 1, "amend": 1,
'oldfieldname': u'amendment_date', "create": 1,
'oldfieldtype': u'Date', "doctype": "DocPerm",
'permlevel': 1, "submit": 1,
'print_hide': 1, "write": 1,
'report_hide': 1, "cancel": 1,
'width': u'160px' "role": "HR User",
}, "permlevel": 0
},
# DocField {
{ "doctype": "DocPerm",
'allow_on_submit': 1, "role": "All",
'doctype': u'DocField', "permlevel": 1
'fieldname': u'approve', }
'fieldtype': u'Button',
'hidden': 1,
'label': u'Approve',
'oldfieldtype': u'Button',
'permlevel': 0,
'print_hide': 1,
'trigger': u'Client'
},
# DocField
{
'allow_on_submit': 1,
'doctype': u'DocField',
'fieldname': u'reject',
'fieldtype': u'Button',
'hidden': 1,
'label': u'Reject',
'oldfieldtype': u'Button',
'permlevel': 0,
'print_hide': 1,
'trigger': u'Client'
},
# DocField
{
'doctype': u'DocField',
'fieldname': u'expense_details',
'fieldtype': u'Section Break',
'label': u'Expense Details',
'oldfieldtype': u'Section Break',
'permlevel': 0
},
# DocField
{
'allow_on_submit': 1,
'colour': u'White:FFF',
'doctype': u'DocField',
'fieldname': u'calculate_total_amount',
'fieldtype': u'Button',
'label': u'Calculate Total Amount',
'oldfieldtype': u'Button',
'permlevel': 0,
'print_hide': 1,
'report_hide': 1,
'trigger': u'Client'
},
# DocField
{
'colour': u'White:FFF',
'doctype': u'DocField',
'fieldname': u'total_claimed_amount',
'fieldtype': u'Currency',
'in_filter': 0,
'label': u'Total Claimed Amount',
'no_copy': 1,
'oldfieldname': u'total_claimed_amount',
'oldfieldtype': u'Currency',
'permlevel': 1,
'reqd': 0,
'width': u'160px'
},
# DocField
{
'colour': u'White:FFF',
'doctype': u'DocField',
'fieldname': u'total_sanctioned_amount',
'fieldtype': u'Currency',
'in_filter': 0,
'label': u'Total Sanctioned Amount',
'no_copy': 1,
'oldfieldname': u'total_sanctioned_amount',
'oldfieldtype': u'Currency',
'permlevel': 1,
'width': u'160px'
},
# DocField
{
'allow_on_submit': 1,
'doctype': u'DocField',
'fieldname': u'update_voucher',
'fieldtype': u'Button',
'hidden': 1,
'label': u'Update Voucher',
'oldfieldtype': u'Button',
'permlevel': 0,
'print_hide': 1,
'trigger': u'Client'
},
# DocField
{
'allow_on_submit': 1,
'doctype': u'DocField',
'fieldname': u'expense_voucher_details',
'fieldtype': u'Table',
'label': u'Expense Claim Details',
'oldfieldname': u'expense_voucher_details',
'oldfieldtype': u'Table',
'options': u'Expense Claim Detail',
'permlevel': 0
},
# DocField
{
'doctype': u'DocField',
'fieldname': u'email_id',
'fieldtype': u'Data',
'hidden': 1,
'label': u'Employees Email Id',
'oldfieldname': u'email_id',
'oldfieldtype': u'Data',
'permlevel': 0,
'print_hide': 1
}
] ]

View File

@ -1,103 +1,80 @@
# DocType, Expense Claim Detail
[ [
{
# These values are common in all dictionaries "owner": "harshada@webnotestech.com",
{ "docstatus": 0,
'creation': '2012-03-27 14:35:56', "creation": "2012-07-03 13:30:39",
'docstatus': 0, "modified_by": "Administrator",
'modified': '2012-03-27 14:35:56', "modified": "2012-12-05 14:22:03"
'modified_by': u'Administrator', },
'owner': u'harshada@webnotestech.com' {
}, "istable": 1,
"name": "__common__",
# These values are common for all DocType "doctype": "DocType",
{ "module": "HR"
'colour': u'White:FFF', },
'doctype': 'DocType', {
'istable': 1, "name": "__common__",
'module': u'HR', "parent": "Expense Claim Detail",
'name': '__common__', "doctype": "DocField",
'section_style': u'Simple', "parenttype": "DocType",
'server_code_error': u' ', "permlevel": 0,
'version': 5 "parentfield": "fields"
}, },
{
# These values are common for all DocField "name": "Expense Claim Detail",
{ "doctype": "DocType"
'doctype': u'DocField', },
'name': '__common__', {
'parent': u'Expense Claim Detail', "oldfieldtype": "Date",
'parentfield': u'fields', "doctype": "DocField",
'parenttype': u'DocType', "label": "Expense Date",
'permlevel': 0 "oldfieldname": "expense_date",
}, "width": "150px",
"fieldname": "expense_date",
# DocType, Expense Claim Detail "fieldtype": "Date",
{ "reqd": 0
'doctype': 'DocType', },
'name': u'Expense Claim Detail' {
}, "oldfieldtype": "Link",
"doctype": "DocField",
# DocField "label": "Expense Claim Type",
{ "oldfieldname": "expense_type",
'doctype': u'DocField', "width": "150px",
'fieldname': u'expense_date', "fieldname": "expense_type",
'fieldtype': u'Date', "fieldtype": "Select",
'label': u'Expense Date', "reqd": 1,
'oldfieldname': u'expense_date', "options": "link:Expense Claim Type"
'oldfieldtype': u'Date', },
'reqd': 0, {
'width': u'150px' "oldfieldtype": "Small Text",
}, "doctype": "DocField",
"label": "Description",
# DocField "oldfieldname": "description",
{ "width": "300px",
'doctype': u'DocField', "fieldname": "description",
'fieldname': u'expense_type', "fieldtype": "Small Text"
'fieldtype': u'Link', },
'label': u'Expense Claim Type', {
'oldfieldname': u'expense_type', "oldfieldtype": "Currency",
'oldfieldtype': u'Link', "doctype": "DocField",
'options': u'Expense Claim Type', "label": "Claim Amount",
'reqd': 1, "oldfieldname": "claim_amount",
'width': u'150px' "width": "150px",
}, "trigger": "Client",
"fieldname": "claim_amount",
# DocField "fieldtype": "Currency",
{ "reqd": 1
'doctype': u'DocField', },
'fieldname': u'description', {
'fieldtype': u'Small Text', "no_copy": 1,
'label': u'Description', "oldfieldtype": "Currency",
'oldfieldname': u'description', "allow_on_submit": 0,
'oldfieldtype': u'Small Text', "doctype": "DocField",
'width': u'300px' "label": "Sanctioned Amount",
}, "oldfieldname": "sanctioned_amount",
"width": "150px",
# DocField "trigger": "Client",
{ "fieldname": "sanctioned_amount",
'doctype': u'DocField', "fieldtype": "Currency"
'fieldname': u'claim_amount', }
'fieldtype': u'Currency',
'label': u'Claim Amount',
'oldfieldname': u'claim_amount',
'oldfieldtype': u'Currency',
'reqd': 1,
'trigger': u'Client',
'width': u'150px'
},
# DocField
{
'allow_on_submit': 1,
'doctype': u'DocField',
'fieldname': u'sanctioned_amount',
'fieldtype': u'Currency',
'label': u'Sanctioned Amount',
'no_copy': 1,
'oldfieldname': u'sanctioned_amount',
'oldfieldtype': u'Currency',
'trigger': u'Client',
'width': u'150px'
}
] ]

View File

@ -8,14 +8,23 @@
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.cscript.onload = function(doc, dt, dn) { cur_frm.cscript.onload = function(doc, dt, dn) {
if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()}); if(!doc.posting_date)
set_multiple(dt,dn,{posting_date:get_today()});
cur_frm.call({
method:"get_approver_list",
callback: function(r) {
cur_frm.set_df_property("leave_approver", "options", r.message);
}
});
} }
cur_frm.cscript.refresh = function(doc, dt, dn) { cur_frm.cscript.refresh = function(doc, dt, dn) {
@ -23,14 +32,16 @@ cur_frm.cscript.refresh = function(doc, dt, dn) {
if(doc.__islocal && !in_list(user_roles, "HR User")) { if(doc.__islocal && !in_list(user_roles, "HR User")) {
cur_frm.set_intro("Fill the form and save it") cur_frm.set_intro("Fill the form and save it")
} else { } else {
if(in_list(user_roles, "HR User")) { if(doc.status=="Open") {
if(doc.status=="Open") { if(user==doc.leave_approver) {
cur_frm.set_intro("Please Approve (and Submit) or Reject, or re-assign to applicant for further review."); cur_frm.set_intro("You are the Leave Approver for this record. Please Update the 'Status' and Save");
cur_frm.toggle_enable("status", true);
} else {
cur_frm.set_intro("This Leave Application is pending approval. Only the Leave Apporver can update status.")
cur_frm.toggle_enable("status", false);
} }
} else { } else {
if(doc.status=="Open") { if(doc.status=="Approved") {
cur_frm.set_intro("Leave application is pending approval.");
} else if(doc.status=="Approved") {
cur_frm.set_intro("Leave application has been approved."); cur_frm.set_intro("Leave application has been approved.");
} else if(doc.status=="Rejected") { } else if(doc.status=="Rejected") {
cur_frm.set_intro("Leave application has been rejected."); cur_frm.set_intro("Leave application has been rejected.");
@ -38,61 +49,67 @@ cur_frm.cscript.refresh = function(doc, dt, dn) {
} }
} }
if(doc.status=="Approved" && doc.docstatus!=1) { if(doc.status=="Approved" && doc.docstatus==0) {
cur_frm.savesubmit() cur_frm.savesubmit()
} }
} }
cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.cscript.employee = function (doc, dt, dn){ cur_frm.cscript.employee = function (doc, dt, dn){
get_leave_balance(doc, dt, dn); get_leave_balance(doc, dt, dn);
} }
cur_frm.cscript.fiscal_year = function (doc, dt, dn){ cur_frm.cscript.fiscal_year = function (doc, dt, dn){
get_leave_balance(doc, dt, dn); get_leave_balance(doc, dt, dn);
} }
cur_frm.cscript.leave_type = function (doc, dt, dn){ cur_frm.cscript.leave_type = function (doc, dt, dn){
get_leave_balance(doc, dt, dn); get_leave_balance(doc, dt, dn);
} }
cur_frm.cscript.half_day = function(doc, dt, dn) { cur_frm.cscript.half_day = function(doc, dt, dn) {
if(doc.from_date) { if(doc.from_date) {
set_multiple(dt,dn,{to_date:doc.from_date}); set_multiple(dt,dn,{to_date:doc.from_date});
calculate_total_days(doc, dt, dn); calculate_total_days(doc, dt, dn);
} }
} }
cur_frm.cscript.from_date = function(doc, dt, dn) { cur_frm.cscript.from_date = function(doc, dt, dn) {
if(cint(doc.half_day) == 1){ if(cint(doc.half_day) == 1){
set_multiple(dt,dn,{to_date:doc.from_date}); set_multiple(dt,dn,{to_date:doc.from_date});
} }
calculate_total_days(doc, dt, dn); calculate_total_days(doc, dt, dn);
} }
cur_frm.cscript.to_date = function(doc, dt, dn) { cur_frm.cscript.to_date = function(doc, dt, dn) {
if(cint(doc.half_day) == 1 && cstr(doc.from_date) && doc.from_date != doc.to_date){ if(cint(doc.half_day) == 1 && cstr(doc.from_date) && doc.from_date != doc.to_date){
msgprint("To Date should be same as From Date for Half Day leave"); msgprint("To Date should be same as From Date for Half Day leave");
set_multiple(dt,dn,{to_date:doc.from_date}); set_multiple(dt,dn,{to_date:doc.from_date});
} }
calculate_total_days(doc, dt, dn); calculate_total_days(doc, dt, dn);
} }
get_leave_balance = function(doc, dt, dn) { get_leave_balance = function(doc, dt, dn) {
if(doc.employee && doc.leave_type && doc.fiscal_year) if(doc.employee && doc.leave_type && doc.fiscal_year) {
get_server_fields('get_leave_balance', '','', doc, dt, dn, 1); cur_frm.call({
method: "get_leave_balance",
args: {
employee: doc.employee,
fiscal_year: doc.fiscal_year,
leave_type: doc.leave_type
}
})
}
} }
calculate_total_days = function(doc, dt, dn) { calculate_total_days = function(doc, dt, dn) {
if(doc.from_date && doc.to_date){ if(doc.from_date && doc.to_date){
if(cint(doc.half_day) == 1) set_multiple(dt,dn,{total_leave_days:0.5}); if(cint(doc.half_day) == 1) set_multiple(dt,dn,{total_leave_days:0.5});
else{ else{
//d = new DateFn(); //d = new DateFn();
//set_multiple(dt,dn,{total_leave_days:d.get_diff(d.str_to_obj(doc.to_date),d.str_to_obj(doc.from_date))+1}); //set_multiple(dt,dn,{total_leave_days:d.get_diff(d.str_to_obj(doc.to_date),d.str_to_obj(doc.from_date))+1});
get_server_fields('get_total_leave_days', '', '', doc, dt, dn, 1); get_server_fields('get_total_leave_days', '', '', doc, dt, dn, 1);
} }
} }
} }
cur_frm.fields_dict.employee.get_query = erpnext.utils.employee_query; cur_frm.fields_dict.employee.get_query = erpnext.utils.employee_query;

View File

@ -30,14 +30,6 @@ class DocType:
self.doc = doc self.doc = doc
self.doclist = doclist self.doclist = doclist
def get_leave_balance(self):
leave_all = sql("select total_leaves_allocated from `tabLeave Allocation` where employee = '%s' and leave_type = '%s' and fiscal_year = '%s' and docstatus = 1" % (self.doc.employee, self.doc.leave_type, self.doc.fiscal_year))
leave_all = leave_all and flt(leave_all[0][0]) or 0
leave_app = sql("select SUM(total_leave_days) from `tabLeave Application` where employee = '%s' and leave_type = '%s' and fiscal_year = '%s' and docstatus = 1" % (self.doc.employee, self.doc.leave_type, self.doc.fiscal_year))
leave_app = leave_app and flt(leave_app[0][0]) or 0
ret = {'leave_balance':leave_all - leave_app}
return ret
def get_holidays(self): def get_holidays(self):
""" """
get total holidays get total holidays
@ -71,7 +63,8 @@ class DocType:
def validate_balance_leaves(self): def validate_balance_leaves(self):
if self.doc.from_date and self.doc.to_date and not self.is_lwp(): if self.doc.from_date and self.doc.to_date and not self.is_lwp():
bal = self.get_leave_balance() bal = get_leave_balance(self.doc.leave_type, self.doc.employee,
self.doc.fiscal_year)["leave_balance"]
tot_leaves = self.get_total_leave_days() tot_leaves = self.get_total_leave_days()
bal, tot_leaves = bal, tot_leaves bal, tot_leaves = bal, tot_leaves
webnotes.conn.set(self.doc,'leave_balance',flt(bal['leave_balance'])) webnotes.conn.set(self.doc,'leave_balance',flt(bal['leave_balance']))
@ -98,6 +91,9 @@ class DocType:
raise Exception raise Exception
def validate(self): def validate(self):
if self.doc.leave_approver == self.doc.owner:
webnotes.msgprint("""Self Approval is not allowed.""", raise_exception=1)
self.validate_to_date() self.validate_to_date()
self.validate_balance_leaves() self.validate_balance_leaves()
self.validate_leave_overlap() self.validate_leave_overlap()
@ -107,3 +103,30 @@ class DocType:
if self.doc.status != "Approved": if self.doc.status != "Approved":
webnotes.msgprint("""Only Approved Leave Applications can be Submitted.""", webnotes.msgprint("""Only Approved Leave Applications can be Submitted.""",
raise_exception=True) raise_exception=True)
@webnotes.whitelist()
def get_leave_balance(employee, leave_type, fiscal_year):
leave_all = webnotes.conn.sql("""select total_leaves_allocated
from `tabLeave Allocation` where employee = %s and leave_type = %s
and fiscal_year = %s and docstatus = 1""", (employee,
leave_type, fiscal_year))
leave_all = leave_all and flt(leave_all[0][0]) or 0
leave_app = webnotes.conn.sql("""select SUM(total_leave_days)
from `tabLeave Application`
where employee = %s and leave_type = %s and fiscal_year = %s
and docstatus = 1""", (employee, leave_type, fiscal_year))
leave_app = leave_app and flt(leave_app[0][0]) or 0
ret = {'leave_balance':leave_all - leave_app}
return ret
@webnotes.whitelist()
def get_approver_list():
roles = [r[0] for r in webnotes.conn.sql("""select distinct parent from `tabUserRole`
where role='Leave Approver'""")]
if not roles:
webnotes.msgprint("No Leave Approvers. Please assign 'Leave Approver' Role to atleast one user.")
return roles

View File

@ -2,9 +2,9 @@
{ {
"owner": "Administrator", "owner": "Administrator",
"docstatus": 0, "docstatus": 0,
"creation": "2012-11-02 17:16:54", "creation": "2012-12-05 14:11:53",
"modified_by": "Administrator", "modified_by": "Administrator",
"modified": "2012-11-30 12:17:27" "modified": "2012-12-05 17:38:26"
}, },
{ {
"is_submittable": 1, "is_submittable": 1,
@ -44,6 +44,15 @@
"fieldtype": "Select", "fieldtype": "Select",
"permlevel": 3 "permlevel": 3
}, },
{
"description": "Leave can be approved by users with Role, \"Leave Approver\"",
"colour": "White:FFF",
"doctype": "DocField",
"label": "Leave Approver",
"fieldname": "leave_approver",
"fieldtype": "Select",
"permlevel": 0
},
{ {
"search_index": 1, "search_index": 1,
"doctype": "DocField", "doctype": "DocField",
@ -215,13 +224,30 @@
"cancel": 1, "cancel": 1,
"permlevel": 0 "permlevel": 0
}, },
{
"amend": 1,
"create": 1,
"doctype": "DocPerm",
"submit": 1,
"write": 1,
"role": "Leave Approver",
"cancel": 1,
"permlevel": 0,
"match": "leave_approver:user"
},
{
"doctype": "DocPerm",
"write": 1,
"role": "HR User",
"permlevel": 2
},
{ {
"amend": 0, "amend": 0,
"create": 0, "create": 0,
"doctype": "DocPerm", "doctype": "DocPerm",
"submit": 0, "submit": 0,
"write": 1, "write": 1,
"role": "HR User", "role": "Leave Approver",
"cancel": 0, "cancel": 0,
"permlevel": 2 "permlevel": 2
}, },
@ -229,11 +255,5 @@
"doctype": "DocPerm", "doctype": "DocPerm",
"role": "All", "role": "All",
"permlevel": 3 "permlevel": 3
},
{
"doctype": "DocPerm",
"write": 1,
"role": "HR User",
"permlevel": 3
} }
] ]

View File

@ -0,0 +1,26 @@
import webnotes
def execute():
# new roles
roles = [r[0] for r in webnotes.conn.sql("""select name from tabRole""")]
if not "Leave Approver" in roles:
webnotes.model_wrapper([{"doctype":"Role", "role_name":"Leave Approver",
"__islocal":1, "module":"HR"}]).save()
if not "Expense Approver" in roles:
webnotes.model_wrapper([{"doctype":"Role", "role_name":"Expense Approver",
"__islocal":1, "module":"HR"}]).save()
# reload
webnotes.clear_perms("Leave Application")
webnotes.reload_doc("hr", "doctype", "leave_application")
webnotes.clear_perms("Expense Claim")
webnotes.reload_doc("hr", "doctype", "expense_claim")
# remove extra space in Approved Expense Vouchers
webnotes.conn.sql("""update `tabExpense Claim` set approval_status='Approved'
where approval_status='Approved '""")
webnotes.conn.commit()
for t in ['__CacheItem', '__SessionCache', 'tabSupport Ticket Response']:
webnotes.conn.sql("drop table if exists `%s`" % t)

View File

@ -0,0 +1,5 @@
def execute():
import webnotes
from webnotes.modules import reload_doc
reload_doc("accounts", "search_criteria", "debtors_ledger")
reload_doc("accounts", "search_criteria", "creditors_ledger")

View File

@ -709,6 +709,10 @@ patch_list = [
'patch_module': 'patches.december_2012', 'patch_module': 'patches.december_2012',
'patch_file': 'deprecate_tds', 'patch_file': 'deprecate_tds',
}, },
{
'patch_module': 'patches.december_2012',
'patch_file': 'expense_leave_reload',
},
{ {
'patch_module': 'patches.december_2012', 'patch_module': 'patches.december_2012',
'patch_file': 'repost_ordered_qty', 'patch_file': 'repost_ordered_qty',
@ -717,4 +721,8 @@ patch_list = [
'patch_module': 'patches.december_2012', 'patch_module': 'patches.december_2012',
'patch_file': 'repost_projected_qty', 'patch_file': 'repost_projected_qty',
}, },
{
'patch_module': 'patches.december_2012',
'patch_file': 'reload_debtors_creditors_ledger',
},
] ]

View File

@ -43,8 +43,8 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) {
} }
cur_frm.cscript.onload = function(doc, cdt, cdn) { cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
if(doc.delivery_note) { if(doc.delivery_note && doc.__islocal) {
var ps_detail = getchildren('Packing Slip Item', doc.name, 'item_details'); var ps_detail = getchildren('Packing Slip Item', doc.name, 'item_details');
if(!(flt(ps_detail[0].net_weight) && cstr(ps_detail[0].weight_uom))) { if(!(flt(ps_detail[0].net_weight) && cstr(ps_detail[0].weight_uom))) {
cur_frm.cscript.update_item_details(doc); cur_frm.cscript.update_item_details(doc);

View File

@ -16,7 +16,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import flt, cint from webnotes.utils import flt, cint, now
class DocType: class DocType:
def __init__(self, d, dl): def __init__(self, d, dl):
@ -56,6 +56,15 @@ class DocType:
Validate if case nos overlap Validate if case nos overlap
If they do, recommend next case no. If they do, recommend next case no.
""" """
if not cint(self.doc.from_case_no):
webnotes.msgprint("Please specify a valid 'From Case No.'", raise_exception=1)
elif not self.doc.to_case_no:
self.doc.to_case_no = self.doc.from_case_no
elif self.doc.from_case_no > self.doc.to_case_no:
webnotes.msgprint("'To Case No.' cannot be less than 'From Case No.'",
raise_exception=1)
res = webnotes.conn.sql("""\ res = webnotes.conn.sql("""\
SELECT name FROM `tabPacking Slip` SELECT name FROM `tabPacking Slip`
WHERE delivery_note = %(delivery_note)s AND docstatus = 1 AND WHERE delivery_note = %(delivery_note)s AND docstatus = 1 AND
@ -92,16 +101,24 @@ class DocType:
item_codes = ", ".join([('"' + d.item_code + '"') for d in item_codes = ", ".join([('"' + d.item_code + '"') for d in
self.doclist]) self.doclist])
items = [d.item_code for d in self.doclist.get({"parentfield": "item_details"})]
if not item_codes: webnotes.msgprint("No Items to Pack", if not item_codes: webnotes.msgprint("No Items to Pack",
raise_exception=1) raise_exception=1)
res = webnotes.conn.sql("""\ # gets item code, qty per item code, latest packed qty per item code and stock uom
SELECT item_code, IFNULL(SUM(qty), 0) as qty, IFNULL(packed_qty, 0) as packed_qty, stock_uom res = webnotes.conn.sql("""select item_code, ifnull(sum(qty), 0) as qty,
FROM `tabDelivery Note Item` (select sum(ifnull(psi.qty, 0) * (abs(ps.to_case_no - ps.from_case_no) + 1))
WHERE parent = "%s" AND item_code IN (%s) from `tabPacking Slip` ps, `tabPacking Slip Item` psi
GROUP BY item_code""" % (self.doc.delivery_note, item_codes), where ps.name = psi.parent and ps.docstatus = 1
as_dict=1) and ps.delivery_note = dni.parent and psi.item_code=dni.item_code)
as packed_qty,
stock_uom
from `tabDelivery Note Item` dni
where parent=%s and item_code in (%s)
group by item_code""" % ("%s", ", ".join(["%s"]*len(items))),
tuple([self.doc.delivery_note] + items), as_dict=1, debug=1)
ps_item_qty = dict([[d.item_code, d.qty] for d in self.doclist]) ps_item_qty = dict([[d.item_code, d.qty] for d in self.doclist])
no_of_cases = cint(self.doc.to_case_no) - cint(self.doc.from_case_no) + 1 no_of_cases = cint(self.doc.to_case_no) - cint(self.doc.from_case_no) + 1
@ -148,28 +165,29 @@ class DocType:
dn_details, ps_item_qty, no_of_cases = self.get_details_for_packing() dn_details, ps_item_qty, no_of_cases = self.get_details_for_packing()
for item in dn_details: for item in dn_details:
if event=='submit': new_packed_qty = flt(item['packed_qty'])
new_packed_qty = flt(item['packed_qty']) + (flt(ps_item_qty[item['item_code']]) * no_of_cases)
elif event=='cancel':
new_packed_qty = flt(item['packed_qty']) - (flt(ps_item_qty[item['item_code']]) * no_of_cases)
if (new_packed_qty < 0) or (new_packed_qty > flt(item['qty'])): if (new_packed_qty < 0) or (new_packed_qty > flt(item['qty'])):
webnotes.msgprint("Invalid new packed quantity for item %s. \ webnotes.msgprint("Invalid new packed quantity for item %s. \
Please try again or contact support@erpnext.com" % item['item_code'], raise_exception=1) Please try again or contact support@erpnext.com" % item['item_code'], raise_exception=1)
delivery_note_item = webnotes.conn.get_value("Delivery Note Item", {
"parent": self.doc.delivery_note, "item_code": item["item_code"]})
webnotes.conn.sql("""\ webnotes.conn.sql("""\
UPDATE `tabDelivery Note Item` UPDATE `tabDelivery Note Item`
SET packed_qty = %s SET packed_qty = %s
WHERE parent = %s AND item_code = %s""", WHERE parent = %s AND item_code = %s""",
(new_packed_qty, self.doc.delivery_note, item['item_code'])) (new_packed_qty, self.doc.delivery_note, item['item_code']))
webnotes.conn.set_value("Delivery Note", self.doc.delivery_note,
"modified", now())
def update_item_details(self): def update_item_details(self):
""" """
Fill empty columns in Packing Slip Item Fill empty columns in Packing Slip Item
""" """
self.doc.from_case_no = self.get_recommended_case_no() if not self.doc.from_case_no:
self.doc.from_case_no = self.get_recommended_case_no()
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
for d in self.doclist: for d in self.doclist:

View File

@ -64,10 +64,6 @@ class DocType(TransactionBase):
doc.fields[r] = ret[r] doc.fields[r] = ret[r]
# Get UOM Details
def get_uom_details(self, arg = ''):
return get_obj(dt='Purchase Common').get_uom_details(arg)
# GET TERMS & CONDITIONS # GET TERMS & CONDITIONS
# ===================================================================================== # =====================================================================================
def get_tc_details(self): def get_tc_details(self):

View File

@ -19,8 +19,8 @@ import webnotes
from webnotes.utils import load_json, cstr, now from webnotes.utils import load_json, cstr, now
@webnotes.whitelist() @webnotes.whitelist()
def update_item(args): def update_item(arg):
args = load_json(args) args = load_json(arg)
webnotes.conn.sql("update `tab%s` set `%s`=%s, modified=%s where name=%s" \ webnotes.conn.sql("update `tab%s` set `%s`=%s, modified=%s where name=%s" \
% (args['dt'], args['fn'], '%s', '%s', '%s'), (args['text'], now(), args['dn'])) % (args['dt'], args['fn'], '%s', '%s', '%s'), (args['text'], now(), args['dn']))

View File

@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@ -1,46 +0,0 @@
.user-card {
border-radius: 5px;
width: 200px;
margin: 11px;
padding: 11px;
background-color: #FFEDBD;
box-shadow: 3px 3px 5px #888;
float: left;
overflow: hidden;
}
.user-card.disabled {
background-color: #eee;
}
.user-card img {
height: 60px;
}
.user-role {
padding: 5px;
width: 45%;
float: left;
}
table.user-perm {
border-collapse: collapse;
}
table.user-perm td, table.user-perm th {
padding: 5px;
text-align: center;
border-bottom: 1px solid #aaa;
min-width: 30px;
}
.subscription-info-box {
margin: 0px 11px;
background-color: #EEEEEE;
border: 1px solid #DDDBDB;
padding: 5px 3px;
}
.subscription-info {
padding: 0px 10px;
}

View File

@ -1,415 +0,0 @@
// ERPNext - web based ERP (http://erpnext.com)
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
$.extend(wn.pages.users, {
onload: function(wrapper) {
var w = wn.pages.users;
wn.ui.make_app_page({
parent: w,
title: "Users",
single_column: true
});
w.profiles = {};
w.refresh();
w.setup();
w.role_editor = new erpnext.RoleEditor();
},
setup: function() {
wn.pages.users.appframe.add_button('+ Add User', function() {
wn.pages.users.add_user();
});
// set roles
var w = wn.pages.users;
$(w).on('click', '.btn.user-roles', function() {
var uid = $(this).parent().parent().attr('data-name');
wn.pages.users.role_editor.show(uid);
});
// settings
$(w).on('click', '.btn.user-settings', function() {
var uid = $(this).parent().parent().attr('data-name');
wn.pages.users.show_settings(uid);
});
// delete
$(w).on('click', 'a.close', function() {
$card = $(this).parent();
var uid = $card.attr('data-name');
$card.css('opacity', 0.6);
wn.call({
method: 'utilities.page.users.users.delete',
args: {'uid': uid},
callback: function(r,rt) {
if(!r.exc)
$card.fadeOut()
}
});
})
},
refresh: function() {
// make the list
wn.call({
method:'utilities.page.users.users.get',
callback: function(r, rt) {
$(wn.pages.users).find('.layout-main').empty();
for(var i in r.message) {
var p = r.message[i];
wn.pages.users.profiles[p.name] = p;
wn.pages.users.render(p);
}
}
});
if(!$('.subscription-info').length && (wn.boot.max_users || wn.boot.expires_on)) {
var $sub_info = $('<div class="subscription-info-box"><div>')
.insertAfter($(wn.pages.users).find('.help'));
if(wn.boot.max_users) {
$sub_info.append(repl('\
<span class="subscription-info"> \
Max Users: <b>%(max_users)s</b> \
</span>', { max_users: wn.boot.max_users }));
}
if(wn.boot.expires_on) {
$sub_info.append(repl('\
<span class="subscription-info"> \
Expires On: <b>%(expires_on)s</b> \
</span>', { expires_on: dateutil.str_to_user(wn.boot.expires_on) }));
}
}
},
render: function(data) {
if(data.file_list) {
data.imgsrc = 'files/' + data.file_list.split('\n')[0].split(',')[1];
} else {
data.imgsrc = 'lib/images/ui/no_img_' + (data.gender=='Female' ? 'f' : 'm') + '.gif';
}
data.fullname = wn.user_info(data.name).fullname;
data.delete_html = '';
if(!data.enabled)
data.delete_html = '<a class="close" title="delete">&times;</a>';
$(wn.pages.users).find('.layout-main').append(repl('<div class="user-card" data-name="%(name)s">\
%(delete_html)s\
<img src="%(imgsrc)s">\
<div class="user-info">\
<b class="user-fullname">%(fullname)s</b><br>\
%(name)s<br>\
<button class="btn btn-small user-roles"><i class="icon-user"></i> Roles</button>\
<button class="btn btn-small user-settings"><i class="icon-cog"></i> Settings</button>\
</div>\
</div>', data));
if(!data.enabled) {
$(wn.pages.users).find('.layout-main .user-card:last')
.addClass('disabled')
.find('.user-fullname').html('Disabled');
}
},
show_settings: function(uid) {
var me = wn.pages.users;
if(!me.settings_dialog)
me.make_settings_dialog();
var p = me.profiles[uid];
me.uid = uid;
me.settings_dialog.set_values({
restrict_ip: p.restrict_ip || '',
login_before: p.login_before || '',
login_after: p.login_after || '',
enabled: p.enabled || 0,
new_password: ''
});
me.settings_dialog.show();
},
make_settings_dialog: function() {
var me = wn.pages.users;
me.settings_dialog = new wn.ui.Dialog({
title: 'Set User Security',
width: 500,
fields: [
{
label:'Enabled',
description: 'Uncheck to disable',
fieldtype: 'Check', fieldname: 'enabled'
},
{
label:'IP Address',
description: 'Restrict user login by IP address, partial ips (111.111.111), \
multiple addresses (separated by commas) allowed',
fieldname:'restrict_ip', fieldtype:'Data'
},
{
label:'Login After',
description: 'User can only login after this hour (0-24)',
fieldtype: 'Int', fieldname: 'login_after'
},
{
label:'Login Before',
description: 'User can only login before this hour (0-24)',
fieldtype: 'Int', fieldname: 'login_before'
},
{
label:'New Password',
description: 'Update the current user password',
fieldtype: 'Data', fieldname: 'new_password'
},
{
label:'Update', fieldtype:'Button', fieldname:'update'
}
]
});
this.settings_dialog.fields_dict.update.input.onclick = function() {
var btn = this;
var args = me.settings_dialog.get_values();
args.user = me.uid;
if (args.new_password) {
me.get_password(btn, args);
} else {
me.update_security(btn, args);
}
};
},
update_security: function(btn, args) {
var me = wn.pages.users;
$(btn).set_working();
$c_page('utilities', 'users', 'update_security', JSON.stringify(args), function(r,rt) {
$(btn).done_working();
if(r.exc) {
msgprint(r.exc);
return;
}
me.settings_dialog.hide();
$.extend(me.profiles[me.uid], me.settings_dialog.get_values());
me.refresh();
});
},
get_password: function(btn, args) {
var me = wn.pages.users;
var pass_d = new wn.ui.Dialog({
title: 'Your Password',
width: 300,
fields: [
{
label: 'Please Enter <b style="color: black">Your Password</b>',
description: "Your password is required to update the user's password",
fieldtype: 'Password', fieldname: 'sys_admin_pwd', reqd: 1
},
{
label: 'Continue', fieldtype: 'Button', fieldname: 'continue'
}
]
});
pass_d.fields_dict.continue.input.onclick = function() {
btn.pwd_dialog.hide();
args.sys_admin_pwd = btn.pwd_dialog.get_values().sys_admin_pwd;
btn.set_working();
me.update_security(btn, args);
btn.done_working();
}
pass_d.show();
btn.pwd_dialog = pass_d;
btn.done_working();
},
add_user: function() {
var me = wn.pages.users;
var active_users = $('.user-card:not(.disabled)');
if(wn.boot.max_users && (active_users.length >= wn.boot.max_users)) {
msgprint(repl("You already have <b>%(active_users)s</b> active users, \
which is the maximum number that you are currently allowed to add. <br /><br /> \
So, to add more users, you can:<br /> \
1. <b>Upgrade to the unlimited users plan</b>, or<br /> \
2. <b>Disable one or more of your existing users and try again</b>",
{active_users: active_users.length}));
return;
}
var d = new wn.ui.Dialog({
title: 'Add User',
width: 400,
fields: [{
fieldtype: 'Data', fieldname: 'user', reqd: 1,
label: 'Email Id of the user to add'
}, {
fieldtype: 'Data', fieldname: 'first_name', reqd: 1, label: 'First Name'
}, {
fieldtype: 'Data', fieldname: 'last_name', label: 'Last Name'
}, {
fieldtype: 'Data', fieldname: 'password', reqd: 1, label: 'Password'
}, {
fieldtype: 'Button', label: 'Add', fieldname: 'add'
}]
});
d.make();
d.fields_dict.add.input.onclick = function() {
v = d.get_values();
if(v) {
d.fields_dict.add.input.set_working();
$c_page('utilities', 'users', 'add_user', v, function(r,rt) {
if(r.exc) { msgprint(r.exc); return; }
else {
wn.boot.user_info[v.user] = {fullname:v.first_name + ' ' + (v.last_name || '')};
d.hide();
me.refresh();
}
})
}
}
d.show();
}
});
erpnext.RoleEditor = Class.extend({
init: function() {
this.dialog = new wn.ui.Dialog({
title: 'Set Roles'
});
var me = this;
$(this.dialog.body).html('<div class="help">Loading...</div>')
wn.call({
method:'utilities.page.users.users.get_roles',
callback: function(r) {
me.roles = r.message;
me.show_roles();
}
});
},
show_roles: function() {
var me = this;
$(this.dialog.body).empty();
for(var i in this.roles) {
$(this.dialog.body).append(repl('<div class="user-role" \
data-user-role="%(role)s">\
<input type="checkbox"> \
<a href="#"><i class="icon-question-sign"></i></a> %(role)s\
</div>', {role: this.roles[i]}));
}
$(this.dialog.body).append('<div style="clear: both">\
<button class="btn btn-small btn-info">Save</button></div>');
$(this.dialog.body).find('button.btn-info').click(function() {
me.save();
});
$(this.dialog.body).find('.user-role a').click(function() {
me.show_permissions($(this).parent().attr('data-user-role'))
return false;
})
},
show: function(uid) {
var me = this;
this.uid = uid;
this.dialog.show();
// set user roles
wn.call({
method:'utilities.page.users.users.get_user_roles',
args: {uid:uid},
callback: function(r, rt) {
$(me.dialog.body).find('input[type="checkbox"]').attr('checked', false);
for(var i in r.message) {
$(me.dialog.body)
.find('[data-user-role="'+r.message[i]
+'"] input[type="checkbox"]').attr('checked',true);
}
}
})
},
save: function() {
var set_roles = [];
var unset_roles = [];
$(this.dialog.body).find('[data-user-role]').each(function() {
var $check = $(this).find('input[type="checkbox"]');
if($check.attr('checked')) {
set_roles.push($(this).attr('data-user-role'));
} else {
unset_roles.push($(this).attr('data-user-role'));
}
})
wn.call({
method:'utilities.page.users.users.update_roles',
args: {
set_roles: JSON.stringify(set_roles),
unset_roles: JSON.stringify(unset_roles),
uid: this.uid
},
btn: $(this.dialog.body).find('.btn-info').get(0),
callback: function() {
}
})
},
show_permissions: function(role) {
// show permissions for a role
var me = this;
if(!this.perm_dialog)
this.make_perm_dialog()
$(this.perm_dialog.body).empty();
wn.call({
method:'utilities.page.users.users.get_perm_info',
args: {role: role},
callback: function(r) {
var $body = $(me.perm_dialog.body);
$body.append('<table class="user-perm"><tbody><tr>\
<th style="text-align: left">Document Type</th>\
<th>Level</th>\
<th>Read</th>\
<th>Write</th>\
<th>Submit</th>\
<th>Cancel</th>\
<th>Amend</th></tr></tbody></table>');
for(var i in r.message) {
var perm = r.message[i];
// if permission -> icon
for(key in perm) {
if(key!='parent' && key!='permlevel') {
if(perm[key]) {
perm[key] = '<i class="icon-ok"></i>';
} else {
perm[key] = '';
}
}
}
$body.find('tbody').append(repl('<tr>\
<td style="text-align: left">%(parent)s</td>\
<td>%(permlevel)s</td>\
<td>%(read)s</td>\
<td>%(write)s</td>\
<td>%(submit)s</td>\
<td>%(cancel)s</td>\
<td>%(amend)s</td>\
</tr>', perm))
}
me.perm_dialog.show();
}
});
},
make_perm_dialog: function() {
this.perm_dialog = new wn.ui.Dialog({
title:'Role Permissions',
width: 500
});
}
})

View File

@ -1,202 +0,0 @@
# ERPNext - web based ERP (http://erpnext.com)
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
import webnotes
import json
from webnotes.model.doc import Document
from webnotes.utils import cint
@webnotes.whitelist()
def get(arg=None):
"""return all users"""
return webnotes.conn.sql("""select name, file_list, enabled, gender,
restrict_ip, login_before, login_after from tabProfile
where docstatus<2 and name not in ('Administrator', 'Guest') order by
ifnull(enabled,0) desc, name""", as_dict=1)
@webnotes.whitelist()
def get_roles(arg=None):
"""return all roles except standard"""
return _get_roles(webnotes.form_dict['uid'])
def _get_roles(user):
"""return all roles except standard"""
return [r[0] for r in webnotes.conn.sql("""select name from tabRole
where name not in ('Administrator', 'Guest', 'All') order by name""", user)]
@webnotes.whitelist()
def get_user_roles(arg=None):
"""get roles for a user"""
return [r[0] for r in webnotes.conn.sql("""select role from tabUserRole
where parent=%s""", webnotes.form_dict['uid'])]
@webnotes.whitelist()
def get_perm_info(arg=None):
"""get permission info"""
return webnotes.conn.sql("""select parent, permlevel, `read`, `write`, submit,
cancel, amend from tabDocPerm where role=%s
and docstatus<2 order by parent, permlevel""",
webnotes.form_dict['role'], as_dict=1)
@webnotes.whitelist()
def update_roles(arg=None):
"""update set and unset roles"""
# remove roles
unset = json.loads(webnotes.form_dict['unset_roles'])
webnotes.conn.sql("""delete from tabUserRole where parent='%s'
and role in ('%s')""" % (webnotes.form_dict['uid'], "','".join(unset)))
# check for 1 system manager
if not webnotes.conn.sql("""select parent from tabUserRole where role='System Manager'
and docstatus<2"""):
webnotes.msgprint("Sorry there must be atleast one 'System Manager'")
raise webnotes.ValidationError
# add roles
roles = get_user_roles()
toset = json.loads(webnotes.form_dict['set_roles'])
for role in toset:
if not role in roles:
d = Document('UserRole')
d.role = role
d.parent = webnotes.form_dict['uid']
d.save()
webnotes.msgprint('Roles Updated')
@webnotes.whitelist()
def update_security(args=''):
args = json.loads(args)
webnotes.conn.set_value('Profile', args['user'], 'restrict_ip', args.get('restrict_ip') or '')
webnotes.conn.set_value('Profile', args['user'], 'login_after', args.get('login_after') or None)
webnotes.conn.set_value('Profile', args['user'], 'login_before', args.get('login_before') or None)
webnotes.conn.set_value('Profile', args['user'], 'enabled', int(args.get('enabled',0)) or 0)
# logout a disabled user
if not int(args.get('enabled',0) or 0):
webnotes.login_manager.logout(user=args['user'])
if args.get('new_password') and args.get('sys_admin_pwd'):
from webnotes.utils import cint
webnotes.conn.sql("update tabProfile set password=password(%s) where name=%s",
(args['new_password'], args['user']))
else:
webnotes.msgprint('Settings Updated')
#
# user addition
#
@webnotes.whitelist()
def add_user(args):
args = json.loads(args)
add_profile(args)
@webnotes.whitelist()
def add_profile(args):
from webnotes.utils import validate_email_add, now
email = args['user']
sql = webnotes.conn.sql
# validate max number of users exceeded or not
import conf
if hasattr(conf, 'max_users'):
active_users = sql("""select count(*) from tabProfile
where ifnull(enabled, 0)=1 and docstatus<2
and name not in ('Administrator', 'Guest')""")[0][0]
if active_users >= conf.max_users and conf.max_users:
# same message as in users.js
webnotes.msgprint("""Alas! <br />\
You already have <b>%(active_users)s</b> active users, \
which is the maximum number that you are currently allowed to add. <br /><br /> \
So, to add more users, you can:<br /> \
1. <b>Upgrade to the unlimited users plan</b>, or<br /> \
2. <b>Disable one or more of your existing users and try again</b>""" \
% {'active_users': active_users}, raise_exception=1)
if not email:
email = webnotes.form_dict.get('user')
if not validate_email_add(email):
raise Exception
return 'Invalid Email Id'
if sql("select name from tabProfile where name = %s", email):
# exists, enable it
sql("update tabProfile set enabled = 1, docstatus=0 where name = %s", email)
webnotes.msgprint('Profile exists, enabled it with new password')
else:
# does not exist, create it!
pr = Document('Profile')
pr.name = email
pr.email = email
pr.first_name = args.get('first_name')
pr.last_name = args.get('last_name')
pr.enabled = 1
pr.user_type = 'System User'
pr.save(1)
if args.get('password'):
sql("""
UPDATE tabProfile
SET password = PASSWORD(%s), modified = %s
WHERE name = %s""", (args.get('password'), now, email))
send_welcome_mail(email, args)
@webnotes.whitelist()
def send_welcome_mail(email, args):
"""send welcome mail to user with password and login url"""
pr = Document('Profile', email)
from webnotes.utils.email_lib import sendmail_md
args.update({
'company': webnotes.conn.get_default('company'),
'password': args.get('password'),
'account_url': webnotes.conn.get_value('Website Settings',
'Website Settings', 'subdomain') or ""
})
if not args.get('last_name'): args['last_name'] = ''
sendmail_md(pr.email, subject="Welcome to ERPNext", msg=welcome_txt % args)
#
# delete user
#
@webnotes.whitelist()
def delete(arg=None):
"""delete user"""
webnotes.conn.sql("update tabProfile set enabled=0, docstatus=2 where name=%s",
webnotes.form_dict['uid'])
webnotes.login_manager.logout(user=webnotes.form_dict['uid'])
welcome_txt = """
## %(company)s
Dear %(first_name)s %(last_name)s
Welcome!
A new account has been created for you, here are your details:
login-id: %(user)s
password: %(password)s
To login to your new ERPNext account, please go to:
%(account_url)s
"""

View File

@ -1,28 +0,0 @@
# Page, users
[
# These values are common in all dictionaries
{
'creation': '2012-02-28 10:29:39',
'docstatus': 0,
'modified': '2012-02-28 10:29:39',
'modified_by': u'Administrator',
'owner': u'Administrator'
},
# These values are common for all Page
{
'doctype': 'Page',
'module': u'Utilities',
'name': '__common__',
'page_name': u'users',
'standard': u'Yes',
'title': u'Users'
},
# Page, users
{
'doctype': 'Page',
'name': u'users'
}
]