Move timetable to a plugin
Plugin repository: https://github.com/kanboard/plugin-timetable
This commit is contained in:
parent
2021dccc5a
commit
e6f547abcf
|
|
@ -14,6 +14,7 @@ Core functionalities moved to plugins:
|
|||
|
||||
* Budget planning: https://github.com/kanboard/plugin-budget
|
||||
* SubtaskForecast: https://github.com/kanboard/plugin-subtask-forecast
|
||||
* Timetable: https://github.com/kanboard/plugin-timetable
|
||||
|
||||
Improvements:
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ Improvements:
|
|||
|
||||
Bug fixes:
|
||||
|
||||
* Fix typo in template that prevent the Gitlab oauth link to be displayed
|
||||
* Fix typo in template that prevent the Gitlab OAuth link to be displayed
|
||||
* Fix Markdown preview links focus
|
||||
* Avoid dropdown menu to be truncated inside a column with scrolling
|
||||
* Deleting subtask doesn't update task time tracking
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Timetable controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Timetable extends User
|
||||
{
|
||||
/**
|
||||
* Display timetable for the user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$from = $this->request->getStringParam('from', date('Y-m-d'));
|
||||
$to = $this->request->getStringParam('to', date('Y-m-d', strtotime('next week')));
|
||||
$timetable = $this->timetable->calculate($user['id'], new DateTime($from), new DateTime($to));
|
||||
|
||||
$this->response->html($this->layout('timetable/index', array(
|
||||
'user' => $user,
|
||||
'timetable' => $timetable,
|
||||
'values' => array(
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
'controller' => 'timetable',
|
||||
'action' => 'index',
|
||||
'user_id' => $user['id'],
|
||||
),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Day Timetable controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Timetableday extends User
|
||||
{
|
||||
/**
|
||||
* Display timetable for the user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('timetable_day/index', array(
|
||||
'timetable' => $this->timetableDay->getByUser($user['id']),
|
||||
'values' => $values + array('user_id' => $user['id']),
|
||||
'errors' => $errors,
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->timetableDay->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->timetableDay->create($values['user_id'], $values['start'], $values['end'])) {
|
||||
$this->session->flash(t('Time slot created successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('timetableday', 'index', array('user_id' => $values['user_id'])));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to save this time slot.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->index($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialag box to remove a row
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('timetable_day/remove', array(
|
||||
'slot_id' => $this->request->getIntegerParam('slot_id'),
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a row
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($this->timetableDay->remove($this->request->getIntegerParam('slot_id'))) {
|
||||
$this->session->flash(t('Time slot removed successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flash(t('Unable to remove this time slot.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('timetableday', 'index', array('user_id' => $user['id'])));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Over-time Timetable controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Timetableextra extends Timetableoff
|
||||
{
|
||||
protected $model = 'timetableExtra';
|
||||
protected $controller_url = 'timetableextra';
|
||||
protected $template_dir = 'timetable_extra';
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Time-off Timetable controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Timetableoff extends User
|
||||
{
|
||||
protected $model = 'timetableOff';
|
||||
protected $controller_url = 'timetableoff';
|
||||
protected $template_dir = 'timetable_off';
|
||||
|
||||
/**
|
||||
* Display timetable for the user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$paginator = $this->paginator
|
||||
->setUrl($this->controller_url, 'index', array('user_id' => $user['id']))
|
||||
->setMax(10)
|
||||
->setOrder('date')
|
||||
->setDirection('desc')
|
||||
->setQuery($this->{$this->model}->getUserQuery($user['id']))
|
||||
->calculate();
|
||||
|
||||
$this->response->html($this->layout($this->template_dir.'/index', array(
|
||||
'values' => $values + array('user_id' => $user['id']),
|
||||
'errors' => $errors,
|
||||
'paginator' => $paginator,
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->{$this->model}->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->{$this->model}->create(
|
||||
$values['user_id'],
|
||||
$values['date'],
|
||||
isset($values['all_day']) && $values['all_day'] == 1,
|
||||
$values['start'],
|
||||
$values['end'],
|
||||
$values['comment'])) {
|
||||
|
||||
$this->session->flash(t('Time slot created successfully.'));
|
||||
$this->response->redirect($this->helper->url->to($this->controller_url, 'index', array('user_id' => $values['user_id'])));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to save this time slot.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->index($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialag box to remove a row
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout($this->template_dir.'/remove', array(
|
||||
'slot_id' => $this->request->getIntegerParam('slot_id'),
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a row
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($this->{$this->model}->remove($this->request->getIntegerParam('slot_id'))) {
|
||||
$this->session->flash(t('Time slot removed successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flash(t('Unable to remove this time slot.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to($this->controller_url, 'index', array('user_id' => $user['id'])));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Week Timetable controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Timetableweek extends User
|
||||
{
|
||||
/**
|
||||
* Display timetable for the user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
if (empty($values)) {
|
||||
|
||||
$day = $this->timetableDay->getByUser($user['id']);
|
||||
|
||||
$values = array(
|
||||
'user_id' => $user['id'],
|
||||
'start' => isset($day[0]['start']) ? $day[0]['start'] : null,
|
||||
'end' => isset($day[0]['end']) ? $day[0]['end'] : null,
|
||||
);
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('timetable_week/index', array(
|
||||
'timetable' => $this->timetableWeek->getByUser($user['id']),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->timetableWeek->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->timetableWeek->create($values['user_id'], $values['day'], $values['start'], $values['end'])) {
|
||||
$this->session->flash(t('Time slot created successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('timetableweek', 'index', array('user_id' => $values['user_id'])));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to save this time slot.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->index($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialag box to remove a row
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->response->html($this->layout('timetable_week/remove', array(
|
||||
'slot_id' => $this->request->getIntegerParam('slot_id'),
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a row
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($this->timetableWeek->remove($this->request->getIntegerParam('slot_id'))) {
|
||||
$this->session->flash(t('Time slot removed successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flash(t('Unable to remove this time slot.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('timetableweek', 'index', array('user_id' => $user['id'])));
|
||||
}
|
||||
}
|
||||
|
|
@ -70,11 +70,6 @@ use Pimple\Container;
|
|||
* @property \Model\TaskPosition $taskPosition
|
||||
* @property \Model\TaskStatus $taskStatus
|
||||
* @property \Model\TaskValidator $taskValidator
|
||||
* @property \Model\Timetable $timetable
|
||||
* @property \Model\TimetableDay $timetableDay
|
||||
* @property \Model\TimetableExtra $timetableExtra
|
||||
* @property \Model\TimetableOff $timetableOff
|
||||
* @property \Model\TimetableWeek $timetableWeek
|
||||
* @property \Model\Transition $transition
|
||||
* @property \Model\User $user
|
||||
* @property \Model\UserSession $userSession
|
||||
|
|
|
|||
|
|
@ -46,6 +46,18 @@ class Hook
|
|||
return isset($this->hooks[$hook]) ? $this->hooks[$hook] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the hook is used
|
||||
*
|
||||
* @access public
|
||||
* @param string $hook
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists($hook)
|
||||
{
|
||||
return isset($this->hooks[$hook]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge listener results with input array
|
||||
*
|
||||
|
|
@ -67,4 +79,21 @@ class Hook
|
|||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute only first listener
|
||||
*
|
||||
* @access public
|
||||
* @param string $hook
|
||||
* @param array $params
|
||||
* @return mixed
|
||||
*/
|
||||
public function first($hook, array $params = array())
|
||||
{
|
||||
foreach ($this->getListeners($hook) as $listener) {
|
||||
return call_user_func_array($listener, $params);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class Loader extends \Core\Base
|
|||
$filename = __DIR__.'/../../../plugins/'.$plugin.'/Schema/'.ucfirst(DB_DRIVER).'.php';
|
||||
|
||||
if (file_exists($filename)) {
|
||||
require($filename);
|
||||
require_once($filename);
|
||||
$this->migrateSchema($plugin);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Kompaktní/plné zobrazení',
|
||||
'No results match:' => 'Žádná shoda:',
|
||||
'Currency' => 'Měna',
|
||||
'Start time' => 'Počáteční datum',
|
||||
'End time' => 'Konečné datum',
|
||||
'Comment' => 'Komentář',
|
||||
'All day' => 'Všechny dny',
|
||||
'Day' => 'Den',
|
||||
'Manage timetable' => 'Spravovat pracovní dobu',
|
||||
'Overtime timetable' => 'Přesčasy',
|
||||
'Time off timetable' => 'Pracovní volno',
|
||||
'Timetable' => 'Pracovní doba',
|
||||
'Work timetable' => 'Pracovní doba',
|
||||
'Week timetable' => 'Týdenní pracovní doba',
|
||||
'Day timetable' => 'Denní pracovní doba',
|
||||
'From' => 'Od',
|
||||
'To' => 'Do',
|
||||
'Time slot created successfully.' => 'Časový úsek byl úspěšně vytvořen.',
|
||||
'Unable to save this time slot.' => 'Nelze uložit tento časový úsek.',
|
||||
'Time slot removed successfully.' => 'Časový úsek byl odstraněn.',
|
||||
'Unable to remove this time slot.' => 'Nelze odstranit tento časový úsek',
|
||||
'Do you really want to remove this time slot?' => 'Opravdu chcete odstranit tento časový úsek?',
|
||||
'Remove time slot' => 'Odstranit časový úsek',
|
||||
'Add new time slot' => 'Přidat nový časový úsek',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Tato pracovní doba se použije když je zaškrtnuto políčko "Celý den" pro plánovanou pracovní dobu i přesčas .',
|
||||
'Files' => 'Soubory',
|
||||
'Images' => 'Obrázky',
|
||||
'Private project' => 'Soukromý projekt',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Přesunout úkol do jiného sloupce, když je úkol přiřazen uživateli.',
|
||||
'Move the task to another column when assignee is cleared' => 'Přesunout úkol do jiného sloupce, když je pověření uživatele vymazáno.',
|
||||
'Source column' => 'Zdrojový sloupec',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
'Transitions' => 'Změny etap',
|
||||
'Executer' => 'Vykonavatel',
|
||||
'Time spent in the column' => 'Trvání jednotlivých etap',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
// 'Compact/wide view' => '',
|
||||
// 'No results match:' => '',
|
||||
// 'Currency' => '',
|
||||
// 'Start time' => '',
|
||||
// 'End time' => '',
|
||||
// 'Comment' => '',
|
||||
// 'All day' => '',
|
||||
// 'Day' => '',
|
||||
// 'Manage timetable' => '',
|
||||
// 'Overtime timetable' => '',
|
||||
// 'Time off timetable' => '',
|
||||
// 'Timetable' => '',
|
||||
// 'Work timetable' => '',
|
||||
// 'Week timetable' => '',
|
||||
// 'Day timetable' => '',
|
||||
// 'From' => '',
|
||||
// 'To' => '',
|
||||
// 'Time slot created successfully.' => '',
|
||||
// 'Unable to save this time slot.' => '',
|
||||
// 'Time slot removed successfully.' => '',
|
||||
// 'Unable to remove this time slot.' => '',
|
||||
// 'Do you really want to remove this time slot?' => '',
|
||||
// 'Remove time slot' => '',
|
||||
// 'Add new time slot' => '',
|
||||
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
|
||||
// 'Files' => '',
|
||||
// 'Images' => '',
|
||||
// 'Private project' => '',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
// 'Move the task to another column when assigned to a user' => '',
|
||||
// 'Move the task to another column when assignee is cleared' => '',
|
||||
// 'Source column' => '',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
// 'Transitions' => '',
|
||||
// 'Executer' => '',
|
||||
// 'Time spent in the column' => '',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Kompakt/Breite-Ansicht',
|
||||
'No results match:' => 'Keine Ergebnisse:',
|
||||
'Currency' => 'Währung',
|
||||
'Start time' => 'Startzeit',
|
||||
'End time' => 'Endzeit',
|
||||
'Comment' => 'Kommentar',
|
||||
'All day' => 'ganztägig',
|
||||
'Day' => 'Tag',
|
||||
'Manage timetable' => 'Zeitplan verwalten',
|
||||
'Overtime timetable' => 'Überstunden Zeitplan',
|
||||
'Time off timetable' => 'Freizeit Zeitplan',
|
||||
'Timetable' => 'Zeitplan',
|
||||
'Work timetable' => 'Arbeitszeitplan',
|
||||
'Week timetable' => 'Wochenzeitplan',
|
||||
'Day timetable' => 'Tageszeitplan',
|
||||
'From' => 'von',
|
||||
'To' => 'bis',
|
||||
'Time slot created successfully.' => 'Zeitfenster erfolgreich erstellt.',
|
||||
'Unable to save this time slot.' => 'Nicht in der Lage, dieses Zeitfenster zu speichern.',
|
||||
'Time slot removed successfully.' => 'Zeitfenster erfolgreich entfernt.',
|
||||
'Unable to remove this time slot.' => 'Nicht in der Lage, dieses Zeitfenster zu entfernen',
|
||||
'Do you really want to remove this time slot?' => 'Soll diese Zeitfenster wirklich gelöscht werden?',
|
||||
'Remove time slot' => 'Zeitfenster entfernen',
|
||||
'Add new time slot' => 'Neues Zeitfenster hinzufügen',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Dieses Zeitfenster wird verwendet, wenn die Checkbox "ganztägig" für Freizeit und Überstunden angeklickt ist.',
|
||||
'Files' => 'Dateien',
|
||||
'Images' => 'Bilder',
|
||||
'Private project' => 'privates Projekt',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein User zugeordnet wurde.',
|
||||
'Move the task to another column when assignee is cleared' => 'Aufgabe in eine andere Spalte verschieben, wenn die Zuordnung gelöscht wurde.',
|
||||
'Source column' => 'Quellspalte',
|
||||
'Show subtask estimates (forecast of future work)' => 'Teilaufgaben-Schätzungen anzeigen (Prognose)',
|
||||
'Transitions' => 'Übergänge',
|
||||
'Executer' => 'Ausführender',
|
||||
'Time spent in the column' => 'Zeit in Spalte verbracht',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Vista compacta/amplia',
|
||||
'No results match:' => 'No hay resultados coincidentes:',
|
||||
'Currency' => 'Moneda',
|
||||
'Start time' => 'Tiempo de inicio',
|
||||
'End time' => 'Tiempo de fin',
|
||||
'Comment' => 'Comentario',
|
||||
'All day' => 'Todos los días',
|
||||
'Day' => 'Día',
|
||||
'Manage timetable' => 'Gestionar horario',
|
||||
'Overtime timetable' => 'Horario de tiempo extra',
|
||||
'Time off timetable' => 'Horario de tiempo libre',
|
||||
'Timetable' => 'Horario',
|
||||
'Work timetable' => 'Horario de trabajo',
|
||||
'Week timetable' => 'Horario semanal',
|
||||
'Day timetable' => 'Horario diario',
|
||||
'From' => 'De',
|
||||
'To' => 'Para',
|
||||
'Time slot created successfully.' => 'Intervalo de tiempo creado correctamente.',
|
||||
'Unable to save this time slot.' => 'No pude grabar este intervalo de tiempo.',
|
||||
'Time slot removed successfully.' => 'Intervalo de tiempo quitado correctamente.',
|
||||
'Unable to remove this time slot.' => 'No pude quitar este intervalo de tiempo.',
|
||||
'Do you really want to remove this time slot?' => '¿Realmente quiere quitar este intervalo de tiempo?',
|
||||
'Remove time slot' => 'Quitar intervalo de tiempo',
|
||||
'Add new time slot' => 'Añadir nuevo intervalo de tiempo',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Este horario se usa cuando se marca la casilla "todos los días" para calendario de tiempo libre y horas extras.',
|
||||
'Files' => 'Ficheros',
|
||||
'Images' => 'Imágenes',
|
||||
'Private project' => 'Proyecto privado',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columna al asignarse al usuario',
|
||||
'Move the task to another column when assignee is cleared' => 'Mover la tarea a otra columna al quitar el concesionario',
|
||||
'Source column' => 'Columna fuente',
|
||||
'Show subtask estimates (forecast of future work)' => 'Mostrar estimaciones para la subtarea (pronóstico de trabajo futuro)',
|
||||
'Transitions' => 'Transiciones',
|
||||
'Executer' => 'Ejecutor',
|
||||
'Time spent in the column' => 'Tiempo transcurrido en la columna',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
// 'Compact/wide view' => '',
|
||||
// 'No results match:' => '',
|
||||
// 'Currency' => '',
|
||||
// 'Start time' => '',
|
||||
// 'End time' => '',
|
||||
// 'Comment' => '',
|
||||
// 'All day' => '',
|
||||
// 'Day' => '',
|
||||
// 'Manage timetable' => '',
|
||||
// 'Overtime timetable' => '',
|
||||
// 'Time off timetable' => '',
|
||||
// 'Timetable' => '',
|
||||
// 'Work timetable' => '',
|
||||
// 'Week timetable' => '',
|
||||
// 'Day timetable' => '',
|
||||
// 'From' => '',
|
||||
// 'To' => '',
|
||||
// 'Time slot created successfully.' => '',
|
||||
// 'Unable to save this time slot.' => '',
|
||||
// 'Time slot removed successfully.' => '',
|
||||
// 'Unable to remove this time slot.' => '',
|
||||
// 'Do you really want to remove this time slot?' => '',
|
||||
// 'Remove time slot' => '',
|
||||
// 'Add new time slot' => '',
|
||||
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
|
||||
// 'Files' => '',
|
||||
// 'Images' => '',
|
||||
// 'Private project' => '',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
// 'Move the task to another column when assigned to a user' => '',
|
||||
// 'Move the task to another column when assignee is cleared' => '',
|
||||
// 'Source column' => '',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
// 'Transitions' => '',
|
||||
// 'Executer' => '',
|
||||
// 'Time spent in the column' => '',
|
||||
|
|
|
|||
|
|
@ -668,28 +668,6 @@ return array(
|
|||
'Compact/wide view' => 'Basculer entre la vue compacte et étendue',
|
||||
'No results match:' => 'Aucun résultat :',
|
||||
'Currency' => 'Devise',
|
||||
'Start time' => 'Date de début',
|
||||
'End time' => 'Date de fin',
|
||||
'Comment' => 'Commentaire',
|
||||
'All day' => 'Toute la journée',
|
||||
'Day' => 'Jour',
|
||||
'Manage timetable' => 'Gérer les horaires',
|
||||
'Overtime timetable' => 'Heures supplémentaires',
|
||||
'Time off timetable' => 'Heures d\'absences',
|
||||
'Timetable' => 'Horaires',
|
||||
'Work timetable' => 'Horaires travaillés',
|
||||
'Week timetable' => 'Horaires de la semaine',
|
||||
'Day timetable' => 'Horaire d\'une journée',
|
||||
'From' => 'Depuis',
|
||||
'To' => 'À',
|
||||
'Time slot created successfully.' => 'Créneau horaire créé avec succès.',
|
||||
'Unable to save this time slot.' => 'Impossible de sauvegarder ce créneau horaire.',
|
||||
'Time slot removed successfully.' => 'Créneau horaire supprimé avec succès.',
|
||||
'Unable to remove this time slot.' => 'Impossible de supprimer ce créneau horaire.',
|
||||
'Do you really want to remove this time slot?' => 'Voulez-vous vraiment supprimer ce créneau horaire ?',
|
||||
'Remove time slot' => 'Supprimer un créneau horaire',
|
||||
'Add new time slot' => 'Ajouter un créneau horaire',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ces horaires sont utilisés lorsque la case « Toute la journée » est cochée pour les heures d\'absences ou supplémentaires programmées.',
|
||||
'Files' => 'Fichiers',
|
||||
'Images' => 'Images',
|
||||
'Private project' => 'Projet privé',
|
||||
|
|
@ -709,7 +687,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci est assignée à quelqu\'un',
|
||||
'Move the task to another column when assignee is cleared' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci n\'est plus assignée',
|
||||
'Source column' => 'Colonne d\'origine',
|
||||
'Show subtask estimates (forecast of future work)' => 'Afficher l\'estimation des sous-tâches (prévision du travail à venir)',
|
||||
'Transitions' => 'Transitions',
|
||||
'Executer' => 'Exécutant',
|
||||
'Time spent in the column' => 'Temps passé dans la colonne',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Kompakt/széles nézet',
|
||||
'No results match:' => 'Nincs találat:',
|
||||
'Currency' => 'Pénznem',
|
||||
'Start time' => 'Kezdés ideje',
|
||||
'End time' => 'Végzés ideje',
|
||||
'Comment' => 'Megjegyzés',
|
||||
'All day' => 'Egész nap',
|
||||
'Day' => 'Nap',
|
||||
'Manage timetable' => 'Időbeosztás kezelése',
|
||||
'Overtime timetable' => 'Túlóra időbeosztás',
|
||||
'Time off timetable' => 'Szabadság időbeosztás',
|
||||
'Timetable' => 'Időbeosztás',
|
||||
'Work timetable' => 'Munka időbeosztás',
|
||||
'Week timetable' => 'Heti időbeosztás',
|
||||
'Day timetable' => 'Napi időbeosztás',
|
||||
'From' => 'Feladó:',
|
||||
'To' => 'Címzett:',
|
||||
'Time slot created successfully.' => 'Időszelet sikeresen létrehozva.',
|
||||
'Unable to save this time slot.' => 'Időszelet mentése sikertelen.',
|
||||
'Time slot removed successfully.' => 'Időszelet sikeresen törölve.',
|
||||
'Unable to remove this time slot.' => 'Időszelet törlése sikertelen.',
|
||||
'Do you really want to remove this time slot?' => 'Biztos törli ezt az időszeletet?',
|
||||
'Remove time slot' => 'Időszelet törlése',
|
||||
'Add new time slot' => 'Új Időszelet',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ez az időbeosztás van használatban ha az "egész nap" jelölőnégyzet be van jelölve a tervezett szabadságnál és túlóránál.',
|
||||
'Files' => 'Fájlok',
|
||||
'Images' => 'Képek',
|
||||
'Private project' => 'Privát projekt',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés után',
|
||||
'Move the task to another column when assignee is cleared' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés törlésekor',
|
||||
'Source column' => 'Forrás oszlop',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
// 'Transitions' => '',
|
||||
// 'Executer' => '',
|
||||
// 'Time spent in the column' => '',
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ return array(
|
|||
'Dark Grey' => 'Abu-abu Gelap',
|
||||
'Pink' => 'Merah Muda',
|
||||
'Teal' => 'Teal',
|
||||
'Cyan'=> 'Sian',
|
||||
'Cyan' => 'Sian',
|
||||
'Lime' => 'Lime',
|
||||
'Light Green' => 'Hijau Muda',
|
||||
'Amber' => 'Amber',
|
||||
|
|
@ -338,7 +338,7 @@ return array(
|
|||
'Display another project' => 'Lihat proyek lain',
|
||||
'Login with my Github Account' => 'Masuk menggunakan akun Github saya',
|
||||
'Link my Github Account' => 'Hubungkan akun Github saya ',
|
||||
'Unlink my Github Account' => 'Putuskan akun Github saya',
|
||||
'Unlink my Github Account' => 'Putuskan akun Github saya',
|
||||
'Created by %s' => 'Dibuat oleh %s',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Modifikasi terakhir pada tanggal %d/%m/%Y à %H:%M',
|
||||
'Tasks Export' => 'Ekspor Tugas',
|
||||
|
|
@ -365,14 +365,12 @@ return array(
|
|||
'Time tracking:' => 'Pelacakan waktu :',
|
||||
'New sub-task' => 'Sub-tugas baru',
|
||||
'New attachment added "%s"' => 'Lampiran baru ditambahkan « %s »',
|
||||
'Comment updated' => 'Komentar ditambahkan',
|
||||
'Comment updated' => 'Komentar diperbaharui',
|
||||
'New comment posted by %s' => 'Komentar baru ditambahkan oleh « %s »',
|
||||
'New attachment' => 'Lampirkan baru',
|
||||
'New comment' => 'Komentar baru',
|
||||
'Comment updated' => 'Komentar diperbaharui',
|
||||
'New subtask' => 'Sub-tugas baru',
|
||||
'Subtask updated' => 'Sub-tugas diperbaharui',
|
||||
'New task' => 'Tugas baru',
|
||||
'Task updated' => 'Tugas diperbaharui',
|
||||
'Task closed' => 'Tugas ditutup',
|
||||
'Task opened' => 'Tugas dibuka',
|
||||
|
|
@ -397,8 +395,6 @@ return array(
|
|||
'Remote' => 'Jauh',
|
||||
'Enabled' => 'Aktif',
|
||||
'Disabled' => 'Nonaktif',
|
||||
'Google account linked' => 'Akun Google yang terhubung',
|
||||
'Github account linked' => 'Akun Github yang terhubung',
|
||||
'Username:' => 'Nama pengguna :',
|
||||
'Name:' => 'Nama :',
|
||||
'Email:' => 'Email :',
|
||||
|
|
@ -669,75 +665,26 @@ return array(
|
|||
'Horizontal scrolling' => 'Horisontal bergulir',
|
||||
'Compact/wide view' => 'Beralih antara tampilan kompak dan diperluas',
|
||||
'No results match:' => 'Tidak ada hasil :',
|
||||
'Remove hourly rate' => 'Hapus tarif per jam',
|
||||
'Do you really want to remove this hourly rate?' => 'Apakah anda yakin akan menghapus tarif per jam ini?',
|
||||
'Hourly rates' => 'Tarif per jam',
|
||||
'Hourly rate' => 'Tarif per jam',
|
||||
'Currency' => 'Mata uang',
|
||||
'Effective date' => 'Tanggal berlaku',
|
||||
'Add new rate' => 'Tambah tarif per jam baru',
|
||||
'Rate removed successfully.' => 'Tarif per jam berhasil dihapus.',
|
||||
'Unable to remove this rate.' => 'Tidak dapat menghapus tarif per jam ini.',
|
||||
'Unable to save the hourly rate.' => 'Tidak dapat menyimpan tarif per jam.',
|
||||
'Hourly rate created successfully.' => 'Tarif per jam berhasil dibuat.',
|
||||
'Start time' => 'Waktu mulai',
|
||||
'End time' => 'Waktu selesai',
|
||||
'Comment' => 'Komentar',
|
||||
'All day' => 'Semua hari',
|
||||
'Day' => 'Hari',
|
||||
'Manage timetable' => 'Mengatur jadwal',
|
||||
'Overtime timetable' => 'Jadwal lembur',
|
||||
'Time off timetable' => 'Jam absensi',
|
||||
'Timetable' => 'Jadwal',
|
||||
'Work timetable' => 'Jadwal kerja',
|
||||
'Week timetable' => 'Jadwal mingguan',
|
||||
'Day timetable' => 'Jadwal harian',
|
||||
'From' => 'Dari',
|
||||
'To' => 'Untuk',
|
||||
'Time slot created successfully.' => 'Slot waktu berhasil dibuat.',
|
||||
'Unable to save this time slot.' => 'Tidak dapat menyimpan slot waktu ini.',
|
||||
'Time slot removed successfully.' => 'Slot waktu berhasil dihapus.',
|
||||
'Unable to remove this time slot.' => 'Tidak dapat menghapus slot waktu ini.',
|
||||
'Do you really want to remove this time slot?' => 'Apakah anda yakin akan menghapus slot waktu ini?',
|
||||
'Remove time slot' => 'Hapus slot waktu',
|
||||
'Add new time slot' => 'Tambah slot waktu baru',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Jadwal ini digunakan ketika kotak centang "sepanjang hari" dicentang untuk dijadwalkan cuti dan lembur.',
|
||||
'Files' => 'Arsip',
|
||||
'Images' => 'Gambar',
|
||||
'Private project' => 'Proyek pribadi',
|
||||
'Amount' => 'Jumlah',
|
||||
'AUD - Australian Dollar' => 'AUD - Dollar Australia',
|
||||
'Budget' => 'Anggaran',
|
||||
'Budget line' => 'Garis anggaran',
|
||||
'Budget line removed successfully.' => 'Garis anggaran berhasil dihapus.',
|
||||
'Budget lines' => 'Garis anggaran',
|
||||
'CAD - Canadian Dollar' => 'CAD - Dollar Kanada',
|
||||
'CHF - Swiss Francs' => 'CHF - Swiss Prancis',
|
||||
'Cost' => 'Biaya',
|
||||
'Cost breakdown' => 'Rincian biaya',
|
||||
'Custom Stylesheet' => 'Kustomisasi Stylesheet',
|
||||
'download' => 'unduh',
|
||||
'Do you really want to remove this budget line?' => 'Apakah anda yakin akan menghapus garis anggaran ini?',
|
||||
'EUR - Euro' => 'EUR - Euro',
|
||||
'Expenses' => 'Beban',
|
||||
'GBP - British Pound' => 'GBP - Poundsterling inggris',
|
||||
'INR - Indian Rupee' => 'INR - Rupe India',
|
||||
'JPY - Japanese Yen' => 'JPY - Yen Jepang',
|
||||
'New budget line' => 'Garis anggaran baru',
|
||||
'NZD - New Zealand Dollar' => 'NZD - Dollar Selandia baru',
|
||||
'Remove a budget line' => 'Hapus garis anggaran',
|
||||
'Remove budget line' => 'Hapus garis anggaran',
|
||||
'RSD - Serbian dinar' => 'RSD - Dinar Serbia',
|
||||
'The budget line have been created successfully.' => 'Garis anggaran berhasil dibuat.',
|
||||
'Unable to create the budget line.' => 'Tidak dapat membuat garis anggaran.',
|
||||
'Unable to remove this budget line.' => 'Tidak dapat menghapus garis anggaran.',
|
||||
'USD - US Dollar' => 'USD - Dollar Amerika',
|
||||
'Remaining' => 'Sisa',
|
||||
'Destination column' => 'Kolom tujuan',
|
||||
'Move the task to another column when assigned to a user' => 'Pindahkan tugas ke kolom lain ketika ditugaskan ke pengguna',
|
||||
'Move the task to another column when assignee is cleared' => 'Pindahkan tugas ke kolom lain ketika orang yang ditugaskan dibersihkan',
|
||||
'Source column' => 'Sumber kolom',
|
||||
'Show subtask estimates (forecast of future work)' => 'Lihat perkiraan subtugas(perkiraan di masa depan)',
|
||||
'Transitions' => 'Transisi',
|
||||
'Executer' => 'Eksekusi',
|
||||
'Time spent in the column' => 'Waktu yang dihabiskan dalam kolom',
|
||||
|
|
@ -748,7 +695,6 @@ return array(
|
|||
'Rate' => 'Tarif',
|
||||
'Change reference currency' => 'Mengubah referensi mata uang',
|
||||
'Add a new currency rate' => 'Tambahkan nilai tukar mata uang baru',
|
||||
'Currency rates are used to calculate project budget.' => 'Nilai tukar mata uang digunakan untuk menghitung anggaran proyek.',
|
||||
'Reference currency' => 'Referensi mata uang',
|
||||
'The currency rate have been added successfully.' => 'Nilai tukar mata uang berhasil ditambahkan.',
|
||||
'Unable to add this currency rate.' => 'Tidak dapat menambahkan nilai tukar mata uang',
|
||||
|
|
@ -880,9 +826,6 @@ return array(
|
|||
'%s moved the task #%d to the first swimlane' => '%s memindahkan tugas n°%d ke swimlane pertama',
|
||||
'%s moved the task #%d to the swimlane "%s"' => '%s memindahkan tugas n°%d ke swimlane « %s »',
|
||||
'Swimlane' => 'Swimlane',
|
||||
'Budget overview' => 'Gambaran anggaran',
|
||||
'Type' => 'Tipe',
|
||||
'There is not enough data to show something.' => 'Tidak ada data yang cukup untuk menunjukkan sesuatu.',
|
||||
'Gravatar' => 'Gravatar',
|
||||
'Hipchat' => 'Hipchat',
|
||||
'Slack' => 'Slack',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Vista compatta/estesa',
|
||||
'No results match:' => 'Nessun risultato trovato:',
|
||||
'Currency' => 'Valuta',
|
||||
'Start time' => 'Data di inizio',
|
||||
'End time' => 'Data di completamento',
|
||||
'Comment' => 'Commento',
|
||||
'All day' => 'Tutto il giorno',
|
||||
'Day' => 'Giorno',
|
||||
'Manage timetable' => 'Gestisci orario',
|
||||
'Overtime timetable' => 'Straordinari',
|
||||
'Time off timetable' => 'Fuori orario',
|
||||
'Timetable' => 'Orario',
|
||||
'Work timetable' => 'Orario di lavoro',
|
||||
'Week timetable' => 'Orario settimanale',
|
||||
'Day timetable' => 'Orario giornaliero',
|
||||
'From' => 'Da',
|
||||
'To' => 'A',
|
||||
'Time slot created successfully.' => 'Fascia oraria creata con successo.',
|
||||
'Unable to save this time slot.' => 'Impossibile creare questa fascia oraria.',
|
||||
'Time slot removed successfully.' => 'Fascia oraria rimossa con successo.',
|
||||
'Unable to remove this time slot.' => 'Impossibile rimuovere questa fascia oraria.',
|
||||
'Do you really want to remove this time slot?' => 'Vuoi davvero rimuovere questa fascia oraria?',
|
||||
'Remove time slot' => 'Rimuovi fascia oraria',
|
||||
'Add new time slot' => 'Aggiungi nuova fascia oraria',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Questo orario è utilizzato quando la casella "tutto il giorno" è selezionata per i fuori orari e per gli straordinari',
|
||||
// 'Files' => '',
|
||||
'Images' => 'Immagini',
|
||||
'Private project' => 'Progetto privato',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Sposta il compito in un\'altra colonna quando viene assegnato ad un utente',
|
||||
'Move the task to another column when assignee is cleared' => 'Sposta il compito in un\'altra colonna quando l\'assegnatario cancellato',
|
||||
'Source column' => 'Colonna sorgente',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
'Transitions' => 'Transizioni',
|
||||
'Executer' => 'Esecutore',
|
||||
'Time spent in the column' => 'Tempo trascorso nella colonna',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'コンパクト/ワイドビュー',
|
||||
'No results match:' => '結果が一致しませんでした',
|
||||
'Currency' => '通貨',
|
||||
'Start time' => '開始時間',
|
||||
'End time' => '終了時間',
|
||||
'Comment' => 'コメント',
|
||||
'All day' => '終日',
|
||||
'Day' => '日',
|
||||
'Manage timetable' => 'タイムテーブルの管理',
|
||||
'Overtime timetable' => '残業タイムテーブル',
|
||||
'Time off timetable' => '休暇タイムテーブル',
|
||||
'Timetable' => 'タイムテーブル',
|
||||
'Work timetable' => 'ワークタイムテーブル',
|
||||
'Week timetable' => '週次タイムテーブル',
|
||||
'Day timetable' => '日時タイムテーブル',
|
||||
'From' => 'ここから',
|
||||
'To' => 'ここまで',
|
||||
// 'Time slot created successfully.' => '',
|
||||
// 'Unable to save this time slot.' => '',
|
||||
// 'Time slot removed successfully.' => '',
|
||||
// 'Unable to remove this time slot.' => '',
|
||||
// 'Do you really want to remove this time slot?' => '',
|
||||
'Remove time slot' => 'タイムスロットの削除',
|
||||
'Add new time slot' => 'タイムラインの追加',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'このタイムテーブルは、残業や休暇で全日がチェックされた場合に用いられます。',
|
||||
'Files' => 'ファイル',
|
||||
'Images' => '画像',
|
||||
'Private project' => 'プライベートプロジェクト',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'ユーザの割り当てをしたらタスクを他のカラムに移動',
|
||||
'Move the task to another column when assignee is cleared' => 'ユーザの割り当てがなくなったらタスクを他のカラムに移動',
|
||||
'Source column' => '移動元のカラム',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
'Transitions' => '履歴',
|
||||
'Executer' => '実行者',
|
||||
'Time spent in the column' => 'カラムでの時間消費',
|
||||
|
|
|
|||
|
|
@ -126,7 +126,6 @@ return array(
|
|||
'The project name is required' => 'Prosjektnavn er påkrevet',
|
||||
'This project must be unique' => 'Prosjektnavnet skal være unikt',
|
||||
'The title is required' => 'Tittel er pårevet',
|
||||
'There is no active project, the first step is to create a new project.' => 'Det er ingen aktive prosjekter. Førstesteg er åopprette et nytt prosjekt.',
|
||||
'Settings saved successfully.' => 'Innstillinger lagret.',
|
||||
'Unable to save your settings.' => 'Innstillinger kunne ikke lagres.',
|
||||
'Database optimization done.' => 'Databaseoptimering er fullført.',
|
||||
|
|
@ -165,8 +164,6 @@ return array(
|
|||
'Date created' => 'Dato for opprettelse',
|
||||
'Date completed' => 'Dato for fullført',
|
||||
'Id' => 'ID',
|
||||
'Completed tasks' => 'Fullførte oppgaver',
|
||||
'Completed tasks for "%s"' => 'Fullførte oppgaver for "%s"',
|
||||
'%d closed tasks' => '%d lukkede oppgaver',
|
||||
'No task for this project' => 'Ingen oppgaver i dette prosjektet',
|
||||
'Public link' => 'Offentligt lenke',
|
||||
|
|
@ -255,25 +252,21 @@ return array(
|
|||
'Expiration date' => 'Utløpsdato',
|
||||
'Remember Me' => 'Husk meg',
|
||||
'Creation date' => 'Opprettelsesdato',
|
||||
'Filter by user' => 'Filtrer efter bruker',
|
||||
'Filter by due date' => 'Filtrer etter forfallsdato',
|
||||
'Everybody' => 'Alle',
|
||||
'Open' => 'Åpen',
|
||||
'Closed' => 'Lukket',
|
||||
'Search' => 'Søk',
|
||||
'Nothing found.' => 'Intet funnet.',
|
||||
'Search in the project "%s"' => 'Søk i prosjektet "%s"',
|
||||
'Due date' => 'Forfallsdato',
|
||||
'Others formats accepted: %s and %s' => 'Andre formater: %s og %s',
|
||||
'Description' => 'Beskrivelse',
|
||||
'%d comments' => '%d kommentarer',
|
||||
'%d comment' => '%d kommentar',
|
||||
'Email address invalid' => 'Ugyldig epost',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Din Google-konto er ikke lengre knyttet til din profil.',
|
||||
'Unable to unlink your Google Account.' => 'Det var ikke mulig ø fjerne din Google-konto.',
|
||||
'Google authentication failed' => 'Google godjenning mislyktes',
|
||||
'Unable to link your Google Account.' => 'Det var ikke mulig åknytte opp til din Google-konto.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Din Google-konto er knyttet til din profil.',
|
||||
// 'Your external account is not linked anymore to your profile.' => '',
|
||||
// 'Unable to unlink your external account.' => '',
|
||||
// 'External authentication failed' => '',
|
||||
// 'Your external account is linked to your profile successfully.' => '',
|
||||
'Email' => 'Epost',
|
||||
'Link my Google Account' => 'Knytt til min Google-konto',
|
||||
'Unlink my Google Account' => 'Fjern knytningen til min Google-konto',
|
||||
|
|
@ -301,7 +294,6 @@ return array(
|
|||
'Category Name' => 'Kategorinavn',
|
||||
'Add a new category' => 'Legg til ny kategori',
|
||||
'Do you really want to remove this category: "%s"?' => 'Vil du fjerne kategorien: "%s"?',
|
||||
'Filter by category' => 'Filter etter kategori',
|
||||
'All categories' => 'Alle kategorier',
|
||||
'No category' => 'Ingen kategori',
|
||||
'The name is required' => 'Navnet er påkrevet',
|
||||
|
|
@ -344,14 +336,9 @@ return array(
|
|||
'Maximum size: ' => 'Maksimum størrelse: ',
|
||||
'Unable to upload the file.' => 'Filen kunne ikke lastes opp.',
|
||||
'Display another project' => 'Vis annet prosjekt...',
|
||||
'Your GitHub account was successfully linked to your profile.' => 'Din GitHub-konto er knyttet til din profil.',
|
||||
'Unable to link your GitHub Account.' => 'Det var ikke mulig å knytte din GitHub-konto.',
|
||||
'GitHub authentication failed' => 'GitHub godkjenning mislyktes',
|
||||
'Your GitHub account is no longer linked to your profile.' => 'Din GitHub-konto er ikke lengere knyttet til din profil.',
|
||||
'Unable to unlink your GitHub Account.' => 'Det var ikke muligt at fjerne forbindelsen til din GitHub-konto.',
|
||||
'Login with my GitHub Account' => 'Login med min GitHub-konto',
|
||||
'Link my GitHub Account' => 'Knytt min GitHub-konto',
|
||||
'Unlink my GitHub Account' => 'Fjern knytningen til min GitHub-konto',
|
||||
// 'Login with my Github Account' => '',
|
||||
// 'Link my Github Account' => '',
|
||||
// 'Unlink my Github Account' => '',
|
||||
'Created by %s' => 'Opprettet av %s',
|
||||
'Last modified on %B %e, %Y at %k:%M %p' => 'Sist endret %d.%m.%Y - %H:%M',
|
||||
'Tasks Export' => 'Oppgave eksport',
|
||||
|
|
@ -408,8 +395,6 @@ return array(
|
|||
'Remote' => 'Fjernstyrt',
|
||||
'Enabled' => 'Aktiv',
|
||||
'Disabled' => 'Deaktivert',
|
||||
'Google account linked' => 'Google-konto knyttet',
|
||||
'Github account linked' => 'GitHub-konto knyttet',
|
||||
'Username:' => 'Brukernavn',
|
||||
'Name:' => 'Navn:',
|
||||
'Email:' => 'Epost:',
|
||||
|
|
@ -445,7 +430,6 @@ return array(
|
|||
'%s updated a comment on the task %s' => '%s oppdaterte en kommentar til oppgaven %s',
|
||||
'%s commented the task %s' => '%s har kommentert oppgaven %s',
|
||||
'%s\'s activity' => '%s\'s aktvitet',
|
||||
'No activity.' => 'Ingen aktivitet',
|
||||
'RSS feed' => 'RSS feed',
|
||||
'%s updated a comment on the task #%d' => '%s oppdaterte en kommentar til oppgaven #%d',
|
||||
'%s commented on the task #%d' => '%s kommenterte oppgaven #%d',
|
||||
|
|
@ -605,14 +589,9 @@ return array(
|
|||
'Language:' => 'Språk',
|
||||
'Timezone:' => 'Tidssone',
|
||||
'All columns' => 'Alle kolonner',
|
||||
'Calendar for "%s"' => 'Kalender for "%s"',
|
||||
'Filter by column' => 'Filtrer etter kolonne',
|
||||
'Filter by status' => 'Filtrer etter status',
|
||||
'Calendar' => 'Kalender',
|
||||
'Next' => 'Neste',
|
||||
// '#%d' => '',
|
||||
'Filter by color' => 'Filtrer etter farge',
|
||||
'Filter by swimlane' => 'Filtrer etter svømmebane',
|
||||
'All swimlanes' => 'Alle svømmebaner',
|
||||
'All colors' => 'Alle farger',
|
||||
'All status' => 'Alle statuser',
|
||||
|
|
@ -627,14 +606,7 @@ return array(
|
|||
// 'Time Tracking' => '',
|
||||
// 'You already have one subtask in progress' => '',
|
||||
'Which parts of the project do you want to duplicate?' => 'Hvilke deler av dette prosjektet ønsker du å kopiere?',
|
||||
'Change dashboard view' => 'Endre visning',
|
||||
'Show/hide activities' => 'Vis/skjul aktiviteter',
|
||||
'Show/hide projects' => 'Vis/skjul prosjekter',
|
||||
'Show/hide subtasks' => 'Vis/skjul deloppgaver',
|
||||
'Show/hide tasks' => 'Vis/skjul oppgaver',
|
||||
'Disable login form' => 'Deaktiver innlogging',
|
||||
'Show/hide calendar' => 'Vis/skjul kalender',
|
||||
'User calendar' => 'Brukerens kalender',
|
||||
// 'Disallow login form' => '',
|
||||
// 'Bitbucket commit received' => '',
|
||||
// 'Bitbucket webhooks' => '',
|
||||
// 'Help on Bitbucket webhooks' => '',
|
||||
|
|
@ -688,82 +660,31 @@ return array(
|
|||
'Keyboard shortcuts' => 'Hurtigtaster',
|
||||
// 'Open board switcher' => '',
|
||||
// 'Application' => '',
|
||||
'Filter recently updated' => 'Filter nylig oppdatert',
|
||||
'since %B %e, %Y at %k:%M %p' => 'siden %B %e, %Y at %k:%M %p',
|
||||
'More filters' => 'Flere filtre',
|
||||
'Compact view' => 'Kompakt visning',
|
||||
'Horizontal scrolling' => 'Bla horisontalt',
|
||||
'Compact/wide view' => 'Kompakt/bred visning',
|
||||
'No results match:' => 'Ingen resultater',
|
||||
// 'Remove hourly rate' => '',
|
||||
// 'Do you really want to remove this hourly rate?' => '',
|
||||
'Hourly rates' => 'Timepriser',
|
||||
'Hourly rate' => 'Timepris',
|
||||
'Currency' => 'Valuta',
|
||||
// 'Effective date' => '',
|
||||
// 'Add new rate' => '',
|
||||
// 'Rate removed successfully.' => '',
|
||||
// 'Unable to remove this rate.' => '',
|
||||
// 'Unable to save the hourly rate.' => '',
|
||||
// 'Hourly rate created successfully.' => '',
|
||||
// 'Start time' => '',
|
||||
// 'End time' => '',
|
||||
'Comment' => 'Kommentar',
|
||||
'All day' => 'Alle dager',
|
||||
'Day' => 'Dag',
|
||||
'Manage timetable' => 'Tidstabell',
|
||||
'Overtime timetable' => 'Overtidstabell',
|
||||
'Time off timetable' => 'Fritidstabell',
|
||||
'Timetable' => 'Tidstabell',
|
||||
'Work timetable' => 'Arbeidstidstabell',
|
||||
'Week timetable' => 'Uketidstabell',
|
||||
'Day timetable' => 'Dagtidstabell',
|
||||
'From' => 'Fra',
|
||||
'To' => 'Til',
|
||||
// 'Time slot created successfully.' => '',
|
||||
// 'Unable to save this time slot.' => '',
|
||||
// 'Time slot removed successfully.' => '',
|
||||
// 'Unable to remove this time slot.' => '',
|
||||
// 'Do you really want to remove this time slot?' => '',
|
||||
// 'Remove time slot' => '',
|
||||
// 'Add new time slot' => '',
|
||||
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
|
||||
'Files' => 'Filer',
|
||||
'Images' => 'Bilder',
|
||||
'Private project' => 'Privat prosjekt',
|
||||
'Amount' => 'Beløp',
|
||||
// 'AUD - Australian Dollar' => '',
|
||||
'Budget' => 'Budsjett',
|
||||
// 'Budget line' => '',
|
||||
// 'Budget line removed successfully.' => '',
|
||||
'Budget lines' => 'Budsjettlinjer',
|
||||
// 'CAD - Canadian Dollar' => '',
|
||||
// 'CHF - Swiss Francs' => '',
|
||||
'Cost' => 'Kostnad',
|
||||
'Cost breakdown' => 'Kostnadsnedbryting',
|
||||
// 'Custom Stylesheet' => '',
|
||||
'download' => 'last ned',
|
||||
// 'Do you really want to remove this budget line?' => '',
|
||||
// 'EUR - Euro' => '',
|
||||
// 'Expenses' => '',
|
||||
// 'GBP - British Pound' => '',
|
||||
// 'INR - Indian Rupee' => '',
|
||||
// 'JPY - Japanese Yen' => '',
|
||||
// 'New budget line' => '',
|
||||
// 'NZD - New Zealand Dollar' => '',
|
||||
// 'Remove a budget line' => '',
|
||||
// 'Remove budget line' => '',
|
||||
// 'RSD - Serbian dinar' => '',
|
||||
// 'The budget line have been created successfully.' => '',
|
||||
// 'Unable to create the budget line.' => '',
|
||||
// 'Unable to remove this budget line.' => '',
|
||||
// 'USD - US Dollar' => '',
|
||||
// 'Remaining' => '',
|
||||
'Destination column' => 'Ny kolonne',
|
||||
'Move the task to another column when assigned to a user' => 'Flytt oppgaven til en annen kolonne når den er tildelt en bruker',
|
||||
'Move the task to another column when assignee is cleared' => 'Flytt oppgaven til en annen kolonne når ppgavetildeling fjernes ',
|
||||
'Source column' => 'Opprinnelig kolonne',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
'Transitions' => 'Statusendringer',
|
||||
// 'Executer' => '',
|
||||
// 'Time spent in the column' => '',
|
||||
|
|
@ -774,7 +695,6 @@ return array(
|
|||
// 'Rate' => '',
|
||||
// 'Change reference currency' => '',
|
||||
// 'Add a new currency rate' => '',
|
||||
// 'Currency rates are used to calculate project budget.' => '',
|
||||
// 'Reference currency' => '',
|
||||
// 'The currency rate have been added successfully.' => '',
|
||||
// 'Unable to add this currency rate.' => '',
|
||||
|
|
@ -906,9 +826,6 @@ return array(
|
|||
// '%s moved the task #%d to the first swimlane' => '',
|
||||
// '%s moved the task #%d to the swimlane "%s"' => '',
|
||||
'Swimlane' => 'Svømmebane',
|
||||
'Budget overview' => 'Budsjettoversikt',
|
||||
// 'Type' => '',
|
||||
// 'There is not enough data to show something.' => '',
|
||||
// 'Gravatar' => '',
|
||||
// 'Hipchat' => '',
|
||||
// 'Slack' => '',
|
||||
|
|
@ -921,7 +838,6 @@ return array(
|
|||
// 'The task have been moved to the first swimlane' => '',
|
||||
// 'The task have been moved to another swimlane:' => '',
|
||||
// 'Overdue tasks for the project "%s"' => '',
|
||||
'There is no completed tasks at the moment.' => 'Ingen fullførte oppgaver funnet.',
|
||||
// 'New title: %s' => '',
|
||||
// 'The task is not assigned anymore' => '',
|
||||
// 'New assignee: %s' => '',
|
||||
|
|
@ -938,7 +854,6 @@ return array(
|
|||
// 'The description have been modified' => '',
|
||||
// 'Do you really want to close the task "%s" as well as all subtasks?' => '',
|
||||
'Swimlane: %s' => 'Svømmebane: %s',
|
||||
'Project calendar' => 'Prosjektkalender',
|
||||
'I want to receive notifications for:' => 'Jeg vil motta varslinger om:',
|
||||
'All tasks' => 'Alle oppgaver',
|
||||
'Only for tasks assigned to me' => 'Kun oppgaver som er tildelt meg',
|
||||
|
|
@ -951,7 +866,7 @@ return array(
|
|||
// '%k:%M %p' => '',
|
||||
// '%%Y-%%m-%%d' => '',
|
||||
'Total for all columns' => 'Totalt for alle kolonner',
|
||||
//'You need at least 2 days of data to show the chart.' => '',
|
||||
// 'You need at least 2 days of data to show the chart.' => '',
|
||||
// '<15m' => '',
|
||||
// '<30m' => '',
|
||||
'Stop timer' => 'Stopp timer',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
// 'Compact/wide view' => '',
|
||||
// 'No results match:' => '',
|
||||
// 'Currency' => '',
|
||||
// 'Start time' => '',
|
||||
// 'End time' => '',
|
||||
// 'Comment' => '',
|
||||
// 'All day' => '',
|
||||
// 'Day' => '',
|
||||
// 'Manage timetable' => '',
|
||||
// 'Overtime timetable' => '',
|
||||
// 'Time off timetable' => '',
|
||||
// 'Timetable' => '',
|
||||
// 'Work timetable' => '',
|
||||
// 'Week timetable' => '',
|
||||
// 'Day timetable' => '',
|
||||
// 'From' => '',
|
||||
// 'To' => '',
|
||||
// 'Time slot created successfully.' => '',
|
||||
// 'Unable to save this time slot.' => '',
|
||||
// 'Time slot removed successfully.' => '',
|
||||
// 'Unable to remove this time slot.' => '',
|
||||
// 'Do you really want to remove this time slot?' => '',
|
||||
// 'Remove time slot' => '',
|
||||
// 'Add new time slot' => '',
|
||||
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
|
||||
// 'Files' => '',
|
||||
// 'Images' => '',
|
||||
// 'Private project' => '',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
// 'Move the task to another column when assigned to a user' => '',
|
||||
// 'Move the task to another column when assignee is cleared' => '',
|
||||
// 'Source column' => '',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
// 'Transitions' => '',
|
||||
// 'Executer' => '',
|
||||
// 'Time spent in the column' => '',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Pełny/Kompaktowy widok',
|
||||
'No results match:' => 'Brak wyników:',
|
||||
'Currency' => 'Waluta',
|
||||
'Start time' => 'Rozpoczęto',
|
||||
'End time' => 'Zakończono',
|
||||
'Comment' => 'Komentarz',
|
||||
'All day' => 'Cały dzień',
|
||||
'Day' => 'Dzień',
|
||||
'Manage timetable' => 'Zarządzaj rozkładami zajęć',
|
||||
'Overtime timetable' => 'Rozkład zajęć - nadgodziny',
|
||||
'Time off timetable' => 'Rozkład zajęć - czas wolny',
|
||||
'Timetable' => 'Rozkład zajęć',
|
||||
'Work timetable' => 'Rozkład zajęć - praca',
|
||||
'Week timetable' => 'Tygodniowy rozkład zajęć',
|
||||
'Day timetable' => 'Dzienny rozkład zajęć',
|
||||
'From' => 'Od',
|
||||
'To' => 'Do',
|
||||
'Time slot created successfully.' => 'Przydział czasowy utworzony.',
|
||||
'Unable to save this time slot.' => 'Nie można zapisać tego przydziału czasowego.',
|
||||
'Time slot removed successfully.' => 'Przydział czasowy usunięty.',
|
||||
'Unable to remove this time slot.' => 'Nie można usunąć tego przydziału czasowego.',
|
||||
'Do you really want to remove this time slot?' => 'Czy na pewno chcesz usunąć ten przedział czasowy?',
|
||||
'Remove time slot' => 'Usuń przedział czasowy',
|
||||
'Add new time slot' => 'Dodaj przedział czasowy',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Ten rozkład zajęć jest używany przypadku zaznaczenia "cały dzień" dla zaplanowanego czasu wolnego i nadgodzin',
|
||||
'Files' => 'Pliki',
|
||||
'Images' => 'Obrazy',
|
||||
'Private project' => 'Projekt prywatny',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Przenieś zadanie do innej kolumny gdy zostanie przypisane do osoby',
|
||||
'Move the task to another column when assignee is cleared' => 'Przenieś zadanie do innej kolumny gdy osoba odpowiedzialna zostanie usunięta',
|
||||
'Source column' => 'Kolumna źródłowa',
|
||||
'Show subtask estimates (forecast of future work)' => 'Pokaż planowane czasy wykonania pod-zadań',
|
||||
'Transitions' => 'Przeniesienia',
|
||||
'Executer' => 'Wykonał',
|
||||
'Time spent in the column' => 'Czas spędzony w tej kolumnie',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Alternar entre a vista compacta e ampliada',
|
||||
'No results match:' => 'Nenhum resultado:',
|
||||
'Currency' => 'Moeda',
|
||||
'Start time' => 'Horário de início',
|
||||
'End time' => 'Horário de término',
|
||||
'Comment' => 'comentário',
|
||||
'All day' => 'Dia inteiro',
|
||||
'Day' => 'Dia',
|
||||
'Manage timetable' => 'Gestão dos horários',
|
||||
'Overtime timetable' => 'Horas extras',
|
||||
'Time off timetable' => 'Horas de ausência',
|
||||
'Timetable' => 'Horários',
|
||||
'Work timetable' => 'Horas trabalhadas',
|
||||
'Week timetable' => 'Horário da semana',
|
||||
'Day timetable' => 'Horário de un dia',
|
||||
'From' => 'Desde',
|
||||
'To' => 'A',
|
||||
'Time slot created successfully.' => 'Intervalo de tempo criado com sucesso.',
|
||||
'Unable to save this time slot.' => 'Impossível de guardar este intervalo de tempo.',
|
||||
'Time slot removed successfully.' => 'Intervalo de tempo removido com sucesso.',
|
||||
'Unable to remove this time slot.' => 'Impossível de remover esse intervalo de tempo.',
|
||||
'Do you really want to remove this time slot?' => 'Você deseja realmente remover este intervalo de tempo?',
|
||||
'Remove time slot' => 'Remover um intervalo de tempo',
|
||||
'Add new time slot' => 'Adicionar um intervalo de tempo',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Esses horários são usados quando a caixa de seleção "Dia inteiro" está marcada para Horas de ausência ou Extras',
|
||||
'Files' => 'Arquivos',
|
||||
'Images' => 'Imagens',
|
||||
'Private project' => 'Projeto privado',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuída a um usuário',
|
||||
'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuída',
|
||||
'Source column' => 'Coluna de origem',
|
||||
'Show subtask estimates (forecast of future work)' => 'Mostrar a estimativa das subtarefas (previsão para o trabalho futuro)',
|
||||
'Transitions' => 'Transições',
|
||||
'Executer' => 'Executor(a)',
|
||||
'Time spent in the column' => 'Tempo gasto na coluna',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Alternar entre a vista compacta e ampliada',
|
||||
'No results match:' => 'Nenhum resultado:',
|
||||
'Currency' => 'Moeda',
|
||||
'Start time' => 'Horário de início',
|
||||
'End time' => 'Horário de término',
|
||||
'Comment' => 'comentário',
|
||||
'All day' => 'Dia inteiro',
|
||||
'Day' => 'Dia',
|
||||
'Manage timetable' => 'Gestão dos horários',
|
||||
'Overtime timetable' => 'Horas extras',
|
||||
'Time off timetable' => 'Horas de ausência',
|
||||
'Timetable' => 'Horários',
|
||||
'Work timetable' => 'Horas trabalhadas',
|
||||
'Week timetable' => 'Horário da semana',
|
||||
'Day timetable' => 'Horário de um dia',
|
||||
'From' => 'Desde',
|
||||
'To' => 'A',
|
||||
'Time slot created successfully.' => 'Intervalo de tempo criado com sucesso.',
|
||||
'Unable to save this time slot.' => 'Impossível guardar este intervalo de tempo.',
|
||||
'Time slot removed successfully.' => 'Intervalo de tempo removido com sucesso.',
|
||||
'Unable to remove this time slot.' => 'Impossível remover esse intervalo de tempo.',
|
||||
'Do you really want to remove this time slot?' => 'Tem a certeza que quer remover este intervalo de tempo?',
|
||||
'Remove time slot' => 'Remover um intervalo de tempo',
|
||||
'Add new time slot' => 'Adicionar um intervalo de tempo',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Esses horários são usados quando a caixa de seleção "Dia inteiro" está marcada para Horas de ausência ou Extras',
|
||||
'Files' => 'Arquivos',
|
||||
'Images' => 'Imagens',
|
||||
'Private project' => 'Projecto privado',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuída a um utilizador',
|
||||
'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuída',
|
||||
'Source column' => 'Coluna de origem',
|
||||
'Show subtask estimates (forecast of future work)' => 'Mostrar a estimativa das subtarefas (previsão para o trabalho futuro)',
|
||||
'Transitions' => 'Transições',
|
||||
'Executer' => 'Executor(a)',
|
||||
'Time spent in the column' => 'Tempo gasto na coluna',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Компактный/широкий вид',
|
||||
'No results match:' => 'Отсутствуют результаты:',
|
||||
'Currency' => 'Валюта',
|
||||
'Start time' => 'Время начала',
|
||||
'End time' => 'Время завершения',
|
||||
'Comment' => 'Комментарий',
|
||||
'All day' => 'Весь день',
|
||||
'Day' => 'День',
|
||||
'Manage timetable' => 'Управление графиками',
|
||||
'Overtime timetable' => 'График сверхурочных',
|
||||
'Time off timetable' => 'Время в графике',
|
||||
'Timetable' => 'График',
|
||||
'Work timetable' => 'Work timetable',
|
||||
'Week timetable' => 'График на неделю',
|
||||
'Day timetable' => 'График на день',
|
||||
'From' => 'От кого',
|
||||
'To' => 'Кому',
|
||||
'Time slot created successfully.' => 'Временной интервал успешно создан.',
|
||||
'Unable to save this time slot.' => 'Невозможно сохранить этот временной интервал.',
|
||||
'Time slot removed successfully.' => 'Временной интервал успешно удален.',
|
||||
'Unable to remove this time slot.' => 'Не удается удалить этот временной интервал.',
|
||||
'Do you really want to remove this time slot?' => 'Вы действительно хотите удалить этот период времени?',
|
||||
'Remove time slot' => 'Удалить новый интервал времени',
|
||||
'Add new time slot' => 'Добавить новый интервал времени',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Это расписание используется, когда флажок "весь день" проверяется на установленное время выключения и сверхурочное время.',
|
||||
'Files' => 'Файлы',
|
||||
'Images' => 'Изображения',
|
||||
'Private project' => 'Приватный проект',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Переместить задачу в другую колонку, когда она назначена пользователю',
|
||||
'Move the task to another column when assignee is cleared' => 'Переместить задачу в другую колонку, когда назначение снято ',
|
||||
'Source column' => 'Исходная колонка',
|
||||
'Show subtask estimates (forecast of future work)' => 'Показать оценку подзадач (прогноз будущей работы)',
|
||||
'Transitions' => 'Перемещения',
|
||||
'Executer' => 'Исполнитель',
|
||||
'Time spent in the column' => 'Время проведенное в колонке',
|
||||
|
|
@ -849,9 +826,6 @@ return array(
|
|||
'%s moved the task #%d to the first swimlane' => '%s задач перемещено #%d в первой дорожке',
|
||||
'%s moved the task #%d to the swimlane "%s"' => '%s задач перемещено #%d в дорожке "%s"',
|
||||
'Swimlane' => 'Дорожки',
|
||||
'Budget overview' => 'Обзор бюджета',
|
||||
'Type' => 'Тип',
|
||||
'There is not enough data to show something.' => 'Недостаточно существующих данных, что бы что-то показать.',
|
||||
'Gravatar' => 'Граватар',
|
||||
'Hipchat' => 'Hipchat',
|
||||
'Slack' => 'Slack',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
// 'Compact/wide view' => '',
|
||||
// 'No results match:' => '',
|
||||
// 'Currency' => '',
|
||||
// 'Start time' => '',
|
||||
// 'End time' => '',
|
||||
// 'Comment' => '',
|
||||
// 'All day' => '',
|
||||
// 'Day' => '',
|
||||
// 'Manage timetable' => '',
|
||||
// 'Overtime timetable' => '',
|
||||
// 'Time off timetable' => '',
|
||||
// 'Timetable' => '',
|
||||
// 'Work timetable' => '',
|
||||
// 'Week timetable' => '',
|
||||
// 'Day timetable' => '',
|
||||
// 'From' => '',
|
||||
// 'To' => '',
|
||||
// 'Time slot created successfully.' => '',
|
||||
// 'Unable to save this time slot.' => '',
|
||||
// 'Time slot removed successfully.' => '',
|
||||
// 'Unable to remove this time slot.' => '',
|
||||
// 'Do you really want to remove this time slot?' => '',
|
||||
// 'Remove time slot' => '',
|
||||
// 'Add new time slot' => '',
|
||||
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
|
||||
// 'Files' => '',
|
||||
// 'Images' => '',
|
||||
// 'Private project' => '',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
// 'Move the task to another column when assigned to a user' => '',
|
||||
// 'Move the task to another column when assignee is cleared' => '',
|
||||
// 'Source column' => '',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
// 'Transitions' => '',
|
||||
// 'Executer' => '',
|
||||
// 'Time spent in the column' => '',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Kompakt/bred vy',
|
||||
'No results match:' => 'Inga matchande resultat',
|
||||
'Currency' => 'Valuta',
|
||||
'Start time' => 'Starttid',
|
||||
'End time' => 'Sluttid',
|
||||
'Comment' => 'Kommentar',
|
||||
'All day' => 'Hela dagen',
|
||||
'Day' => 'Dag',
|
||||
'Manage timetable' => 'Hantera timplan',
|
||||
'Overtime timetable' => 'Övertidstimplan',
|
||||
'Time off timetable' => 'Ledighetstimplan',
|
||||
'Timetable' => 'Timplan',
|
||||
'Work timetable' => 'Arbetstimplan',
|
||||
'Week timetable' => 'Veckotidplan',
|
||||
'Day timetable' => 'Dagstimplan',
|
||||
'From' => 'Från',
|
||||
'To' => 'Till',
|
||||
'Time slot created successfully.' => 'Tidslucka skapad.',
|
||||
'Unable to save this time slot.' => 'Kunde inte spara tidsluckan.',
|
||||
'Time slot removed successfully.' => 'Tidsluckan tog bort.',
|
||||
'Unable to remove this time slot.' => 'Kunde inte ta bort tidsluckan.',
|
||||
'Do you really want to remove this time slot?' => 'Vill du verkligen ta bort tidsluckan?',
|
||||
'Remove time slot' => 'Ta bort tidslucka',
|
||||
'Add new time slot' => 'Lägg till ny tidslucka',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => 'Denna tidslucka används när kryssrutan "hela dagen" är kryssad vid schemalagd ledighet eller övertid.',
|
||||
'Files' => 'Filer',
|
||||
'Images' => 'Bilder',
|
||||
'Private project' => 'Privat projekt',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'Flytta uppgiften till en annan kolumn när den tilldelats en användare',
|
||||
'Move the task to another column when assignee is cleared' => 'Flytta uppgiften till en annan kolumn när tilldelningen tas bort.',
|
||||
'Source column' => 'Källkolumn',
|
||||
'Show subtask estimates (forecast of future work)' => 'Visa uppskattningar för deluppgifter (prognos för framtida arbete)',
|
||||
'Transitions' => 'Övergångar',
|
||||
'Executer' => 'Verkställare',
|
||||
'Time spent in the column' => 'Tid i kolumnen.',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'พอดี/กว้าง มุมมอง',
|
||||
'No results match:' => 'ไม่มีผลลัพท์ที่ตรง',
|
||||
'Currency' => 'สกุลเงิน',
|
||||
'Start time' => 'เวลาเริ่มต้น',
|
||||
'End time' => 'เวลาจบ',
|
||||
'Comment' => 'ความคิดเห็น',
|
||||
'All day' => 'ทั้งวัน',
|
||||
'Day' => 'วัน',
|
||||
'Manage timetable' => 'จัดการตารางเวลา',
|
||||
'Overtime timetable' => 'ตารางเวลาโอที',
|
||||
'Time off timetable' => 'ตารางเวลาวันหยุด',
|
||||
'Timetable' => 'ตารางเวลา',
|
||||
'Work timetable' => 'ตารางเวลางาน',
|
||||
'Week timetable' => 'ตารางเวลาสัปดาห์',
|
||||
'Day timetable' => 'ตารางเวลาวัน',
|
||||
'From' => 'จาก',
|
||||
'To' => 'ถึง',
|
||||
'Time slot created successfully.' => 'สร้างช่วงเวลาเรียบร้อยแล้ว',
|
||||
'Unable to save this time slot.' => 'ไม่สามารถบันทึกช่วงเวลานี้',
|
||||
'Time slot removed successfully.' => 'ลบช่วงเวลาเรียบร้อยแล้ว',
|
||||
'Unable to remove this time slot.' => 'ไม่สามารถลบช่วงเวลาได้',
|
||||
'Do you really want to remove this time slot?' => 'คุณต้องการลบช่วงเวลานี้?',
|
||||
'Remove time slot' => 'ลบช่วงเวลา',
|
||||
'Add new time slot' => 'เพิ่มช่วงเวลาใหม่',
|
||||
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
|
||||
'Files' => 'ไฟล์',
|
||||
'Images' => 'รูปภาพ',
|
||||
'Private project' => 'โปรเจคส่วนตัว',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => 'ย้ายงานไปคอลัมน์อื่นเมื่อกำหนดบุคคลรับผิดชอบ',
|
||||
'Move the task to another column when assignee is cleared' => 'ย้ายงานไปคอลัมน์อื่นเมื่อไม่กำหนดบุคคลรับผิดชอบ',
|
||||
'Source column' => 'คอลัมน์ต้นทาง',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
'Transitions' => 'การเปลี่ยนคอลัมน์',
|
||||
'Executer' => 'ผู้ประมวลผล',
|
||||
'Time spent in the column' => 'เวลาที่ใช้ในคอลัมน์',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => 'Ekrana sığdır / Geniş görünüm',
|
||||
// 'No results match:' => '',
|
||||
// 'Currency' => '',
|
||||
// 'Start time' => '',
|
||||
// 'End time' => '',
|
||||
// 'Comment' => '',
|
||||
// 'All day' => '',
|
||||
// 'Day' => '',
|
||||
// 'Manage timetable' => '',
|
||||
// 'Overtime timetable' => '',
|
||||
// 'Time off timetable' => '',
|
||||
// 'Timetable' => '',
|
||||
// 'Work timetable' => '',
|
||||
// 'Week timetable' => '',
|
||||
// 'Day timetable' => '',
|
||||
// 'From' => '',
|
||||
// 'To' => '',
|
||||
// 'Time slot created successfully.' => '',
|
||||
// 'Unable to save this time slot.' => '',
|
||||
// 'Time slot removed successfully.' => '',
|
||||
// 'Unable to remove this time slot.' => '',
|
||||
// 'Do you really want to remove this time slot?' => '',
|
||||
// 'Remove time slot' => '',
|
||||
// 'Add new time slot' => '',
|
||||
// 'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '',
|
||||
// 'Files' => '',
|
||||
// 'Images' => '',
|
||||
// 'Private project' => '',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
// 'Move the task to another column when assigned to a user' => '',
|
||||
// 'Move the task to another column when assignee is cleared' => '',
|
||||
// 'Source column' => '',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
// 'Transitions' => '',
|
||||
// 'Executer' => '',
|
||||
// 'Time spent in the column' => '',
|
||||
|
|
|
|||
|
|
@ -666,28 +666,6 @@ return array(
|
|||
'Compact/wide view' => '紧凑/宽视图',
|
||||
'No results match:' => '无匹配结果:',
|
||||
'Currency' => '货币',
|
||||
'Start time' => '开始时间',
|
||||
'End time' => '结束时1间',
|
||||
'Comment' => '注释',
|
||||
'All day' => '全天',
|
||||
'Day' => '日期',
|
||||
'Manage timetable' => '管理时间表',
|
||||
// 'Overtime timetable' => '',
|
||||
'Time off timetable' => '加班时间表',
|
||||
'Timetable' => '时间表',
|
||||
'Work timetable' => '工作时间表',
|
||||
'Week timetable' => '周时间表',
|
||||
'Day timetable' => '日时间表',
|
||||
'From' => '从',
|
||||
'To' => '到',
|
||||
'Time slot created successfully.' => '成功创建时间段。',
|
||||
'Unable to save this time slot.' => '无法保存此时间段。',
|
||||
'Time slot removed successfully.' => '成功删除时间段。',
|
||||
'Unable to remove this time slot.' => '无法删除此时间段。',
|
||||
'Do you really want to remove this time slot?' => '确认要删除此时间段吗?',
|
||||
'Remove time slot' => '删除时间段',
|
||||
'Add new time slot' => '添加新时间段',
|
||||
'This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.' => '如果在放假和加班计划中选择全天,则会使用这里配置的时间段。',
|
||||
'Files' => '文件',
|
||||
'Images' => '图片',
|
||||
'Private project' => '私人项目',
|
||||
|
|
@ -707,7 +685,6 @@ return array(
|
|||
'Move the task to another column when assigned to a user' => '指定负责人时移动到其它栏目',
|
||||
'Move the task to another column when assignee is cleared' => '移除负责人时移动到其它栏目',
|
||||
'Source column' => '原栏目',
|
||||
// 'Show subtask estimates (forecast of future work)' => '',
|
||||
'Transitions' => '变更',
|
||||
'Executer' => '执行者',
|
||||
'Time spent in the column' => '栏目中的时间消耗',
|
||||
|
|
|
|||
|
|
@ -150,13 +150,14 @@ class SubtaskTimeTracking extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param integer $start
|
||||
* @param integer $end
|
||||
* @param string $start ISO-8601 format
|
||||
* @param string $end
|
||||
* @return array
|
||||
*/
|
||||
public function getUserCalendarEvents($user_id, $start, $end)
|
||||
{
|
||||
$result = $this->getUserQuery($user_id)
|
||||
$hook = 'model:subtask-time-tracking:calendar:events';
|
||||
$events = $this->getUserQuery($user_id)
|
||||
->addCondition($this->getCalendarCondition(
|
||||
$this->dateParser->getTimestampFromIsoFormat($start),
|
||||
$this->dateParser->getTimestampFromIsoFormat($end),
|
||||
|
|
@ -165,9 +166,16 @@ class SubtaskTimeTracking extends Base
|
|||
))
|
||||
->findAll();
|
||||
|
||||
$result = $this->timetable->calculateEventsIntersect($user_id, $result, $start, $end);
|
||||
if ($this->hook->exists($hook)) {
|
||||
$events = $this->hook->first($hook, array(
|
||||
'user_id' => $user_id,
|
||||
'events' => $events,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
));
|
||||
}
|
||||
|
||||
return $this->toCalendarEvents($result);
|
||||
return $this->toCalendarEvents($events);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -293,6 +301,7 @@ class SubtaskTimeTracking extends Base
|
|||
*/
|
||||
public function getTimeSpent($subtask_id, $user_id)
|
||||
{
|
||||
$hook = 'model:subtask-time-tracking:calculate:time-spent';
|
||||
$start_time = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('subtask_id', $subtask_id)
|
||||
|
|
@ -300,14 +309,23 @@ class SubtaskTimeTracking extends Base
|
|||
->eq('end', 0)
|
||||
->findOneColumn('start');
|
||||
|
||||
if ($start_time) {
|
||||
$start = new DateTime;
|
||||
$start->setTimestamp($start_time);
|
||||
|
||||
return $this->timetable->calculateEffectiveDuration($user_id, $start, new DateTime);
|
||||
if (empty($start_time)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
$end = new DateTime;
|
||||
$start = new DateTime;
|
||||
$start->setTimestamp($start_time);
|
||||
|
||||
if ($this->hook->exists($hook)) {
|
||||
return $this->hook->first($hook, array(
|
||||
'user_id' => $user_id,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
));
|
||||
}
|
||||
|
||||
return $this->dateParser->getHours($start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,356 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use DateTime;
|
||||
use DateInterval;
|
||||
|
||||
/**
|
||||
* Timetable
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Timetable extends Base
|
||||
{
|
||||
/**
|
||||
* User time slots
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $day;
|
||||
private $week;
|
||||
private $overtime;
|
||||
private $timeoff;
|
||||
|
||||
/**
|
||||
* Get a set of events by using the intersection between the timetable and the time tracking data
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param array $events Time tracking data
|
||||
* @param string $start ISO8601 date
|
||||
* @param string $end ISO8601 date
|
||||
* @return array
|
||||
*/
|
||||
public function calculateEventsIntersect($user_id, array $events, $start, $end)
|
||||
{
|
||||
$start_dt = new DateTime($start);
|
||||
$start_dt->setTime(0, 0);
|
||||
|
||||
$end_dt = new DateTime($end);
|
||||
$end_dt->setTime(23, 59);
|
||||
|
||||
$timetable = $this->calculate($user_id, $start_dt, $end_dt);
|
||||
|
||||
// The user has no timetable
|
||||
if (empty($this->week)) {
|
||||
return $events;
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach ($events as $event) {
|
||||
$results = array_merge($results, $this->calculateEventIntersect($event, $timetable));
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a serie of events based on the timetable and the provided event
|
||||
*
|
||||
* @access public
|
||||
* @param array $event
|
||||
* @param array $timetable
|
||||
* @return array
|
||||
*/
|
||||
public function calculateEventIntersect(array $event, array $timetable)
|
||||
{
|
||||
$events = array();
|
||||
|
||||
foreach ($timetable as $slot) {
|
||||
|
||||
$start_ts = $slot[0]->getTimestamp();
|
||||
$end_ts = $slot[1]->getTimestamp();
|
||||
|
||||
if ($start_ts > $event['end']) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($event['start'] <= $start_ts) {
|
||||
$event['start'] = $start_ts;
|
||||
}
|
||||
|
||||
if ($event['start'] >= $start_ts && $event['start'] <= $end_ts) {
|
||||
|
||||
if ($event['end'] >= $end_ts) {
|
||||
$events[] = array_merge($event, array('end' => $end_ts));
|
||||
}
|
||||
else {
|
||||
$events[] = $event;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate effective worked hours by taking into consideration the timetable
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param \DateTime $start
|
||||
* @param \DateTime $end
|
||||
* @return float
|
||||
*/
|
||||
public function calculateEffectiveDuration($user_id, DateTime $start, DateTime $end)
|
||||
{
|
||||
$end_timetable = clone($end);
|
||||
$end_timetable->setTime(23, 59);
|
||||
|
||||
$timetable = $this->calculate($user_id, $start, $end_timetable);
|
||||
$found_start = false;
|
||||
$hours = 0;
|
||||
|
||||
// The user has no timetable
|
||||
if (empty($this->week)) {
|
||||
return $this->dateParser->getHours($start, $end);
|
||||
}
|
||||
|
||||
foreach ($timetable as $slot) {
|
||||
|
||||
$isStartSlot = $this->dateParser->withinDateRange($start, $slot[0], $slot[1]);
|
||||
$isEndSlot = $this->dateParser->withinDateRange($end, $slot[0], $slot[1]);
|
||||
|
||||
// Start and end are within the same time slot
|
||||
if ($isStartSlot && $isEndSlot) {
|
||||
return $this->dateParser->getHours($start, $end);
|
||||
}
|
||||
|
||||
// We found the start slot
|
||||
if (! $found_start && $isStartSlot) {
|
||||
$found_start = true;
|
||||
$hours = $this->dateParser->getHours($start, $slot[1]);
|
||||
}
|
||||
else if ($found_start) {
|
||||
|
||||
// We found the end slot
|
||||
if ($isEndSlot) {
|
||||
$hours += $this->dateParser->getHours($slot[0], $end);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
||||
// Sum hours of the intermediate time slots
|
||||
$hours += $this->dateParser->getHours($slot[0], $slot[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The start date was not found in regular hours so we get the nearest time slot
|
||||
if (! empty($timetable) && ! $found_start) {
|
||||
$slot = $this->findClosestTimeSlot($start, $timetable);
|
||||
|
||||
if ($start < $slot[0]) {
|
||||
return $this->calculateEffectiveDuration($user_id, $slot[0], $end);
|
||||
}
|
||||
}
|
||||
|
||||
return $hours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the nearest time slot
|
||||
*
|
||||
* @access public
|
||||
* @param DateTime $date
|
||||
* @param array $timetable
|
||||
* @return array
|
||||
*/
|
||||
public function findClosestTimeSlot(DateTime $date, array $timetable)
|
||||
{
|
||||
$values = array();
|
||||
|
||||
foreach ($timetable as $slot) {
|
||||
$t1 = abs($slot[0]->getTimestamp() - $date->getTimestamp());
|
||||
$t2 = abs($slot[1]->getTimestamp() - $date->getTimestamp());
|
||||
|
||||
$values[] = min($t1, $t2);
|
||||
}
|
||||
|
||||
asort($values);
|
||||
return $timetable[key($values)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timetable for a user for a given date range
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param \DateTime $start
|
||||
* @param \DateTime $end
|
||||
* @return array
|
||||
*/
|
||||
public function calculate($user_id, DateTime $start, DateTime $end)
|
||||
{
|
||||
$timetable = array();
|
||||
|
||||
$this->day = $this->timetableDay->getByUser($user_id);
|
||||
$this->week = $this->timetableWeek->getByUser($user_id);
|
||||
$this->overtime = $this->timetableExtra->getByUserAndDate($user_id, $start->format('Y-m-d'), $end->format('Y-m-d'));
|
||||
$this->timeoff = $this->timetableOff->getByUserAndDate($user_id, $start->format('Y-m-d'), $end->format('Y-m-d'));
|
||||
|
||||
for ($today = clone($start); $today <= $end; $today->add(new DateInterval('P1D'))) {
|
||||
$week_day = $today->format('N');
|
||||
$timetable = array_merge($timetable, $this->getWeekSlots($today, $week_day));
|
||||
$timetable = array_merge($timetable, $this->getOvertimeSlots($today, $week_day));
|
||||
}
|
||||
|
||||
return $timetable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return worked time slots for the given day
|
||||
*
|
||||
* @access public
|
||||
* @param \DateTime $today
|
||||
* @param string $week_day
|
||||
* @return array
|
||||
*/
|
||||
public function getWeekSlots(DateTime $today, $week_day)
|
||||
{
|
||||
$slots = array();
|
||||
$dayoff = $this->getDayOff($today);
|
||||
|
||||
if (! empty($dayoff) && $dayoff['all_day'] == 1) {
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ($this->week as $slot) {
|
||||
if ($week_day == $slot['day']) {
|
||||
$slots = array_merge($slots, $this->getDayWorkSlots($slot, $dayoff, $today));
|
||||
}
|
||||
}
|
||||
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overtime time slots for the given day
|
||||
*
|
||||
* @access public
|
||||
* @param \DateTime $today
|
||||
* @param string $week_day
|
||||
* @return array
|
||||
*/
|
||||
public function getOvertimeSlots(DateTime $today, $week_day)
|
||||
{
|
||||
$slots = array();
|
||||
|
||||
foreach ($this->overtime as $slot) {
|
||||
|
||||
$day = new DateTime($slot['date']);
|
||||
|
||||
if ($week_day == $day->format('N')) {
|
||||
|
||||
if ($slot['all_day'] == 1) {
|
||||
$slots = array_merge($slots, $this->getDaySlots($today));
|
||||
}
|
||||
else {
|
||||
$slots[] = $this->getTimeSlot($slot, $day);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get worked time slots and remove time off
|
||||
*
|
||||
* @access public
|
||||
* @param array $slot
|
||||
* @param array $dayoff
|
||||
* @param \DateTime $today
|
||||
* @return array
|
||||
*/
|
||||
public function getDayWorkSlots(array $slot, array $dayoff, DateTime $today)
|
||||
{
|
||||
$slots = array();
|
||||
|
||||
if (! empty($dayoff) && $dayoff['start'] < $slot['end']) {
|
||||
|
||||
if ($dayoff['start'] > $slot['start']) {
|
||||
$slots[] = $this->getTimeSlot(array('end' => $dayoff['start']) + $slot, $today);
|
||||
}
|
||||
|
||||
if ($dayoff['end'] < $slot['end']) {
|
||||
$slots[] = $this->getTimeSlot(array('start' => $dayoff['end']) + $slot, $today);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$slots[] = $this->getTimeSlot($slot, $today);
|
||||
}
|
||||
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get regular day work time slots
|
||||
*
|
||||
* @access public
|
||||
* @param \DateTime $today
|
||||
* @return array
|
||||
*/
|
||||
public function getDaySlots(DateTime $today)
|
||||
{
|
||||
$slots = array();
|
||||
|
||||
foreach ($this->day as $day) {
|
||||
$slots[] = $this->getTimeSlot($day, $today);
|
||||
}
|
||||
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start and end time slot for a given day
|
||||
*
|
||||
* @access public
|
||||
* @param array $slot
|
||||
* @param \DateTime $today
|
||||
* @return array
|
||||
*/
|
||||
public function getTimeSlot(array $slot, DateTime $today)
|
||||
{
|
||||
$date = $today->format('Y-m-d');
|
||||
|
||||
return array(
|
||||
new DateTime($date.' '.$slot['start']),
|
||||
new DateTime($date.' '.$slot['end']),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return day off time slot
|
||||
*
|
||||
* @access public
|
||||
* @param \DateTime $today
|
||||
* @return array
|
||||
*/
|
||||
public function getDayOff(DateTime $today)
|
||||
{
|
||||
foreach ($this->timeoff as $day) {
|
||||
|
||||
if ($day['date'] === $today->format('Y-m-d')) {
|
||||
return $day;
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Timetable Workweek
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TimetableDay extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'timetable_day';
|
||||
|
||||
/**
|
||||
* Get the timetable for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getByUser($user_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('start')->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new time slot in the database
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param string $start Start hour (24h format)
|
||||
* @param string $end End hour (24h format)
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function create($user_id, $start, $end)
|
||||
{
|
||||
$values = array(
|
||||
'user_id' => $user_id,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
);
|
||||
|
||||
return $this->persist(self::TABLE, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific time slot
|
||||
*
|
||||
* @access public
|
||||
* @param integer $slot_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($slot_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $slot_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('user_id', t('Field required')),
|
||||
new Validators\Required('start', t('Field required')),
|
||||
new Validators\Required('end', t('Field required')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Timetable over-time
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TimetableExtra extends TimetableOff
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'timetable_extra';
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Timetable time off
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TimetableOff extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'timetable_off';
|
||||
|
||||
/**
|
||||
* Get query to fetch everything (pagination)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getUserQuery($user_id)
|
||||
{
|
||||
return $this->db->table(static::TABLE)->eq('user_id', $user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timetable for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getByUser($user_id)
|
||||
{
|
||||
return $this->db->table(static::TABLE)->eq('user_id', $user_id)->desc('date')->asc('start')->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timetable for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param string $start_date
|
||||
* @param string $end_date
|
||||
* @return array
|
||||
*/
|
||||
public function getByUserAndDate($user_id, $start_date, $end_date)
|
||||
{
|
||||
return $this->db->table(static::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->gte('date', $start_date)
|
||||
->lte('date', $end_date)
|
||||
->desc('date')
|
||||
->asc('start')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new time slot in the database
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param string $date Day (ISO8601 format)
|
||||
* @param boolean $all_day All day flag
|
||||
* @param float $start Start hour (24h format)
|
||||
* @param float $end End hour (24h format)
|
||||
* @param string $comment
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function create($user_id, $date, $all_day, $start = '', $end = '', $comment = '')
|
||||
{
|
||||
$values = array(
|
||||
'user_id' => $user_id,
|
||||
'date' => $date,
|
||||
'all_day' => (int) $all_day, // Postgres fix
|
||||
'start' => $all_day ? '' : $start,
|
||||
'end' => $all_day ? '' : $end,
|
||||
'comment' => $comment,
|
||||
);
|
||||
|
||||
return $this->persist(static::TABLE, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific time slot
|
||||
*
|
||||
* @access public
|
||||
* @param integer $slot_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($slot_id)
|
||||
{
|
||||
return $this->db->table(static::TABLE)->eq('id', $slot_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('user_id', t('Field required')),
|
||||
new Validators\Required('date', t('Field required')),
|
||||
new Validators\Numeric('all_day', t('This value must be numeric')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Timetable Workweek
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TimetableWeek extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'timetable_week';
|
||||
|
||||
/**
|
||||
* Get the timetable for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getByUser($user_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('day')->asc('start')->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new time slot in the database
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param string $day Day of the week (ISO-8601)
|
||||
* @param string $start Start hour (24h format)
|
||||
* @param string $end End hour (24h format)
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function create($user_id, $day, $start, $end)
|
||||
{
|
||||
$values = array(
|
||||
'user_id' => $user_id,
|
||||
'day' => $day,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
);
|
||||
|
||||
return $this->persist(self::TABLE, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific time slot
|
||||
*
|
||||
* @access public
|
||||
* @param integer $slot_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($slot_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $slot_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('user_id', t('Field required')),
|
||||
new Validators\Required('day', t('Field required')),
|
||||
new Validators\Numeric('day', t('This value must be numeric')),
|
||||
new Validators\Required('start', t('Field required')),
|
||||
new Validators\Required('end', t('Field required')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -321,52 +321,6 @@ function version_53($pdo)
|
|||
$pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent FLOAT DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_51($pdo)
|
||||
{
|
||||
$pdo->exec('CREATE TABLE timetable_day (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
start VARCHAR(5) NOT NULL,
|
||||
end VARCHAR(5) NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE=InnoDB CHARSET=utf8');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_week (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
day INT NOT NULL,
|
||||
start VARCHAR(5) NOT NULL,
|
||||
end VARCHAR(5) NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE=InnoDB CHARSET=utf8');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_off (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
date VARCHAR(10) NOT NULL,
|
||||
all_day TINYINT(1) DEFAULT 0,
|
||||
start VARCHAR(5) DEFAULT 0,
|
||||
end VARCHAR(5) DEFAULT 0,
|
||||
comment TEXT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE=InnoDB CHARSET=utf8');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_extra (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
date VARCHAR(10) NOT NULL,
|
||||
all_day TINYINT(1) DEFAULT 0,
|
||||
start VARCHAR(5) DEFAULT 0,
|
||||
end VARCHAR(5) DEFAULT 0,
|
||||
comment TEXT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE=InnoDB CHARSET=utf8');
|
||||
}
|
||||
|
||||
function version_49($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
|
||||
|
|
|
|||
|
|
@ -314,48 +314,6 @@ function version_34($pdo)
|
|||
$pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent REAL DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_32($pdo)
|
||||
{
|
||||
$pdo->exec('CREATE TABLE timetable_day (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"start" VARCHAR(5) NOT NULL,
|
||||
"end" VARCHAR(5) NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_week (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"day" INTEGER NOT NULL,
|
||||
"start" VARCHAR(5) NOT NULL,
|
||||
"end" VARCHAR(5) NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_off (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"date" VARCHAR(10) NOT NULL,
|
||||
"all_day" BOOLEAN DEFAULT \'0\',
|
||||
"start" VARCHAR(5) DEFAULT 0,
|
||||
"end" VARCHAR(5) DEFAULT 0,
|
||||
"comment" TEXT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_extra (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"date" VARCHAR(10) NOT NULL,
|
||||
"all_day" BOOLEAN DEFAULT \'0\',
|
||||
"start" VARCHAR(5) DEFAULT 0,
|
||||
"end" VARCHAR(5) DEFAULT 0,
|
||||
"comment" TEXT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
}
|
||||
|
||||
function version_30($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
|
||||
|
|
|
|||
|
|
@ -291,48 +291,6 @@ function version_52($pdo)
|
|||
$pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent REAL DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_50($pdo)
|
||||
{
|
||||
$pdo->exec('CREATE TABLE timetable_day (
|
||||
"id" INTEGER PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"start" TEXT NOT NULL,
|
||||
"end" TEXT NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_week (
|
||||
"id" INTEGER PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"day" INTEGER NOT NULL,
|
||||
"start" TEXT NOT NULL,
|
||||
"end" TEXT NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_off (
|
||||
"id" INTEGER PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"date" TEXT NOT NULL,
|
||||
"all_day" INTEGER DEFAULT 0,
|
||||
"start" TEXT DEFAULT 0,
|
||||
"end" TEXT DEFAULT 0,
|
||||
"comment" TEXT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$pdo->exec('CREATE TABLE timetable_extra (
|
||||
"id" INTEGER PRIMARY KEY,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"date" TEXT NOT NULL,
|
||||
"all_day" INTEGER DEFAULT 0,
|
||||
"start" TEXT DEFAULT 0,
|
||||
"end" TEXT DEFAULT 0,
|
||||
"comment" TEXT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
}
|
||||
|
||||
function version_48($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
|
||||
|
|
|
|||
|
|
@ -56,11 +56,6 @@ class ClassProvider implements ServiceProviderInterface
|
|||
'TaskPosition',
|
||||
'TaskStatus',
|
||||
'TaskValidator',
|
||||
'Timetable',
|
||||
'TimetableDay',
|
||||
'TimetableExtra',
|
||||
'TimetableWeek',
|
||||
'TimetableOff',
|
||||
'Transition',
|
||||
'User',
|
||||
'UserSession',
|
||||
|
|
|
|||
|
|
@ -6,22 +6,24 @@
|
|||
|
||||
<?= $this->form->csrf() ?>
|
||||
|
||||
<h3><?= t('Project calendar view') ?></h3>
|
||||
<div class="listing">
|
||||
<h3><?= t('Project calendar view') ?></h3>
|
||||
<?= $this->form->radios('calendar_project_tasks', array(
|
||||
'date_creation' => t('Show tasks based on the creation date'),
|
||||
'date_started' => t('Show tasks based on the start date'),
|
||||
), $values) ?>
|
||||
</div>
|
||||
|
||||
<h3><?= t('User calendar view') ?></h3>
|
||||
<div class="listing">
|
||||
<h3><?= t('User calendar view') ?></h3>
|
||||
<?= $this->form->radios('calendar_user_tasks', array(
|
||||
'date_creation' => t('Show tasks based on the creation date'),
|
||||
'date_started' => t('Show tasks based on the start date'),
|
||||
), $values) ?>
|
||||
</div>
|
||||
|
||||
<h4><?= t('Subtasks time tracking') ?></h4>
|
||||
<div class="listing">
|
||||
<h3><?= t('Subtasks time tracking') ?></h3>
|
||||
<?= $this->form->checkbox('calendar_user_subtasks_time_tracking', t('Show subtasks based on the time tracking'), 1, $values['calendar_user_subtasks_time_tracking'] == 1) ?>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Timetable') ?></h2>
|
||||
<ul>
|
||||
<li><?= $this->url->link(t('Day timetable'), 'timetableday', 'index', array('user_id' => $user['id'])) ?></li>
|
||||
<li><?= $this->url->link(t('Week timetable'), 'timetableweek', 'index', array('user_id' => $user['id'])) ?></li>
|
||||
<li><?= $this->url->link(t('Time off timetable'), 'timetableoff', 'index', array('user_id' => $user['id'])) ?></li>
|
||||
<li><?= $this->url->link(t('Overtime timetable'), 'timetableextra', 'index', array('user_id' => $user['id'])) ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form method="get" action="?" autocomplete="off" class="form-inline">
|
||||
|
||||
<?= $this->form->hidden('controller', $values) ?>
|
||||
<?= $this->form->hidden('action', $values) ?>
|
||||
<?= $this->form->hidden('user_id', $values) ?>
|
||||
|
||||
<?= $this->form->label(t('From'), 'from') ?>
|
||||
<?= $this->form->text('from', $values, array(), array(), 'form-date') ?>
|
||||
|
||||
<?= $this->form->label(t('To'), 'to') ?>
|
||||
<?= $this->form->text('to', $values, array(), array(), 'form-date') ?>
|
||||
|
||||
<input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/>
|
||||
</form>
|
||||
|
||||
<?php if (! empty($timetable)): ?>
|
||||
<hr/>
|
||||
<h3><?= t('Work timetable') ?></h3>
|
||||
<table class="table-fixed table-stripped">
|
||||
<tr>
|
||||
<th><?= t('Day') ?></th>
|
||||
<th><?= t('Start') ?></th>
|
||||
<th><?= t('End') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($timetable as $slot): ?>
|
||||
<tr>
|
||||
<td><?= dt('%B %e, %Y', $slot[0]->getTimestamp()) ?></td>
|
||||
<td><?= dt('%k:%M %p', $slot[0]->getTimestamp()) ?></td>
|
||||
<td><?= dt('%k:%M %p', $slot[1]->getTimestamp()) ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<?php endif ?>
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Day timetable') ?></h2>
|
||||
</div>
|
||||
|
||||
<?php if (! empty($timetable)): ?>
|
||||
|
||||
<table class="table-fixed table-stripped">
|
||||
<tr>
|
||||
<th><?= t('Start time') ?></th>
|
||||
<th><?= t('End time') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($timetable as $slot): ?>
|
||||
<tr>
|
||||
<td><?= $slot['start'] ?></td>
|
||||
<td><?= $slot['end'] ?></td>
|
||||
<td>
|
||||
<?= $this->url->link(t('Remove'), 'timetableday', 'confirm', array('user_id' => $user['id'], 'slot_id' => $slot['id'])) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<h3><?= t('Add new time slot') ?></h3>
|
||||
<?php endif ?>
|
||||
|
||||
<form method="post" action="<?= $this->url->href('timetableday', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->form->hidden('user_id', $values) ?>
|
||||
<?= $this->form->csrf() ?>
|
||||
|
||||
<?= $this->form->label(t('Start time'), 'start') ?>
|
||||
<?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('End time'), 'end') ?>
|
||||
<?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="alert alert-info">
|
||||
<?= t('This timetable is used when the checkbox "all day" is checked for scheduled time off and overtime.') ?>
|
||||
</p>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Remove time slot') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info"><?= t('Do you really want to remove this time slot?') ?></p>
|
||||
|
||||
<div class="form-actions">
|
||||
<?= $this->url->link(t('Yes'), 'timetableday', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?>
|
||||
<?= t('or') ?>
|
||||
<?= $this->url->link(t('cancel'), 'timetableday', 'index', array('user_id' => $user['id'])) ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Overtime timetable') ?></h2>
|
||||
</div>
|
||||
|
||||
<?php if (! $paginator->isEmpty()): ?>
|
||||
|
||||
<table class="table-fixed table-stripped">
|
||||
<tr>
|
||||
<th><?= $paginator->order(t('Day'), 'Day') ?></th>
|
||||
<th><?= $paginator->order(t('All day'), 'all_day') ?></th>
|
||||
<th><?= $paginator->order(t('Start time'), 'start') ?></th>
|
||||
<th><?= $paginator->order(t('End time'), 'end') ?></th>
|
||||
<th class="column-40"><?= t('Comment') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($paginator->getCollection() as $slot): ?>
|
||||
<tr>
|
||||
<td><?= $slot['date'] ?></td>
|
||||
<td><?= $slot['all_day'] == 1 ? t('Yes') : t('No') ?></td>
|
||||
<td><?= $slot['start'] ?></td>
|
||||
<td><?= $slot['end'] ?></td>
|
||||
<td><?= $this->e($slot['comment']) ?></td>
|
||||
<td>
|
||||
<?= $this->url->link(t('Remove'), 'timetableextra', 'confirm', array('user_id' => $user['id'], 'slot_id' => $slot['id'])) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<?= $paginator ?>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<form method="post" action="<?= $this->url->href('timetableextra', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->form->hidden('user_id', $values) ?>
|
||||
<?= $this->form->csrf() ?>
|
||||
|
||||
<?= $this->form->label(t('Day'), 'date') ?>
|
||||
<?= $this->form->text('date', $values, $errors, array('required'), 'form-date') ?>
|
||||
|
||||
<?= $this->form->checkbox('all_day', t('All day'), 1) ?>
|
||||
|
||||
<?= $this->form->label(t('Start time'), 'start') ?>
|
||||
<?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('End time'), 'end') ?>
|
||||
<?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('Comment'), 'comment') ?>
|
||||
<?= $this->form->text('comment', $values, $errors) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Remove time slot') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info"><?= t('Do you really want to remove this time slot?') ?></p>
|
||||
|
||||
<div class="form-actions">
|
||||
<?= $this->url->link(t('Yes'), 'timetableextra', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?>
|
||||
<?= t('or') ?>
|
||||
<?= $this->url->link(t('cancel'), 'timetableextra', 'index', array('user_id' => $user['id'])) ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Time off timetable') ?></h2>
|
||||
</div>
|
||||
|
||||
<?php if (! $paginator->isEmpty()): ?>
|
||||
|
||||
<table class="table-fixed table-stripped">
|
||||
<tr>
|
||||
<th><?= $paginator->order(t('Day'), 'Day') ?></th>
|
||||
<th><?= $paginator->order(t('All day'), 'all_day') ?></th>
|
||||
<th><?= $paginator->order(t('Start time'), 'start') ?></th>
|
||||
<th><?= $paginator->order(t('End time'), 'end') ?></th>
|
||||
<th class="column-40"><?= t('Comment') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($paginator->getCollection() as $slot): ?>
|
||||
<tr>
|
||||
<td><?= $slot['date'] ?></td>
|
||||
<td><?= $slot['all_day'] == 1 ? t('Yes') : t('No') ?></td>
|
||||
<td><?= $slot['start'] ?></td>
|
||||
<td><?= $slot['end'] ?></td>
|
||||
<td><?= $this->e($slot['comment']) ?></td>
|
||||
<td>
|
||||
<?= $this->url->link(t('Remove'), 'timetableoff', 'confirm', array('user_id' => $user['id'], 'slot_id' => $slot['id'])) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<?= $paginator ?>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<form method="post" action="<?= $this->url->href('timetableoff', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->form->hidden('user_id', $values) ?>
|
||||
<?= $this->form->csrf() ?>
|
||||
|
||||
<?= $this->form->label(t('Day'), 'date') ?>
|
||||
<?= $this->form->text('date', $values, $errors, array('required'), 'form-date') ?>
|
||||
|
||||
<?= $this->form->checkbox('all_day', t('All day'), 1) ?>
|
||||
|
||||
<?= $this->form->label(t('Start time'), 'start') ?>
|
||||
<?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('End time'), 'end') ?>
|
||||
<?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('Comment'), 'comment') ?>
|
||||
<?= $this->form->text('comment', $values, $errors) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Remove time slot') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info"><?= t('Do you really want to remove this time slot?') ?></p>
|
||||
|
||||
<div class="form-actions">
|
||||
<?= $this->url->link(t('Yes'), 'timetableoff', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?>
|
||||
<?= t('or') ?>
|
||||
<?= $this->url->link(t('cancel'), 'timetableoff', 'index', array('user_id' => $user['id'])) ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Week timetable') ?></h2>
|
||||
</div>
|
||||
|
||||
<?php if (! empty($timetable)): ?>
|
||||
|
||||
<table class="table-fixed table-stripped">
|
||||
<tr>
|
||||
<th><?= t('Day') ?></th>
|
||||
<th><?= t('Start time') ?></th>
|
||||
<th><?= t('End time') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($timetable as $slot): ?>
|
||||
<tr>
|
||||
<td><?= $this->dt->getWeekDay($slot['day']) ?></td>
|
||||
<td><?= $slot['start'] ?></td>
|
||||
<td><?= $slot['end'] ?></td>
|
||||
<td>
|
||||
<?= $this->url->link(t('Remove'), 'timetableweek', 'confirm', array('user_id' => $user['id'], 'slot_id' => $slot['id'])) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<h3><?= t('Add new time slot') ?></h3>
|
||||
<?php endif ?>
|
||||
|
||||
<form method="post" action="<?= $this->url->href('timetableweek', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->form->hidden('user_id', $values) ?>
|
||||
<?= $this->form->csrf() ?>
|
||||
|
||||
<?= $this->form->label(t('Day'), 'day') ?>
|
||||
<?= $this->form->select('day', $this->dt->getWeekDays(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('Start time'), 'start') ?>
|
||||
<?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('End time'), 'end') ?>
|
||||
<?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Remove time slot') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info"><?= t('Do you really want to remove this time slot?') ?></p>
|
||||
|
||||
<div class="form-actions">
|
||||
<?= $this->url->link(t('Yes'), 'timetableweek', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?>
|
||||
<?= t('or') ?>
|
||||
<?= $this->url->link(t('cancel'), 'timetableweek', 'index', array('user_id' => $user['id'])) ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -62,9 +62,6 @@
|
|||
<li <?= $this->app->getRouterController() === 'user' && $this->app->getRouterAction() === 'authentication' ? 'class="active"' : '' ?>>
|
||||
<?= $this->url->link(t('Edit Authentication'), 'user', 'authentication', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<li <?= $this->app->getRouterController() === 'timetable' ? 'class="active"' : '' ?>>
|
||||
<?= $this->url->link(t('Manage timetable'), 'timetable', 'index', array('user_id' => $user['id'])) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
|
||||
<?= $this->hook->render('template:user:sidebar:actions', array('user' => $user)) ?>
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
Budget management
|
||||
=================
|
||||
|
||||
Budget management is based on the subtask time tracking, the user timetable and the user hourly rate.
|
||||
|
||||
This section is available from project settings page: **Project > Budget**. There is also a shortcut from the dropdown menu on the board.
|
||||
|
||||
Budget lines
|
||||
------------
|
||||
|
||||

|
||||
|
||||
Budget lines are used to define a budget for the project.
|
||||
This budget can be adjusted by adding a new entry with an effective date.
|
||||
|
||||
Cost breakdown
|
||||
--------------
|
||||
|
||||

|
||||
|
||||
Based on the subtask time tracking table and user information you can see the cost of each subtask.
|
||||
|
||||
The time spent is rounded to nearest quarter.
|
||||
|
||||
Budget graph
|
||||
------------
|
||||
|
||||

|
||||
|
||||
Finally, by combining all information we can generate a graph:
|
||||
|
||||
- Expenses represents user cost
|
||||
- Budget lines are the provisioned budget
|
||||
- Remaining is the budget left at the given time
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
Gestion du budget
|
||||
=================
|
||||
|
||||
La gestion du budget repose sur le suivi du temps d'une sous-tâche, l'emploi du temps de l'utilisateur et le taux horaire de l'utilisateur.
|
||||
|
||||
Cette section est accessible depuis la page de paramètres du projet : **Project > Budget**. Il existe également un raccourci depuis le menu déroulant sur le tableau.
|
||||
|
||||
Lignes budgétaires
|
||||
------------
|
||||
|
||||

|
||||
|
||||
Les lignes budgétaires sont utilisées pour définir le budget du projet.
|
||||
Celui-ci peut être ajusté en ajoutant une nouvelle entrée avec une date effective.
|
||||
|
||||
Détail des coûts
|
||||
--------------
|
||||
|
||||

|
||||
|
||||
Selon le tableau qui donne le suivi temporel de la sous-tâche et les informations sur l'utilisateur vous pouvez voir le coût de chaque sous-tâche.
|
||||
|
||||
Le temps passé est arrondi au quart d'heure le plus proche.
|
||||
|
||||
Graphique du budget
|
||||
------------
|
||||
|
||||

|
||||
|
||||
Finalement, en combinant toutes les informations nous pouvons générer un graphique :
|
||||
|
||||
- Les dépenses représentent le coût utilisateur
|
||||
- Les lignes budgétaires sont le budget prévisionnel
|
||||
- Le restant est le budget qui reste après un délai donné
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
Hourly Rate
|
||||
===========
|
||||
|
||||
Each user can have a predefined hourly rate.
|
||||
This feature is used for budget calculation.
|
||||
|
||||
To define a new price, go to **User profile > Hourly rates**.
|
||||
|
||||

|
||||
|
||||
Each hourly rate can have an effective date and and different currency.
|
||||
|
|
@ -26,7 +26,6 @@ Using Kanboard
|
|||
- [Project permissions](project-permissions.markdown)
|
||||
- [Swimlanes](swimlanes.markdown)
|
||||
- [Calendar](calendar.markdown)
|
||||
- [Budget](budget.markdown)
|
||||
- [Analytics](analytics.markdown)
|
||||
- [Gantt chart for tasks](gantt-chart-tasks.markdown)
|
||||
- [Gantt chart for projects](gantt-chart-projects.markdown)
|
||||
|
|
@ -49,8 +48,6 @@ Using Kanboard
|
|||
|
||||
- [User management](user-management.markdown)
|
||||
- [Notifications](notifications.markdown)
|
||||
- [Hourly rate](hourly-rate.markdown)
|
||||
- [Timetable](timetable.markdown)
|
||||
- [Two factor authentication](2fa.markdown)
|
||||
|
||||
### Settings
|
||||
|
|
|
|||
|
|
@ -94,6 +94,27 @@ $this->hook->on('hook_name', $callable);
|
|||
|
||||
The first argument is the name of the hook and the second is a PHP callable.
|
||||
|
||||
### Hooks executed only one time
|
||||
|
||||
Some hooks can have only one listener:
|
||||
|
||||
#### model:subtask-time-tracking:calculate:time-spent
|
||||
|
||||
- Override time spent calculation when subtask timer is stopped
|
||||
- Arguments:
|
||||
- `$user_id` (integer)
|
||||
- `$start` (DateTime)
|
||||
- `$end` (DateTime)
|
||||
|
||||
#### model:subtask-time-tracking:calendar:events
|
||||
|
||||
- Override subtask time tracking events to display the calendar
|
||||
- Arguments:
|
||||
- `$user_id` (integer)
|
||||
- `$events` (array)
|
||||
- `$start` (string, ISO-8601 format)
|
||||
- `$end` (string, ISO-8601 format)
|
||||
|
||||
### Merge hooks
|
||||
|
||||
"Merge hooks" act in the same way as the function `array_merge`. The hook callback must return an array. This array will be merged with the default one.
|
||||
|
|
@ -313,5 +334,7 @@ Kanboard will compare the version defined in your schema and the version stored
|
|||
Examples of plugins
|
||||
-------------------
|
||||
|
||||
- Budget planning: https://github.com/kanboard/plugin-budget
|
||||
- Theme plugin sample: https://github.com/kanboard/plugin-example-theme
|
||||
- [Budget planning](https://github.com/kanboard/plugin-budget)
|
||||
- [User timetable](https://github.com/kanboard/plugin-timetable)
|
||||
- [Subtask Forecast](https://github.com/kanboard/plugin-subtask-forecast)
|
||||
- [Theme plugin sample](https://github.com/kanboard/plugin-example-theme)
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
User Timetable
|
||||
==============
|
||||
|
||||
Each user can have a predefined timetable.
|
||||
This feature mainly is used for time tracking, project budget calculation and to display subtasks in the calendar.
|
||||
|
||||
Each user have his own timetable. At the moment, that need to be specified manually for each person.
|
||||
You can also schedule time-off or overtime.
|
||||
|
||||
The timetable section is available from the user profile: **User profile > Timetable**.
|
||||
|
||||
Work timetable
|
||||
--------------
|
||||
|
||||
This timetable is dynamically calculated according to the regular week timetable, time-off and overtime.
|
||||
|
||||

|
||||
|
||||
Week timetable
|
||||
--------------
|
||||
|
||||

|
||||
|
||||
The week timetable is used to define regular work hours for the selected user.
|
||||
|
||||
To add a new time slot, just select the day of the week and the time range.
|
||||
|
||||
Time off timetable
|
||||
------------------
|
||||
|
||||
The time-off timetable is used to schedule not worked time slot.
|
||||
This time is deducted from the regular work hours.
|
||||
|
||||
When you check the box "All day", the regular day timetable is used to define the regular work hours.
|
||||
|
||||
Overtime timetable
|
||||
------------------
|
||||
|
||||

|
||||
|
||||
The overtime timetable is used to define worked hours outside of regular hours.
|
||||
|
||||
Day timetable
|
||||
-------------
|
||||
|
||||
This timetable is used when the checkbox "All day" is checked for overtime and time-off entries.
|
||||
|
|
@ -17,6 +17,16 @@ class HookTest extends Base
|
|||
$this->assertEquals(array('A', 'B'), $h->getListeners('myhook'));
|
||||
}
|
||||
|
||||
public function testExists()
|
||||
{
|
||||
$h = new Hook;
|
||||
$this->assertFalse($h->exists('myhook'));
|
||||
|
||||
$h->on('myhook', 'A');
|
||||
|
||||
$this->assertTrue($h->exists('myhook'));
|
||||
}
|
||||
|
||||
public function testMergeWithNoBinding()
|
||||
{
|
||||
$h = new Hook;
|
||||
|
|
@ -59,4 +69,28 @@ class HookTest extends Base
|
|||
$this->assertEquals($expected, $result);
|
||||
$this->assertEquals($expected, $values);
|
||||
}
|
||||
|
||||
public function testFirstWithNoBinding()
|
||||
{
|
||||
$h = new Hook;
|
||||
|
||||
$result = $h->first('myhook', array('p' => 2));
|
||||
$this->assertEquals(null, $result);
|
||||
}
|
||||
|
||||
public function testFirstWithMultipleBindings()
|
||||
{
|
||||
$h = new Hook;
|
||||
|
||||
$h->on('myhook', function($p) {
|
||||
return $p + 1;
|
||||
});
|
||||
|
||||
$h->on('myhook', function($p) {
|
||||
return $p;
|
||||
});
|
||||
|
||||
$result = $h->first('myhook', array('p' => 3));
|
||||
$this->assertEquals(4, $result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,256 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__.'/../Base.php';
|
||||
|
||||
use Model\User;
|
||||
use Model\Timetable;
|
||||
use Model\TimetableDay;
|
||||
use Model\TimetableWeek;
|
||||
use Model\TimetableOff;
|
||||
use Model\TimetableExtra;
|
||||
|
||||
class TimetableTest extends Base
|
||||
{
|
||||
public function testCalculateWorkDays()
|
||||
{
|
||||
$w = new TimetableWeek($this->container);
|
||||
$t = new Timetable($this->container);
|
||||
|
||||
$this->assertNotFalse($w->create(1, 1, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 1, '13:00', '17:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '13:00', '17:00'));
|
||||
|
||||
$monday = new DateTime('next Monday');
|
||||
|
||||
$timetable = $t->calculate(1, $monday, new DateTime('next Monday + 6 days'));
|
||||
$this->assertNotEmpty($timetable);
|
||||
$this->assertCount(4, $timetable);
|
||||
|
||||
$this->assertEquals($monday->format('Y-m-d').' 09:30', $timetable[0][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 12:00', $timetable[0][1]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 13:00', $timetable[1][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 17:00', $timetable[1][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($monday->add(new DateInterval('P1D'))->format('Y-m-d').' 09:30', $timetable[2][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 12:00', $timetable[2][1]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 13:00', $timetable[3][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 17:00', $timetable[3][1]->format('Y-m-d H:i'));
|
||||
}
|
||||
|
||||
public function testCalculateOverTime()
|
||||
{
|
||||
$d = new TimetableDay($this->container);
|
||||
$w = new TimetableWeek($this->container);
|
||||
$e = new TimetableExtra($this->container);
|
||||
$t = new Timetable($this->container);
|
||||
|
||||
$monday = new DateTime('next Monday');
|
||||
$tuesday = new DateTime('next Monday + 1 day');
|
||||
$friday = new DateTime('next Monday + 4 days');
|
||||
|
||||
$this->assertNotFalse($d->create(1, '08:00', '12:00'));
|
||||
$this->assertNotFalse($d->create(1, '14:00', '18:00'));
|
||||
|
||||
$this->assertNotFalse($w->create(1, 1, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 1, '13:00', '17:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '13:00', '17:00'));
|
||||
|
||||
$this->assertNotFalse($e->create(1, $tuesday->format('Y-m-d'), 0, '17:00', '22:00'));
|
||||
$this->assertNotFalse($e->create(1, $friday->format('Y-m-d'), 1));
|
||||
|
||||
$timetable = $t->calculate(1, $monday, new DateTime('next Monday + 6 days'));
|
||||
$this->assertNotEmpty($timetable);
|
||||
$this->assertCount(7, $timetable);
|
||||
|
||||
$this->assertEquals($monday->format('Y-m-d').' 09:30', $timetable[0][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 12:00', $timetable[0][1]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 13:00', $timetable[1][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 17:00', $timetable[1][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 09:30', $timetable[2][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 12:00', $timetable[2][1]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 13:00', $timetable[3][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 17:00', $timetable[3][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 17:00', $timetable[4][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 22:00', $timetable[4][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($friday->format('Y-m-d').' 08:00', $timetable[5][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($friday->format('Y-m-d').' 12:00', $timetable[5][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($friday->format('Y-m-d').' 14:00', $timetable[6][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($friday->format('Y-m-d').' 18:00', $timetable[6][1]->format('Y-m-d H:i'));
|
||||
}
|
||||
|
||||
public function testCalculateTimeOff()
|
||||
{
|
||||
$d = new TimetableDay($this->container);
|
||||
$w = new TimetableWeek($this->container);
|
||||
$o = new TimetableOff($this->container);
|
||||
$t = new Timetable($this->container);
|
||||
|
||||
$monday = new DateTime('next Monday');
|
||||
$tuesday = new DateTime('next Monday + 1 day');
|
||||
$friday = new DateTime('next Monday + 4 days');
|
||||
|
||||
$this->assertNotFalse($d->create(1, '08:00', '12:00'));
|
||||
$this->assertNotFalse($d->create(1, '14:00', '18:00'));
|
||||
|
||||
$this->assertNotFalse($w->create(1, 1, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 1, '13:00', '17:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '13:00', '17:00'));
|
||||
$this->assertNotFalse($w->create(1, 5, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 5, '13:00', '17:00'));
|
||||
|
||||
$this->assertNotFalse($o->create(1, $tuesday->format('Y-m-d'), 0, '14:00', '15:00'));
|
||||
$this->assertNotFalse($o->create(1, $monday->format('Y-m-d'), 1));
|
||||
|
||||
$timetable = $t->calculate(1, $monday, new DateTime('next Monday + 6 days'));
|
||||
$this->assertNotEmpty($timetable);
|
||||
$this->assertCount(5, $timetable);
|
||||
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 09:30', $timetable[0][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 12:00', $timetable[0][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 13:00', $timetable[1][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 14:00', $timetable[1][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 15:00', $timetable[2][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 17:00', $timetable[2][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($friday->format('Y-m-d').' 09:30', $timetable[3][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($friday->format('Y-m-d').' 12:00', $timetable[3][1]->format('Y-m-d H:i'));
|
||||
|
||||
$this->assertEquals($friday->format('Y-m-d').' 13:00', $timetable[4][0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($friday->format('Y-m-d').' 17:00', $timetable[4][1]->format('Y-m-d H:i'));
|
||||
}
|
||||
|
||||
public function testClosestTimeSlot()
|
||||
{
|
||||
$w = new TimetableWeek($this->container);
|
||||
$t = new Timetable($this->container);
|
||||
|
||||
$this->assertNotFalse($w->create(1, 1, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 1, '13:00', '17:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '13:00', '17:00'));
|
||||
|
||||
$monday = new DateTime('next Monday');
|
||||
$tuesday = new DateTime('next Monday + 1 day');
|
||||
|
||||
$timetable = $t->calculate(1, new DateTime('next Monday'), new DateTime('next Monday + 6 days'));
|
||||
$this->assertNotEmpty($timetable);
|
||||
$this->assertCount(4, $timetable);
|
||||
|
||||
// Start to work before timetable
|
||||
$date = clone($monday);
|
||||
$date->setTime(5, 02);
|
||||
|
||||
$slot = $t->findClosestTimeSlot($date, $timetable);
|
||||
$this->assertNotEmpty($slot);
|
||||
$this->assertEquals($monday->format('Y-m-d').' 09:30', $slot[0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 12:00', $slot[1]->format('Y-m-d H:i'));
|
||||
|
||||
// Start to work at the end of the timeslot
|
||||
$date = clone($monday);
|
||||
$date->setTime(12, 02);
|
||||
|
||||
$slot = $t->findClosestTimeSlot($date, $timetable);
|
||||
$this->assertNotEmpty($slot);
|
||||
$this->assertEquals($monday->format('Y-m-d').' 09:30', $slot[0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 12:00', $slot[1]->format('Y-m-d H:i'));
|
||||
|
||||
// Start to work at lunch time
|
||||
$date = clone($monday);
|
||||
$date->setTime(12, 32);
|
||||
|
||||
$slot = $t->findClosestTimeSlot($date, $timetable);
|
||||
$this->assertNotEmpty($slot);
|
||||
$this->assertEquals($monday->format('Y-m-d').' 13:00', $slot[0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($monday->format('Y-m-d').' 17:00', $slot[1]->format('Y-m-d H:i'));
|
||||
|
||||
// Start to work early in the morning
|
||||
$date = clone($tuesday);
|
||||
$date->setTime(8, 02);
|
||||
|
||||
$slot = $t->findClosestTimeSlot($date, $timetable);
|
||||
$this->assertNotEmpty($slot);
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 09:30', $slot[0]->format('Y-m-d H:i'));
|
||||
$this->assertEquals($tuesday->format('Y-m-d').' 12:00', $slot[1]->format('Y-m-d H:i'));
|
||||
}
|
||||
|
||||
public function testCalculateDuration()
|
||||
{
|
||||
$w = new TimetableWeek($this->container);
|
||||
$t = new Timetable($this->container);
|
||||
|
||||
$this->assertNotFalse($w->create(1, 1, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 1, '13:00', '17:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '09:30', '12:00'));
|
||||
$this->assertNotFalse($w->create(1, 2, '13:00', '17:00'));
|
||||
|
||||
$monday = new DateTime('next Monday');
|
||||
$tuesday = new DateTime('next Monday + 1 day');
|
||||
|
||||
// Different day
|
||||
$start = clone($monday);
|
||||
$start->setTime(16, 02);
|
||||
|
||||
$end = clone($tuesday);
|
||||
$end->setTime(10, 03);
|
||||
|
||||
$this->assertEquals(1.5, $t->calculateEffectiveDuration(1, $start, $end));
|
||||
|
||||
// Same time slot
|
||||
$start = clone($monday);
|
||||
$start->setTime(16, 02);
|
||||
|
||||
$end = clone($monday);
|
||||
$end->setTime(17, 03);
|
||||
|
||||
$this->assertEquals(1, $t->calculateEffectiveDuration(1, $start, $end));
|
||||
|
||||
// Intermediate time slot
|
||||
$start = clone($monday);
|
||||
$start->setTime(10, 02);
|
||||
|
||||
$end = clone($tuesday);
|
||||
$end->setTime(16, 03);
|
||||
|
||||
$this->assertEquals(11.5, $t->calculateEffectiveDuration(1, $start, $end));
|
||||
|
||||
// Different day
|
||||
$start = clone($monday);
|
||||
$start->setTime(9, 02);
|
||||
|
||||
$end = clone($tuesday);
|
||||
$end->setTime(10, 03);
|
||||
|
||||
$this->assertEquals(7, $t->calculateEffectiveDuration(1, $start, $end));
|
||||
|
||||
// Start before first time slot
|
||||
$start = clone($monday);
|
||||
$start->setTime(5, 32);
|
||||
|
||||
$end = clone($tuesday);
|
||||
$end->setTime(11, 17);
|
||||
|
||||
$this->assertEquals(8.25, $t->calculateEffectiveDuration(1, $start, $end));
|
||||
}
|
||||
|
||||
public function testCalculateDurationWithEmptyTimetable()
|
||||
{
|
||||
$t = new Timetable($this->container);
|
||||
|
||||
$start = new DateTime('next Monday');
|
||||
$start->setTime(16, 02);
|
||||
|
||||
$end = new DateTime('next Monday');
|
||||
$end->setTime(17, 03);
|
||||
|
||||
$this->assertEquals(1, $t->calculateEffectiveDuration(1, $start, $end));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue