Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 172
0.00% covered (danger)
0.00%
0 / 34
CRAP
0.00% covered (danger)
0.00%
0 / 1
Bin
0.00% covered (danger)
0.00%
0 / 172
0.00% covered (danger)
0.00%
0 / 34
6006
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 readByID
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 createFromRow
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
42
 create
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
20
 createSubCategories
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
42
 editSubCategories
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 update
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
42
 delete
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 hide
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 activate
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 markAsUsed
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 canBeHidden
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isHidden
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEvents
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCategories
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 getAgePercent
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 percent2Color
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 dateDiffTs
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setName
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getMainCategory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setMainCategory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSubCat1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSubCat1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSubCat2
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSubCat2
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSubCat3
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSubCat3
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDateCreated
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setDateCreated
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLocation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setLocation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAgeDate
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setAgeDate
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace BuyerKiosk\Backstock;
4
5use Symfony\Component\Validator\Constraints\DateTime;
6
7class Bin extends Backstock
8{
9    public $id;
10    public $uuid;
11    public $name;
12    public $mainCategory;
13    public $subCat1;
14    public $subCat2;
15    public $subCat3;
16    public $catColor;
17    public $dateCreatedReadable;
18    public $dateCreatedSortable;
19    public $dateCreated;
20    public $onSite;
21    public $location;
22    public $locationID;
23    public $deleted;
24    public $active;
25    public $hasBeenUsed;
26    public $hiddenAt;
27    public $age;
28    public $agePercent;
29    public $ageColor;
30    public $ageDate;
31    public $actionStringDate;
32    public $actionStringEmployee;
33    public $actionStringAction;
34    public $actionStringCategory;
35    public $actionString;
36    public $categories;
37    public function __construct(\Store $store)
38    {
39        parent::__construct($store);
40    }
41
42    public function readByID($id)
43    {
44        $this->log->LogDebug("ID BEFORE QUERY: ".$id);
45        try {
46            $stmt = $this->storeDB->prepare("SELECT *,DATE(ageDate) as dateNoTime  FROM bsBins WHERE id = :id");
47            $stmt->bindValue(":id", $id);
48            if ($stmt->execute()) {
49                $row = $stmt->fetch(\PDO::FETCH_ASSOC);
50                if (isset($row) && $row['id'] > 0) {
51                    $this->log->LogDebug("ID: ".$row['id']);
52                    return $this->createFromRow($row);
53                }
54            }
55        } catch (\PDOException $e) {
56            $this->log->LogDebug($e->getMessage());
57        }
58        return false;
59    }
60
61    public function createFromRow($row)
62    {
63        if (isset($row) && $row['id'] > 0) {
64            $dateTime = getDateRange($row['dateCreated']);
65            $this->log->LogDebug("DateNoTime: " . $row['dateNoTime']);
66            $this->id = $row['id'];
67            $this->uuid = $row['uuid'];
68            $this->mainCategory = $row['mainCategory'];
69            $this->subCat1 = $row['subCat1'];
70            $this->name = $row['name'];
71            $this->subCat2 = $row['subCat2'];
72            $this->subCat3 = $row['subCat3'];
73            $this->dateCreated = $row['dateCreated'];
74            $this->location = $row['location'];
75            $this->locationID = $row['location'];
76            $this->deleted = $row['deleted'];
77            $this->active = isset($row['active']) ? $row['active'] : 1;
78            $this->hasBeenUsed = isset($row['hasBeenUsed']) ? $row['hasBeenUsed'] : 0;
79            $this->hiddenAt = isset($row['hiddenAt']) ? $row['hiddenAt'] : null;
80            $this->ageDate = $row['ageDate'];
81            $this->age = $this->dateDiffTs($row['dateNoTime']);
82            $this->agePercent = $this->getAgePercent($this->age);
83            $this->ageColor = $this->percent2Color($this->agePercent);
84            $this->categories = $this->getCategories();
85            return true;
86        }
87        return false;
88    }
89
90    public function create($subCatsArray)
91    {
92        try {
93            $this->log->LogDebug("Insert Bin");
94            $stmt = $this->storeDB->prepare("INSERT INTO bsBins (uuid, mainCategory, name, location, dateCreated, ageDate) VALUES(:uuid, :mainCategory,  :name,:location, CURRENT_TIMESTAMP, :ageDate )");
95            $stmt->bindValue(":mainCategory", $this->mainCategory);
96            $stmt->bindValue(":name", $this->name);
97            $stmt->bindValue(":location", $this->location);
98            $stmt->bindValue(":uuid", gen_uuid(8));
99            if(isset($this->ageDate)) {
100                $stmt->bindValue(":ageDate", $this->ageDate);
101            } else {
102                $date = new \DateTime('now', new \DateTimeZone('UTC'));
103                $stmt->bindValue(":ageDate", $date->format("Y-m-d H:i:s"));
104            }
105            $this->log->LogDebug("Before Execute");
106            if ($stmt->execute()) {
107                $this->log->LogDebug("Execute Success");
108                $this->id = $this->storeDB->lastInsertId();
109                $this->createSubCategories($subCatsArray);
110                return true;
111            } else {
112                $this->log->LogDebug("Execute Failed");
113                $this->log->LogFatal($this->storeDB->errorInfo());
114            }
115
116        } catch (\PDOException $e) {
117            $this->log->LogDebug($e->getMessage());
118        }
119        return false;
120    }
121    public function createSubCategories($cats) {
122        if(isset($cats) && count($cats) > 0) {
123            foreach($cats as $category) {
124                try {
125                    $stmt = $this->storeDB->prepare("INSERT INTO bsBin_Cat (binID, catID) VALUES (:binID, :catID)");
126                    $stmt->bindValue(":binID", $this->id);
127                    $stmt->bindValue(":catID", $category);
128                    if(!$stmt->execute()) {
129                        return false;
130                    }
131                } catch (\PDOException $e) {
132                    $this->log->LogDebug($e->getMessage());
133                    return false;
134                }
135            }
136
137        }
138        return true;
139    }
140    public function editSubCategories($cats) {
141        //Delete all categories previously assigned to this bin
142        try {
143            $stmt = $this->storeDB->prepare("DELETE FROM bsBin_Cat WHERE binID = :binID");
144            $stmt->bindValue(":binID", $this->id);
145            if(!$stmt->execute()) {
146                return false;
147            }
148        } catch (\PDOException $e) {
149            $this->log->LogDebug($e->getMessage());
150            return false;
151        }
152        if(isset($cats) && count($cats) > 0) {
153            return $this->createSubCategories($cats);
154        }
155        return true;
156
157    }
158
159    public function update()
160    {
161        try {
162            $stmt = $this->storeDB->prepare("UPDATE bsBins SET mainCategory = :mainCategory, name=:name, location = :location, ageDate = :ageDate WHERE id = :id");
163            $stmt->bindValue(":id", $this->id);
164            $stmt->bindValue(":name", $this->name);
165            // mainCategory can be null for emptied bins
166            if ($this->mainCategory === null || $this->mainCategory === '' || $this->mainCategory === 0) {
167                $stmt->bindValue(":mainCategory", null, \PDO::PARAM_NULL);
168            } else {
169                $stmt->bindValue(":mainCategory", $this->mainCategory);
170            }
171            $stmt->bindValue(":location", $this->location);
172            $stmt->bindValue(":ageDate", $this->ageDate);
173            if ($stmt->execute()) {
174                return true;
175            }
176        } catch (\PDOException $e) {
177            $this->log->LogDebug($e->getMessage());
178        }
179        return false;
180    }
181
182    public function delete()
183    {
184        try {
185            $stmt = $this->storeDB->prepare("UPDATE bsBins SET deleted = 1 WHERE id = :id");
186            $stmt->bindValue(":id", $this->id);
187            if ($stmt->execute()) {
188                return true;
189            }
190        } catch (\PDOException $e) {
191            $this->log->LogDebug($e->getMessage());
192        }
193        return false;
194    }
195
196    /**
197     * Hide a bin (make it inactive) - used when a bin is emptied
198     * This is different from delete - the bin can be reactivated if scanned
199     */
200    public function hide()
201    {
202        try {
203            $stmt = $this->storeDB->prepare("UPDATE bsBins SET active = 0, hiddenAt = CURRENT_TIMESTAMP WHERE id = :id");
204            $stmt->bindValue(":id", $this->id);
205            if ($stmt->execute()) {
206                $this->active = 0;
207                return true;
208            }
209        } catch (\PDOException $e) {
210            $this->log->LogDebug($e->getMessage());
211        }
212        return false;
213    }
214
215    /**
216     * Reactivate a hidden bin - used when a hidden bin is scanned
217     */
218    public function activate()
219    {
220        try {
221            $stmt = $this->storeDB->prepare("UPDATE bsBins SET active = 1, hiddenAt = NULL WHERE id = :id");
222            $stmt->bindValue(":id", $this->id);
223            if ($stmt->execute()) {
224                $this->active = 1;
225                $this->hiddenAt = null;
226                return true;
227            }
228        } catch (\PDOException $e) {
229            $this->log->LogDebug($e->getMessage());
230        }
231        return false;
232    }
233
234    /**
235     * Mark a bin as having been used (had actions performed on it)
236     */
237    public function markAsUsed()
238    {
239        if ($this->hasBeenUsed) {
240            return true; // Already marked
241        }
242        try {
243            $stmt = $this->storeDB->prepare("UPDATE bsBins SET hasBeenUsed = 1 WHERE id = :id");
244            $stmt->bindValue(":id", $this->id);
245            if ($stmt->execute()) {
246                $this->hasBeenUsed = 1;
247                return true;
248            }
249        } catch (\PDOException $e) {
250            $this->log->LogDebug($e->getMessage());
251        }
252        return false;
253    }
254
255    /**
256     * Check if this bin can be hidden (must have been used)
257     */
258    public function canBeHidden()
259    {
260        return $this->hasBeenUsed == 1;
261    }
262
263    /**
264     * Check if this bin is currently hidden
265     */
266    public function isHidden()
267    {
268        return $this->active == 0;
269    }
270
271    public function getEvents()
272    {
273
274    }
275
276    public function getCategories() {
277        $categoriesArray = [];
278        try {
279            $stmt = $this->storeDB->prepare("SELECT catID, name, color FROM bsBin_Cat JOIN bsCategories ON bsBin_Cat.catID = bsCategories.id WHERE bsBin_Cat.binID = :binID");
280            $stmt->bindValue(":binID", $this->id);
281            if ($stmt->execute()) {
282               while($row =  $stmt->fetch(\PDO::FETCH_ASSOC)) {
283                   $row['color'] = preg_replace('/[^A-Za-z0-9\-]/', '', $row['color']);
284                   $categoriesArray[] = $row;
285               }
286            }
287        } catch (\PDOException $e) {
288            $this->log->LogDebug($e->getMessage());
289        }
290        return $categoriesArray;
291    }
292
293    public function getAgePercent($thisAge)
294    {
295        // Cap at 100% for bins aged 365+ days
296        $percent = round((int)$thisAge / 365, 2) * 100;
297        return min($percent, 100);
298    }
299
300    /**
301     * Get age color based on discrete buckets matching the aging report
302     * - Green (#22c55e): 0-90 days
303     * - Yellow/Amber (#f59e0b): 91-180 days
304     * - Orange (#ef4444): 181-365 days
305     * - Dark Red (#991b1b): 366+ days
306     */
307    private function percent2Color($value, $brightness = 255, $max = 100, $min = 0, $thirdColorHex = '00')
308    {
309        // Use the actual age in days for color determination
310        $ageDays = $this->age;
311
312        if ($ageDays <= 90) {
313            return '22c55e'; // Green
314        } elseif ($ageDays <= 180) {
315            return 'f59e0b'; // Yellow/Amber
316        } elseif ($ageDays <= 365) {
317            return 'ef4444'; // Orange/Red
318        } else {
319            return '991b1b'; // Dark Red
320        }
321    }
322
323    private function dateDiffTs($start_ts, $end_ts = null)
324    {
325        if ($end_ts == null) {
326            $end_ts = new \DateTime("now");
327        }
328        $start_ts = new \DateTime($start_ts);
329
330        $result = $start_ts->diff($end_ts)->format("%a");
331        //$this->log->LogDebug("Date Diff: ".$result);
332        return $result;
333    }
334
335    /**
336     * @return mixed
337     */
338    public function getName()
339    {
340        return $this->name;
341    }
342
343    /**
344     * @param mixed $name
345     */
346    public function setName($name)
347    {
348        if(strlen($name) > 0) {
349            $this->name = $name;
350            return true;
351        }
352        return false;
353    }
354
355    /**
356     * @return mixed
357     */
358    public function getMainCategory()
359    {
360        return $this->mainCategory;
361    }
362
363    /**
364     * @param mixed $mainCategory
365     */
366    public function setMainCategory($mainCategory)
367    {
368        $this->mainCategory = $mainCategory;
369    }
370
371    /**
372     * @return mixed
373     */
374    public function getSubCat1()
375    {
376        return $this->subCat1;
377    }
378
379    /**
380     * @param mixed $subCat1
381     */
382    public function setSubCat1($subCat1)
383    {
384        $this->subCat1 = $subCat1;
385    }
386
387    /**
388     * @return mixed
389     */
390    public function getSubCat2()
391    {
392        return $this->subCat2;
393    }
394
395    /**
396     * @param mixed $subCat2
397     */
398    public function setSubCat2($subCat2)
399    {
400        $this->subCat2 = $subCat2;
401    }
402
403    /**
404     * @return mixed
405     */
406    public function getSubCat3()
407    {
408        return $this->subCat3;
409    }
410
411    /**
412     * @param mixed $subCat3
413     */
414    public function setSubCat3($subCat3)
415    {
416        $this->subCat3 = $subCat3;
417    }
418
419    /**
420     * @return mixed
421     */
422    public function getDateCreated()
423    {
424        return $this->dateCreated;
425    }
426
427    /**
428     * @param mixed $dateCreated
429     */
430    public function setDateCreated($dateCreated)
431    {
432        $this->dateCreated = $dateCreated;
433    }
434
435    /**
436     * @return mixed
437     */
438    public function getLocation()
439    {
440        return $this->location;
441    }
442
443    /**
444     * @param mixed $location
445     */
446    public function setLocation($location)
447    {
448        $this->location = $location;
449    }
450
451    /**
452     * @return mixed
453     */
454    public function getAgeDate()
455    {
456        return $this->ageDate;
457    }
458
459    /**
460     * @param mixed $ageDate
461     */
462    public function setAgeDate($ageDate)
463    {
464        $this->ageDate = $ageDate;
465    }
466
467
468}