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

This commit is contained in:
Brahma K 2011-08-31 09:02:31 +05:30
commit 3b575b5790
10 changed files with 153 additions and 95 deletions

View File

@ -10,12 +10,12 @@ pscript.onload_dashboard = function() {
pscript.dashboard_settings = { pscript.dashboard_settings = {
company: sys_defaults.company, company: sys_defaults.company,
start: dateutil.obj_to_str(dateutil.add_days(new Date(), -60)), start: dateutil.obj_to_str(dateutil.add_days(new Date(), -180)),
end: dateutil.obj_to_str(new Date()), end: dateutil.obj_to_str(new Date()),
interval: 7 interval: 30
} }
var ph = new PageHeader($('.dashboard .header').get(0), 'Dashboards'); var ph = new PageHeader($('.dashboard .header').get(0), 'Dashboard');
var db = new Dashboard(); var db = new Dashboard();
ph.add_button('Settings', db.show_settings); ph.add_button('Settings', db.show_settings);
@ -46,17 +46,19 @@ Dashboard = function() {
// give an id! // give an id!
var cell = $td(t,ridx,cidx); var cell = $td(t,ridx,cidx);
var title = $a(cell, 'div', 'dashboard-title', '', data[i][0].title); var title = $a(cell, 'div', 'dashboard-title', '', data[i][0].title);
var parent = $a(cell, 'div', 'dashboard-graph') var parent = $a(cell, 'div', 'dashboard-graph');
if(data[i][0].comment);
var comment = $a(cell, 'div', 'comment', '', data[i][0].comment)
parent.id = '_dashboard' + ridx + '-' + cidx; parent.id = '_dashboard' + ridx + '-' + cidx;
// render graph // render graph
me.render_graph(parent.id, data[i][1]); me.render_graph(parent.id, data[i][1], data[i][0].fillColor);
cidx++; cidx++;
} }
}, },
render_graph: function(parent, values) { render_graph: function(parent, values, fillColor) {
var vl = []; var vl = [];
$.each(values, function(i,v) { $.each(values, function(i,v) {
vl.push([dateutil.str_to_user(v[0]), v[1]]); vl.push([dateutil.str_to_user(v[0]), v[1]]);
@ -84,7 +86,8 @@ Dashboard = function() {
pad: 1.05, pad: 1.05,
tickOptions: {formatString: '%d'} tickOptions: {formatString: '%d'}
} }
} },
seriesColors: [fillColor]
}); });
}, },

View File

