PaymentOrder::isExported()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * Copyright (C) 2020  Jan Böhmer
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Affero General Public License as published
7
 * by the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU Affero General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Affero General Public License
16
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 */
18
19
namespace App\Entity;
20
21
use App\Entity\Contracts\DBElementInterface;
22
use App\Entity\Contracts\TimestampedElementInterface;
23
use App\Entity\Embeddable\PayeeInfo;
24
use App\Repository\PaymentOrderRepository;
25
use App\Validator\FSRNotBlocked;
26
use DateTime;
27
use Doctrine\Common\Collections\ArrayCollection;
28
use Doctrine\Common\Collections\Collection;
29
use Doctrine\ORM\Mapping as ORM;
30
use Symfony\Component\Validator\Constraints as Assert;
31
use Vich\UploaderBundle\Entity\File;
32
use Vich\UploaderBundle\Mapping\Annotation as Vich;
33
34
/**
35
 * This entity represents a request to pay money for something to a persons bank account.
36
 * The applicant (which can be some other person than the payment receiver) submits a payment order ("Zahlungsauftrag")
37
 * via the front end form. The finance officers of the department for which the payment order was submitted receive an
38
 * email with a verification link where they have to confirm the payment oder.
39
 * In the backend the back office officers can see all submitted payment orders. In the first step it is checked if a
40
 * payment order is factually correct, then it is exported (as SEPA-XML) to the online banking system. Another officer
41
 * then checks if the payment order is factually correct and then approve the payment in the online banking.
42
 *
43
 * @ORM\Entity(repositoryClass=PaymentOrderRepository::class)
44
 * @ORM\Table("payment_orders")
45
 * @Vich\Uploadable()
46
 * @ORM\HasLifecycleCallbacks()
47
 */
