Failed Conditions
Push — master ( a427b2...509ea4 )
by Adrien
16:44
created

Bookable::setInitialPrice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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