@ -2,58 +2,63 @@ dashboards = [
{ {
'type': 'account', 'type': 'account',
'account': 'Income', 'account': 'Income',
'title': 'Income' 'title': 'Income',
'fillColor': '#90EE90'
}, },
{ {
'type': 'account', 'type': 'account',
'account': 'Expenses', 'account': 'Expenses',
'title': 'Expenses' 'title': 'Expenses',
'fillColor': '#90EE90'
}, },
{ {
'type': 'from_company', 'type': 'receivables',
'account': 'receivables_group', 'title': 'Receivables',
'title': 'Receivables' 'fillColor': '#FFE4B5'
}, },
{ {
'type': 'from_company', 'type': 'payables',
'account': 'payables_group', 'title': 'Payables',
'title': 'Payables' 'fillColor': '#FFE4B5'
}, },
{ {
'type': 'cash', 'type': 'collection',
'debit_or_credit': 'Debit', 'title': 'Collection',
'title': 'Cash Inflow' 'comment':'This info comes from the accounts your have marked as "Bank or Cash"',
'fillColor': '#DDA0DD'
}, },
{ {
'type': 'cash', 'type': 'payments',
'debit_or_credit': 'Credit', 'title': 'Payments',
'title': 'Cash Outflow' 'comment':'This info comes from the accounts your have marked as "Bank or Cash"',
'fillColor': '#DDA0DD'
}, },
{ {
'type': 'creation', 'type': 'creation',
'doctype': 'Quotation', 'doctype': 'Quotation',
'title': 'New Quotations' 'title': 'New Quotations',
'fillColor': '#ADD8E6'
}, },
{ {
'type': 'creation', 'type': 'creation',
'doctype': 'Sales Order', 'doctype': 'Sales Order',
'title': 'New Orders' 'title': 'New Orders',
'fillColor': '#ADD8E6'
} }
] ]
class DashboardWidget: class DashboardWidget:
def __init__(self, company, start, end, interval): def __init__(self, company, start, end, interval):
import webnotes
from webnotes.utils import getdate from webnotes.utils import getdate
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
import webnotes
self.company = company self.company = company
self.abbr = webnotes.conn.get_value('Company', company, 'abbr') self.abbr = webnotes.conn.get_value('Company', company, 'abbr')
@ -61,11 +66,7 @@ class DashboardWidget:
self.end = getdate(end) self.end = getdate(end)
self.interval = interval self.interval = interval
self.fiscal_year = webnotes.conn.sql("""
select name from `tabFiscal Year`
where year_start_date <= %s and
DATE_ADD(year_start_date, INTERVAL 1 YEAR) >= %s
""", (start, start))[0][0]
self.glc = get_obj('GL Control') self.glc = get_obj('GL Control')
self.cash_accounts = [d[0] for d in webnotes.conn.sql(""" self.cash_accounts = [d[0] for d in webnotes.conn.sql("""
select name from tabAccount select name from tabAccount
@ -73,6 +74,13 @@ class DashboardWidget:
and company = %s and docstatus = 0 and company = %s and docstatus = 0
""", company)] """, company)]
self.receivables_group = webnotes.conn.get_value('Company', company,'receivables_group')
self.payables_group = webnotes.conn.get_value('Company', company,'payables_group')
# list of bank and cash accounts
self.bc_list = [s[0] for s in webnotes.conn.sql("select name from tabAccount where account_type='Bank or Cash'")]
def timeline(self): def timeline(self):
""" """
get the timeline for the dashboard get the timeline for the dashboard
@ -99,11 +107,12 @@ class DashboardWidget:
""" """
Generate the dasboard Generate the dasboard
""" """
from webnotes.utils import flt
tl = self.timeline() tl = self.timeline()
self.out = [] self.out = []
for i in range(len(tl)-1): for i in range(len(tl)-1):
self.out.append([tl[i+1].strftime('%Y-%m-%d'), self.value(opts, tl[i], tl[i+1]) or 0]) self.out.append([tl[i+1].strftime('%Y-%m-%d'), flt(self.value(opts, tl[i], tl[i+1])) or 0])
return self.out return self.out
@ -125,7 +134,18 @@ class DashboardWidget:
print acc print acc
raise e raise e
return self.glc.get_as_on_balance(acc, self.fiscal_year, start, debit_or_credit, lft, rgt) return self.glc.get_as_on_balance(acc, self.get_fiscal_year(start), start, debit_or_credit, lft, rgt)
def get_fiscal_year(self, dt):
"""
get fiscal year from date
"""
import webnotes
return webnotes.conn.sql("""
select name from `tabFiscal Year`
where year_start_date <= %s and
DATE_ADD(year_start_date, INTERVAL 1 YEAR) >= %s
""", (dt, dt))[0][0]
def get_creation_trend(self, doctype, start, end): def get_creation_trend(self, doctype, start, end):
""" """
@ -158,6 +178,26 @@ class DashboardWidget:
return debit_or_credit=='Credit' and float(ret[1]-ret[0]) or float(ret[0]-ret[1]) return debit_or_credit=='Credit' and float(ret[1]-ret[0]) or float(ret[0]-ret[1])
def get_bank_amt(self, debit_or_credit, master_type, start, end):
"""
Get collection (reduction in receivables over a period)
"""
import webnotes
reg = '('+'|'.join(self.bc_list) + ')'
return webnotes.conn.sql("""
select sum(t1.%s)
from `tabGL Entry` t1, tabAccount t2
where t1.account = t2.name
and t2.master_type='%s'
and t1.%s > 0
and t1.against REGEXP '%s'
and ifnull(t1.is_cancelled, 'No')='No'
and t1.posting_date between '%s' and '%s'
""" % (debit_or_credit, master_type, debit_or_credit, reg, start, end))[0][0]
def value(self, opts, start, end): def value(self, opts, start, end):
""" """
Value of the series on a particular date Value of the series on a particular date
@ -170,17 +210,17 @@ class DashboardWidget:
return self.get_account_amt(opts['account'], start, end, debit_or_credit) return self.get_account_amt(opts['account'], start, end, debit_or_credit)
elif opts['type']=='from_company': elif opts['type']=='receivables':
acc = webnotes.conn.get_value('Company', self.company, \ return self.get_account_balance(self.receivables_group, end)[2]
opts['account'].split('.')[-1])
return self.get_account_balance(acc, start)[2] elif opts['type']=='payables':
return self.get_account_balance(self.payables_group, end)[2]
elif opts['type']=='cash': elif opts['type']=='collection':
if opts['debit_or_credit']=='Credit': return self.get_bank_amt('credit', 'Customer', start, end)
return sum([self.get_account_amt(acc, start, end, opts['debit_or_credit']) for acc in self.cash_accounts]) or 0
elif opts['debit_or_credit']=='Debit': elif opts['type']=='payments':
return sum([self.get_account_amt(acc, start, end, opts['debit_or_credit']) for acc in self.cash_accounts]) or 0 return self.get_bank_amt('debit', 'Supplier', start, end)
elif opts['type']=='creation': elif opts['type']=='creation':
return self.get_creation_trend(opts['doctype'], start, end) return self.get_creation_trend(opts['doctype'], start, end)

