Passed
Push — master ( 16b7e2...ecaa2e )
by Sylvain
10:31
created

Bookable::setSimultaneousBookingMaximum()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Model;
6
7
use Application\DBAL\Types\BookableStateType;
8
use Application\DBAL\Types\BookingStatusType;
9
use Application\DBAL\Types\BookingTypeType;
10
use Application\Repository\BookableTagRepository;
11
use Application\Traits\HasCode;
12
use Application\Traits\HasRemarks;
13
use Cake\Chronos\Date;
14
use Doctrine\Common\Collections\ArrayCollection;
15
use Doctrine\Common\Collections\Collection;
16
use Doctrine\ORM\Mapping as ORM;
17
use Ecodev\Felix\Model\Traits\HasDescription;
18
use Ecodev\Felix\Model\Traits\HasName;
19
use GraphQL\Doctrine\Annotation as API;
20
use Money\Money;
21
22
/**
23
 * An item that can be booked by a user
24
 *
25
 * @ORM\Entity(repositoryClass="Application\Repository\BookableRepository")
26
 * @API\Filters({
27
 *     @API\Filter(field="custom", operator="Application\Api\Input\Operator\BookableUsageOperatorType", type="id"),
28
 *     @API\Filter(field="bookableBookingCount", operator="Application\Api\Input\Operator\BookableBookingCount\BookableBookingCountEqualOperatorType", type="int"),
29
 *     @API\Filter(field="bookableBookingCount", operator="Application\Api\Input\Operator\BookableBookingCount\BookableBookingCountGreaterOperatorType", type="int"),
30
 *     @API\Filter(field="bookableBookingCount", operator="Application\Api\Input\Operator\BookableBookingCount\BookableBookingCountGreaterOrEqualOperatorType", type="int"),
31
 *     @API\Filter(field="bookableBookingCount", operator="Application\Api\Input\Operator\BookableBookingCount\BookableBookingCountLessOperatorType", type="int"),
32
 *     @API\Filter(field="bookableBookingCount", operator="Application\Api\Input\Operator\BookableBookingCount\BookableBookingCountLessOrEqualOperatorType", type="int"),
33
 * })
34
 */
