forked from extern/egroupware
WIP timesheet timers: allow to add more time on an existing timesheet with right click "Start timer"
This commit is contained in:
parent
03fdceb847
commit
d8e993dc75
@ -59,7 +59,8 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
|
||||
// initiate specific timer, only if running or paused
|
||||
if (_state.specific?.start || _state.specific?.paused)
|
||||
{
|
||||
startTimer(specific, _state.specific?.start, _state.specific?.offset); // to show offset / paused time
|
||||
startTimer(specific, _state.specific?.start, _state.specific?.offset, _state.specific.app_id); // to show offset / paused time
|
||||
specific.id = _state.specific.id;
|
||||
if (_state.specific?.paused)
|
||||
{
|
||||
stopTimer(specific, true);
|
||||
@ -126,10 +127,25 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
|
||||
break;
|
||||
}
|
||||
// persist state
|
||||
egw.request('timesheet.EGroupware\\Timesheet\\Events.ajax_event', [getState(_action, _time)]).then(() => {
|
||||
egw.request('timesheet.EGroupware\\Timesheet\\Events.ajax_event', [getState(_action, _time)]).then((tse_id) => {
|
||||
if (_action.substring(8) === 'specific')
|
||||
{
|
||||
specific.id = tse_id;
|
||||
}
|
||||
if (_action === 'specific-stop')
|
||||
{
|
||||
egw.open(null, 'timesheet', 'add', {events: 'specific'});
|
||||
let type = 'add';
|
||||
let extra = {events: 'specific'};
|
||||
if (specific.app_id && specific.app_id.substring(0, 11) === 'timesheet::')
|
||||
{
|
||||
extra.ts_id = specific.app_id.substring(11);
|
||||
type = 'edit';
|
||||
}
|
||||
egw.open(null, 'timesheet', type, extra);
|
||||
|
||||
// unset the app_id and the tse_id to not associate the next start with it
|
||||
specific.app_id = undefined;
|
||||
specific.id = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -231,8 +247,9 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
|
||||
* @param object _timer
|
||||
* @param string|Date|undefined _start to initialise with time different from current time
|
||||
* @param number|undefined _offset to set an offset
|
||||
* @param string|undefined _app_id
|
||||
*/
|
||||
function startTimer(_timer, _start, _offset)
|
||||
function startTimer(_timer, _start, _offset, _app_id)
|
||||
{
|
||||
// update _timer state object
|
||||
if (_start)
|
||||
@ -249,6 +266,7 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
|
||||
}
|
||||
_timer.offset = 0; // it's now set in start-time
|
||||
_timer.paused = false;
|
||||
_timer.app_id = _app_id;
|
||||
|
||||
// update now
|
||||
update();
|
||||
@ -311,6 +329,44 @@ egw.extend('timer', egw.MODULE_GLOBAL, function()
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Start timer for given app and id
|
||||
*
|
||||
* @param {Object} _action
|
||||
* @param {Array} _senders
|
||||
*/
|
||||
start_timer(_action, _senders)
|
||||
{
|
||||
if (_action.parent.data.nextmatch?.getSelection().all || _senders.length !== 1)
|
||||
{
|
||||
egw.message(egw.lang('You must select a single entry!'), 'error');
|
||||
return;
|
||||
}
|
||||
// timer already running, ask user if he wants to associate it with the entry, or cancel
|
||||
if (specific.start || specific.paused)
|
||||
{
|
||||
Et2Dialog.show_dialog((_button) => {
|
||||
if (_button === Et2Dialog.OK_BUTTON)
|
||||
{
|
||||
if (specific.paused)
|
||||
{
|
||||
startTimer(specific, undefined, undefined, _senders[0].id);
|
||||
}
|
||||
else
|
||||
{
|
||||
specific.app_id = _senders[0].id;
|
||||
egw.request('timesheet.EGroupware\\Timesheet\\Events.ajax_updateAppId', [specific.id, specific.app_id]);
|
||||
}
|
||||
}
|
||||
},
|
||||
egw.lang('Do you want to associate it with the selected %1 entry?', egw.lang(_senders[0].id.split('::')[0])),
|
||||
egw.lang('Timer already running or paused'), {},
|
||||
Et2Dialog.BUTTONS_OK_CANCEL, Et2Dialog.QUESTION_MESSAGE, undefined, egw);
|
||||
return;
|
||||
}
|
||||
startTimer(specific, undefined, undefined, _senders[0].id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create timer in top-menu
|
||||
*
|
||||
|
@ -82,27 +82,57 @@ class timesheet_ui extends timesheet_bo
|
||||
{
|
||||
$this->data = array(
|
||||
'ts_start' => $this->today,
|
||||
'start_time' => '', // force empty start-time
|
||||
'end_time' => Api\DateTime::to($this->now,'H:i'),
|
||||
'start_time' => '', // force empty start-time
|
||||
'end_time' => Api\DateTime::to($this->now, 'H:i'),
|
||||
'ts_owner' => $GLOBALS['egw_info']['user']['account_id'],
|
||||
'cat_id' => (int) $_REQUEST['cat_id'],
|
||||
'ts_status'=> $GLOBALS['egw_info']['user']['preferences']['timesheet']['predefined_status'],
|
||||
'cat_id' => (int)$_REQUEST['cat_id'],
|
||||
'ts_status' => $GLOBALS['egw_info']['user']['preferences']['timesheet']['predefined_status'],
|
||||
'ts_project' => $_REQUEST['ts_project'],
|
||||
'ts_title_blur' => $_REQUEST['ts_project'],
|
||||
'events' => [],
|
||||
);
|
||||
if(!is_numeric($_REQUEST['ts_project']))
|
||||
if (!is_numeric($_REQUEST['ts_project']))
|
||||
{
|
||||
$this->data['pm_id'] = $this->find_pm_id($_REQUEST['ts_project']);
|
||||
}
|
||||
if (isset($_REQUEST['events']))
|
||||
}
|
||||
// are we supposed to add pending events, to a new or an existing timesheet
|
||||
if (isset($_REQUEST['events']))
|
||||
{
|
||||
$pending = Events::getPending($_REQUEST['events'] === 'overall', $time);
|
||||
$this->data['events'] = array_merge($this->data['events'], array_values($pending));
|
||||
$start = $this->data['events'][0]['tse_time'];
|
||||
$this->data['ts_start'] = $start;
|
||||
$this->data['start_time'] = Api\DateTime::server2user($start, 'H:s');
|
||||
$this->data['end_time'] = '';
|
||||
$this->data['ts_duration'] = (int)$this->data['ts_duration'] + round($time / 60); // minutes
|
||||
$this->data['ts_quantity'] = (float)$this->data['ts_quantity'] + $this->data['ts_duration'] / 60.0; // hours
|
||||
// check if any of the events contains an app::id to link the timesheet to
|
||||
foreach($pending as $event)
|
||||
{
|
||||
$this->data['events'] = array_values(Events::getPending($_REQUEST['events'] === 'overall', $time));
|
||||
$start = $this->data['events'][0]['tse_time'];
|
||||
$this->data['ts_start'] = $start;
|
||||
$this->data['start_time'] = Api\DateTime::server2user($start, 'H:s');
|
||||
$this->data['end_time'] = '';
|
||||
$this->data['ts_duration'] = round($time / 60); // minutes
|
||||
$this->data['ts_quantity'] = $this->data['ts_duration'] / 60.0; // hours
|
||||
if (!empty($event['tse_app']) && $event['tse_app'] !== TIMESHEET_APP && !empty($event['tse_app_id']))
|
||||
{
|
||||
// existing timesheets can be directly linked (takes care to not link multiple times)
|
||||
if ($this->data['ts_id'])
|
||||
{
|
||||
Api\Link::link(TIMESHEET_APP, $this->data['ts_id'], $event['tse_app'], $event['tse_app_id']);
|
||||
}
|
||||
// new timesheets will be linked on saving (need to check to not add multiple times)
|
||||
else
|
||||
{
|
||||
if (!isset($this->data['link_to']['to_id']))
|
||||
{
|
||||
$this->data['link_to']['to_id'] = [];
|
||||
}
|
||||
if (!in_array($app_id = [
|
||||
'to_app' => $event['tse_app'],
|
||||
'to_id' => $event['tse_app_id'],
|
||||
], $this->data['link_to']['to_id']))
|
||||
{
|
||||
$this->data['link_to']['to_id'][] = $app_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($this->data['events']))
|
||||
@ -288,7 +318,8 @@ class timesheet_ui extends timesheet_bo
|
||||
{
|
||||
Link::link(TIMESHEET_APP,$this->data['ts_id'],$content['link_to']['to_id']);
|
||||
}
|
||||
if (empty($content['ts_id']) && !empty($content['events']))
|
||||
// associate events with the now stored timesheet (need to run for existing timesheets too, if new events are added!)
|
||||
if (!empty($content['events']))
|
||||
{
|
||||
Events::addToTimesheet($this->data['ts_id'], array_map(static function($event)
|
||||
{
|
||||
@ -1117,6 +1148,19 @@ class timesheet_ui extends timesheet_bo
|
||||
'disableClass' => 'th',
|
||||
);
|
||||
}
|
||||
// if specific timer is NOT disabled, allow to book further time on existing sheets
|
||||
$config = Api\Config::read(TIMESHEET_APP);
|
||||
if (!in_array('specific', $config['disable_timer'] ?? []))
|
||||
{
|
||||
$actions['timer'] = array(
|
||||
'icon' => 'timesheet/navbar',
|
||||
'caption' => 'Start timer',
|
||||
'onExecute' => 'javaScript:app.timesheet.egw.start_timer',
|
||||
'allowOnMultiple' => false,
|
||||
'group' => $group,
|
||||
'disableClass' => 'th',
|
||||
);
|
||||
}
|
||||
$group++;
|
||||
$actions += array(
|
||||
'documents' => timesheet_merge::document_action(
|
||||
|
@ -29,6 +29,7 @@ deletes this field timesheet de Dieses Feld löschen
|
||||
determines the order the fields are displayed timesheet de verändert die Reihenfolge der angezeigten Felder
|
||||
directory with documents to insert entries timesheet de Ordner mit Dokumenten zum Einfügen.
|
||||
disable timers timesheet de Zeitnehmer ausschalten
|
||||
do you want to associate it with the selected %1 entry? common de Wollen Sie ihn mit dem ausgewählten %1 Eintrag verbinden?
|
||||
each value is a line like <id>[=<label>] timesheet de jeder Wert ist eine Zeile in dem Format <id>[=|label>]
|
||||
edit status common de Status bearbeiten
|
||||
edit this entry timesheet de diesen Eintrag bearbeiten
|
||||
@ -129,6 +130,7 @@ show a quantity sum (eg. to sum up negative overtime) admin de Zeige eine Mengen
|
||||
skip record timesheet de Eintrag überspringen
|
||||
start timesheet de Start
|
||||
start & stop timer common de Zeitnehmer starten & stoppen
|
||||
start timer timesheet de Zeitnehmer starten
|
||||
starttime timesheet de Startzeit
|
||||
starttime has to be before endtime !!! timesheet de Startzeit muss vor der Endzeit liegen !!!
|
||||
status timesheet de Status
|
||||
@ -149,6 +151,7 @@ this week timesheet de Diese Woche
|
||||
this year timesheet de Dieses Jahr
|
||||
ticket modified by %1 at %2 timesheet de Ticket geändert von %1 am %2
|
||||
timer common de Zeitnehmer
|
||||
timer already running or paused common de Zeitnehmer läuft bereits oder ist pausiert
|
||||
timesheet common de Stundenzettel
|
||||
timesheet csv export timesheet de Export des Stundenzettels als CSV
|
||||
timesheet csv import timesheet de Stundenzettels CSV Import
|
||||
|
@ -29,6 +29,7 @@ deletes this field timesheet en Deletes this field
|
||||
determines the order the fields are displayed timesheet en Determines the order the fields are displayed
|
||||
directory with documents to insert entries timesheet en Directory with documents to insert entries
|
||||
disable timers timesheet en Disable timers
|
||||
do you want to associate it with the selected %1 entry? common en Do you want to associate it with the selected %1 entry?
|
||||
each value is a line like <id>[=<label>] timesheet en Each value is a line like <id>[=<label>]
|
||||
edit status common en Edit status
|
||||
edit this entry timesheet en Edit this entry
|
||||
@ -129,6 +130,7 @@ show a quantity sum (eg. to sum up negative overtime) admin en Show a quantity s
|
||||
skip record timesheet en Skip record
|
||||
start timesheet en Start
|
||||
start & stop timer common en Start & stop timer
|
||||
start timer timesheet en Start timer
|
||||
starttime timesheet en Start time
|
||||
starttime has to be before endtime !!! timesheet en Start time has to be before endtime!
|
||||
status timesheet en Status
|
||||
@ -149,6 +151,7 @@ this week timesheet en This week
|
||||
this year timesheet en This year
|
||||
ticket modified by %1 at %2 timesheet en Ticket modified by %1 at %2
|
||||
timer common en Timer
|
||||
timer already running or paused common en Timer already running or paused
|
||||
timesheet common en Time Sheet
|
||||
timesheet csv export timesheet en TimeSheet CSV export
|
||||
timesheet csv import timesheet en TimeSheet CSV import
|
||||
|
@ -75,6 +75,7 @@ class Events extends Api\Storage\Base
|
||||
* Record/persist timer events from UI
|
||||
*
|
||||
* @param array $state
|
||||
* @return int tse_id of created event
|
||||
* @throws Api\Exception
|
||||
* @throws Api\Exception\WrongParameter
|
||||
*/
|
||||
@ -92,6 +93,16 @@ class Events extends Api\Storage\Base
|
||||
$type = ($timer === 'overall' ? self::OVERALL : 0) |
|
||||
($action === 'start' ? self::START : ($action === 'stop' ? self::STOP : self::PAUSE));
|
||||
|
||||
$app = $id = $ts_id = null;
|
||||
if ($timer === 'specific' && !empty($state['app_id']))
|
||||
{
|
||||
list($app, $id) = explode('::', $state['app_id'], 2);
|
||||
if ($app === self::APP)
|
||||
{
|
||||
$ts_id = $id;
|
||||
}
|
||||
}
|
||||
|
||||
$this->init();
|
||||
$this->save([
|
||||
'tse_timestamp' => new Api\DateTime(),
|
||||
@ -99,6 +110,9 @@ class Events extends Api\Storage\Base
|
||||
'account_id' => $this->user,
|
||||
'tse_modifier' => $this->user,
|
||||
'tse_type' => $type,
|
||||
'tse_app' => $app,
|
||||
'tse_app_id' => $id,
|
||||
'ts_id' => $ts_id,
|
||||
]);
|
||||
|
||||
// create timesheet for stopped working time
|
||||
@ -113,6 +127,27 @@ class Events extends Api\Storage\Base
|
||||
Api\Json\Response::get()->message(lang('Error storing working time').': '.$e->getMessage(), 'error');
|
||||
}
|
||||
}
|
||||
return $this->data['tse_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set app::id on an already started timer
|
||||
*
|
||||
* @param int $tse_id id of the running timer-event
|
||||
* @param string $app_id "app::id" string
|
||||
* @return void
|
||||
* @throws Api\Db\Exception\InvalidSql
|
||||
*/
|
||||
public function ajax_updateAppId(int $tse_id, string $app_id)
|
||||
{
|
||||
if ($tse_id > 0 && !empty($app_id))
|
||||
{
|
||||
list($app, $id) = explode('::', $app_id);
|
||||
$this->db->update(self::TABLE, [
|
||||
'tse_app' => $app,
|
||||
'tse_app_id' => $id,
|
||||
], ['tse_id' => $tse_id], __LINE__, __FILE__, self::APP);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -184,6 +219,21 @@ class Events extends Api\Storage\Base
|
||||
else
|
||||
{
|
||||
self::evaluate($state['specific'], $row);
|
||||
|
||||
// if event is associated with an app:id, set it again
|
||||
if ($row['tse_app'] && $row['tse_app_id'])
|
||||
{
|
||||
$state['specific']['app_id'] = $row['tse_app'].'::'.$row['tse_app_id'];
|
||||
}
|
||||
// for not stopped events, set the tse_id, so we can associate with an "app:id"
|
||||
if ($row['tse_type'] & self::STOP)
|
||||
{
|
||||
$state['specific']['id'] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$state['specific']['id'] = $row['tse_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
// format start-times in UTZ as JavaScript Date() understands
|
||||
|
Loading…
Reference in New Issue
Block a user