Booking::setStartDate()   A
last analyzed

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\Api\Input\Operator\RunningServicesOperatorType;
8
use Application\Enum\BookingStatus;
9
use Application\Repository\BookingRepository;
10
use Application\Service\Invoicer;
11
use Application\Traits\HasRemarks;
12
use Cake\Chronos\Chronos;
13
use Doctrine\ORM\Mapping as ORM;
14
use Ecodev\Felix\Model\Traits\HasInternalRemarks;
15
use GraphQL\Doctrine\Attribute as API;
16
use Money\Money;
17
18
/**
19
 * A booking linking a user and a bookable.
20
 */
21
#[API\Sorting(\Application\Api\Input\Sorting\Bookable::class)]
22
#[ORM\Entity(BookingRepository::class)]
23
#[ORM\AssociationOverrides([new ORM\AssociationOverride(name: 'owner', inversedBy: 'bookings')])]
24
#[ORM\Index(name: 'start_date', columns: ['start_date'])]
25
#[API\Filter(field: 'custom', operator: RunningServicesOperatorType::class, type: 'UserID')]
26
class Booking extends AbstractModel
27
{
28
    use HasInternalRemarks;
29
    use HasRemarks;
30
31
    #[ORM\Column(type: 'enum', length: 10, options: ['default' => BookingStatus::Application])]
32
    private BookingStatus $status = BookingStatus::Application;
33
34
    #[ORM\Column(type: 'integer', options: ['unsigned' => true, 'default' => 1])]
35
    private int $participantCount = 1;
36
37
    #[ORM\Column(type: 'string', length: 50, options: ['default' => ''])]
38
    private string $destination = '';
39
40
    #[ORM\Column(type: 'text', length: 65535, options: ['default' => ''])]
41
    private string $startComment = '';
42
43
    #[ORM\Column(type: 'text', length: 65535, options: ['default' => ''])]
44
    private string $endComment = '';
45
46
    #[ORM\Column(type: 'datetime')]
47
    private Chronos $startDate;
48
49
    #[ORM\Column(type: 'datetime', nullable: true)]
50
    private ?Chronos $endDate = null;
51
52
    #[ORM\Column(type: 'string', length: 50, options: ['default' => ''])]
53
    private string $estimatedEndDate = '';
54
55
    #[ORM\JoinColumn(onDelete: 'CASCADE')]
56
    #[ORM\ManyToOne(targetEntity: Bookable::class, inversedBy: 'bookings')]
57
    private ?Bookable $bookable = null;
58
59 43
    public function __construct()
60
    {
61 43
    }
62
63 19
    public function setOwner(?User $owner = null): void
64
    {
65 19
        if ($this->getOwner()) {
66 4
            $this->getOwner()->bookingRemoved($this);
67
        }
68
69 19
        parent::setOwner($owner);
70
71 19
        if ($this->getOwner()) {
72 17
            $this->getOwner()->bookingAdded($this);
73
        }
74
75 19
        $this->invoiceInitial();
76
    }
77
78
    /**
79
     * Total count of participant, at least 1.
80
     */
81
    public function getParticipantCount(): int
82
    {
83
        return $this->participantCount;
84
    }
85
86 3
    public function setParticipantCount(int $participantCount): void
87
    {
88 3
        $this->participantCount = $participantCount;
89
    }
90
91
    public function getDestination(): string
92
    {
93
        return $this->destination;
94
    }
95
96 3
    public function setDestination(string $destination): void
97
    {
98 3
        $this->destination = $destination;
99
    }
100
101
    public function getStartComment(): string
102
    {
103
        return $this->startComment;
104
    }
105
106 3
    public function setStartComment(string $startComment): void
107
    {
108 3
        $this->startComment = $startComment;
109
    }
110
111
    public function getEndComment(): string
112
    {
113
        return $this->endComment;
114
    }
115
116 4
    public function setEndComment(string $endComment): void
117
    {
118 4
        $this->endComment = $endComment;
119
    }
120
121
    public function getStartDate(): Chronos
122
    {
123
        return $this->startDate;
124
    }
125
126 4
    public function setStartDate(Chronos $startDate): void
127
    {
128 4
        $this->startDate = $startDate;
129
    }
130
131 22
    public function getEndDate(): ?Chronos
132
    {
133 22
        return $this->endDate;
134
    }
135
136 7
    public function setEndDate(?Chronos $endDate): void
137
    {
138 7
        $this->endDate = $endDate;
139
    }
140
141
    public function getEstimatedEndDate(): string
142
    {
143
        return $this->estimatedEndDate;
144
    }
145
146 3
    public function setEstimatedEndDate(string $estimatedEndDate): void
147
    {
148 3
        $this->estimatedEndDate = $estimatedEndDate;
149
    }
150
151
    /**
152
     * Get bookable, may be null for "my own material" case.
153
     */
154 63
    public function getBookable(): ?Bookable
155
    {
156 63
        return $this->bookable;
157
    }
158
159
    /**
160
     * Set bookable.
161
     */
162 29
    public function setBookable(?Bookable $bookable): void
163
    {
164 29
        if ($this->bookable) {
165 1
            $this->bookable->bookingRemoved($this);
166
        }
167
168 29
        $this->bookable = $bookable;
169
170 29
        if ($this->bookable) {
171 29
            $this->bookable->bookingAdded($this);
172
        }
173
174 29
        $this->invoiceInitial();
175
    }
176
177 26
    public function getStatus(): BookingStatus
178
    {
179 26
        return $this->status;
180
    }
181
182 29
    public function setStatus(BookingStatus $status): void
183
    {
184 29
        $previousStatus = $this->status;
185 29
        $this->status = $status;
186 29
        $this->invoiceInitial($previousStatus);
187
    }
188
189
    /**
190
     * Mark the booking as terminated with an optional comment,
191
     * but only if not already terminated.
192
     */
193 4
    public function terminate(?string $comment): void
194
    {
195
        // Booking can only be terminated once
196 4
        if (!$this->getEndDate()) {
197 4
            $this->setEndDate(new Chronos());
198 4
            if ($comment) {
199 1
                $this->setEndComment($comment);
200
            }
201
        }
202
    }
203
204
    /**
205
     * If the booking is complete, will make initial invoicing.
206
     */
207 40
    private function invoiceInitial(?BookingStatus $previousStatus = null): void
208
    {
209 40
        if (!$this->getOwner() || !$this->getBookable()) {
210 40
            return;
211
        }
212
213
        global $container;
214
215
        /** @var Invoicer $invoicer */
216 9
        $invoicer = $container->get(Invoicer::class);
217 9
        $invoicer->invoiceInitial($this->getOwner(), $this, $previousStatus);
218
    }
219
220
    /**
221
     * Returns the next invoiceable periodic price.
222
     *
223
     * In case it uses shared admin_assigned bookables, the price is divided by the number of usages
224
     */
225 8
    public function getPeriodicPrice(): Money
226
    {
227 8
        $bookable = $this->getBookable();
228 8
        $bookings = $bookable->getPeriodicPriceDividerBookings();
229
230 8
        if (!$bookings) {
231 7
            return $bookable->getPeriodicPrice();
232
        }
233
234 2
        return $bookable->getPeriodicPrice()->divide(count($bookings));
235
    }
236
}
237