Completed
Push — master ( bcdba8...408d98 )
by Jan
03:54
created

Part::isObsolete()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
c 0
b 0
f 0
nc 4
nop 0
dl 0
loc 15
rs 10
1
<?php
2
/**
3
 *
4
 * part-db version 0.1
5
 * Copyright (C) 2005 Christoph Lechner
6
 * http://www.cl-projects.de/
7
 *
8
 * part-db version 0.2+
9
 * Copyright (C) 2009 K. Jacobs and others (see authors.php)
10
 * http://code.google.com/p/part-db/
11
 *
12
 * Part-DB Version 0.4+
13
 * Copyright (C) 2016 - 2019 Jan Böhmer
14
 * https://github.com/jbtronics
15
 *
16
 * This program is free software; you can redistribute it and/or
17
 * modify it under the terms of the GNU General Public License
18
 * as published by the Free Software Foundation; either version 2
19
 * of the License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
29
 *
30
 */
31
32
declare(strict_types=1);
33
34
/**
35
 * part-db version 0.1
36
 * Copyright (C) 2005 Christoph Lechner
37
 * http://www.cl-projects.de/.
38
 *
39
 * part-db version 0.2+
40
 * Copyright (C) 2009 K. Jacobs and others (see authors.php)
41
 * http://code.google.com/p/part-db/
42
 *
43
 * Part-DB Version 0.4+
44
 * Copyright (C) 2016 - 2019 Jan Böhmer
45
 * https://github.com/jbtronics
46
 *
47
 * This program is free software; you can redistribute it and/or
48
 * modify it under the terms of the GNU General Public License
49
 * as published by the Free Software Foundation; either version 2
50
 * of the License, or (at your option) any later version.
51
 *
52
 * This program is distributed in the hope that it will be useful,
53
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
54
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
55
 * GNU General Public License for more details.
56
 *
57
 * You should have received a copy of the GNU General Public License
58
 * along with this program; if not, write to the Free Software
59
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
60
 */
61
62
namespace App\Entity\Parts;
63
64
use App\Entity\Attachments\Attachment;
65
use App\Entity\Attachments\AttachmentContainingDBElement;
66
use App\Entity\Devices\Device;
67
use App\Entity\PriceInformations\Orderdetail;
68
use App\Security\Annotations\ColumnSecurity;
69
use Doctrine\ORM\Mapping as ORM;
70
71
use Symfony\Component\Validator\Constraints as Assert;
72
73
/**
74
 * Class Part.
75
 *
76
 * @ORM\Entity(repositoryClass="App\Repository\PartRepository")
77
 * @ORM\Table("`parts`")
78
 */
