Booking::invoiceInitial()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 11
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 1
crap 3
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
    public function __construct() {}
60
61 19
    public function setOwner(?User $owner = null): void
62
    {
63 19
        if ($this->getOwner()) {
64 4
            $this->getOwner()->bookingRemoved($this);
65
        }
66
67 19
        parent::setOwner($owner);
68
69 19
        if ($this->getOwner()) {
70 17
            $this->getOwner()->bookingAdded($this);
71
        }
72
73 19
        $this->invoiceInitial();
74
    }
75
76
    /**
77
     * Total count of participant, at least 1.
78
     */
79
    public function getParticipantCount(): int
80
    {
81
        return $this->participantCount;
82
    }
83
84 3
    public function setParticipantCount(int $participantCount): void
85
    {
86 3
        $this->participantCount = $participantCount;
87
    }
88
89
    public function getDestination(): string
90
    {
91
        return $this->destination;
92
    }
93
94 3
    public function setDestination(string $destination): void
95
    {
96 3
        $this->destination = $destination;
97
    }
98
99
    public function getStartComment(): string
100
    {
101
        return $this->startComment;
102
    }
103
104 3
    public function setStartComment(string $startComment): void
105
    {
106 3
        $this->startComment = $startComment;
107
    }
108
109
    public function getEndComment(): string
110
    {
111
        return $this->endComment;
112
    }
113
114 4
    public function setEndComment(string $endComment): void
115
    {
116 4
        $this->endComment = $endComment;
117
    }
118
119
    public function getStartDate(): Chronos
120
    {
121
        return $this->startDate;
122
    }
123
124 4
    public function setStartDate(Chronos $startDate): void
125
    {
126 4
        $this->startDate = $startDate;
127
    }
128
129 22
    public function getEndDate(): ?Chronos
130
    {
131 22
        return $this->endDate;
132
    }
133
134 7
    public function setEndDate(?Chronos $endDate): void
135
    {
136 7
        $this->endDate = $endDate;
137
    }
138
139
    public function getEstimatedEndDate(): string
140
    {
141
        return $this->estimatedEndDate;
142
    }
143
144 3
    public function setEstimatedEndDate(string $estimatedEndDate): void
145
    {
146 3
        $this->estimatedEndDate = $estimatedEndDate;
147
    }
148
149
    /**
150
     * Get bookable, may be null for "my own material" case.
151
     */
152 63
    public function getBookable(): ?Bookable
153
    {
154 63
        return $this->bookable;
155
    }
156
157
    /**
158
     * Set bookable.
159
     */
160 29
    public function setBookable(?Bookable $bookable): void
161
    {
162 29
        if ($this->bookable) {
163 1
            $this->bookable->bookingRemoved($this);
164
        }
165
166 29
        $this->bookable = $bookable;
167
168 29
        if ($this->bookable) {
169 29
            $this->bookable->bookingAdded($this);
170
        }
171
172 29
        $this->invoiceInitial();
173
    }
174
175 26
    public function getStatus(): BookingStatus
176
    {
177 26
        return $this->status;
178
    }
179
180 29
    public function setStatus(BookingStatus $status): void
181
    {
182 29
        $previousStatus = $this->status;
183 29
        $this->status = $status;
184 29
        $this->invoiceInitial($previousStatus);
185
    }
186
187
    /**
188
     * Mark the booking as terminated with an optional comment,
189
     * but only if not already terminated.
190
     */
191 4
    public function terminate(?string $comment): void
192
    {
193
        // Booking can only be terminated once
194 4
        if (!$this->getEndDate()) {
195 4
            $this->setEndDate(new Chronos());
196 4
            if ($comment) {
197 1
                $this->setEndComment($comment);
198
            }
199
        }
200
    }
201
202
    /**
203
     * If the booking is complete, will make initial invoicing.
204
     */
205 40
    private function invoiceInitial(?BookingStatus $previousStatus = null): void
206
    {
207 40
        if (!$this->getOwner() || !$this->getBookable()) {
208 40
            return;
209
        }
210
211
        global $container;
212
213
        /** @var Invoicer $invoicer */
214 9
        $invoicer = $container->get(Invoicer::class);
215 9
        $invoicer->invoiceInitial($this->getOwner(), $this, $previousStatus);
216
    }
217
218
    /**
219
     * Returns the next invoiceable periodic price.
220
     *
221
     * In case it uses shared admin_assigned bookables, the price is divided by the number of usages
222
     */
223 8
    public function getPeriodicPrice(): Money
224
    {
225 8
        $bookable = $this->getBookable();
226 8
        $bookings = $bookable->getPeriodicPriceDividerBookings();
227
228 8
        if (!$bookings) {
229 7
            return $bookable->getPeriodicPrice();
230
        }
231
232 2
        return $bookable->getPeriodicPrice()->divide(count($bookings));
233
    }
234
}
235