327 lines
12 KiB
Cheetah
327 lines
12 KiB
Cheetah
[%# "Dry run" of SuperWorktime
|
||
# License: Dual-license GPL 3.0+ or MPL 1.1+
|
||
# Author: Vitaliy Filippov <vitalif@mail.ru>
|
||
#%]
|
||
|
||
[% title = "Массовый ввод трудозатрат" %]
|
||
[% PROCESS global/header.html.tmpl
|
||
title = title
|
||
javascript_urls = [ "js/calendar.js", "js/change-columns.js" ]
|
||
style_urls = [ "skins/standard/calendar.css", "skins/standard/buglist.css" ]
|
||
%]
|
||
|
||
<p>
|
||
Данная форма используется для массового ввода трудозатрат[% IF wt_admin %],
|
||
в том числе задним числом и от имени нескольких пользователей[% END %].<br />
|
||
Для введения трудозатрат задним числом и от имени других пользователей
|
||
требуется членство в группе <b>worktimeadmin</b>.
|
||
</p>
|
||
|
||
[% hidden_fields = {
|
||
'chfieldfrom' => 1
|
||
'chfieldto' => 1
|
||
'chfieldwho' => 1
|
||
'period_from' => 1
|
||
'period_to' => 1
|
||
'period_who' => 1
|
||
'worktime_user' => 3
|
||
'worktime_date' => 3
|
||
'save_worktime' => 3
|
||
'token' => 3
|
||
'format' => 3
|
||
'comment' => 2
|
||
'divide_min_inc' => 2
|
||
'divide_other_bug_id' => 2
|
||
} %]
|
||
|
||
[% for_all_users = "за всех участников" %]
|
||
|
||
[% MACRO hidden_inputs BLOCK %]
|
||
[% FOR k = query_params.keys %]
|
||
[% IF !hidden_fields.$k || hidden_fields.$k == HideIndex %]
|
||
[% IF query_params.$k.size %]
|
||
[% FOR vk = query_params.$k %]
|
||
<input type="hidden" name="[% k | html %]" value="[% vk | html %]" />
|
||
[% END %]
|
||
[% ELSE %]
|
||
<input type="hidden" name="[% k | html %]" value="[% query_params.$k | html %]" />
|
||
[% END %]
|
||
[% END %]
|
||
[% END %]
|
||
[% END %]
|
||
|
||
<fieldset>
|
||
<legend>Выборка трудозатрат:</legend>
|
||
<form action="?" method="POST">
|
||
<input type="hidden" name="format" value="superworktime" />
|
||
|
||
[% hidden_inputs(HideIndex=2) %]
|
||
|
||
<p style="margin: 8px 0">
|
||
Баг изменён:
|
||
с <input type="text" name="chfieldfrom" id="chfieldfrom" value="[% query_params.chfieldfrom | html %]" onchange="check_who_enabled()" />
|
||
по <input type="text" name="chfieldto" id="chfieldto" value="[% query_params.chfieldto | html %]" onchange="check_who_enabled()" />
|
||
<span style="color: #aaa">(YYYY-MM-DD HH:MM:SS)</span>
|
||
пользователем: <input type="text" name="chfieldwho" id="chfieldwho" value="[% query_params.chfieldwho | html %]" />
|
||
</p>
|
||
<p style="margin: 0">
|
||
Period Worktime:
|
||
с <input type="text" name="period_from" id="period_from" value="[% query_params.period_from || query_params.period_to || query_params.period_who ? query_params.period_from : query_params.chfieldfrom | html %]" />
|
||
по <input type="text" name="period_to" id="period_to" value="[% query_params.period_from || query_params.period_to || query_params.period_who ? query_params.period_to : query_params.chfieldto | html %]" />
|
||
<span style="color: #aaa">(YYYY-MM-DD HH:MM:SS)</span>
|
||
пользователь: <input type="text" name="period_who" id="period_who" value="[% query_params.period_from || query_params.period_to || query_params.period_who ? query_params.period_who : query_params.chfieldwho | html %]" />
|
||
<input type="submit" value=" Показать трудозатраты " />
|
||
</p>
|
||
</form>
|
||
</fieldset>
|
||
|
||
[% search_description | none %]
|
||
|
||
<hr />
|
||
|
||
[% IF bugs.size > 9 %]
|
||
<p class="bz_result_count">
|
||
[% bugs.size %] [%+ terms.bugs %] found.
|
||
</p>
|
||
[% END %]
|
||
|
||
<form action="?" method="POST" onsubmit="wt_user_check()">
|
||
|
||
<input type="hidden" name="format" value="superworktime" />
|
||
<input type="hidden" name="save_worktime" value="1" />
|
||
<input type="hidden" name="token" value="[% token | html %]" />
|
||
[% hidden_inputs(HideIndex=1) %]
|
||
|
||
[% BLOCK worktime_th %]
|
||
<th>Списать время</th>
|
||
[% END %]
|
||
|
||
[% BLOCK worktime_td %]
|
||
<td[% IF bug.product_notimetracking %] style="background-color: #FFC0C0"[% END %]>
|
||
<input type="text" style="text-align: right" name="wtime_[% bug.bug_id %]"
|
||
id="wtime_[% bug.bug_id %]" value="[% worktimes.${bug.bug_id} || '' %]"
|
||
onkeypress="refresh_total_wt()" onchange="refresh_total_wt()" />
|
||
</td>
|
||
[% END %]
|
||
|
||
[% BLOCK worktime_total %]
|
||
<td id="wtime_total" class="bz_total">0</td>
|
||
[% END %]
|
||
|
||
[% SET bug_table_callback = "worktime_td" %]
|
||
[% SET bug_header_callback = "worktime_th" %]
|
||
[% SET bug_time_summary_line_callback = "worktime_total" %]
|
||
[% PROCESS list/table.html.tmpl %]
|
||
|
||
<fieldset>
|
||
<legend>Списать время:</legend>
|
||
[% IF wt_admin %]
|
||
На дату: <input type="text" name="worktime_date" value="[% worktime_date | html %]" /> <span style="color: #aaa">(YYYY-MM-DD HH:MM:SS)</span>
|
||
За пользователя:
|
||
<input type="hidden" name="worktime_user" id="worktime_user_real" value="" />
|
||
<input type="text" id="worktime_user" value="[% worktime_user | html %]"
|
||
onfocus="wt_user_focus()" onblur="wt_user_blur()" />
|
||
[% ELSE %]
|
||
На дату: <b>[% worktime_date | html %]</b>
|
||
За пользователя: <b>[%+ Bugzilla.user.login | html %]</b>
|
||
[% END %]
|
||
<input value=" Списать время " type="submit" style="font-weight: bold" />
|
||
<input type="hidden" name="dry_run" id="dry_run" value="" />
|
||
<input value=" Предпросмотр " type="button" onclick="do_dry_run(this.form)" />
|
||
<br />
|
||
Добавить комментарий: <input type="text" name="comment" value="[% comment | html %]" size="50" />
|
||
[% IF wt_admin %]
|
||
<p style="margin: 8px 0 0 0">
|
||
<span id="divide_other_bug_id_text">Распределить пропорционально участию в одном баге:</span>
|
||
<input type="text" name="divide_other_bug_id" id="divide_other_bug_id" size="6" value="" />
|
||
<span id="divide_other_bug_id_text_2">(пропорционально участию в каждом, если пусто)</span>
|
||
</p>
|
||
<p style="margin: 8px 0 0 0">
|
||
<input type="checkbox" name="move_time" id="move_time" value="1" onclick="check_move(this)" />
|
||
<label for="move_time">
|
||
Перенести время с указанного бага на баги в списке – на баги списка будет списано
|
||
в сумме ровно столько часов, сколько было затрачено за заданный период на баг, с которого переносится время,
|
||
пропорционально введённым значениям времени.
|
||
</label>
|
||
</p>
|
||
[% END %]
|
||
</fieldset>
|
||
|
||
<fieldset id="divide_hours_cont">
|
||
<legend>Распределение часов по багам:</legend>
|
||
Сумма: <input type="text" id="divide_hours" onchange="divide_hours_check()" onkeypress="divide_hours_check()" />
|
||
минимум по: <input type="text" id="divide_min_inc" name="divide_min_inc" value="0.1" />
|
||
<input value=" Распределить часы " type="button" onclick="divide_hours_click()" />
|
||
(пропорционально / равномерно, будут добавлены к значениям в таблице)
|
||
</fieldset>
|
||
|
||
</form>
|
||
|
||
<script type="text/javascript">
|
||
<!--
|
||
var period_times = { [% FOREACH bug = bugs %][% IF NOT loop.first %],[% END %]'[% bug.bug_id %]':[% bug.interval_time || 0 %][% END %] };
|
||
var period_times_sorted;
|
||
function divide_hours_click()
|
||
{
|
||
var min_inc = bzParseTime(document.getElementById('divide_min_inc').value);
|
||
var sum = bzParseTime(document.getElementById('divide_hours').value);
|
||
if (!sum || sum != sum)
|
||
{
|
||
alert('Нечего распределять! Введите число, время в формате HH:MM, или в днях 1d, 2d, 3d и т.п.');
|
||
return;
|
||
}
|
||
if (!period_times_sorted)
|
||
{
|
||
// Чтобы сумма списываемых часов сходилась, а результаты были логичными
|
||
// (т.е. чтобы в баг с бОльшим временем попадало большее время),
|
||
// пропорциональное распределение лучше делать в порядке от меньших
|
||
// значений к бОльшим. Посему сортируем список багов.
|
||
period_times_sorted = [];
|
||
for (var i in period_times)
|
||
{
|
||
period_times_sorted.push(i);
|
||
}
|
||
period_times_sorted.sort(function(a, b)
|
||
{
|
||
return period_times[a] < period_times[b] ? -1 : (period_times[a] > period_times[b] ? 1 : 0);
|
||
});
|
||
}
|
||
var period_sum = 0, period_count = 0;
|
||
for (var i in period_times)
|
||
{
|
||
period_sum += period_times[i];
|
||
period_count++;
|
||
}
|
||
var cur, inc, ed, i;
|
||
var total_inc = 0;
|
||
for (var ii in period_times_sorted)
|
||
{
|
||
i = period_times_sorted[ii];
|
||
ed = document.getElementById('wtime_'+i);
|
||
cur = bzParseTime(ed.value);
|
||
if (!cur || cur != cur)
|
||
cur = 0;
|
||
if (period_sum == 0)
|
||
inc = sum/period_count;
|
||
else
|
||
inc = sum*period_times[i]/period_sum;
|
||
inc = Math.round(inc*100)/100;
|
||
if (inc < min_inc && inc > -min_inc)
|
||
{
|
||
// не списываем время совсем уж мелкими суммами
|
||
period_count--;
|
||
if (period_sum != 0)
|
||
period_sum -= period_times[i];
|
||
continue;
|
||
}
|
||
// Следующие строчки - извратик, призванный корректировать
|
||
// возникающую ошибку округления, чтобы реальная сумма
|
||
// списываемых часов была более близка к заданной
|
||
sum -= inc;
|
||
period_count--;
|
||
period_sum -= period_times[i];
|
||
cur += inc;
|
||
total_inc += inc;
|
||
cur = Math.round(cur*100)/100;
|
||
ed.value = cur;
|
||
}
|
||
refresh_total_wt();
|
||
}
|
||
function divide_hours_check()
|
||
{
|
||
var val = document.getElementById('divide_hours').value;
|
||
var sum = bzParseTime(val);
|
||
var incorrect = val !== '' && (sum != sum || sum === null || sum === undefined);
|
||
document.getElementById('divide_hours_cont').style.backgroundColor = incorrect ? '#ffe0e0' : '';
|
||
}
|
||
function wt_user_blur()
|
||
{
|
||
var ed = document.getElementById('worktime_user');
|
||
if (!ed.value)
|
||
{
|
||
ed.value = '[% for_all_users %]';
|
||
ed.style.color = 'red';
|
||
document.getElementById('divide_other_bug_id').value = '';
|
||
}
|
||
document.getElementById('divide_other_bug_id_text').style.color = ed.value ? 'gray' : '';
|
||
document.getElementById('divide_other_bug_id_text_2').style.color = ed.value ? 'gray' : '';
|
||
document.getElementById('divide_other_bug_id').disabled = ed.value ? true : false;
|
||
}
|
||
function wt_user_focus()
|
||
{
|
||
var ed = document.getElementById('worktime_user');
|
||
if (ed.value == '[% for_all_users %]')
|
||
{
|
||
ed.value = '';
|
||
ed.style.color = '';
|
||
}
|
||
}
|
||
function wt_user_check()
|
||
{
|
||
var ed = document.getElementById('worktime_user');
|
||
document.getElementById('worktime_user_real').value = ed.value == '[% for_all_users %]' ? '' : ed.value;
|
||
}
|
||
function refresh_total_wt()
|
||
{
|
||
var sum = 0;
|
||
for (var i in period_times)
|
||
{
|
||
ed = document.getElementById('wtime_'+i);
|
||
cur = bzParseTime(ed.value);
|
||
if (cur == cur && cur)
|
||
sum += cur;
|
||
}
|
||
sum = Math.round(sum*100)/100;
|
||
document.getElementById('wtime_total').innerHTML = sum;
|
||
}
|
||
function check_who_enabled()
|
||
{
|
||
var f = document.getElementById('chfieldfrom');
|
||
var t = document.getElementById('chfieldto');
|
||
var w = document.getElementById('chfieldwho');
|
||
var enab = f.value != '' || t.value != '';
|
||
w.style.backgroundColor = enab ? '' : '#f0f0f0';
|
||
w.disabled = !enab;
|
||
}
|
||
function check_move(c)
|
||
{
|
||
if (c.checked)
|
||
{
|
||
var e = true;
|
||
for (var i in period_times)
|
||
{
|
||
if (document.getElementById('wtime_'+i).value != '')
|
||
{
|
||
e = false;
|
||
break;
|
||
}
|
||
}
|
||
if (e)
|
||
{
|
||
for (var i in period_times)
|
||
document.getElementById('wtime_'+i).value = '1';
|
||
}
|
||
}
|
||
}
|
||
function do_dry_run(frm)
|
||
{
|
||
wt_user_check();
|
||
frm.dry_run.value = '1';
|
||
frm.target = '_blank';
|
||
frm.submit();
|
||
frm.dry_run.value = '';
|
||
frm.target = '';
|
||
}
|
||
wt_user_blur();
|
||
check_who_enabled();
|
||
Calendar.set('chfieldfrom');
|
||
Calendar.set('chfieldto');
|
||
new SimpleAutocomplete("chfieldwho", userAutocomplete, { emptyText: 'No users found' });
|
||
Calendar.set('period_from');
|
||
Calendar.set('period_to');
|
||
new SimpleAutocomplete("period_who", userAutocomplete, { emptyText: 'No users found' });
|
||
//-->
|
||
</script>
|
||
|
||
[% PROCESS global/footer.html.tmpl %]
|