TimeCalculatorService::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 1
eloc 11
nc 1
nop 5
1
<?php
2
3
namespace JhFlexiTime\Service;
4
5
use JhFlexiTime\Entity\UserSettings;
6
use JhFlexiTime\Repository\BookingRepositoryInterface;
7
use ZfcUser\Entity\UserInterface;
8
use JhFlexiTime\DateTime\DateTime;
9
use JhFlexiTime\Options\ModuleOptions;
10
use JhFlexiTime\Repository\BalanceRepositoryInterface;
11
12
/**
13
 *
14
 * Class TimeCalculatorService
15
 * @package JhFlexiTime\Service
16
 * @author Aydin Hassan <[email protected]>
17
 */
18
class TimeCalculatorService
19
{
20
21
    /**
22
     * @var \JhFlexiTime\Repository\BookingRepositoryInterface
23
     */
24
    protected $bookingRepository;
25
26
    /**
27
     * @var BalanceRepositoryInterface
28
     */
29
    protected $balanceRepository;
30
31
    /**
32
     * @var \JhFlexiTime\Service\PeriodServiceInterface
33
     */
34
    protected $periodService;
35
36
    /**
37
     * @var \JhFlexiTime\Options\ModuleOptions
38
     */
39
    protected $options;
40
41
    /**
42
     * @var DateTime
43
     */
44
    protected $today;
45
46
    /**
47
     * @param ModuleOptions $options
48
     * @param BookingRepositoryInterface $bookingRepository
49
     * @param BalanceRepositoryInterface $balanceRepository
50
     * @param PeriodServiceInterface $periodService
51
     * @param DateTime $date
52
     */
53
    public function __construct(
54
        ModuleOptions $options,
55
        BookingRepositoryInterface $bookingRepository,
56
        BalanceRepositoryInterface $balanceRepository,
57
        PeriodServiceInterface $periodService,
58
        DateTime $date
59
    ) {
60
        $this->options              = $options;
61
        $this->bookingRepository    = $bookingRepository;
62
        $this->balanceRepository    = $balanceRepository;
63
        $this->periodService        = $periodService;
64
        $this->today                = $date;
65
    }
66
67
    /**
68
     * @param UserInterface $user
69
     * @param DateTime $period
70
     * @return array
71
     */
72
    public function getWeekTotals(UserInterface $user, DateTime $period)
73
    {
74
        $week    = $this->periodService->getFirstAndLastDayOfWeek($period);
75
        $tWorked = $this->bookingRepository->getTotalBookedBetweenByUser($user, $week['firstDay'], $week['lastDay']);
76
        $tHours  = $this->periodService->getNumWorkingDaysInWeek($period) * $this->options->getHoursInDay();
77
78
        return [
79
            'weekTotalWorkedHours'  => $tWorked,
80
            'weekTotalHours'        => $tHours,
81
            'balance'               => $tWorked - $tHours,
82
        ];
83
    }
84
85
    /**
86
     *
87
     * @param UserInterface $user
88
     * @param DateTime $userStartDate
89
     * @param DateTime $period
90
     * @return array
91
     */
92
    public function getTotals(UserInterface $user, DateTime $userStartDate, DateTime $period)
93
    {
94
        $startDate          = $this->getStartDate($userStartDate, $period);
95
        $endDate            = $this->getEndDate($period, $this->today);
96
97
        $monthTotalWorked   = $this->getTotalWorkedHours($user, $startDate, $endDate, $this->today);
98
        $monthBalance       = $this->getMonthBalance($startDate, $endDate, $this->today, $monthTotalWorked);
99
100
        $balanceForward = 0;
101
        $runningBalance = $monthBalance;
102
        if (!$this->userStartedInMonth($userStartDate, $this->today)) {
103
            $balanceForward = $this->getBalanceForward($user);
104
            $runningBalance = $this->getRunningBalance($user, $balanceForward);
105
        }
106
107
        $monthTotalHours    = $this->periodService->getTotalHoursBetweenDates($startDate, $startDate->endOfMonth());
108
        $remainingHours     = $this->getRemainingHours($period, $this->today);
109
110
        $totals = [
111
            'monthTotalWorkedHours' => $monthTotalWorked,
112
            'monthTotalHours'       => $monthTotalHours,
113
            'monthBalance'          => $monthBalance,
114
            'runningBalance'        => $runningBalance,
115
            'monthRemainingHours'   => $remainingHours,
116
            'balanceForward'        => $balanceForward,
117
        ];
118
119
        return array_map(function ($val) {
120
            return round($val, 2);
121
        }, $totals);
122
    }
123
124
    /**
125
     * This gets the balance of the CURRENT month, regardless of which month you are viewing.
126
     * Viewing the running balance and balance forward of a particular month is not supported.
127
     *
128
     * @param UserInterface $user
129
     * @param float         $balanceForward
130
     *
131
     * @return float
132
     */
133
    private function getRunningBalance(UserInterface $user, $balanceForward)
134
    {
135
        $totalHoursThisMonth = $this->periodService->getTotalHoursBetweenDates(
136
            $this->today->startOfMonth(),
137
            $this->today
138
        );
139
        $bookedThisMonth     = $this->bookingRepository->getMonthBookedToDateTotalByUser($user, $this->today);
140
        $monthBalance        = $bookedThisMonth - $totalHoursThisMonth;
141
        $balanceForward      += $monthBalance;
142
143
        return $balanceForward;
144
    }
145
146
    /***
147
     * @param UserInterface $user
148
     * @return float
149
     */
150
    private function getBalanceForward(UserInterface $user)
151
    {
152
        $balanceEntity = $this->balanceRepository->findOneByUser($user);
153
        return ($balanceEntity) ? $balanceEntity->getBalance() : 0;
154
    }
155
156
    /**
157
     * @param DateTime $period
158
     * @param DateTime $today
159
     * @return float
160
     */
161
    private function getRemainingHours(DateTime $period, DateTime $today)
162
    {
163
        //previous month
164
        if ($period < $today->startOfMonth()) {
165
            return 0;
166
        }
167
168
        //current month
169
        if ($period->isSameMonthAndYear($today)) {
170
            return $this->periodService->getTotalHoursBetweenDates($today, $today->endOfMonth());
171
        }
172
173
        //future month
174
        return $this->periodService->getTotalHoursBetweenDates($period->startOfMonth(), $period->endOfMonth());
175
    }
176
177
    /**
178
     * Get the total hours worked for the given period
179
     *
180
     * @param UserInterface $user
181
     * @param DateTime $startDate
182
     * @param DateTime $endDate
183
     * @param DateTime $today
184
     * @return float|int
185
     */
186
    public function getTotalWorkedHours(UserInterface $user, DateTime $startDate, DateTime $endDate, DateTime $today)
187
    {
188
        if ($startDate <= $today->endOfMonth()) {
189
            return $this->bookingRepository->getTotalBookedBetweenByUser($user, $startDate, $endDate);
190
        }
191
192
        //future booking, you can't work hours in the future
193
        return 0;
194
    }
195
196
    /**
197
     * Get the month balance. How much has been worked out of how many hours have passed.
198
     * If we are looking at a future date, no hours have passed
199
     *
200
     * @param DateTime $startDate
201
     * @param DateTime $endDate
202
     * @param DateTime $today
203
     * @param float $monthTotalWorked
204
     * @return float
205
     */
206
    private function getMonthBalance(DateTime $startDate, DateTime $endDate, DateTime $today, $monthTotalWorked)
207
    {
208
        //future
209
        $hoursElapsed = 0;
210
211
        //the past + present
212
        if ($startDate <= $today->endOfMonth()) {
213
            $hoursElapsed = $this->periodService->getTotalHoursBetweenDates($startDate, $endDate);
214
        }
215
216
        return $monthTotalWorked - $hoursElapsed;
217
    }
218
219
    /**
220
     * Compute the start date to base total calculation on
221
     * If the user joined started in the month passed in, then the start date
222
     * should be the user's start date.
223
     *
224
     * If not, the start date should be the first day of the passed in month.
225
     * This is because we don't want base calculations on a date which was before the
226
     * user started
227
     *
228
     * @param DateTime $userStartDate
229
     * @param DateTime $period
230
     * @return DateTime
231
     */
232
    private function getStartDate(DateTime $userStartDate, DateTime $period)
233
    {
234
        if ($userStartDate->isSameMonthAndYear($period)) {
235
            return clone $userStartDate;
236
        }
237
        return $period->startOfMonth();
238
    }
239
240
    /**
241
     * Compute the end date to base total calculation on
242
     * If the passed in month is the current month, then the end date should be the current day
243
     *  - we don't want to include days which haven't gone by yet
244
     *
245
     * Other wise the end date should just be the last day in the period we are querying
246
     *
247
     * @param DateTime $period
248
     * @param DateTime $today
249
     * @return DateTime
250
     */
251
    private function getEndDate(DateTime $period, DateTime $today)
252
    {
253
        if ($period < $today->startOfMonth()) {
254
            return $period->endOfMonth();
255
        }
256
257
        return clone $today;
258
    }
259
260
    /**
261
     * Did the user start in the given month?
262
     *
263
     * @param DateTime $userStartDate
264
     * @param DateTime $today
265
     * @return bool
266
     */
267
    private function userStartedInMonth(DateTime $userStartDate, DateTime $today)
268
    {
269
        return $userStartDate->isSameMonthAndYear($today);
270
    }
271
}
272