79
class Part extends AttachmentContainingDBElement
80
{
81
    public const INSTOCK_UNKNOWN = -2;
82
83
    /**
84
     * @ORM\OneToMany(targetEntity="App\Entity\Attachments\PartAttachment", mappedBy="element")
85
     */
86
    protected $attachments;
87
88
    /**
89
     * @var Category
90
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="parts")
91
     * @ORM\JoinColumn(name="id_category", referencedColumnName="id")
92
     */
93
    protected $category;
94
95
    /**
96
     * @var Footprint|null
97
     * @ORM\ManyToOne(targetEntity="Footprint", inversedBy="parts")
98
     * @ORM\JoinColumn(name="id_footprint", referencedColumnName="id")
99
     *
100
     * @ColumnSecurity(prefix="footprint", type="object")
101
     */
102
    protected $footprint;
103
104
    /**
105
     * @var Manufacturer|null
106
     * @ORM\ManyToOne(targetEntity="Manufacturer", inversedBy="parts")
107
     * @ORM\JoinColumn(name="id_manufacturer", referencedColumnName="id")
108
     *
109
     * @ColumnSecurity(prefix="manufacturer", type="object")
110
     */
111
    protected $manufacturer;
112
113
    /**
114
     * @var Attachment
115
     * @ORM\ManyToOne(targetEntity="App\Entity\Attachments\Attachment")
116
     * @ORM\JoinColumn(name="id_master_picture_attachement", referencedColumnName="id")
117
     *
118
     * @ColumnSecurity(prefix="attachments", type="object")
119
     */
120
    protected $master_picture_attachment;
121
122
    /**
123
     * @var Orderdetail[]
124
     * @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part")
125
     *
126
     * @ColumnSecurity(prefix="orderdetails", type="object")
127
     */
128
    protected $orderdetails;
129
130
    /**
131
     * @var Orderdetail
132
     * @ORM\OneToOne(targetEntity="App\Entity\PriceInformations\Orderdetail")
133
     * @ORM\JoinColumn(name="order_orderdetails_id", referencedColumnName="id")
134
     *
135
     * @ColumnSecurity(prefix="order", type="object")
136
     */
137
    protected $order_orderdetail;
138
139
    //TODO
140
    protected $devices;
141
142
    /**
143
     *  @ColumnSecurity(type="datetime")
144
     *  @ORM\Column(type="datetimetz", name="datetime_added")
145
     */
146
    protected $addedDate;
147
148
    /**
149
     * @var \DateTime The date when this element was modified the last time.
150
     * @ORM\Column(type="datetimetz", name="last_modified")
151
     * @ColumnSecurity(type="datetime")
152
     */
153
    protected $lastModified;
154
155
    /**********************
156
     * Propertys
157
     ***********************/
158
159
    /**
160
     * @var string
161
     * @ORM\Column(type="string")
162
     *
163
     * @ColumnSecurity(prefix="name")
164
     */
165
    protected $name;
166
167
    /**
168
     * @var string
169
     * @ORM\Column(type="text")
170
     *
171
     * @ColumnSecurity(prefix="description")
172
     */
173
    protected $description = '';
174
175
    /**
176
     * @var ?PartLot[]
177
     * @ORM\OneToMany(targetEntity="PartLot", mappedBy="part")
178
     */
179
    protected $partLots;
180
181
    /**
182
     * @var int
183
     * @ORM\Column(type="integer")
184
     * @Assert\GreaterThanOrEqual(0)
185
     *
186
     * @ColumnSecurity(prefix="mininstock", type="integer")
187
     */
188
    protected $mininstock = 0;
189
190
    /**
191
     * @var float
192
     * @ORM\Column(type="float")
193
     * @Assert\PositiveOrZero()
194
     */
195
    protected $minamount;
196
197
    /**
198
     * @var string
199
     * @ORM\Column(type="text")
200
     * @ColumnSecurity(prefix="comment")
201
     */
202
    protected $comment = '';
203
204
    /**
205
     * @var bool
206
     * @ORM\Column(type="boolean")
207
     */
208
    protected $visible = true;
209
210
    /**
211
     * @var bool
212
     * @ORM\Column(type="boolean")
213
     * @ColumnSecurity(type="boolean")
214
     */
215
    protected $favorite = false;
216
217
    /**
218
     * @var int
219
     * @ORM\Column(type="integer")
220
     * @ColumnSecurity(prefix="order", type="integer")
221
     */
222
    protected $order_quantity = 0;
223
224
    /**
225
     * @var bool
226
     * @ORM\Column(type="boolean")
227
     * @ColumnSecurity(prefix="order", type="boolean")
228
     */
229
    protected $manual_order = false;
230
231
    /**
232
     * @var string
233
     * @ORM\Column(type="string")
234
     * @ColumnSecurity(prefix="manufacturer", type="string", placeholder="")
235
     */
236
    protected $manufacturer_product_url = '';
237
238
    /**
239
     * @var string
240
     * @ORM\Column(type="string")
241
     * @ColumnSecurity(prefix="manufacturer", type="string", placeholder="")
242
     */
243
    protected $manufacturer_product_number;
244
245
    /**
246
     * @var bool Determines if this part entry needs review (for example, because it is work in progress)
247
     * @ORM\Column(type="boolean")
248
     */
249
    protected $needs_review = false;
250
251
    /**
252
     * @var MeasurementUnit The unit in which the part's amount is measured.
253
     * @ORM\ManyToOne(targetEntity="MeasurementUnit")
254
     * @ORM\JoinColumn(name="id_part_unit", referencedColumnName="id", nullable=true)
255
     */
256
    protected $partUnit;
257
258
    /**
259
     * @var string A comma seperated list of tags, assocciated with the part.
260
     * @ORM\Column(type="text")
261
     */
262
    protected $tags;
263
264
    /**
265
     * @var float|null How much a single part unit weighs in gramms.
266
     * @ORM\Column(type="float", nullable=true)
267
     */
268
    protected $mass;
269
270
    /**
271
     * Returns the ID as an string, defined by the element class.
272
     * This should have a form like P000014, for a part with ID 14.
273
     *
274
     * @return string The ID as a string;
275
     */
276
    public function getIDString(): string
277
    {
278
        return 'P'.sprintf('%06d', $this->getID());
279
    }
280
281
    /*********************************************************************************
282
     * Getters
283
     ********************************************************************************/
284
285
    /**
286
     * Get the description string like it is saved in the database.
287
     * This can contain BBCode, it is not parsed yet.
288
     *
289
     * @return string the description
290
     */
291
    public function getDescription(): string
292
    {
293
        return  htmlspecialchars($this->description);
294
    }
295
296
    /**
297
     *  Get the count of parts which are in stock.
298
     *  When the instock is unkown, then Part::INSTOCK_UNKNOWN is returned.
299
     *
300
     * @return int count of parts which are in stock
301
     */
302
    public function getInstock(): int
303
    {
304
        return $this->instock;
305
    }
306
307
    /**
308
     * Check if the value of the Instock is unknown.
309
     *
310
     * @return bool True, if the value of the instock is unknown.
311
     */
312
    public function isInstockUnknown(): bool
313
    {
314
        return $this->instock <= static::INSTOCK_UNKNOWN;
315
    }
316
317
    /** @noinspection ReturnTypeCanBeDeclaredInspection */
318
319
    /**
320
     *  Get the count of parts which must be in stock at least.
321
     *
322
     * @return int count of parts which must be in stock at least
323
     */
324
    public function getMinInstock(): int
325
    {
326
        return $this->mininstock;
327
    }
328
329
    /**
330
     *  Get the comment associated with this part.
331
     *
332
     * @return string The raw/unparsed comment
333
     */
334
    public function getComment(): string
335
    {
336
        return htmlspecialchars($this->comment);
337
    }
338
339
    /**
340
     *  Get if this part is obsolete.
341
     *
342
     *     A Part is marked as "obsolete" if all their orderdetails are marked as "obsolete".
343
     *          If a part has no orderdetails, the part isn't marked as obsolete.
344
     *
345
     * @return bool true, if this part is obsolete. false, if this part isn't obsolete
346
     */
347
    public function isObsolete(): bool
348
    {
349
        $all_orderdetails = $this->getOrderdetails();
350
351
        if (0 === count($all_orderdetails)) {
352
            return false;
353
        }
354
355
        foreach ($all_orderdetails as $orderdetails) {
356
            if (!$orderdetails->getObsolete()) {
357
                return false;
358
            }
359
        }
360
361
        return true;
362
    }
363
364
    /**
365
     *  Get if this part is visible.
366
     *
367
     * @return bool true if this part is visible
368
     *              false if this part isn't visible
369
     */
370
    public function isVisible(): bool
371
    {
372
        return $this->visible;
373
    }
374
375
    /**
376
     * Get if this part is a favorite.
377
     *
378
     * @return bool * true if this part is a favorite
379
     *              * false if this part is not a favorite.
380
     */
381
    public function isFavorite(): bool
382
    {
383
        return $this->favorite;
384
    }
385
386
    /**
387
     *  Get the selected order orderdetails of this part.
388
     *
389
     * @return Orderdetail the selected order orderdetails
390
     *
391
     * @throws Exception
392
     */
393
    public function getOrderOrderdetails(): ?Orderdetail
394
    {
395
        //TODO
396
        /*
397
        if ($this->order_orderdetails->getObsolete()) {
398
            $this->setOrderOrderdetailsID(null);
399
            $this->order_orderdetails = null;
400
        }*/
401
402
        return $this->order_orderdetail;
403
    }
404
405
    /**
406
     *  Get the order quantity of this part.
407
     *
408
     * @return int the order quantity
409
     */
410
    public function getOrderQuantity(): int
411
    {
412
        return $this->order_quantity;
413
    }
414
415
    /**
416
     *  Get the minimum quantity which should be ordered.
417
     *
418
     * @param bool $with_devices * if true, all parts from devices which are marked as "to order" will be included in the calculation
419
     *                           * if false, only max(mininstock - instock, 0) will be returned
420
     *
421
     * @return int the minimum order quantity
422
     *
423
     * @throws Exception
424
     */
425
    public function getMinOrderQuantity(bool $with_devices = true): int
0 ignored issues
show
Unused Code introduced by
The parameter $with_devices is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

425
    public function getMinOrderQuantity(/** @scrutinizer ignore-unused */ bool $with_devices = true): int

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
426
    {
427
        //TODO
428
        throw new \Exception('Not implemented yet...');
429
        /*
430
        if ($with_devices) {
431
        $count_must_order = 0;      // for devices with "order_only_missing_parts == false"
432
        $count_should_order = 0;    // for devices with "order_only_missing_parts == true"
433
        $deviceparts = DevicePart::getOrderDeviceParts($this->database, $this->current_user, $this->log, $this->getID());
434
        foreach ($deviceparts as $devicepart) {
435
        /** @var $devicepart DevicePart */
436
        /* @var $device Device */ /*
437
    $device = $devicepart->getDevice();
438
    if ($device->getOrderOnlyMissingParts()) {
439
    $count_should_order += $device->getOrderQuantity() * $devicepart->getMountQuantity();
440
    } else {
441
    $count_must_order += $device->getOrderQuantity() * $devicepart->getMountQuantity();
442
    }
443
    }
444
445
    return $count_must_order + max(0, $this->getMinInstock() - $this->getInstock() + $count_should_order);
446
    } else {
447
    return max(0, $this->getMinInstock() - $this->getInstock());
448
    } **/
449
    }
450
451
    /**
452
     *  Check if this part is marked for manual ordering.
453
     *
454
     * @return bool the "manual_order" attribute
455
     */
456
    public function isMarkedForManualOrder(): bool
457
    {
458
        return $this->manual_order;
459
    }
460
461
    /**
462
     * Check if the part is automatically marked for Ordering, because the instock value is smaller than the min instock value.
463
     * This is called automatic ordering.
464
     *
465
     * @return bool True, if the part should be ordered.
466
     */
467
    public function isAutoOrdered(): bool
468
    {
469
        //Parts with negative instock never gets ordered.
470
        if ($this->getInstock() < 0) {
471
            return false;
472
        }
473
474
        return $this->getInstock() < $this->getMinInstock();
475
    }
476
477
    /**
478
     *  Get the link to the website of the article on the manufacturers website
479
     *  When no this part has no explicit url set, then it is tried to generate one from the Manufacturer of this part
480
     *  automatically.
481
     *
482
     * @param
483
     *
484
     * @return string the link to the article
485
     */
486
    public function getManufacturerProductUrl(): string
487
    {
488
        if ('' !== $this->manufacturer_product_url) {
489
            return $this->manufacturer_product_url;
490
        }
491
492
        if (null !== $this->getManufacturer()) {
493
            return $this->getManufacturer()->getAutoProductUrl($this->name);
494
        }
495
496
        return ''; // no url is available
497
    }
498
499
    /**
500
     * Similar to getManufacturerProductUrl, but here only the database value is returned.
501
     *
502
     * @return string The manufacturer url saved in DB for this part.
503
     */
504
    public function getOwnProductURL(): string
505
    {
506
        return $this->manufacturer_product_url;
507
    }
508
509
    /**
510
     *  Get the category of this part.
511
     *
512
     * There is always a category, for each part!
513
     *
514
     * @return Category the category of this part
515
     */
516
    public function getCategory(): Category
517
    {
518
        return $this->category;
519
    }
520
521
    /**
522
     *  Get the footprint of this part (if there is one).
523
     *
524
     * @return Footprint the footprint of this part (if there is one)
525
     */
526
    public function getFootprint(): ?Footprint
527
    {
528
        return $this->footprint;
529
    }
530
531
    /**
532
     *  Get the storelocation of this part (if there is one).
533
     *
534
     * @return Storelocation the storelocation of this part (if there is one)
535
     */
536
    public function getStorelocation(): ?Storelocation
537
    {
538
        return $this->storelocation;
539
    }
540
541
    /**
542
     *  Get the manufacturer of this part (if there is one).
543
     *
544
     * @return Manufacturer the manufacturer of this part (if there is one)
545
     */
546
    public function getManufacturer(): ?Manufacturer
547
    {
548
        return $this->manufacturer;
549
    }
550
551
    /**
552
     *  Get the master picture "Attachement"-object of this part (if there is one).
553
     *
554
     * @return Attachment the master picture Attachement of this part (if there is one)
555
     */
556
    public function getMasterPictureAttachement(): ?Attachment
557
    {
558
        return $this->master_picture_attachment;
559
    }
560
561
    /**
562
     *  Get all orderdetails of this part.
563
     *
564
     * @param bool $hide_obsolete If true, obsolete orderdetails will NOT be returned
565
     *
566
     * @return Orderdetail[] * all orderdetails as a one-dimensional array of Orderdetails objects
567
     *                        (empty array if there are no ones)
568
     *                        * the array is sorted by the suppliers names / minimum order quantity
569
     *
570
     * @throws Exception if there was an error
571
     */
572
    public function getOrderdetails(bool $hide_obsolete = false)
573
    {
574
        if ($hide_obsolete) {
575
            $orderdetails = $this->orderdetails;
576
            foreach ($orderdetails as $key => $details) {
577
                if ($details->getObsolete()) {
578
                    unset($orderdetails[$key]);
579
                }
580
            }
581
582
            return $orderdetails;
583
        } else {
584
            return $this->orderdetails;
585
        }
586
    }
587
588
    /**
589
     *  Get all devices which uses this part.
590
     *
591
     * @return Device[] * all devices which uses this part as a one-dimensional array of Device objects
592
     *                  (empty array if there are no ones)
593
     *                  * the array is sorted by the devices names
594
     *
595
     * @throws Exception if there was an error
596
     */
597
    public function getDevices(): array
598
    {
599
        return $this->devices;
600
    }
601
602
    /**
603
     *  Get all suppliers of this part.
604
     *
605
     * This method simply gets the suppliers of the orderdetails and prepare them.\n
606
     * You can get the suppliers as an array or as a string with individual delimeter.
607
     *
608
     * @param bool        $object_array  * if true, this method returns an array of Supplier objects
609
     *                                   * if false, this method returns an array of strings
610
     * @param string|null $delimeter     * if this is a string and "$object_array == false",
611
     *                                   this method returns a string with all
612
     *                                   supplier names, delimeted by "$delimeter"
613
     * @param bool        $full_paths    * if true and "$object_array = false", the returned
614
     *                                   suppliernames are full paths (path + name)
615
     *                                   * if true and "$object_array = false", the returned
616
     *                                   suppliernames are only the names (without path)
617
     * @param bool        $hide_obsolete If true, suppliers from obsolete orderdetails will NOT be returned
618
     *
619
     * @return array  all suppliers as a one-dimensional array of Supplier objects
620
     *                (if "$object_array == true")
621
     * @return array  all supplier-names as a one-dimensional array of strings
622
     *                ("if $object_array == false" and "$delimeter == NULL")
623
     * @return string a sting of all supplier names, delimeted by $delimeter
624
     *                ("if $object_array == false" and $delimeter is a string)
625
     *
626
     * @throws Exception if there was an error
627
     */
628
    public function getSuppliers(bool $object_array = true, $delimeter = null, bool $full_paths = false, bool $hide_obsolete = false)
629
    {
630
        $suppliers = array();
631
        $orderdetails = $this->getOrderdetails($hide_obsolete);
632
633
        foreach ($orderdetails as $details) {
634
            $suppliers[] = $details->getSupplier();
635
        }
636
637
        if ($object_array) {
638
            return $suppliers;
639
        } else {
640
            $supplier_names = array();
641
            foreach ($suppliers as $supplier) {
642
                /* @var Supplier $supplier */
643
                if ($full_paths) {
644
                    $supplier_names[] = $supplier->getFullPath();
645
                } else {
646
                    $supplier_names[] = $supplier->getName();
647
                }
648
            }
649
650
            if (\is_string($delimeter)) {
651
                return implode($delimeter, $supplier_names);
0 ignored issues
show
Bug Best Practice introduced by
The expression return implode($delimeter, $supplier_names) returns the type string which is incompatible with the documented return type array.
Loading history...
652
            }
653
654
            return $supplier_names;
655
        }
656
    }
657
658
    /**
659
     *  Get all supplier-part-Nrs.
660
     *
661
     * This method simply gets the suppliers-part-Nrs of the orderdetails and prepare them.\n
662
     * You can get the numbers as an array or as a string with individual delimeter.
663
     *
664
     * @param string|null $delimeter     * if this is a string, this method returns a delimeted string
665
     *                                   * otherwise, this method returns an array of strings
666
     * @param bool        $hide_obsolete If true, supplierpartnrs from obsolete orderdetails will NOT be returned
667
     *
668
     * @return array  all supplierpartnrs as an array of strings (if "$delimeter == NULL")
669
     * @return string all supplierpartnrs as a string, delimeted ba $delimeter (if $delimeter is a string)
670
     *
671
     * @throws Exception if there was an error
672
     */
673
    public function getSupplierPartNrs($delimeter = null, bool $hide_obsolete = false)
674
    {
675
        $supplierpartnrs = array();
676
677
        foreach ($this->getOrderdetails($hide_obsolete) as $details) {
678
            $supplierpartnrs[] = $details->getSupplierPartNr();
679
        }
680
681
        if (\is_string($delimeter)) {
682
            return implode($delimeter, $supplierpartnrs);
0 ignored issues
show
Bug Best Practice introduced by
The expression return implode($delimeter, $supplierpartnrs) returns the type string which is incompatible with the documented return type array.
Loading history...
683
        } else {
684
            return $supplierpartnrs;
685
        }
686
    }
687
688
    /**
689
     *  Get all prices of this part.
690
     *
691
     * This method simply gets the prices of the orderdetails and prepare them.\n
692
     * In the returned array/string there is a price for every supplier.
693
     * @param int $quantity this is the quantity to choose the correct priceinformation
694
     * @param int|null $multiplier * This is the multiplier which will be applied to every single price
695
     *                                   * If you pass NULL, the number from $quantity will be used
696
     * @param bool $hide_obsolete If true, prices from obsolete orderdetails will NOT be returned
697
     *
698
     * @return float[]  all prices as an array of floats (if "$delimeter == NULL" & "$float_array == true")
699
     *
700
     * @throws \Exception if there was an error
701
     */
702
    public function getPrices(int $quantity = 1, $multiplier = null, bool $hide_obsolete = false) : array
703
    {
704
        $prices = array();
705
706
        foreach ($this->getOrderdetails($hide_obsolete) as $details) {
707
            $prices[] = $details->getPrice($quantity, $multiplier);
708
        }
709
710
        return $prices;
711
    }
712
713
    /**
714
     *  Get the average price of all orderdetails.
715
     *
716
     * With the $multiplier you're able to multiply the price before it will be returned.
717
     * This is useful if you want to have the price as a string with currency, but multiplied with a factor.
718
     *
719
     * @param int      $quantity        this is the quantity to choose the correct priceinformations
720
     * @param int|null $multiplier      * This is the multiplier which will be applied to every single price
721
     *                                  * If you pass NULL, the number from $quantity will be used
722
     *
723
     * @return float  price (if "$as_money_string == false")
724
     *
725
     * @throws \Exception if there was an error
726
     */
727
    public function getAveragePrice(int $quantity = 1, $multiplier = null) : ?float
728
    {
729
        $prices = $this->getPrices($quantity, $multiplier, true);
730
        $average_price = null;
731
732
        $count = 0;
733
        foreach ($prices as $price) {
734
            if (null !== $price) {
735
                $average_price += $price;
736
                ++$count;
737
            }
738
        }
739
740
        if ($count > 0) {
741
            $average_price /= $count;
742
        }
743
744
        return $average_price;
745
    }
746
747
    /**
748
     *  Get the filename of the master picture (absolute path from filesystem root).
749
     *
750
     * @param bool $use_footprint_filename * if true, and this part has no picture, this method
751
     *                                     will return the filename of its footprint (if available)
752
     *                                     * if false, and this part has no picture,
753
     *                                     this method will return NULL
754
     *
755
     * @return string the whole path + filename from filesystem root as a UNIX path (with slashes)
756
     *
757
     * @throws \Exception if there was an error
758
     */
759
    public function getMasterPictureFilename(bool $use_footprint_filename = false): ?string
760
    {
761
        $master_picture = $this->getMasterPictureAttachement(); // returns an Attachement-object
762
763
        if (null !== $master_picture) {
764
            return $master_picture->getPath();
765
        }
766
767
        if ($use_footprint_filename) {
768
            $footprint = $this->getFootprint();
769
            if (null !== $footprint) {
770
                return $footprint->getFilename();
771
            }
772
        }
773
774
        return null;
775
    }
776
777
    /**
778
     * Parses the selected fields and extract Properties of the part.
779
     *
780
     * @param bool $use_description Use the description field for parsing
781
     * @param bool $use_comment     Use the comment field for parsing
782
     * @param bool $use_name        Use the name field for parsing
783
     * @param bool $force_output    Properties are parsed even if properties are disabled.
784
     *
785
     * @return array A array of PartProperty objects.
786
     * @return array If Properties are disabled or nothing was detected, then an empty array is returned.
787
     *
788
     * @throws Exception
789
     */
790
    public function getProperties(bool $use_description = true, bool $use_comment = true, bool $use_name = true, bool $force_output = false): array
0 ignored issues
show
Unused Code introduced by
The parameter $force_output is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

790
    public function getProperties(bool $use_description = true, bool $use_comment = true, bool $use_name = true, /** @scrutinizer ignore-unused */ bool $force_output = false): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $use_comment is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

790
    public function getProperties(bool $use_description = true, /** @scrutinizer ignore-unused */ bool $use_comment = true, bool $use_name = true, bool $force_output = false): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $use_description is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

790
    public function getProperties(/** @scrutinizer ignore-unused */ bool $use_description = true, bool $use_comment = true, bool $use_name = true, bool $force_output = false): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $use_name is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

790
    public function getProperties(bool $use_description = true, bool $use_comment = true, /** @scrutinizer ignore-unused */ bool $use_name = true, bool $force_output = false): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
791
    {
792
        //TODO
793
        throw new \Exception('Not implemented yet!');
794
        /*
795
        global $config;
796
797
        if ($config['properties']['active'] || $force_output) {
798
            if ($this->getCategory()->getDisableProperties(true)) {
799
                return array();
800
            }
801
802
            $name = array();
803
            $desc = array();
804
            $comm = array();
805
806
            if ($use_name === true) {
807
                $name = $this->getCategory()->getPartnameRegexObj()->getProperties($this->getName());
808
            }
809
            if ($use_description === true) {
810
                $desc = PartProperty::parseDescription($this->getDescription());
811
            }
812
            if ($use_comment === true) {
813
                $comm = PartProperty::parseDescription($this->getComment(false));
814
            }
815
816
            return array_merge($name, $desc, $comm);
817
        } else {
818
            return array();
819
        }*/
820
    }
821
822
    /**
823
     * Returns a loop (array) of the array representations of the properties of this part.
824
     *
825
     * @param bool $use_description Use the description field for parsing
826
     * @param bool $use_comment     Use the comment field for parsing
827
     *
828
     * @return array A array of arrays with the name and value of the properties.
829
     */
830
    public function getPropertiesLoop(bool $use_description = true, bool $use_comment = true, bool $use_name = true): array
0 ignored issues
show
Unused Code introduced by
The parameter $use_comment is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

830
    public function getPropertiesLoop(bool $use_description = true, /** @scrutinizer ignore-unused */ bool $use_comment = true, bool $use_name = true): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $use_name is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

830
    public function getPropertiesLoop(bool $use_description = true, bool $use_comment = true, /** @scrutinizer ignore-unused */ bool $use_name = true): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $use_description is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

830
    public function getPropertiesLoop(/** @scrutinizer ignore-unused */ bool $use_description = true, bool $use_comment = true, bool $use_name = true): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
831
    {
832
        //TODO
833
        throw new \Exception('Not implemented yet!');
834
        $arr = array();
0 ignored issues
show
Unused Code introduced by
$arr = array() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
835
        foreach ($this->getProperties($use_description, $use_comment, $use_name) as $property) {
836
            /* @var PartProperty $property */
837
            $arr[] = $property->getArray(true);
838
        }
839
840
        return $arr;
841
    }
842
843
    /*
844
    public function hasValidName() : bool
845
    {
846
        return self::isValidName($this->getName(), $this->getCategory());
847
    } */
848
849
    /********************************************************************************
850
     *
851
     *   Setters
852
     *
853
     *********************************************************************************/
854
855
    /**
856
     *  Set the description.
857
     *
858
     * @param string $new_description the new description
859
     *
860
     * @return self
861
     */
862
    public function setDescription(?string $new_description): self
863
    {
864
        $this->description = $new_description;
865
866
        return $this;
867
    }
868
869
    /**
870
     *  Set the count of parts which are in stock.
871
     *
872
     * @param int $new_instock the new count of parts which are in stock
873
     *
874
     * @return self
875
     */
876
    public function setInstock(int $new_instock, $comment = null): self
0 ignored issues
show
Unused Code introduced by
The parameter $comment is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

876
    public function setInstock(int $new_instock, /** @scrutinizer ignore-unused */ $comment = null): self

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
877
    {
878
        //Assert::natural($new_instock, 'New instock must be positive. Got: %s');
879
880
        $old_instock = $this->getInstock();
0 ignored issues
show
Unused Code introduced by
The assignment to $old_instock is dead and can be removed.
Loading history...
881
        $this->instock = $new_instock;
0 ignored issues
show
Bug Best Practice introduced by
The property instock does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
882
        //TODO
883
        /*
884
        InstockChangedEntry::add(
885
            $this->database,
886
            $this->current_user,
887
            $this->log,
888
            $this,
889
            $old_instock,
890
            $new_instock,
891
            $comment
892
        );*/
893
894
        return $this;
895
    }
896
897
    /**
898
     * Sets the unknown status of this part.
899
     * When the instock is currently unknown and you pass false, then the instock is set to zero.
900
     * If the instock is not unknown and you pass false, nothing is changed.
901
     *
902
     * @param bool $new_unknown Set this to true if the instock should be marked as unknown.
903
     *
904
     * @return Part
905
     */
906
    public function setInstockUnknown(bool $new_unknown): self
907
    {
908
        if (true === $new_unknown) {
909
            $this->instock = self::INSTOCK_UNKNOWN;
0 ignored issues
show
Bug Best Practice introduced by
The property instock does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
910
        } elseif ($this->isInstockUnknown()) {
911
            $this->setInstock(0);
912
        }
913
914
        return $this;
915
    }
916
917
    /**
918
     * Withdrawal the given number of parts.
919
     *
920
     * @param $count int The number of parts which should be withdrawan.
921
     * @param $comment string A comment that should be associated with the withdrawal.
922
     *
923
     * @return self
924
     */
925
    public function withdrawalParts(int $count, $comment = null): self
0 ignored issues
show
Unused Code introduced by
The parameter $comment is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

925
    public function withdrawalParts(int $count, /** @scrutinizer ignore-unused */ $comment = null): self

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
926
    {
927
        //Assert::greaterThan($count,0, 'Count of withdrawn parts must be greater 0! Got %s!');
928
        //Assert::greaterThan($count, $this->instock, 'You can not withdraw more parts, than there are existing!');
929
930
        $old_instock = $this->getInstock();
931
        $new_instock = $old_instock - $count;
932
933
        //TODO
934
        /*
935
        InstockChangedEntry::add(
936
            $this->database,
937
            $this->current_user,
938
            $this->log,
939
            $this,
940
            $old_instock,
941
            $new_instock,
942
            $comment
943
        );*/
944
945
        $this->instock = $new_instock;
0 ignored issues
show
Bug Best Practice introduced by
The property instock does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
946
947
        return $this;
948
    }
949
950
    /**
951
     * Add the given number of parts.
952
     *
953
     * @param $count int The number of parts which should be withdrawan.
954
     * @param $comment string A comment that should be associated with the withdrawal.
955
     *
956
     * @return self
957
     */
958
    public function addParts(int $count, string $comment = null): self
0 ignored issues
show
Unused Code introduced by
The parameter $comment is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

958
    public function addParts(int $count, /** @scrutinizer ignore-unused */ string $comment = null): self

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
959
    {
960
        //Assert::greaterThan($count, 0, 'Count of added parts must be greater zero! Got %s.');
961
962
        //TODO
963
964
        $old_instock = $this->getInstock();
965
        $new_instock = $old_instock + $count;
966
967
        //TODO
968
        /*
969
        InstockChangedEntry::add(
970
            $this->database,
971
            $this->current_user,
972
            $this->log,
973
            $this,
974
            $old_instock,
975
            $new_instock,
976
            $comment
977
        );*/
978
979
        $this->instock = $new_instock;
0 ignored issues
show
Bug Best Practice introduced by
The property instock does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
980
981
        return $this;
982
    }
983
984
    /**
985
     *  Set the count of parts which should be in stock at least.
986
     *
987
     * @param int $new_mininstock the new count of parts which should be in stock at least
988
     *
989
     * @return self
990
     */
991
    public function setMinInstock(int $new_mininstock): self
992
    {
993
        //Assert::natural($new_mininstock, 'The new minimum instock value must be positive! Got %s.');
994
995
        $this->mininstock = $new_mininstock;
996
997
        return $this;
998
    }
999
1000
    /**
1001
     *  Set the comment.
1002
     *
1003
     * @param string $new_comment the new comment
1004
     *
1005
     * @return self
1006
     */
1007
    public function setComment(string $new_comment): self
1008
    {
1009
        $this->comment = $new_comment;
1010
1011
        return $this;
1012
    }
1013
1014
    /**
1015
     *  Set the "manual_order" attribute.
1016
     *
1017
     * @param bool     $new_manual_order          the new "manual_order" attribute
1018
     * @param int      $new_order_quantity        the new order quantity
1019
     * @param int|null $new_order_orderdetails_id * the ID of the new order orderdetails
1020
     *                                            * or Zero for "no order orderdetails"
1021
     *                                            * or NULL for automatic order orderdetails
1022
     *                                            (if the part has exactly one orderdetails,
1023
     *                                            set this orderdetails as order orderdetails.
1024
     *                                            Otherwise, set "no order orderdetails")
1025
     *
1026
     * @return self
1027
     */
1028
    public function setManualOrder(bool $new_manual_order, int $new_order_quantity = 1, $new_order_orderdetails_id = null): self
0 ignored issues
show
Unused Code introduced by
The parameter $new_order_orderdetails_id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1028
    public function setManualOrder(bool $new_manual_order, int $new_order_quantity = 1, /** @scrutinizer ignore-unused */ $new_order_orderdetails_id = null): self

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1029
    {
1030
        //Assert::greaterThan($new_order_quantity, 0, 'The new order quantity must be greater zero. Got %s!');
1031
1032
        $this->manual_order = $new_manual_order;
1033
1034
        //TODO;
1035
        /* $this->order_orderdetail = $new_order_orderdetails_id; */
1036
        $this->order_quantity = $new_order_quantity;
1037
1038
        return $this;
1039
    }
1040
1041
    /**
1042
     *  Set the ID of the order orderdetails.
1043
     *
1044
     * @param int|null $new_order_orderdetails_id * the new order orderdetails ID
1045
     *                                            * Or, to remove the orderdetails, pass a NULL
1046
     *
1047
     * @return self
1048
     */
1049
    public function setOrderOrderdetailsID($new_order_orderdetails_id): self
0 ignored issues
show
Unused Code introduced by
The parameter $new_order_orderdetails_id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1049
    public function setOrderOrderdetailsID(/** @scrutinizer ignore-unused */ $new_order_orderdetails_id): self

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1050
    {
1051
        //TODO
1052
        throw new \Exception('Not implemented yet...');
1053
1054
        return $this;
0 ignored issues
show
Unused Code introduced by
return $this is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1055
    }
1056
1057
    /**
1058
     *  Set the order quantity.
1059
     *
1060
     * @param int $new_order_quantity the new order quantity
1061
     *
1062
     * @return self
1063
     */
1064
    public function setOrderQuantity(int $new_order_quantity): self
1065
    {
1066
        //Assert::greaterThan($new_order_quantity,0, 'The new order quantity must be greater zero. Got %s!');
1067
1068
        $this->order_quantity = $new_order_quantity;
1069
1070
        return $this;
1071
    }
1072
1073
    /**
1074
     *  Set the category of this Part.
1075
     *
1076
     *     Every part must have a valid category (in contrast to the
1077
     *          attributes "footprint", "storelocation", ...)!
1078
     *
1079
     * @param Category $category The new category of this part
1080
     *
1081
     * @return self
1082
     */
1083
    public function setCategory(Category $category): self
1084
    {
1085
        $this->category = $category;
1086
1087
        return $this;
1088
    }
1089
1090
    /**
1091
     * Set the new Footprint of this Part.
1092
     *
1093
     * @param Footprint|null $new_footprint The new footprint of this part. Set to null, if this part should not have
1094
     *                                      a footprint.
1095
     *
1096
     * @return self
1097
     */
1098
    public function setFootprint(?Footprint $new_footprint): self
1099
    {
1100
        $this->footprint = $new_footprint;
1101
1102
        return $this;
1103
    }
1104
1105
    /**
1106
     * Set the new store location of this part.
1107
     *
1108
     * @param Storelocation|null $new_storelocation The new Storelocation of this part. Set to null, if this part should
1109
     *                                              not have a storelocation.
1110
     *
1111
     * @return Part
1112
     */
1113
    public function setStorelocation(?Storelocation $new_storelocation): self
1114
    {
1115
        $this->storelocation = $new_storelocation;
0 ignored issues
show
Bug Best Practice introduced by
The property storelocation does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1116
1117
        return $this;
1118
    }
1119
1120
    /**
1121
     * Sets the new manufacturer of this part.
1122
     *
1123
     * @param Manufacturer|null $new_manufacturer The new Manufacturer of this part. Set to null, if this part should
1124
     *                                            not have a manufacturer.
1125
     *
1126
     * @return Part
1127
     */
1128
    public function setManufacturer(?Manufacturer $new_manufacturer): self
1129
    {
1130
        $this->manufacturer = $new_manufacturer;
1131
1132
        return $this;
1133
    }
1134
1135
    /**
1136
     * Set the favorite status for this part.
1137
     *
1138
     * @param $new_favorite_status bool The new favorite status, that should be applied on this part.
1139
     *      Set this to true, when the part should be a favorite.
1140
     *
1141
     * @return self
1142
     */
1143
    public function setFavorite(bool $new_favorite_status): self
1144
    {
1145
        $this->favorite = $new_favorite_status;
1146
1147
        return $this;
1148
    }
1149
1150
    /**
1151
     * Sets the URL to the manufacturer site about this Part. Set to "" if this part should use the automatically URL based on its manufacturer.
1152
     *
1153
     * @param string $new_url The new url
1154
     *
1155
     * @return self
1156
     */
1157
    public function setManufacturerProductURL(string $new_url): self
1158
    {
1159
        $this->manufacturer_product_url = $new_url;
1160
1161
        return $this;
1162
    }
1163
1164
    /**
1165
     *  Set the ID of the master picture Attachement.
1166
     *
1167
     * @param int|null $new_master_picture_attachement_id * the ID of the Attachement object of the master picture
1168
     *                                                    * NULL means "no master picture"
1169
     *
1170
     * @throws Exception if the new ID is not valid
1171
     * @throws Exception if there was an error
1172
     *
1173
     * @return self
1174
     */
1175
    public function setMasterPictureAttachementID($new_master_picture_attachement_id): self
0 ignored issues
show
Unused Code introduced by
The parameter $new_master_picture_attachement_id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1175
    public function setMasterPictureAttachementID(/** @scrutinizer ignore-unused */ $new_master_picture_attachement_id): self

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1176
    {
1177
        //TODO
1178
        throw new \Exception('Not implemented yet!');
1179
1180
        return $this;
0 ignored issues
show
Unused Code introduced by
return $this is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1181
    }
1182
}
1183