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:
return [r[0] for r in webnotes.conn.sql("""select name from tabCompany
where docstatus!=2""")]
@webnotes.whitelist()
def get_children():
args = webnotes.form_dict

View File

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

View File

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

View File

@ -199,23 +199,35 @@ cur_frm.cscript.update_stock_qty = function(doc,cdt,cdn){
}
//==================== 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];
if (d.item_code && d.uom) {
call_back = function(doc, cdt, cdn){
cur_frm.cscript.calc_amount(doc, 2);
}
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);
// don't make mistake of calling update_stock_qty() the get_uom_details returns stock_qty as per conversion factor properly
$.extend(args, {
item_code: d.item_code,
uom: d.uom,
stock_qty: flt(d.stock_qty),
});
if(d.item_code && d.uom) {
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 =========================================================
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 ======================================================================

View File

@ -182,39 +182,6 @@ class DocType(TransactionBase):
ret = { 'projected_qty' : bin and flt(bin[0]['projected_qty']) or 0 }
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 ---
def update_last_purchase_rate(self, obj, is_submit):
@ -683,3 +650,27 @@ class DocType(TransactionBase):
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))
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
def get_bin_details(self, 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'}
},
{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: '12%', content:'transaction_date',
css: {'text-align': 'right', 'color':'#777'},

View File

@ -121,11 +121,6 @@ class DocType:
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
#-----------------------------
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)
# 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.fields[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):
for item in self.item_doclist:

View File