View File

@ -25,12 +25,7 @@ pscript.home_make_body = function() {
wrapper.banner_area = $a(wrapper.head, 'div'); wrapper.banner_area = $a(wrapper.head, 'div');
wrapper.setup_wizard_area = $a(wrapper.body, 'div', 'setup-wizard') wrapper.setup_wizard_area = $a(wrapper.body, 'div', 'setup-wizard');
wrapper.system_message_area = $a(wrapper.body, 'div', '',
{marginBottom:'16px', padding:'8px', backgroundColor:'#FFD', border:'1px dashed #AA6', display:'none'})
} }
// ================================== // ==================================
@ -436,8 +431,8 @@ FeedList.prototype.make_head = function() {
// head // head
$a(this.head,'h1','', {display:'inline'}, 'Home'); $a(this.head,'h1','', {display:'inline'}, 'Home');
$a(this.head,'span','link_type', {marginLeft:'7px'}, '[?]', function() { $a(this.head,'span','link_type', {marginLeft:'7px'}, 'help', function() {
msgprint('<b>What appears here?</b> This is where you get updates of everything you are allowed to access and generates an update') msgprint('<b>What appears here?</b> This is where you get updates of everything you are permitted to follow')
}) })
// refresh // refresh
@ -445,6 +440,11 @@ FeedList.prototype.make_head = function() {
{cursor:'pointer', marginLeft:'7px', fontSize:'11px'}, 'refresh', {cursor:'pointer', marginLeft:'7px', fontSize:'11px'}, 'refresh',
function() { me.run(); } function() { me.run(); }
); );
if(has_common(user_roles, ['System Manager','Accounts Manager'])) {
$btn(this.head, 'Dashboard', function() {loadpage('dashboard'); }, {marginLeft:'7px'})
}
} }
FeedList.prototype.run = function() { FeedList.prototype.run = function() {
@ -594,14 +594,15 @@ HomeStatusBar = function() {
this.render = function(r) { this.render = function(r) {
this.wrapper.innerHTML = ''; this.wrapper.innerHTML = '';
this.span = $a(this.wrapper, 'span', 'home-status-link') this.span = $a(this.wrapper, 'span', 'link_type', {fontWeight:'bold'});
this.span.onclick = function() { loadpage('My Company') } this.span.onclick = function() { loadpage('My Company') }
if(r.unread_messages) { if(r.unread_messages) {
this.span.innerHTML = '<span class="home-status-unread">' + r.unread_messages + '</span> unread message' + (cint(r.unread_messages) > 1 ? 's' : ''); this.span.innerHTML = '<span class="home-status-unread">' + r.unread_messages + '</span> unread';
} else { } else {
this.span.innerHTML = 'No unread messages.'; this.span.innerHTML = 'Team / Messages';
} }
} }
} }
@ -614,10 +615,6 @@ pscript.home_make_status = function() {
function(r,rt) { function(r,rt) {
home_status_bar.render(r.message); home_status_bar.render(r.message);
// system_messages
if(r.message.system_message)
pscript.show_system_message(wrapper, r.message.system_message);
// render online users // render online users
pscript.online_users_obj.render(r.message.online_users); pscript.online_users_obj.render(r.message.online_users);
pscript.online_users = r.message.online_users; pscript.online_users = r.message.online_users;
@ -630,24 +627,6 @@ pscript.home_make_status = function() {
); );
} }
// show system message
// -------------------
pscript.show_system_message = function(wrapper, msg) {
$ds(wrapper.system_message_area);
var txt = $a(wrapper.system_message_area, 'div', '', {lineHeight:'1.6em'});
txt.innerHTML = msg;
var span = $ln($a(wrapper.system_message_area, 'div', '', {textAlign:'right'}), 'Dismiss'.bold(),
function(me) {
me.set_working();
$c_obj('Home Control', 'dismiss_message', '', function(r,rt) {
me.done_working();
$(wrapper.system_message_area).slideUp();
});
}, {fontSize:'11px'}
)
}
// complete my company registration // complete my company registration
// -------------------------------- // --------------------------------
pscript.complete_registration = function() pscript.complete_registration = function()

View File

@ -29,17 +29,10 @@ def get_status_details(arg=None):
online = get_online_users() online = get_online_users()
# system messages # system messages
msg_id = webnotes.conn.get_global('system_message_id')
msg = ''
if msg_id and msg_id != webnotes.conn.get_global('system_message_id', webnotes.session['user']):
msg = webnotes.conn.get_global('system_message')
ret = { ret = {
'user_count': len(online) or 0, 'user_count': len(online) or 0,
'unread_messages': get_unread_messages(), 'unread_messages': get_unread_messages(),
'online_users': online or [], 'online_users': online or [],
'system_message':msg,
'is_trial': webnotes.conn.get_global('is_trial'), 'is_trial': webnotes.conn.get_global('is_trial'),
'days_to_expiry': (webnotes.conn.get_global('days_to_expiry') or '0'), 'days_to_expiry': (webnotes.conn.get_global('days_to_expiry') or '0'),
'setup_status': get_setup_status() 'setup_status': get_setup_status()
@ -56,9 +49,20 @@ def get_setup_status():
percent = 20 percent = 20
ret = [] ret = []
header = webnotes.conn.get_value('Control Panel', None, 'client_name') or '' def is_header_set():
if header.startswith('<div style="padding:4px; font-size:20px;">'\ header = webnotes.conn.get_value('Control Panel', None, 'client_name') or ''
+webnotes.conn.get_value('Control Panel', None, 'company_name')):
if header.startswith('<div style="padding:4px; font-size:20px;">'\
+webnotes.conn.get_value('Control Panel', None, 'company_name')):
return False
elif 'Banner Comes Here' in header:
return False
else:
return True
if not is_header_set():
ret.append('<a href="#!Form/Personalize/Personalize">Upload your company banner</a>') ret.append('<a href="#!Form/Personalize/Personalize">Upload your company banner</a>')
else: else:
percent += 20 percent += 20

View File

@ -657,7 +657,7 @@ MemberCoversationComment = function(cell, det, conv) {
} }
var st = (!det.docstatus ? {fontWeight: 'bold'} : null); var st = (!det.docstatus ? {fontWeight: 'bold'} : null);
this.msg = $a(this.comment, 'span', 'social', st, ': ' + det.creation); this.msg = $a(this.comment, 'span', 'social', st, ': ' + det.comment);
if(det.full_name==user) { if(det.full_name==user) {
$y(this.wrapper, {backgroundColor: '#D9D9F3'}); $y(this.wrapper, {backgroundColor: '#D9D9F3'});

View File

@ -0,0 +1,8 @@
class DocType:
def __init__(self, d, dl):
self.doc, self.doclist = d, dl
def on_trash(self):
import webnotes
webnotes.conn.sql("delete from tabAnswer where question=%s", self.doc.name)

View File

@ -19,12 +19,25 @@ KBItemToolbar = function(args, kb) {
this.line1.innerHTML = repl('By %(name)s | %(when)s', { this.line1.innerHTML = repl('By %(name)s | %(when)s', {
name: wn.utils.full_name(this.det.first_name, this.det.last_name), name: wn.utils.full_name(this.det.first_name, this.det.last_name),
when: wn.datetime.comment_when(this.det.modified) when: wn.datetime.comment_when(this.det.modified)
}) });
// allow system manager to delete questions / answers
if(has_common(user_roles, ['Administrator', 'System Manager'])) {
this.line1.innerHTML += ' | '
$ln(this.line1, 'delete', me.del);
}
} }
this.make_vote = function() { this.make_vote = function() {
new KBPoints(this.line2, this.det.points, this.det._users_voted, this.doctype, this.det.name, this.det.owner); new KBPoints(this.line2, this.det.points, this.det._users_voted, this.doctype, this.det.name, this.det.owner);
}
this.del = function() {
this.innerHTML = 'deleting...'; this.disabled = 1;
$c_page('knowledge_base', 'questions', 'delete', {dt:me.doctype, dn:me.det.name}, function(r,rt) {
// reload the list
kb.list.run()
});
} }
this.make_tags = function() { this.make_tags = function() {

View File

@ -128,7 +128,7 @@ KBAnswerList = function(args) {
as_dict: 1, as_dict: 1,
no_results_message: 'No answers yet, be the first one to answer!', no_results_message: 'No answers yet, be the first one to answer!',
render_row: function(body, data) { render_row: function(body, data) {
new KBAnswer(body, data) new KBAnswer(body, data, me)
}, },
get_query: function() { get_query: function() {
return repl("SELECT t1.name, t1.owner, t1.answer, t1.points, t1._users_voted, t2.first_name, " return repl("SELECT t1.name, t1.owner, t1.answer, t1.points, t1._users_voted, t2.first_name, "
@ -150,7 +150,7 @@ KBAnswerList = function(args) {
// answer // answer
// by xxx | on xxx // by xxx | on xxx
// points yyy // points yyy
KBAnswer = function(body, data) { KBAnswer = function(body, data, ans_list) {
body.className = 'qv-answer'; body.className = 'qv-answer';
new EditableText({ new EditableText({
parent: body, parent: body,
@ -169,7 +169,7 @@ KBAnswer = function(body, data) {
det: data, det: data,
with_tags: 0, with_tags: 0,
doctype: 'Answer' doctype: 'Answer'
}, null) }, ans_list)
} }

View File

@ -35,3 +35,11 @@ def vote(arg):
(p, cstr(res[1]) + ',' + webnotes.user.name, args['dn'])) (p, cstr(res[1]) + ',' + webnotes.user.name, args['dn']))
return p return p
def delete(arg):
"""
delete a question or answer (called from kb toolbar)
"""
args = load_json(arg)
from webnotes.model import delete_doc
delete_doc(args['dt'], args['dn'])

View File

@ -1,7 +1,8 @@
# REMEMBER to update this # REMEMBER to update this
# ======================== # ========================
last_patch = 349 last_patch = 350
#------------------------------------------- #-------------------------------------------
def execute(patch_no): def execute(patch_no):
@ -268,4 +269,6 @@ def execute(patch_no):
# reload profile with new fields for security # reload profile with new fields for security
delete_doc('DocType', 'Profile') delete_doc('DocType', 'Profile')
reload_doc('core', 'doctype', 'profile') reload_doc('core', 'doctype', 'profile')
elif patch_no == 350:
reload_doc('stock', 'doctype', 'delivery_note_detail')
reload_doc('stock', 'doctype', 'item_customer_detail')