Passed
Push — master ( cb7b55...2c5639 )
by Sam
07:49
created

Bookable::getPeriodicPriceDividerBookings()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

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