@ -1,5 +1,7 @@
erpnext.updates = [
["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 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",

View File

@ -3,4 +3,6 @@ install_docs = [
{"doctype":"Role", "role_name":"Employee", "name":"Employee"},
{"doctype":"Role", "role_name":"HR Manager", "name":"HR Manager"},
{"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/>.
cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.cscript.onload = function(doc,cdt,cdn){
//
if(!doc.approval_status) set_multiple(cdt,cdn,{approval_status:'Draft'});
if(doc.employee) cur_frm.cscript.employee(doc,cdt,cdn);
if(!doc.approval_status)
cur_frm.set_value("approval_status", "Draft")
if (doc.__islocal) {
if(doc.amended_from) set_multiple(cdt,cdn,{approval_status:'Draft'});
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.set_value("posting_date", dateutil.get_today());
if(doc.amended_from)
cur_frm.set_value("approval_status", "Draft");
cur_frm.cscript.clear_sanctioned(doc);
}
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){
hide_field('calculate_total_amount');
if(user == doc.exp_approver && doc.approval_status == 'Submitted'){
unhide_field(['update_voucher', 'approve', 'reject', 'calculate_total_amount']);
cur_frm.fields_dict['expense_voucher_details'].grid.set_column_disp('sanctioned_amount', true);
set_field_permlevel('remark', 0);
cur_frm.set_intro("");
if(doc.__islocal && !in_list(user_roles, "HR User")) {
cur_frm.set_intro("Fill the form and save it")
} else {
hide_field(['update_voucher', 'approve', 'reject']);
cur_frm.fields_dict['expense_voucher_details'].grid.set_column_disp('sanctioned_amount', false);
set_field_permlevel('remark', 1);
if(doc.approval_status=="Draft") {
if(user==doc.exp_approver) {
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) {
if(cint(doc.docstatus) == 0) {
doc.approval_status = "Draft";
}
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){
if(doc.approval_status == 'Draft'){
var val = getchildren('Expense Claim Detail', doc.name, 'expense_voucher_details', doc.doctype);
var total_claim =0;
for(var i = 0; i<val.length; i++){
val[i].sanctioned_amount = val[i].claim_amount;
total_claim = flt(total_claim)+flt(val[i].claim_amount);
refresh_field('sactioned_amount', val[i].name, 'expense_voucher_details');
doc.total_claimed_amount = 0;
doc.total_sanctioned_amount = 0;
$.each(wn.model.get("Expense Claim Detail", {parent:doc.name}), function(i, d) {
doc.total_claimed_amount += d.claim_amount;
if(d.sanctioned_amount==null) {
d.sanctioned_amount = d.claim_amount;
}
doc.total_claimed_amount = flt(total_claim);
refresh_field('total_claimed_amount');
}
else if(doc.approval_status == 'Submitted'){
var val = getchildren('Expense Claim Detail', doc.name, 'expense_voucher_details', doc.doctype);
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');
}
doc.total_sanctioned_amount += d.sanctioned_amount;
});
refresh_field("total_claimed_amount");
refresh_field('total_sanctioned_amount');
}
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.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) {
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.fields_dict.employee.get_query = erpnext.utils.employee_query;
}

View File

@ -17,86 +17,27 @@
from __future__ import unicode_literals
import webnotes
from webnotes.utils import add_days, cstr
from webnotes.model import db_exists
from webnotes.model.wrapper import getlist, copy_doclist
from webnotes.model.code import get_obj
from webnotes.utils import add_days
from webnotes.model.wrapper import getlist
from webnotes import form, msgprint
sql = webnotes.conn.sql
class DocType:
def __init__(self, doc, doclist=[]):
self.doc = doc
self.doclist = doclist
def get_employee_name(self):
emp_dtl = sql("select employee_name,company_email from `tabEmployee` where name=%s", self.doc.employee)
emp_nm = emp_dtl and emp_dtl[0][0] or ''
self.doc.employee_name = emp_nm
self.doc.email_id = emp_dtl and emp_dtl[0][1] or ''
return cstr(emp_nm)
def get_approver_lst(self):
approver_lst =[]
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(self):
if self.doc.exp_approver == self.doc.owner:
webnotes.msgprint("""Self Approval is not allowed.""", raise_exception=1)
self.validate_fiscal_year()
self.validate_exp_details()
def on_submit(self):
if self.doc.approval_status=="Draft":
webnotes.msgprint("""Please set Approval Status to 'Approved' or 'Rejected' before submitting""",
raise_exception=1)
def validate_fiscal_year(self):
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):
msgprint("Posting Date is not within the Fiscal Year selected")
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):
if not getlist(self.doclist, 'expense_voucher_details'):
msgprint("Please add expense voucher details")
raise Exception
if not self.doc.total_claimed_amount:
msgprint("Please calculate Total Claimed Amount")
raise Exception
if not self.doc.exp_approver:
msgprint("Please select Expense Claim approver")
raise Exception
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)
@webnotes.whitelist()
def get_approver_list():
roles = [r[0] for r in webnotes.conn.sql("""select distinct parent from `tabUserRole`
where role='Expense Approver'""")]
if not roles:
webnotes.msgprint("No Expense Approvers. Please assign 'Expense Approver' Role to atleast one user.")
return roles

View File

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

View File

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

View File

@ -8,14 +8,23 @@
//
// 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
// 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/>.
// 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) {
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) {
@ -23,14 +32,16 @@ cur_frm.cscript.refresh = function(doc, dt, dn) {
if(doc.__islocal && !in_list(user_roles, "HR User")) {
cur_frm.set_intro("Fill the form and save it")
} else {
if(in_list(user_roles, "HR User")) {
if(doc.status=="Open") {
cur_frm.set_intro("Please Approve (and Submit) or Reject, or re-assign to applicant for further review.");
if(doc.status=="Open") {
if(user==doc.leave_approver) {
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 {
if(doc.status=="Open") {
cur_frm.set_intro("Leave application is pending approval.");
} else if(doc.status=="Approved") {
if(doc.status=="Approved") {
cur_frm.set_intro("Leave application has been approved.");
} else if(doc.status=="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.add_fetch('employee','employee_name','employee_name');
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){
get_leave_balance(doc, dt, dn);
get_leave_balance(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) {
if(doc.from_date) {
set_multiple(dt,dn,{to_date:doc.from_date});
calculate_total_days(doc, dt, dn);
}
if(doc.from_date) {
set_multiple(dt,dn,{to_date:doc.from_date});
calculate_total_days(doc, dt, dn);
}
}
cur_frm.cscript.from_date = function(doc, dt, dn) {
if(cint(doc.half_day) == 1){
set_multiple(dt,dn,{to_date:doc.from_date});
}
calculate_total_days(doc, dt, dn);
if(cint(doc.half_day) == 1){
set_multiple(dt,dn,{to_date:doc.from_date});
}
calculate_total_days(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){
msgprint("To Date should be same as From Date for Half Day leave");
set_multiple(dt,dn,{to_date:doc.from_date});
}
calculate_total_days(doc, dt, dn);
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");
set_multiple(dt,dn,{to_date:doc.from_date});
}
calculate_total_days(doc, dt, dn);
}
get_leave_balance = function(doc, dt, dn) {
if(doc.employee && doc.leave_type && doc.fiscal_year)
get_server_fields('get_leave_balance', '','', doc, dt, dn, 1);
if(doc.employee && doc.leave_type && doc.fiscal_year) {
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) {
if(doc.from_date && doc.to_date){
if(cint(doc.half_day) == 1) set_multiple(dt,dn,{total_leave_days:0.5});
else{
//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});
get_server_fields('get_total_leave_days', '', '', doc, dt, dn, 1);
}
}
if(doc.from_date && doc.to_date){
if(cint(doc.half_day) == 1) set_multiple(dt,dn,{total_leave_days:0.5});
else{
//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});
get_server_fields('get_total_leave_days', '', '', doc, dt, dn, 1);
}
}
}
cur_frm.fields_dict.employee.get_query = erpnext.utils.employee_query;

View File

@ -30,14 +30,6 @@ class DocType:
self.doc = doc
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):
"""
get total holidays
@ -71,7 +63,8 @@ class DocType:
def validate_balance_leaves(self):
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()
bal, tot_leaves = bal, tot_leaves
webnotes.conn.set(self.doc,'leave_balance',flt(bal['leave_balance']))
@ -98,6 +91,9 @@ class DocType:
raise Exception
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_balance_leaves()
self.validate_leave_overlap()
@ -107,3 +103,30 @@ class DocType:
if self.doc.status != "Approved":
webnotes.msgprint("""Only Approved Leave Applications can be Submitted.""",
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",
"docstatus": 0,
"creation": "2012-11-02 17:16:54",
"creation": "2012-12-05 14:11:53",
"modified_by": "Administrator",
"modified": "2012-11-30 12:17:27"
"modified": "2012-12-05 17:38:26"
},
{
"is_submittable": 1,
@ -44,6 +44,15 @@
"fieldtype": "Select",
"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,
"doctype": "DocField",
@ -215,13 +224,30 @@
"cancel": 1,
"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,
"create": 0,
"doctype": "DocPerm",
"submit": 0,
"write": 1,
"role": "HR User",
"role": "Leave Approver",
"cancel": 0,
"permlevel": 2
},
@ -229,11 +255,5 @@
"doctype": "DocPerm",
"role": "All",
"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_file': 'deprecate_tds',
},
{
'patch_module': 'patches.december_2012',
'patch_file': 'expense_leave_reload',
},
{
'patch_module': 'patches.december_2012',
'patch_file': 'repost_ordered_qty',
@ -717,4 +721,8 @@ patch_list = [
'patch_module': 'patches.december_2012',
'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) {
if(doc.delivery_note) {
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
if(doc.delivery_note && doc.__islocal) {
var ps_detail = getchildren('Packing Slip Item', doc.name, 'item_details');
if(!(flt(ps_detail[0].net_weight) && cstr(ps_detail[0].weight_uom))) {
cur_frm.cscript.update_item_details(doc);

View File

@ -16,7 +16,7 @@
from __future__ import unicode_literals
import webnotes
from webnotes.utils import flt, cint
from webnotes.utils import flt, cint, now
class DocType:
def __init__(self, d, dl):
@ -56,6 +56,15 @@ class DocType:
Validate if case nos overlap
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("""\
SELECT name FROM `tabPacking Slip`
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
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",
raise_exception=1)
res = webnotes.conn.sql("""\
SELECT item_code, IFNULL(SUM(qty), 0) as qty, IFNULL(packed_qty, 0) as packed_qty, stock_uom
FROM `tabDelivery Note Item`
WHERE parent = "%s" AND item_code IN (%s)
GROUP BY item_code""" % (self.doc.delivery_note, item_codes),
as_dict=1)
# gets item code, qty per item code, latest packed qty per item code and stock uom
res = webnotes.conn.sql("""select item_code, ifnull(sum(qty), 0) as qty,
(select sum(ifnull(psi.qty, 0) * (abs(ps.to_case_no - ps.from_case_no) + 1))
from `tabPacking Slip` ps, `tabPacking Slip Item` psi
where ps.name = psi.parent and ps.docstatus = 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])
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()
for item in dn_details:
if event=='submit':
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)
new_packed_qty = flt(item['packed_qty'])
if (new_packed_qty < 0) or (new_packed_qty > flt(item['qty'])):
webnotes.msgprint("Invalid new packed quantity for item %s. \
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("""\
UPDATE `tabDelivery Note Item`
SET packed_qty = %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):
"""
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
for d in self.doclist:

View File

@ -64,10 +64,6 @@ class DocType(TransactionBase):
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
# =====================================================================================
def get_tc_details(self):

View File

@ -19,8 +19,8 @@ import webnotes
from webnotes.utils import load_json, cstr, now
@webnotes.whitelist()
def update_item(args):
args = load_json(args)
def update_item(arg):
args = load_json(arg)
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']))

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'
}
]