48
class PaymentOrder implements DBElementInterface, TimestampedElementInterface, \Serializable
49
{
50
    use TimestampTrait;
51
52
    public const FUNDING_ID_REGEX = '/^(FA|M)-\d{3,4}-20\d{2}(_\d{2})?$/';
53
54
    /**
55
     * @ORM\Id()
56
     * @ORM\GeneratedValue()
57
     * @ORM\Column(type="integer")
58
     */
59
    private $id;
60
61
    /**
62
     * @var PayeeInfo
63
     * @ORM\Embedded(class="App\Entity\Embeddable\PayeeInfo")
64
     * @Assert\Valid()
65
     */
66
    private $bank_info;
67
68
    /**
69
     * @var string "Vorname"
70
     * @ORM\Column(type="string")
71
     * @Assert\NotBlank()
72
     */
73
    private $first_name = '';
74
75
    /**
76
     * @var string "Nachname"
77
     * @ORM\Column(type="string")
78
     * @Assert\NotBlank()
79
     */
80
    private $last_name = '';
81
82
    /**
83
     * @var Department "Struktur/Organisation"
84
     * @ORM\ManyToOne(targetEntity="App\Entity\Department")
85
     * @ORM\JoinColumn(nullable=false)
86
     * @Assert\NotNull()
87
     * @FSRNotBlocked(groups={"fsr_blocked"})
88
     */
89
    private $department;
90
91
    /**
92
     * @var string "Projektbezeichnung"
93
     * @Assert\NotBlank()
94
     * @Assert\Length(max=70, maxMessage="validator.project_name.too_long")
95
     * @ORM\Column(type="string")
96
     */
97
    private $project_name = '';
98
99
    /**
100
     * @var int "Betrag"
101
     * @ORM\Column(type="integer")
102
     * @Assert\Positive()
103
     */
104
    private $amount = null;
105
106
    /**
107
     * @var bool "mathematisch richtig"
108
     * @ORM\Column(type="boolean")
109
     */
110
    private $mathematically_correct = false;
111
112
    /**
113
     * @var bool
114
     * @ORM\Column(type="boolean")
115
     */
116
    private $exported = false;
117
118
    /**
119
     * @var bool "sachlich richtig"
120
     * @ORM\Column(type="boolean")
121
     */
122
    private $factually_correct = false;
123
124
    /**
125
     * @ORM\Column(type="text")
126
     */
127
    private $comment = '';
128
129
    /**
130
     * @var string "Mittelfreigabe / Finanzantrag"
131
     * @ORM\Column(type="string")
132
     * @Assert\Regex(PaymentOrder::FUNDING_ID_REGEX)
133
     */
134
    private $funding_id = '';
135
136
    /**
137
     * @var string|null
138
     * @ORM\Column(type="string", nullable=true)
139
     */
140
    private $confirm1_token = null;
141
142
    /**
143
     * @var DateTime|null
144
     * @ORM\Column(type="datetime", nullable=true)
145
     */
146
    private $confirm1_timestamp = null;
147
148
    /**
149
     * @var string|null
150
     * @ORM\Column(type="string", nullable=true)
151
     */
152
    private $confirm2_token = null;
153
154
    /**
155
     * @var DateTime|null
156
     * @ORM\Column(type="datetime", nullable=true)
157
     */
158
    private $confirm2_timestamp = null;
159
160
    /**
161
     * @var bool Is FSR-Kom resolution
162
     * @ORM\Column(type="boolean")
163
     */
164
    private $fsr_kom_resolution = false;
165
166
    /**
167
     * @var DateTime|null
168
     * @ORM\Column(type="date", nullable=true)
169
     * @Assert\LessThanOrEqual(value="today", message="validator.resolution_must_not_be_in_future")
170
     * @Assert\GreaterThan(value="-3 years", message="validator.resolution_too_old")
171
     * @Assert\Expression("value !== null || (this.getDepartment() !== null && this.getDepartment().getType() != 'fsr' && this.isFsrKomResolution() === false)", message="validator.resolution_date.needed_for_fsr_fsrkom")
172
     */
173
    private $resolution_date = null;
174
175
    /**
176
     * @var string
177
     * @ORM\Column(type="string", nullable=false)
178
     * @Assert\Email()
179
     */
180
    private $contact_email = '';
181
182
    /**
183
     * @var DateTime|null
184
     * @ORM\Column(type="datetime", nullable=true)
185
     */
186
    private $booking_date = null;
187
188
    /**
189
     * @var bool
190
     * @ORM\Column(type="boolean")
191
     */
192
    private $references_exported = false;
193
194
    /*
195
     * Associated files
196
     */
197
198
    /**
199
     * @Vich\UploadableField(mapping="payment_orders_form", fileNameProperty="printed_form.name", size="printed_form.size", mimeType="printed_form.mimeType", originalName="printed_form.originalName", dimensions="printed_form.dimensions")
200
     *
201
     * @var \Symfony\Component\HttpFoundation\File\File|null
202
     * @Assert\File(
203
     *     maxSize = "1024k",
204
     *     mimeTypes = {"application/pdf", "application/x-pdf"},
205
     *     mimeTypesMessage = "validator.upload_pdf"
206
     * )
207
     */
208
    private $printed_form_file;
209
210
    /**
211
     * @ORM\Embedded(class="Vich\UploaderBundle\Entity\File")
212
     *
213
     * @var File
214
     */
215
    private $printed_form;
216
217
    /**
218
     * @Vich\UploadableField(mapping="payment_orders_references", fileNameProperty="references.name", size="references.size", mimeType="references.mimeType", originalName="references.originalName", dimensions="references.dimensions")
219
     *
220
     * @var \Symfony\Component\HttpFoundation\File\File|null
221
     * @Assert\NotBlank(groups={"frontend"})
222
     * @Assert\File(
223
     *     maxSize = "10M",
224
     *     mimeTypes = {"application/pdf", "application/x-pdf"},
225
     *     mimeTypesMessage = "validator.upload_pdf"
226
     * )
227
     */
228
    private $references_file;
229
230
    /**
231
     * @var Collection|SEPAExport[]
232
     * @ORM\ManyToMany(targetEntity="App\Entity\SEPAExport", mappedBy="associated_payment_orders")
233
     */
234
    private $associated_sepa_exports;
235
236
    /**
237
     * @ORM\Embedded(class="Vich\UploaderBundle\Entity\File")
238
     *
239
     * @var File
240
     */
241
    private $references;
242
243
    public function __construct()
244
    {
245
        $this->bank_info = new PayeeInfo();
246
247
        $this->associated_sepa_exports = new ArrayCollection();
248
249
        $this->references = new File();
250
        $this->printed_form = new File();
251
    }
252
253
    public function getId(): ?int
254
    {
255
        return $this->id;
256
    }
257
258
    /**
259
     * Returns the bank info associated with this payment order.
260
     *
261
     *@see PayeeInfo
262
     */
263
    public function getBankInfo(): PayeeInfo
264
    {
265
        return $this->bank_info;
266
    }
267
268
    /**
269
     * Returns the bank info associated with this payment order.
270
     *
271
     *@see PayeeInfo
272
     */
273
    public function setBankInfo(PayeeInfo $bank_info): PaymentOrder
274
    {
275
        $this->bank_info = $bank_info;
276
277
        return $this;
278
    }
279
280
    /**
281
     * Returns the full name of person which has submitted this payment order.
282
     */
283
    public function getFullName(): string
284
    {
285
        if (empty($this->getFirstName())) {
286
            return $this->getLastName();
287
        }
288
        if (empty($this->getLastName())) {
289
            return $this->getFirstName();
290
        }
291
292
        return $this->getFirstName().' '.$this->getLastName();
293
    }
294
295
    /**
296
     * Returns the first name of the person which has submitted this payment order.
297
     */
298
    public function getFirstName(): string
299
    {
300
        return $this->first_name;
301
    }
302
303
    /**
304
     * Sets the first name of the person which has submitted this payment order.
305
     */
306
    public function setFirstName(string $first_name): PaymentOrder
307
    {
308
        $this->first_name = $first_name;
309
310
        return $this;
311
    }
312
313
    /**
314
     * Returns the last name of the person which has submitted this payment order.
315
     */
316
    public function getLastName(): string
317
    {
318
        return $this->last_name;
319
    }
320
321
    /**
322
     * Sets the last name of the person which has submitted this payment order.
323
     */
324
    public function setLastName(string $last_name): PaymentOrder
325
    {
326
        $this->last_name = $last_name;
327
328
        return $this;
329
    }
330
331
    /**
332
     * Returns the department for which this payment order was submitted.
333
     *
334
     * @return Department
335
     */
336
    public function getDepartment(): ?Department
337
    {
338
        return $this->department;
339
    }
340
341
    /**
342
     * Returns the department for which this payment order was submitted.
343
     */
344
    public function setDepartment(Department $department): PaymentOrder
345
    {
346
        $this->department = $department;
347
348
        return $this;
349
    }
350
351
    /**
352
     * Returns the name of the project which caused this payment order.
353
     * This value will be used in the bank reference value by default.
354
     */
355
    public function getProjectName(): string
356
    {
357
        return $this->project_name;
358
    }
359
360
    /**
361
     * Returns the name of the project which caused this payment order.
362
     * This value will be used in the bank reference value by default.
363
     */
364
    public function setProjectName(string $project_name): PaymentOrder
365
    {
366
        $this->project_name = $project_name;
367
368
        return $this;
369
    }
370
371
    /**
372
     * Returns the amount that should be paid in (euro) cents.
373
     *
374
     * @return int
375
     */
376
    public function getAmount(): ?int
377
    {
378
        return $this->amount;
379
    }
380
381
    /**
382
     * Sets the amount that should be paid in (euro) cents.
383
     */
384
    public function setAmount(int $amount): PaymentOrder
385
    {
386
        $this->amount = $amount;
387
388
        return $this;
389
    }
390
391
    /**
392
     * Returns the amount formatted as euro string (with dot as decimal separator) in the form "123.45" (only 2 digits).
393
     * Returns null if no amount was set.
394
     */
395
    public function getAmountString(): ?string
396
    {
397
        if (null === $this->amount) {
398
            return null;
399
        }
400
401
        //%F (with big F) is important here, to always output with a dot
402
        return sprintf('%.2F', $this->amount / 100);
403
    }
404
405
    /**
406
     * Returns whether this payment order was checked as mathematically correct.
407
     * This means it was checked that the amount and data in this payment order matches the data on the invoice.
408
     */
409
    public function isMathematicallyCorrect(): bool
410
    {
411
        return $this->mathematically_correct;
412
    }
413
414
    /**
415
     * Sets whether this payment order was checked as mathematically correct.
416
     * This means it was checked that the amount and data in this payment order matches the data on the invoice.
417
     */
418
    public function setMathematicallyCorrect(bool $mathematically_correct): PaymentOrder
419
    {
420
        $this->mathematically_correct = $mathematically_correct;
421
422
        return $this;
423
    }
424
425
    /**
426
     * Returns whether this payment order was checked as factually correct.
427
     * This means it was checked that this payment is really needed. In our context it also means that an payment order
428
     * was payed out and is finished.
429
     */
430
    public function isFactuallyCorrect(): bool
431
    {
432
        return $this->factually_correct;
433
    }
434
435
    /**
436
     * Sets whether this payment order was checked as factually correct.
437
     * This means it was checked that this payment is really needed. In our context it also means that an payment order
438
     * was payed out and is finished.
439
     */
440
    public function setFactuallyCorrect(bool $factually_correct): PaymentOrder
441
    {
442
        $this->factually_correct = $factually_correct;
443
444
        //Update the status of booking date
445
        if ($factually_correct) {
446
            $this->booking_date = new \DateTime();
447
        } else {
448
            $this->booking_date = null;
449
        }
450
451
        return $this;
452
    }
453
454
    /**
455
     * Returns whether this payment order was exported as SEPA-XML (and imported in online banking).
456
     * This is automatically set when payment orders are exported.
457
     */
458
    public function isExported(): bool
459
    {
460
        return $this->exported;
461
    }
462
463
    /**
464
     * Sets whether this payment order was exported as SEPA-XML (and imported in online banking).
465
     * This is automatically set when payment orders are exported.
466
     */
467
    public function setExported(bool $exported): PaymentOrder
468
    {
469
        $this->exported = $exported;
470
471
        return $this;
472
    }
473
474
    /**
475
     * Returns the comment associated with this payment order.
476
     * This can be HTML if changed in the backend.
477
     */
478
    public function getComment(): string
479
    {
480
        return $this->comment;
481
    }
482
483
    /**
484
     * Returns the comment associated with this payment order.
485
     * This can be HTML if changed in the backend.
486
     */
487
    public function setComment(string $comment): PaymentOrder
488
    {
489
        $this->comment = $comment;
490
491
        return $this;
492
    }
493
494
    /**
495
     * Returns the funding ID associated with this payment order (if it existing).
496
     * Returns an empty string if no funding ID is associated.
497
     */
498
    public function getFundingId(): string
499
    {
500
        return $this->funding_id;
501
    }
502
503
    /**
504
     * Sets the funding ID associated with this payment order (if it existing).
505
     * Set to an empty string if no funding ID is associated.
506
     */
507
    public function setFundingId(string $funding_id): self
508
    {
509
        $this->funding_id = $funding_id;
510
511
        return $this;
512
    }
513
514
    /**
515
     * Return the HTTPFoundation File associated that contains the PDF version of this payment order.
516
     */
517
    public function getPrintedFormFile(): ?\Symfony\Component\HttpFoundation\File\File
518
    {
519
        return $this->printed_form_file;
520
    }
521
522
    /**
523
     * Sets the HTTPFoundation File associated that contains the PDF version of this payment order.
524
     */
525
    public function setPrintedFormFile(?\Symfony\Component\HttpFoundation\File\File $printed_form_file): PaymentOrder
526
    {
527
        $this->printed_form_file = $printed_form_file;
528
529
        if (null !== $printed_form_file) {
530
            /* It is required that at least one field changes if you are using doctrine
531
             otherwise the event listeners won't be called and the file is lost
532
             But we dont really want to change  the last time value, so just copy it and create a new reference
533
            so doctrine thinks something has changed, but practically everything looks the same */
534
            $this->last_modified = clone ($this->last_modified ?? new \DateTime());
535
        }
536
537
        return $this;
538
    }
539
540
    /**
541
     * Return the Vich File associated that contains the PDF version of this payment order.
542
     */
543
    public function getPrintedForm(): File
544
    {
545
        return $this->printed_form;
546
    }
547
548
    /**
549
     * Return the Vich File associated that contains the PDF version of this payment order.
550
     */
551
    public function setPrintedForm(File $printed_form): PaymentOrder
552
    {
553
        $this->printed_form = $printed_form;
554
555
        return $this;
556
    }
557
558
    /**
559
     * Return the HTTPFoundation File associated that contains the invoice for this payment order.
560
     */
561
    public function getReferencesFile(): ?\Symfony\Component\HttpFoundation\File\File
562
    {
563
        return $this->references_file;
564
    }
565
566
    /**
567
     * Sets the HTTPFoundation File associated that contains the invoice for this payment order.
568
     */
569
    public function setReferencesFile(?\Symfony\Component\HttpFoundation\File\File $references_file): PaymentOrder
570
    {
571
        $this->references_file = $references_file;
572
573
        if (null !== $references_file) {
574
            /* It is required that at least one field changes if you are using doctrine
575
             otherwise the event listeners won't be called and the file is lost
576
             But we dont really want to change  the last time value, so just copy it and create a new reference
577
            so doctrine thinks something has changed, but practically everything looks the same */
578
            $this->last_modified = clone ($this->last_modified ?? new \DateTime());
579
        }
580
581
        return $this;
582
    }
583
584
    /**
585
     * Return the Vich File associated that contains the invoice for this payment order.
586
     */
587
    public function getReferences(): File
588
    {
589
        return $this->references;
590
    }
591
592
    /**
593
     * Sets the HTTPFoundation File associated that contains the invoice for this payment order.
594
     */
595
    public function setReferences(File $references): PaymentOrder
596
    {
597
        $this->references = $references;
598
599
        return $this;
600
    }
601
602
    /**
603
     * Returns the (hashed) token that can be used to access the confirmation1 page of this payment page.
604
     * This value be verified with the password_verify() function.
605
     * Can be null if no confirmation should be possible for this payment order.
606
     */
607
    public function getConfirm1Token(): ?string
608
    {
609
        return $this->confirm1_token;
610
    }
611
612
    /**
613
     * Returns the (hashed) token that can be used to access the confirmation1 page of this payment page.
614
     * This value be created with the password_hash() function.
615
     * Can be null if no confirmation should be possible for this payment order.
616
     */
617
    public function setConfirm1Token(?string $confirm1_token): PaymentOrder
618
    {
619
        $this->confirm1_token = $confirm1_token;
620
621
        return $this;
622
    }
623
624
    /**
625
     * Returns the timestamp when the first confirmation for this payment order was submitted.
626
     * Returns null if this payment order is not confirmed (yet).
627
     */
628
    public function getConfirm1Timestamp(): ?DateTime
629
    {
630
        return $this->confirm1_timestamp;
631
    }
632
633
    /**
634
     * Sets the timestamp when the first confirmation for this payment order was submitted.
635
     * Set to null if this payment order is not confirmed (yet).
636
     */
637
    public function setConfirm1Timestamp(?DateTime $confirm1_timestamp): PaymentOrder
638
    {
639
        $this->confirm1_timestamp = $confirm1_timestamp;
640
641
        return $this;
642
    }
643
644
    /**
645
     *  Returns the (hashed) token that can be used to access the confirmation2 page of this payment page.
646
     * This value be verified with the password_verify() function.
647
     * Can be null if no confirmation should be possible for this payment order.
648
     */
649
    public function getConfirm2Token(): ?string
650
    {
651
        return $this->confirm2_token;
652
    }
653
654
    /**
655
     * Sets the (hashed) token that can be used to access the confirmation1 page of this payment page.
656
     * This value be created with the password_hash() function.
657
     * Can be null if no confirmation should be possible for this payment order.
658
     */
659
    public function setConfirm2Token(?string $confirm2_token): PaymentOrder
660
    {
661
        $this->confirm2_token = $confirm2_token;
662
663
        return $this;
664
    }
665
666
    /**
667
     * Returns the timestamp when the second confirmation for this payment order was submitted.
668
     * Returns null if this payment order is not confirmed (yet).
669
     */
670
    public function getConfirm2Timestamp(): ?DateTime
671
    {
672
        return $this->confirm2_timestamp;
673
    }
674
675
    /**
676
     * Sets the timestamp when the second confirmation for this payment order was submitted.
677
     * Set to null if this payment order is not confirmed (yet).
678
     */
679
    public function setConfirm2Timestamp(?DateTime $confirm2_timestamp): PaymentOrder
680
    {
681
        $this->confirm2_timestamp = $confirm2_timestamp;
682
683
        return $this;
684
    }
685
686
    /**
687
     * Returns whether this payment order is confirmed (by both instances).
688
     */
689
    public function isConfirmed(): bool
690
    {
691
        return null !== $this->confirm1_timestamp && null !== $this->confirm2_timestamp;
692
    }
693
694
    /**
695
     * Returns the email of the person which has submitted this payment order and which is used for answering questions.
696
     */
697
    public function getContactEmail(): string
698
    {
699
        return $this->contact_email;
700
    }
701
702
    /**
703
     * Sets the email of the person which has submitted this payment order and which is used for answering questions.
704
     */
705
    public function setContactEmail(string $contact_email): PaymentOrder
706
    {
707
        $this->contact_email = $contact_email;
708
709
        return $this;
710
    }
711
712
    /**
713
     * Returns whether this is an payment order for an resolution of the FSR-Kom (these are handled differently).
714
     */
715
    public function isFsrKomResolution(): bool
716
    {
717
        return $this->fsr_kom_resolution;
718
    }
719
720
    /**
721
     * Sets whether this is an payment order for an resolution of the FSR-Kom (these are handled differently).
722
     */
723
    public function setFsrKomResolution(bool $fsr_kom_resolution): PaymentOrder
724
    {
725
        $this->fsr_kom_resolution = $fsr_kom_resolution;
726
727
        return $this;
728
    }
729
730
    /**
731
     * Returns the date when the resolution that causes this payment order was passed.
732
     * This value is optional as not every payment order needs an resolution.
733
     * Only the date is shown for this DateTime.
734
     */
735
    public function getResolutionDate(): ?DateTime
736
    {
737
        return $this->resolution_date;
738
    }
739
740
    /**
741
     * Sets the date when the resolution that causes this payment order was passed.
742
     * This value is optional as not every payment order needs an resolution.
743
     * Only the date is shown for this DateTime.
744
     */
745
    public function setResolutionDate(?DateTime $resolution_date): PaymentOrder
746
    {
747
        $this->resolution_date = $resolution_date;
748
749
        return $this;
750
    }
751
752
    /**
753
     * Returns the datetime when this payment was booked in banking.
754
     * Returns null if payment_order was not booked yet.
755
     * The value is set automatically to now when the "factually_checked" field is set.
756
     */
757
    public function getBookingDate(): ?DateTime
758
    {
759
        return $this->booking_date;
760
    }
761
762
    /**
763
     * Manually set the datetime when this payment was booked in banking.
764
     * Set to null if payment_order was not booked yet.
765
     * The value is set automatically to now when the "factually_checked" field is set.
766
     */
767
    public function setBookingDate(?DateTime $booking_date): PaymentOrder
768
    {
769
        $this->booking_date = $booking_date;
770
771
        return $this;
772
    }
773
774
    /**
775
     * Returns whether the references for this payment order were already exported.
776
     */
777
    public function isReferencesExported(): bool
778
    {
779
        return $this->references_exported;
780
    }
781
782
    /**
783
     * Sets whether the references for this payment order were already exported.
784
     */
785
    public function setReferencesExported(bool $references_exported): PaymentOrder
786
    {
787
        $this->references_exported = $references_exported;
788
789
        return $this;
790
    }
791
792
    /**
793
     * @return SEPAExport[]|Collection
794
     */
795
    public function getAssociatedSepaExports()
796
    {
797
        return $this->associated_sepa_exports;
798
    }
799
800
801
802
    public function serialize()
803
    {
804
        return serialize($this->getId());
805
    }
806
807
    public function unserialize($serialized)
808
    {
809
        $this->id = unserialize($serialized);
810
    }
811
812
    /**
813
     * Get the ID as string like ZA0005.
814
     */
815
    public function getIDString(): string
816
    {
817
        return sprintf('ZA%04d', $this->getId());
818
    }
819
}
820