35
class Bookable extends AbstractModel
36
{
37
    use HasName;
38
    use HasDescription;
39
    use HasCode;
40
    use HasRemarks;
41
42
    /**
43
     * @var Money
44
     *
45
     * @ORM\Column(type="Money", options={"default" = 0})
46
     */
47
    private $initialPrice;
48
49
    /**
50
     * @var Money
51
     *
52
     * @ORM\Column(type="Money", options={"default" = 0})
53
     */
54
    private $periodicPrice;
55
56
    /**
57
     * @var Money
58
     *
59
     * @ORM\Column(type="Money",  options={"default" = 0, "unsigned" = true})
60
     */
61
    private $purchasePrice;
62
63
    /**
64
     * @var int
65
     *
66
     * @ORM\Column(type="smallint", options={"default" = "-1"})
67
     */
68
    private $simultaneousBookingMaximum = 1;
69
70
    /**
71
     * @var string
72
     *
73
     * @ORM\Column(type="BookingType", length=10, options={"default" = BookingTypeType::SELF_APPROVED})
74
     */
75
    private $bookingType = BookingTypeType::SELF_APPROVED;
76
77
    /**
78
     * @var bool
79
     *
80
     * @ORM\Column(type="boolean", options={"default" = 1})
81
     */
82
    private $isActive = true;
83
84
    /**
85
     * @var string
86
     *
87
     * @ORM\Column(type="BookableState", length=10, options={"default" = BookableStateType::GOOD})
88
     */
89
    private $state = BookableStateType::GOOD;
90
91
    /**
92
     * @var null|Date
93
     * @ORM\Column(type="date", nullable=true)
94
     */
95
    private $verificationDate;
96
97
    /**
98
     * @var Collection
99
     *
100
     * @ORM\ManyToMany(targetEntity="BookableTag", mappedBy="bookables")
101
     */
102
    private $bookableTags;
103
104
    /**
105
     * @var Collection
106
     * @ORM\OneToMany(targetEntity="Booking", mappedBy="bookable")
107
     */
108
    private $bookings;
109
110
    /**
111
     * @var Collection
112
     * @ORM\ManyToMany(targetEntity="License", mappedBy="bookables")
113
     */
114
    private $licenses;
115
116
    /**
117
     * @var null|Image
118
     * @ORM\OneToOne(targetEntity="Image", orphanRemoval=true)
119
     * @ORM\JoinColumn(name="image_id", referencedColumnName="id")
120
     */
121
    private $image;
122
123
    /**
124
     * @var null|Account
125
     *
126
     * @ORM\ManyToOne(targetEntity="Account")
127
     * @ORM\JoinColumns({
128
     *     @ORM\JoinColumn(nullable=true, onDelete="CASCADE")
129
     * })
130
     */
131
    private $creditAccount;
132
133
    /**
134
     * Constructor
135
     */
136 15
    public function __construct()
137
    {
138 15
        $this->initialPrice = Money::CHF(0);
139 15
        $this->periodicPrice = Money::CHF(0);
140 15
        $this->purchasePrice = Money::CHF(0);
141
142 15
        $this->bookings = new ArrayCollection();
143 15
        $this->licenses = new ArrayCollection();
144 15
        $this->bookableTags = new ArrayCollection();
145 15
    }
146
147 3
    public function getBookings(): Collection
148
    {
149 3
        return $this->bookings;
150
    }
151
152
    /**
153
     * Notify the bookable that it has a new booking.
154
     * This should only be called by Booking::addBookable()
155
     */
156 11
    public function bookingAdded(Booking $booking): void
157
    {
158 11
        $this->bookings->add($booking);
159 11
    }
160
161
    /**
162
     * Notify the bookable that it a booking was removed.
163
     * This should only be called by Booking::removeBookable()
164
     */
165 1
    public function bookingRemoved(Booking $booking): void
166
    {
167 1
        $this->bookings->removeElement($booking);
168 1
    }
169
170 3
    public function getLicenses(): Collection
171
    {
172 3
        return $this->licenses;
173
    }
174
175
    /**
176
     * Notify the bookable that it has a new license.
177
     * This should only be called by License::addBookable()
178
     */
179 1
    public function licenseAdded(License $license): void
180
    {
181 1
        $this->licenses->add($license);
182 1
    }
183
184
    /**
185
     * Notify the bookable that it a license was removed.
186
     * This should only be called by License::removeBookable()
187
     */
188 1
    public function licenseRemoved(License $license): void
189
    {
190 1
        $this->licenses->removeElement($license);
191 1
    }
192
193 6
    public function getInitialPrice(): Money
194
    {
195 6
        return $this->initialPrice;
196
    }
197
198 6
    public function setInitialPrice(Money $initialPrice): void
199
    {
200 6
        $this->initialPrice = $initialPrice;
201 6
    }
202
203 10
    public function getPeriodicPrice(): Money
204
    {
205 10
        return $this->periodicPrice;
206
    }
207
208 8
    public function setPeriodicPrice(Money $periodicPrice): void
209
    {
210 8
        $this->periodicPrice = $periodicPrice;
211 8
    }
212
213
    public function getPurchasePrice(): Money
214
    {
215
        return $this->purchasePrice;
216
    }
217
218
    public function setPurchasePrice(Money $purchasePrice): void
219
    {
220
        $this->purchasePrice = $purchasePrice;
221
    }
222
223 1
    public function getSimultaneousBookingMaximum(): int
224
    {
225 1
        return $this->simultaneousBookingMaximum;
226
    }
227
228
    public function setSimultaneousBookingMaximum(int $simultaneousBookingMaximum): void
229
    {
230
        $this->simultaneousBookingMaximum = $simultaneousBookingMaximum;
231
    }
232
233
    /**
234
     * @API\Field(type="BookingType")
235
     */
236 13
    public function getBookingType(): string
237
    {
238 13
        return $this->bookingType;
239
    }
240
241
    /**
242
     * Whether this bookable can be booked
243
     */
244 2
    public function isActive(): bool
245
    {
246 2
        return $this->isActive;
247
    }
248
249
    /**
250
     * Whether this bookable can be booked
251
     */
252
    public function setIsActive(bool $isActive): void
253
    {
254
        $this->isActive = $isActive;
255
    }
256
257
    /**
258
     * @API\Input(type="BookingType")
259
     */
260 1
    public function setBookingType(string $state): void
261
    {
262 1
        $this->bookingType = $state;
263 1
    }
264
265
    /**
266
     * State of the bookable
267
     *
268
     * @API\Field(type="BookableState")
269
     */
270
    public function getState(): string
271
    {
272
        return $this->state;
273
    }
274
275
    /**
276
     * State of the bookable
277
     *
278
     * @API\Input(type="BookableState")
279
     */
280
    public function setState(string $state): void
281
    {
282
        $this->state = $state;
283
    }
284
285
    /**
286
     * The date then the bookable was last checked
287
     */
288
    public function getVerificationDate(): ?Date
289
    {
290
        return $this->verificationDate;
291
    }
292
293
    /**
294
     * The date then the bookable was last checked
295
     */
296
    public function setVerificationDate(?Date $verificationDate): void
297
    {
298
        $this->verificationDate = $verificationDate;
299
    }
300
301 6
    public function getBookableTags(): Collection
302
    {
303 6
        return $this->bookableTags;
304
    }
305
306
    /**
307
     * Notify the user that it has a new bookableTag.
308
     * This should only be called by BookableTag::addUser()
309
     */
310
    public function bookableTagAdded(BookableTag $bookableTag): void
311
    {
312
        $this->bookableTags->add($bookableTag);
313
    }
314
315
    /**
316
     * Notify the user that it a bookableTag was removed.
317
     * This should only be called by BookableTag::removeUser()
318
     */
319
    public function bookableTagRemoved(BookableTag $bookableTag): void
320
    {
321
        $this->bookableTags->removeElement($bookableTag);
322
    }
323
324 1
    public function getImage(): ?Image
325
    {
326 1
        return $this->image;
327
    }
328
329 1
    public function setImage(?Image $image): void
330
    {
331
        // We must trigger lazy loading, otherwise Doctrine will seriously
332
        // mess up lifecycle callbacks and delete unrelated image on disk
333 1
        if ($this->image) {
334 1
            $this->image->getFilename();
335
        }
336
337 1
        $this->image = $image;
338 1
    }
339
340
    /**
341
     * The account to credit when booking this bookable
342
     */
343 8
    public function getCreditAccount(): ?Account
344
    {
345 8
        return $this->creditAccount;
346
    }
347
348
    /**
349
     * The account to credit when booking this bookable
350
     */
351 5
    public function setCreditAccount(?Account $creditAccount): void
352
    {
353 5
        $this->creditAccount = $creditAccount;
354 5
    }
355
356
    /**
357
     * Returns list of active bookings
358
     *
359
     * Limits to admin-assigned, application and admin-approved
360
     *
361
     * @return Booking[]
362
     */
363 1
    public function getSharedBookings(): array
364
    {
365 1
        $bookableType = $this->getBookingType();
366 1
        $bookableTypesAllowed = [BookingTypeType::ADMIN_ASSIGNED, BookingTypeType::APPLICATION, BookingTypeType::ADMIN_APPROVED];
367
368 1
        if (!in_array($bookableType, $bookableTypesAllowed, true)) {
369 1
            return [];
370
        }
371
372
        // Only consider approved and unterminated bookings
373 1
        $bookings = $this->getBookings()->filter(function (Booking $booking): bool {
374 1
            return !$booking->getEndDate() && $booking->getStatus() !== BookingStatusType::APPLICATION;
375 1
        })->toArray();
376
377 1
        return $bookings;
378
    }
379
380
    /**
381
     * Return a list of effective active bookings including sharing conditions.
382
     *
383
     * Only "admin-assigned" + storage tags are sharable bookables. In this case, a list of bookings is returned.
384
     *
385
     * For other bookable types, returns null
386
     *
387
     * @return Booking[]
388
     */
389 6
    public function getPeriodicPriceDividerBookings(): array
390
    {
391 6
        $isAdminAssigned = $this->getBookingType() === BookingTypeType::ADMIN_ASSIGNED;
392
393 6
        $isTagAllowed = false;
394 6
        $allowedTagIds = [BookableTagRepository::STORAGE_ID, BookableTagRepository::FORMATION_ID, BookableTagRepository::WELCOME_ID];
395 6
        foreach ($this->getBookableTags() as $tag) {
396 2
            if (in_array($tag->getId(), $allowedTagIds, true)) {
397 1
                $isTagAllowed = true;
398
399 1
                break;
400
            }
401
        }
402
403 6
        if (!$isAdminAssigned || !$isTagAllowed) {
404 6
            return [];
405
        }
406
407 1
        $bookings = $this->getBookings()->filter(function (Booking $booking): bool {
408 1
            return !$booking->getEndDate();
409 1
        })->toArray();
410
411 1
        return $bookings;
412
    }
413
}
414