diff options
author | Frederic Guillot <fred@kanboard.net> | 2015-03-09 21:37:10 -0400 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2015-03-09 21:37:10 -0400 |
commit | c870508923f62e90a81fe39d923d7776dd1e9634 (patch) | |
tree | f0c8966fe226b5c48730c29d3eaefb51a9010b8b /app | |
parent | 732899564517c6efd9ef965f3f843309a222ce50 (diff) |
Add user timetables
Diffstat (limited to 'app')
42 files changed, 1876 insertions, 5 deletions
diff --git a/app/Controller/Base.php b/app/Controller/Base.php index e175bc40..ec202c06 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -57,6 +57,11 @@ use Symfony\Component\EventDispatcher\Event; * @property \Model\TaskPosition $taskPosition * @property \Model\TaskPermission $taskPermission * @property \Model\TaskStatus $taskStatus + * @property \Model\Timetable $timetable + * @property \Model\TimetableDay $timetableDay + * @property \Model\TimetableWeek $timetableWeek + * @property \Model\TimetableExtra $timetableExtra + * @property \Model\TimetableOff $timetableOff * @property \Model\TaskValidator $taskValidator * @property \Model\TaskLink $taskLink * @property \Model\CommentHistory $commentHistory diff --git a/app/Controller/HourlyRate.php b/app/Controller/HourlyRate.php index f8f88d17..8d96e5ca 100644 --- a/app/Controller/HourlyRate.php +++ b/app/Controller/HourlyRate.php @@ -8,7 +8,7 @@ namespace Controller; * @package controller * @author Frederic Guillot */ -class HourlyRate extends User +class Hourlyrate extends User { /** * Display rate and form diff --git a/app/Controller/Timetable.php b/app/Controller/Timetable.php new file mode 100644 index 00000000..65edb44c --- /dev/null +++ b/app/Controller/Timetable.php @@ -0,0 +1,39 @@ +<?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'], + ), + ))); + } +} diff --git a/app/Controller/Timetableday.php b/app/Controller/Timetableday.php new file mode 100644 index 00000000..eea44ae1 --- /dev/null +++ b/app/Controller/Timetableday.php @@ -0,0 +1,88 @@ +<?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('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('timetableday', 'index', array('user_id' => $user['id']))); + } +} diff --git a/app/Controller/Timetableextra.php b/app/Controller/Timetableextra.php new file mode 100644 index 00000000..c362bd1a --- /dev/null +++ b/app/Controller/Timetableextra.php @@ -0,0 +1,16 @@ +<?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'; +} diff --git a/app/Controller/Timetableoff.php b/app/Controller/Timetableoff.php new file mode 100644 index 00000000..19a6fab1 --- /dev/null +++ b/app/Controller/Timetableoff.php @@ -0,0 +1,107 @@ +<?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($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($this->controller_url, 'index', array('user_id' => $user['id']))); + } +} diff --git a/app/Controller/Timetableweek.php b/app/Controller/Timetableweek.php new file mode 100644 index 00000000..829f4402 --- /dev/null +++ b/app/Controller/Timetableweek.php @@ -0,0 +1,99 @@ +<?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('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('timetableweek', 'index', array('user_id' => $user['id']))); + } +} diff --git a/app/Core/Helper.php b/app/Core/Helper.php index 01ebb08f..78267feb 100644 --- a/app/Core/Helper.php +++ b/app/Core/Helper.php @@ -677,4 +677,53 @@ class Helper array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'redirect' => $redirect) ); } + + /** + * Get all hours for day + * + * @access public + * @return array + */ + public function getDayHours() + { + $values = array(); + + foreach (range(0, 23) as $hour) { + foreach (array(0, 30) as $minute) { + $time = sprintf('%02d:%02d', $hour, $minute); + $values[$time] = $time; + } + } + + return $values; + } + + /** + * Get all days of a week + * + * @access public + * @return array + */ + public function getWeekDays() + { + $values = array(); + + foreach (range(1, 7) as $day) { + $values[$day] = $this->getWeekDay($day); + } + + return $values; + } + + /** + * Get the localized day name from the day number + * + * @access public + * @param integer $day Day number + * @return string + */ + public function getWeekDay($day) + { + return dt('%A', strtotime('next Monday +'.($day - 1).' days')); + } } diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 9d6c7343..f31bfa08 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Ingen', 'edit' => 'rediger', 'Edit' => 'Rediger', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index 44619315..ec1d20ca 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Keines', 'edit' => 'Bearbeiten', 'Edit' => 'Bearbeiten', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index 91ffe5e8..aaacd7f8 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Ninguno', 'edit' => 'modificar', 'Edit' => 'Modificar', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index 28f96069..bd3a7d68 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Ei mikään', 'edit' => 'muokkaa', 'Edit' => 'Muokkaa', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index 50e96a47..df1d8428 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -753,4 +753,26 @@ return array( 'Unable to remove this rate.' => 'Impossible de supprimer ce taux horaire.', 'Unable to save the hourly rate.' => 'Impossible de sauvegarder ce taux horaire.', 'Hourly rate created successfully.' => 'Taux horaire créé avec succès.', + '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.', ); diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index 07d7ec3b..fed84522 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Nincs', 'edit' => 'szerkesztés', 'Edit' => 'Szerkesztés', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index c72dc9e0..d6c57371 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Nessuno', 'edit' => 'modificare', 'Edit' => 'Modificare', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index 2cfab36a..dd727d0e 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'なし', 'edit' => '変更', 'Edit' => '変更', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index 39e3bb85..deb5a280 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Brak', 'edit' => 'edytuj', 'Edit' => 'Edytuj', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index 8436514e..f81bc675 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Nenhum', 'edit' => 'editar', 'Edit' => 'Editar', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index 199160c0..a4caa545 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Отсутствует', 'edit' => 'изменить', 'Edit' => 'Изменить', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index 0fc9dfeb..47ac8b26 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Ingen', 'edit' => 'redigera', 'Edit' => 'Redigera', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index 5f3d7c3d..43395db5 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'ไม่มี', 'edit' => 'แก้ไข', 'Edit' => 'แก้ไข', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index 3ea8c80c..e2d4e72c 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => 'Hiçbiri', 'edit' => 'düzenle', 'Edit' => 'Düzenle', @@ -738,4 +740,37 @@ return array( 'Horizontal scrolling' => 'Geniş görünüm', 'Compact/wide view' => 'Ekrana sığdır / Geniş görünüm', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index 575fe442..f1bbd87c 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -1,6 +1,8 @@ <?php return array( + // 'number.decimals_separator' => '', + // 'number.thousands_separator' => '', 'None' => '无', 'edit' => '编辑', 'Edit' => '编辑', @@ -738,4 +740,37 @@ return array( // 'Horizontal scrolling' => '', // 'Compact/wide view' => '', // 'No results match:' => '', + // 'Remove hourly rate' => '', + // 'Do you really want to remove this hourly rate?' => '', + // 'Hourly rates' => '', + // 'Hourly rate' => '', + // 'Currency' => '', + // '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' => '', + // '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.' => '', ); diff --git a/app/Model/Timetable.php b/app/Model/Timetable.php new file mode 100644 index 00000000..eb37cefd --- /dev/null +++ b/app/Model/Timetable.php @@ -0,0 +1,194 @@ +<?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 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(); + } +} diff --git a/app/Model/TimetableDay.php b/app/Model/TimetableDay.php new file mode 100644 index 00000000..0c7bf20b --- /dev/null +++ b/app/Model/TimetableDay.php @@ -0,0 +1,87 @@ +<?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() + ); + } +} diff --git a/app/Model/TimetableExtra.php b/app/Model/TimetableExtra.php new file mode 100644 index 00000000..48db662d --- /dev/null +++ b/app/Model/TimetableExtra.php @@ -0,0 +1,22 @@ +<?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'; +} diff --git a/app/Model/TimetableOff.php b/app/Model/TimetableOff.php new file mode 100644 index 00000000..aa064f05 --- /dev/null +++ b/app/Model/TimetableOff.php @@ -0,0 +1,125 @@ +<?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' => $all_day, + '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() + ); + } +} diff --git a/app/Model/TimetableWeek.php b/app/Model/TimetableWeek.php new file mode 100644 index 00000000..b22b3b7e --- /dev/null +++ b/app/Model/TimetableWeek.php @@ -0,0 +1,91 @@ +<?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() + ); + } +} diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index a4b8c25a..44ca7fd4 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,53 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 50; +const VERSION = 51; + +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_50($pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index 7054bca2..fe01b1e9 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,12 +6,54 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 31; +const VERSION = 32; + +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_31($pdo) { $pdo->exec("CREATE TABLE hourly_rates ( - id SERIAL, + id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL, rate REAL DEFAULT 0, date_effective INTEGER NOT NULL, diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index a389fc17..eaa5c734 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,49 @@ use Core\Security; use PDO; use Model\Link; -const VERSION = 49; +const VERSION = 50; + +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_49($pdo) { diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index 67f59dd8..a94bb745 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -49,6 +49,11 @@ class ClassProvider implements ServiceProviderInterface 'TaskPosition', 'TaskStatus', 'TaskValidator', + 'Timetable', + 'TimetableDay', + 'TimetableWeek', + 'TimetableOff', + 'TimetableExtra', 'TimeTracking', 'User', 'UserSession', diff --git a/app/Template/timetable/index.php b/app/Template/timetable/index.php new file mode 100644 index 00000000..27cbe39c --- /dev/null +++ b/app/Template/timetable/index.php @@ -0,0 +1,44 @@ +<div class="page-header"> + <h2><?= t('Timetable') ?></h2> + <ul> + <li><?= $this->a(t('Day timetable'), 'timetableday', 'index', array('user_id' => $user['id'])) ?></li> + <li><?= $this->a(t('Week timetable'), 'timetableweek', 'index', array('user_id' => $user['id'])) ?></li> + <li><?= $this->a(t('Time off timetable'), 'timetableoff', 'index', array('user_id' => $user['id'])) ?></li> + <li><?= $this->a(t('Overtime timetable'), 'timetableextra', 'index', array('user_id' => $user['id'])) ?></li> + </ul> +</div> + +<form method="get" action="?" autocomplete="off" class="form-inline"> + + <?= $this->formHidden('controller', $values) ?> + <?= $this->formHidden('action', $values) ?> + <?= $this->formHidden('user_id', $values) ?> + + <?= $this->formLabel(t('From'), 'from') ?> + <?= $this->formText('from', $values, array(), array(), 'form-date') ?> + + <?= $this->formLabel(t('To'), 'to') ?> + <?= $this->formText('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 ?>
\ No newline at end of file diff --git a/app/Template/timetable_day/index.php b/app/Template/timetable_day/index.php new file mode 100644 index 00000000..50aca602 --- /dev/null +++ b/app/Template/timetable_day/index.php @@ -0,0 +1,45 @@ +<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->a(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->u('timetableday', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off"> + + <?= $this->formHidden('user_id', $values) ?> + <?= $this->formCsrf() ?> + + <?= $this->formLabel(t('Start time'), 'start') ?> + <?= $this->formSelect('start', $this->getDayHours(), $values, $errors) ?> + + <?= $this->formLabel(t('End time'), 'end') ?> + <?= $this->formSelect('end', $this->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>
\ No newline at end of file diff --git a/app/Template/timetable_day/remove.php b/app/Template/timetable_day/remove.php new file mode 100644 index 00000000..b3ee8775 --- /dev/null +++ b/app/Template/timetable_day/remove.php @@ -0,0 +1,13 @@ +<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->a(t('Yes'), 'timetableday', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?> + <?= t('or') ?> + <?= $this->a(t('cancel'), 'timetableday', 'index', array('user_id' => $user['id'])) ?> + </div> +</div>
\ No newline at end of file diff --git a/app/Template/timetable_extra/index.php b/app/Template/timetable_extra/index.php new file mode 100644 index 00000000..a0a55bec --- /dev/null +++ b/app/Template/timetable_extra/index.php @@ -0,0 +1,56 @@ +<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->a(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->u('timetableextra', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off"> + + <?= $this->formHidden('user_id', $values) ?> + <?= $this->formCsrf() ?> + + <?= $this->formLabel(t('Day'), 'date') ?> + <?= $this->formText('date', $values, $errors, array('required'), 'form-date') ?> + + <?= $this->formCheckbox('all_day', t('All day'), 1) ?> + + <?= $this->formLabel(t('Start time'), 'start') ?> + <?= $this->formSelect('start', $this->getDayHours(), $values, $errors) ?> + + <?= $this->formLabel(t('End time'), 'end') ?> + <?= $this->formSelect('end', $this->getDayHours(), $values, $errors) ?> + + <?= $this->formLabel(t('Comment'), 'comment') ?> + <?= $this->formText('comment', $values, $errors) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + </div> +</form>
\ No newline at end of file diff --git a/app/Template/timetable_extra/remove.php b/app/Template/timetable_extra/remove.php new file mode 100644 index 00000000..d8dc5b3b --- /dev/null +++ b/app/Template/timetable_extra/remove.php @@ -0,0 +1,13 @@ +<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->a(t('Yes'), 'timetableextra', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?> + <?= t('or') ?> + <?= $this->a(t('cancel'), 'timetableextra', 'index', array('user_id' => $user['id'])) ?> + </div> +</div>
\ No newline at end of file diff --git a/app/Template/timetable_off/index.php b/app/Template/timetable_off/index.php new file mode 100644 index 00000000..f35d331e --- /dev/null +++ b/app/Template/timetable_off/index.php @@ -0,0 +1,56 @@ +<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->a(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->u('timetableoff', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off"> + + <?= $this->formHidden('user_id', $values) ?> + <?= $this->formCsrf() ?> + + <?= $this->formLabel(t('Day'), 'date') ?> + <?= $this->formText('date', $values, $errors, array('required'), 'form-date') ?> + + <?= $this->formCheckbox('all_day', t('All day'), 1) ?> + + <?= $this->formLabel(t('Start time'), 'start') ?> + <?= $this->formSelect('start', $this->getDayHours(), $values, $errors) ?> + + <?= $this->formLabel(t('End time'), 'end') ?> + <?= $this->formSelect('end', $this->getDayHours(), $values, $errors) ?> + + <?= $this->formLabel(t('Comment'), 'comment') ?> + <?= $this->formText('comment', $values, $errors) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + </div> +</form>
\ No newline at end of file diff --git a/app/Template/timetable_off/remove.php b/app/Template/timetable_off/remove.php new file mode 100644 index 00000000..64863781 --- /dev/null +++ b/app/Template/timetable_off/remove.php @@ -0,0 +1,13 @@ +<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->a(t('Yes'), 'timetableoff', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?> + <?= t('or') ?> + <?= $this->a(t('cancel'), 'timetableoff', 'index', array('user_id' => $user['id'])) ?> + </div> +</div>
\ No newline at end of file diff --git a/app/Template/timetable_week/index.php b/app/Template/timetable_week/index.php new file mode 100644 index 00000000..8fb51909 --- /dev/null +++ b/app/Template/timetable_week/index.php @@ -0,0 +1,46 @@ +<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->getWeekDay($slot['day']) ?></td> + <td><?= $slot['start'] ?></td> + <td><?= $slot['end'] ?></td> + <td> + <?= $this->a(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->u('timetableweek', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off"> + + <?= $this->formHidden('user_id', $values) ?> + <?= $this->formCsrf() ?> + + <?= $this->formLabel(t('Day'), 'day') ?> + <?= $this->formSelect('day', $this->getWeekDays(), $values, $errors) ?> + + <?= $this->formLabel(t('Start time'), 'start') ?> + <?= $this->formSelect('start', $this->getDayHours(), $values, $errors) ?> + + <?= $this->formLabel(t('End time'), 'end') ?> + <?= $this->formSelect('end', $this->getDayHours(), $values, $errors) ?> + + <div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + </div> +</form>
\ No newline at end of file diff --git a/app/Template/timetable_week/remove.php b/app/Template/timetable_week/remove.php new file mode 100644 index 00000000..f8eb2bbe --- /dev/null +++ b/app/Template/timetable_week/remove.php @@ -0,0 +1,13 @@ +<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->a(t('Yes'), 'timetableweek', 'remove', array('user_id' => $user['id'], 'slot_id' => $slot_id), true, 'btn btn-red') ?> + <?= t('or') ?> + <?= $this->a(t('cancel'), 'timetableweek', 'index', array('user_id' => $user['id'])) ?> + </div> +</div>
\ No newline at end of file diff --git a/app/Template/user/sidebar.php b/app/Template/user/sidebar.php index 88977a9e..1af10c1d 100644 --- a/app/Template/user/sidebar.php +++ b/app/Template/user/sidebar.php @@ -43,6 +43,9 @@ <li> <?= $this->a(t('Hourly rates'), 'hourlyrate', 'index', array('user_id' => $user['id'])) ?> </li> + <li> + <?= $this->a(t('Manage timetable'), 'timetable', 'index', array('user_id' => $user['id'])) ?> + </li> <?php endif ?> <?php if ($this->userSession->isAdmin() && ! $this->userSession->isCurrentUser($user['id'])): ?> |