knjtasks 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +66 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/files/database_schema.rb +174 -0
- data/lib/knjtasks.rb +172 -0
- data/locales/da_DK/LC_MESSAGES/default.mo +0 -0
- data/locales/da_DK/LC_MESSAGES/default.po +1153 -0
- data/locales/da_DK/title.txt +1 -0
- data/locales/en_GB/title.txt +1 -0
- data/models/class_comment.rb +44 -0
- data/models/class_customer.rb +13 -0
- data/models/class_email_check.rb +7 -0
- data/models/class_project.rb +22 -0
- data/models/class_task.rb +163 -0
- data/models/class_task_assigned_user.rb +62 -0
- data/models/class_task_check.rb +26 -0
- data/models/class_timelog.rb +82 -0
- data/models/class_user.rb +125 -0
- data/models/class_user_project_link.rb +66 -0
- data/models/class_user_rank.rb +3 -0
- data/models/class_user_rank_link.rb +17 -0
- data/models/class_user_task_list_link.rb +17 -0
- data/pages/admin.rhtml +7 -0
- data/pages/comment_edit.rhtml +121 -0
- data/pages/comment_update_id_per_obj.rhtml +41 -0
- data/pages/customer_edit.rhtml +69 -0
- data/pages/customer_search.rhtml +80 -0
- data/pages/customer_show.rhtml +50 -0
- data/pages/frontpage.rhtml +198 -0
- data/pages/project_edit.rhtml +129 -0
- data/pages/project_search.rhtml +82 -0
- data/pages/project_show.rhtml +203 -0
- data/pages/task_check_edit.rhtml +98 -0
- data/pages/task_edit.rhtml +168 -0
- data/pages/task_search.rhtml +131 -0
- data/pages/task_show.rhtml +454 -0
- data/pages/timelog_edit.rhtml +134 -0
- data/pages/timelog_search.rhtml +318 -0
- data/pages/user_edit.rhtml +223 -0
- data/pages/user_login.rhtml +83 -0
- data/pages/user_profile.rhtml +89 -0
- data/pages/user_rank_search.rhtml +95 -0
- data/pages/user_search.rhtml +136 -0
- data/pages/user_show.rhtml +87 -0
- data/pages/workstatus.rhtml +320 -0
- data/scripts/fckeditor_validate_login.rb +23 -0
- data/spec/knjtasks_spec.rb +115 -0
- data/spec/spec_helper.rb +12 -0
- data/threads/thread_mail_task_comments.rb +114 -0
- data/www/api/task.rhtml +9 -0
- data/www/api/user.rhtml +20 -0
- data/www/clean.rhtml +14 -0
- data/www/css/default.css +186 -0
- data/www/gfx/body_bg.jpg +0 -0
- data/www/gfx/button_bg.png +0 -0
- data/www/gfx/main_box_design.png +0 -0
- data/www/gfx/main_box_left.png +0 -0
- data/www/gfx/main_box_right.png +0 -0
- data/www/gfx/main_box_top.png +0 -0
- data/www/gfx/main_box_top_left.png +0 -0
- data/www/gfx/main_box_top_right.png +0 -0
- data/www/index.rhtml +154 -0
- data/www/js/default.js +112 -0
- metadata +208 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
<%
|
2
|
+
print _site.header(_("Search for users")) if !_get["ajaxsearch"]
|
3
|
+
|
4
|
+
if !_get["ajaxsearch"] or (_get["ajaxsearch"] and _get["choice"] != "dosearch")
|
5
|
+
%>
|
6
|
+
<form method="get"<%if _get["ajaxsearch"]; print " onsubmit=\"return do_ajax_search();\""; end%>>
|
7
|
+
<%=Knj::Web.hiddens([{:name => :show, :value => :user_search}, {:name => :choice, :value => :dosearch}])%>
|
8
|
+
|
9
|
+
<%
|
10
|
+
if _get["not_in_task_id"]
|
11
|
+
print Knj::Web.hiddens([{:name => :not_in_task_id, :value => _get["not_in_task_id"]}])
|
12
|
+
end
|
13
|
+
%>
|
14
|
+
|
15
|
+
<%=_site.boxt(_("Enter criteria"), "350px")%>
|
16
|
+
<table class="form" id="formusersearch">
|
17
|
+
<%
|
18
|
+
print Knj::Web.inputs([{
|
19
|
+
:title => _("Name"),
|
20
|
+
:name => :texname,
|
21
|
+
:value => _get["texname"],
|
22
|
+
:descr => _("Enter a part of the name of the user you are looking for.")
|
23
|
+
}])
|
24
|
+
%>
|
25
|
+
<tr>
|
26
|
+
<td colspan="2" class="buttons">
|
27
|
+
<%if _site.has_rank?("admin")%>
|
28
|
+
<input type="button" value="<%=_("Add new")%>" onclick="location.href='?show=user_edit';" />
|
29
|
+
<%end%>
|
30
|
+
<input type="submit" value="<%=_("Search")%>" />
|
31
|
+
</td>
|
32
|
+
</tr>
|
33
|
+
</table>
|
34
|
+
<%=_site.boxb%>
|
35
|
+
|
36
|
+
</form>
|
37
|
+
|
38
|
+
<%
|
39
|
+
if _get["ajaxsearch"]
|
40
|
+
%>
|
41
|
+
<br />
|
42
|
+
|
43
|
+
<div id="divresults">
|
44
|
+
<div class="error"><%=_("Please search...")%></div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<script type="text/javascript">
|
48
|
+
modal_on_opened(function(){
|
49
|
+
$("#texname").focus();
|
50
|
+
});
|
51
|
+
</script>
|
52
|
+
<%
|
53
|
+
end
|
54
|
+
%>
|
55
|
+
|
56
|
+
<script type="text/javascript">
|
57
|
+
$("#texname").focus();
|
58
|
+
|
59
|
+
function do_ajax_search(){
|
60
|
+
formdata = forms_inputs_read($("#formusersearch"));
|
61
|
+
|
62
|
+
urlstr = "clean.rhtml?show=user_search&ajaxsearch=true&choice=dosearch&jscallback=<%=_get['jscallback']%>&"
|
63
|
+
urlstr += $.param(formdata)
|
64
|
+
|
65
|
+
$.ajax({type: "GET", url: urlstr, complete: function(data){
|
66
|
+
$("#divresults").slideUp("fast", function(){
|
67
|
+
$("#divresults").html(data.responseText);
|
68
|
+
$("#divresults").slideDown("fast");
|
69
|
+
});
|
70
|
+
}});
|
71
|
+
|
72
|
+
return false;
|
73
|
+
}
|
74
|
+
</script>
|
75
|
+
<%
|
76
|
+
end
|
77
|
+
|
78
|
+
if _get["choice"] == "dosearch"
|
79
|
+
args = {"orderby" => ["name", "username"]}
|
80
|
+
|
81
|
+
if _get["texname"].to_s.length > 0
|
82
|
+
args["name_search"] = _get["texname"]
|
83
|
+
end
|
84
|
+
|
85
|
+
users = _ob.list(:User, args)
|
86
|
+
|
87
|
+
%>
|
88
|
+
<br />
|
89
|
+
|
90
|
+
<%=_site.boxt(_("Results"), "350px")%>
|
91
|
+
<table class="list">
|
92
|
+
<thead>
|
93
|
+
<tr>
|
94
|
+
<th><%=_("Name")%></th>
|
95
|
+
</tr>
|
96
|
+
</thead>
|
97
|
+
<tbody>
|
98
|
+
<%
|
99
|
+
count = 0
|
100
|
+
users.each do |user|
|
101
|
+
next if !user.has_view_access?(_site.user)
|
102
|
+
count += 1
|
103
|
+
|
104
|
+
%>
|
105
|
+
<tr>
|
106
|
+
<td>
|
107
|
+
<%
|
108
|
+
if _get["jscallback"]
|
109
|
+
%>
|
110
|
+
<a href="javascript: <%=_get["jscallback"]%>('<%=user.id%>');"><%=user.name.html%></a>
|
111
|
+
<%
|
112
|
+
else
|
113
|
+
print user.html
|
114
|
+
end
|
115
|
+
%>
|
116
|
+
</td>
|
117
|
+
</tr>
|
118
|
+
<%
|
119
|
+
end
|
120
|
+
|
121
|
+
if count <= 0
|
122
|
+
%>
|
123
|
+
<tr>
|
124
|
+
<td colspan="1" class="error">
|
125
|
+
<%=_("No users were found.")%>
|
126
|
+
</td>
|
127
|
+
</tr>
|
128
|
+
<%
|
129
|
+
end
|
130
|
+
%>
|
131
|
+
</tbody>
|
132
|
+
</table>
|
133
|
+
<%=_site.boxb%>
|
134
|
+
<%
|
135
|
+
end
|
136
|
+
%>
|
@@ -0,0 +1,87 @@
|
|
1
|
+
<%
|
2
|
+
user = _ob.get(:User, _get["user_id"])
|
3
|
+
_hb.alert(_("You do not have access to view this user.")) if !user.has_view_access?(_site.user)
|
4
|
+
|
5
|
+
print _site.header(sprintf(_("Show user: %s."), user.name.html))
|
6
|
+
|
7
|
+
if _site.has_rank?("admin")
|
8
|
+
%>
|
9
|
+
<%=_site.boxt(_("Actions"), 450)%>
|
10
|
+
<input type="button" value="<%=_("Edit")%>" onclick="location.href='?show=user_edit&user_id=<%=user.id%>';" />
|
11
|
+
<%=_site.boxb%>
|
12
|
+
|
13
|
+
<br />
|
14
|
+
<%
|
15
|
+
end
|
16
|
+
%>
|
17
|
+
|
18
|
+
<%=_site.boxt(_("Information"), 450)%>
|
19
|
+
<table class="form">
|
20
|
+
<%
|
21
|
+
print Knj::Web.inputs([{
|
22
|
+
:title => _("Name"),
|
23
|
+
:type => :info,
|
24
|
+
:value => user.name.html,
|
25
|
+
:descr => _("The real name of the user.")
|
26
|
+
},{
|
27
|
+
:title => _("Email"),
|
28
|
+
:type => :info,
|
29
|
+
:value => "<a href=\"mailto:#{user[:email].html}\">#{user[:email].html}</a>",
|
30
|
+
:descr => _("The email of the user.")
|
31
|
+
}])
|
32
|
+
%>
|
33
|
+
</table>
|
34
|
+
<%=_site.boxb%>
|
35
|
+
|
36
|
+
<br />
|
37
|
+
|
38
|
+
<%=_site.boxt(_("Tasks"), 800)%>
|
39
|
+
<table class="list">
|
40
|
+
<thead>
|
41
|
+
<tr>
|
42
|
+
<th><%=_"Task"%></th>
|
43
|
+
<th><%=_"Status"%></th>
|
44
|
+
<th><%=_"Date"%></th>
|
45
|
+
<th><%=_"Project"%></th>
|
46
|
+
</tr>
|
47
|
+
</thead>
|
48
|
+
<tbody>
|
49
|
+
<%
|
50
|
+
tasks = user.tasks("orderby" => [["date_added", "desc"], ["id", "desc"]])
|
51
|
+
|
52
|
+
count = 0
|
53
|
+
tasks.each do |task|
|
54
|
+
next if !task.has_view_access?(_site.user)
|
55
|
+
count += 1
|
56
|
+
|
57
|
+
%>
|
58
|
+
<tr>
|
59
|
+
<td>
|
60
|
+
<%=task.html%>
|
61
|
+
</td>
|
62
|
+
<td>
|
63
|
+
<%=task.status_str.html%>
|
64
|
+
</td>
|
65
|
+
<td class="nowrap" style="width: 100px;">
|
66
|
+
<%=task.date_added_str(:time => false)%>
|
67
|
+
</td>
|
68
|
+
<td>
|
69
|
+
<%=task.project_html%>
|
70
|
+
</td>
|
71
|
+
</tr>
|
72
|
+
<%
|
73
|
+
end
|
74
|
+
|
75
|
+
if count <= 0
|
76
|
+
%>
|
77
|
+
<tr>
|
78
|
+
<td colspan="2" class="error">
|
79
|
+
<%=_("No tasks were found for this user.")%>
|
80
|
+
</td>
|
81
|
+
</tr>
|
82
|
+
<%
|
83
|
+
end
|
84
|
+
%>
|
85
|
+
</tbody>
|
86
|
+
</table>
|
87
|
+
<%=_site.boxb%>
|
@@ -0,0 +1,320 @@
|
|
1
|
+
<%
|
2
|
+
_hb.alert(_("You need to be logged in to view this page.")).back if !_site.user
|
3
|
+
|
4
|
+
cur_date = Datet.new
|
5
|
+
|
6
|
+
if _get["choice"] == "dofilter"
|
7
|
+
date_from = Datet.in(_get["texdatefrom"])
|
8
|
+
date_to = Datet.in(_get["texdateto"])
|
9
|
+
else
|
10
|
+
date_from = Datet.new
|
11
|
+
date_from.day = 1
|
12
|
+
|
13
|
+
date_to = Datet.new
|
14
|
+
date_to.day = date_to.days_in_month
|
15
|
+
end
|
16
|
+
|
17
|
+
stats = {
|
18
|
+
:date_logs => {},
|
19
|
+
:projects => {},
|
20
|
+
:totals => {:earned => 0, :earned_time => 0, :earned_transport => 0, :transport_length => 0}
|
21
|
+
}
|
22
|
+
|
23
|
+
sql = "
|
24
|
+
SELECT
|
25
|
+
SUM(TIME_TO_SEC(Timelog.time)) AS time,
|
26
|
+
SUM(TIME_TO_SEC(Timelog.time_transport)) AS time_transport,
|
27
|
+
Timelog.date,
|
28
|
+
Timelog.transport_length,
|
29
|
+
Project.id AS project_id
|
30
|
+
|
31
|
+
FROM
|
32
|
+
Timelog,
|
33
|
+
Task,
|
34
|
+
Project
|
35
|
+
|
36
|
+
WHERE
|
37
|
+
Task.id = Timelog.task_id AND
|
38
|
+
Project.id = Task.project_id AND
|
39
|
+
Timelog.date >= '#{_db.date_out(date_from, {:time => false})}' AND
|
40
|
+
Timelog.date <= '#{_db.date_out(date_to, {:time => false})}'
|
41
|
+
"
|
42
|
+
|
43
|
+
if _get["projects"].to_s.length > 0
|
44
|
+
project_ids = _get["projects"].to_s.split(";")
|
45
|
+
sql += " AND Task.project_id IN (#{Knj::ArrayExt.join(:arr => project_ids, :sep => ",", :surr => "'", :callback => [_db, :esc])})"
|
46
|
+
end
|
47
|
+
|
48
|
+
sql += "
|
49
|
+
GROUP BY
|
50
|
+
Timelog.id
|
51
|
+
|
52
|
+
ORDER BY
|
53
|
+
Timelog.date,
|
54
|
+
Timelog.id
|
55
|
+
"
|
56
|
+
|
57
|
+
_db.q(sql) do |data|
|
58
|
+
date = Datet.in(data[:date])
|
59
|
+
dj = date.day.to_i
|
60
|
+
dm = date.month.to_i
|
61
|
+
dy = date.year.to_i
|
62
|
+
|
63
|
+
hours = data[:time].to_f / 3600
|
64
|
+
hours_transport = data[:time_transport].to_f / 3600
|
65
|
+
|
66
|
+
earned_time = hours * 200
|
67
|
+
earned_transport = hours_transport * 200
|
68
|
+
earned_total = earned_time + earned_transport
|
69
|
+
|
70
|
+
stats[:date_logs][dy] = {} if !stats[:date_logs].has_key?(dy)
|
71
|
+
stats[:date_logs][dy][dm] = {} if !stats[:date_logs][dy].has_key?(dm)
|
72
|
+
stats[:date_logs][dy][dm][dj] = {:time => 0, :time_transport => 0, :transport_length => 0, :earned_total => 0, :earned_time => 0, :earned_transport => 0} if !stats[:date_logs][dy][dm].has_key?(dj)
|
73
|
+
stats[:date_logs][dy][dm][dj][:time] += data[:time].to_i
|
74
|
+
stats[:date_logs][dy][dm][dj][:time_transport] += data[:time_transport].to_i
|
75
|
+
stats[:date_logs][dy][dm][dj][:transport_length] += data[:transport_length].to_i
|
76
|
+
stats[:date_logs][dy][dm][dj][:earned_time] += earned_time
|
77
|
+
stats[:date_logs][dy][dm][dj][:earned_transport] += earned_transport
|
78
|
+
stats[:date_logs][dy][dm][dj][:earned_total] += earned_transport
|
79
|
+
|
80
|
+
stats[:projects][data[:project_id]] = {:time => 0, :time_transport => 0, :earned_total => 0} if !stats[:projects].has_key?(data[:project_id])
|
81
|
+
stats[:projects][data[:project_id]][:time] += data[:time].to_i
|
82
|
+
stats[:projects][data[:project_id]][:time_transport] += data[:time_transport].to_i
|
83
|
+
stats[:projects][data[:project_id]][:earned_total] += earned_total
|
84
|
+
|
85
|
+
stats[:totals][:earned] += earned_total
|
86
|
+
stats[:totals][:earned_time] += earned_time
|
87
|
+
stats[:totals][:earned_transport] += earned_transport
|
88
|
+
stats[:totals][:transport_length] += data[:transport_length].to_i
|
89
|
+
end
|
90
|
+
|
91
|
+
stats[:projects] = stats[:projects].sort do |a, b|
|
92
|
+
ap = _ob.get(:Project, a[0])
|
93
|
+
bp = _ob.get(:Project, b[0])
|
94
|
+
|
95
|
+
ap.name <=> bp.name
|
96
|
+
end
|
97
|
+
|
98
|
+
print _site.header(_("Work-status"))
|
99
|
+
%>
|
100
|
+
|
101
|
+
<form method="get" onsubmit="return do_filter();">
|
102
|
+
<%=Knj::Web.hiddens([{:name => :show, :value => :workstatus},{:name => :choice, :value => :dofilter},{:name => :projects}])%>
|
103
|
+
|
104
|
+
<%=_site.boxt(_("Enter criteria"), 350)%>
|
105
|
+
<table class="form">
|
106
|
+
<%
|
107
|
+
print Knj::Web.inputs([{
|
108
|
+
:title => _("Date from"),
|
109
|
+
:name => :texdatefrom,
|
110
|
+
:value => date_from.out(:time => false)
|
111
|
+
},{
|
112
|
+
:title => _("Date to"),
|
113
|
+
:name => :texdateto,
|
114
|
+
:value => date_to.out(:time => false)
|
115
|
+
},{
|
116
|
+
:title => _("Project"),
|
117
|
+
:name => :selprojects,
|
118
|
+
:value => nil,
|
119
|
+
:opts => _ob.list_optshash(:Project, {:list_args => {"orderby" => "name"}}),
|
120
|
+
:multiple => true,
|
121
|
+
:size => 15,
|
122
|
+
:values => _get["projects"].to_s.split(";")
|
123
|
+
}])
|
124
|
+
%>
|
125
|
+
<tr>
|
126
|
+
<td colspan="2" class="buttons">
|
127
|
+
<input type="submit" value="<%=_"Filter"%>" />
|
128
|
+
</td>
|
129
|
+
</tr>
|
130
|
+
</table>
|
131
|
+
<%=_site.boxb%>
|
132
|
+
|
133
|
+
</form>
|
134
|
+
|
135
|
+
<script type="text/javascript">
|
136
|
+
function do_filter(){
|
137
|
+
$("input[name=projects]").val(select_values({sel: $("#selprojects"), selected: true, expl: ";"}));
|
138
|
+
return true;
|
139
|
+
}
|
140
|
+
</script>
|
141
|
+
|
142
|
+
<br />
|
143
|
+
|
144
|
+
<%=_site.boxt(_("Results"), 700)%>
|
145
|
+
<table class="list">
|
146
|
+
<thead>
|
147
|
+
<tr>
|
148
|
+
<th><%=_"Date"%></th>
|
149
|
+
<th class="tdr"><%=_"Hours"%></th>
|
150
|
+
<th class="tdr"><%=_"Hours driving"%></th>
|
151
|
+
<th class="tdr"><%=_"Earned"%></th>
|
152
|
+
<th class="tdr"><%=_"Driving earned"%></th>
|
153
|
+
<th class="tdr"><%=_"Driving length"%></th>
|
154
|
+
</tr>
|
155
|
+
</thead>
|
156
|
+
<tbody>
|
157
|
+
<%
|
158
|
+
stats[:date_logs].each do |year, year_data|
|
159
|
+
if date_from.year != date_to.year
|
160
|
+
%>
|
161
|
+
<tr>
|
162
|
+
<td colspan="6">
|
163
|
+
<h2><%=year%></h2>
|
164
|
+
</td>
|
165
|
+
</tr>
|
166
|
+
<%
|
167
|
+
end
|
168
|
+
|
169
|
+
year_data.each do |month, month_data|
|
170
|
+
month_date = Datet.in("#{year}-#{month}-1")
|
171
|
+
|
172
|
+
if date_from.year != date_to.year or date_from.month != date_to.month
|
173
|
+
%>
|
174
|
+
<tr>
|
175
|
+
<td colspan="6">
|
176
|
+
<h3><%=month%></h3>
|
177
|
+
</td>
|
178
|
+
</tr>
|
179
|
+
<%
|
180
|
+
end
|
181
|
+
|
182
|
+
1.upto(month_date.days_in_month) do |date|
|
183
|
+
date_data = month_data[date.to_i]
|
184
|
+
|
185
|
+
if date_data
|
186
|
+
hours = date_data[:time].to_f / 3600
|
187
|
+
hours_driving = date_data[:time_transport].to_f / 3600
|
188
|
+
earned = date_data[:earned_time]
|
189
|
+
earned_transport = date_data[:earned_transport]
|
190
|
+
transport_length = date_data[:transport_length]
|
191
|
+
else
|
192
|
+
hours = 0
|
193
|
+
hours_driving = 0
|
194
|
+
earned = 0
|
195
|
+
earned_transport = 0
|
196
|
+
transport_length = 0
|
197
|
+
end
|
198
|
+
|
199
|
+
cur_date_is = (cur_date.day.to_i == date.to_i and cur_date.year.to_i == year.to_i and cur_date.month.to_i == month.to_i)
|
200
|
+
|
201
|
+
%>
|
202
|
+
<tr<%if cur_date_is; print " style=\"font-weight: bold;\""; end%>>
|
203
|
+
<td>
|
204
|
+
<%=date%>
|
205
|
+
</td>
|
206
|
+
<td class="tdr">
|
207
|
+
<%=Knj::Locales.number_out(hours, 1)%>
|
208
|
+
</td>
|
209
|
+
<td class="tdr">
|
210
|
+
<%=Knj::Locales.number_out(hours_driving, 1)%>
|
211
|
+
</td>
|
212
|
+
<td class="tdr">
|
213
|
+
<%=Knj::Locales.number_out(earned, 0)%>
|
214
|
+
</td>
|
215
|
+
<td class="tdr">
|
216
|
+
<%=Knj::Locales.number_out(earned_transport, 0)%>
|
217
|
+
</td>
|
218
|
+
<td class="tdr">
|
219
|
+
<%=Knj::Locales.number_out(transport_length, 0)%>
|
220
|
+
</td>
|
221
|
+
</tr>
|
222
|
+
<%
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
%>
|
227
|
+
</tbody>
|
228
|
+
</table>
|
229
|
+
<%=_site.boxb%>
|
230
|
+
|
231
|
+
<br />
|
232
|
+
|
233
|
+
<%=_site.boxt(_("Overview"), 450)%>
|
234
|
+
<table class="form">
|
235
|
+
<%
|
236
|
+
tax_40 = stats[:totals][:earned] * 0.4
|
237
|
+
|
238
|
+
print Knj::Web.inputs([{
|
239
|
+
:title => _("Hours"),
|
240
|
+
:type => :info,
|
241
|
+
:align => :right,
|
242
|
+
:value => Knj::Locales.number_out(stats[:totals][:earned_time], 2)
|
243
|
+
},{
|
244
|
+
:title => _("Transport"),
|
245
|
+
:type => :info,
|
246
|
+
:align => :right,
|
247
|
+
:value => Knj::Locales.number_out(stats[:totals][:earned_transport], 2)
|
248
|
+
},{
|
249
|
+
:title => _("Total earned"),
|
250
|
+
:type => :info,
|
251
|
+
:align => :right,
|
252
|
+
:value => Knj::Locales.number_out(stats[:totals][:earned], 2)
|
253
|
+
},{
|
254
|
+
:title => _("Tax") + " (40%)",
|
255
|
+
:type => :info,
|
256
|
+
:align => :right,
|
257
|
+
:value => Knj::Locales.number_out(tax_40, 2)
|
258
|
+
},{
|
259
|
+
:title => _("Total after tax"),
|
260
|
+
:type => :info,
|
261
|
+
:align => :right,
|
262
|
+
:value => Knj::Locales.number_out(stats[:totals][:earned].to_f - tax_40.to_f, 2)
|
263
|
+
},{
|
264
|
+
:title => _("Total transport"),
|
265
|
+
:type => :info,
|
266
|
+
:align => :right,
|
267
|
+
:value => _hb.num(stats[:totals][:transport_length], 2)
|
268
|
+
}])
|
269
|
+
%>
|
270
|
+
</table>
|
271
|
+
<%=_site.boxb%>
|
272
|
+
|
273
|
+
<br />
|
274
|
+
|
275
|
+
<%=_site.boxt(_("Projects"), 450)%>
|
276
|
+
<table class="list">
|
277
|
+
<thead>
|
278
|
+
<tr>
|
279
|
+
<th><%=_"Project"%></th>
|
280
|
+
<th class="tdr"><%=_"Hours"%></th>
|
281
|
+
<th class="tdr"><%=_"Transport"%></th>
|
282
|
+
<th class="tdr"><%=_"Earned"%></th>
|
283
|
+
</tr>
|
284
|
+
</thead>
|
285
|
+
<tbody>
|
286
|
+
<%
|
287
|
+
stats[:projects].each do |project_id, project_data|
|
288
|
+
if project_id.to_i > 0
|
289
|
+
project = _ob.get(:Project, project_id)
|
290
|
+
else
|
291
|
+
project = nil
|
292
|
+
end
|
293
|
+
|
294
|
+
%>
|
295
|
+
<tr>
|
296
|
+
<td>
|
297
|
+
<%
|
298
|
+
if project
|
299
|
+
print project.html
|
300
|
+
else
|
301
|
+
print "[#{_("no project")}]"
|
302
|
+
end
|
303
|
+
%>
|
304
|
+
</td>
|
305
|
+
<td class="tdr">
|
306
|
+
<%=Knj::Locales.number_out(project_data[:time].to_f / 3600, 2)%>
|
307
|
+
</td>
|
308
|
+
<td class="tdr">
|
309
|
+
<%=Knj::Locales.number_out(project_data[:earned_transport].to_f, 2)%>
|
310
|
+
</td>
|
311
|
+
<td class="tdr">
|
312
|
+
<%=Knj::Locales.number_out(project_data[:earned_total], 2)%>
|
313
|
+
</td>
|
314
|
+
</tr>
|
315
|
+
<%
|
316
|
+
end
|
317
|
+
%>
|
318
|
+
</tbody>
|
319
|
+
</table>
|
320
|
+
<%=_site.boxb%>
|