Completed
Push — master ( 080abf...a1810f )
by Nicolaas
01:41
created

code/model/BuyableStockCalculatedQuantity.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 *@author: Nicolaas [at] Sunny Side Up . Co . Nz
4
 *@description:
5
 * works out the quantity available for each buyable
6
 * based on the the number of items sold, recorded in BuyableStockOrderEntry,
7
 * and manual corrections, recorded in BuyableStockManualUpdate.
8
 *
9
 *
10
 **/
11
12
class BuyableStockCalculatedQuantity extends DataObject
13
{
14
    private static $db = array(
15
        "BaseQuantity" => "Int",
16
        "BuyableID" => "Int",
17
        "BuyableClassName" => "Varchar"
18
    );
19
20
    private static $has_many = array(
21
        "BuyableStockOrderEntry" => "BuyableStockOrderEntry",
22
        "BuyableStockManualUpdate" => "BuyableStockManualUpdate"
23
    );
24
25
    private static $defaults = array(
26
        "BaseQuantity" => 0
27
    );
28
29
    private static $casting = array(
30
        "Name" => "Varchar",
31
        "Buyable" => "DataObject",
32
        "UnlimitedStock" => "Boolean"
33
    );
34
35
    //MODEL ADMIN STUFF
36
    private static $searchable_fields = array(
37
        "BaseQuantity"
38
    );
39
40
    private static $field_labels = array(
41
        "BaseQuantity" => "Calculated Quantity On Hand",
42
        "BuyableID" => "Buyable ID",
43
        "LastEdited" => "Last Calculated"
44
    );
45
46
    private static $summary_fields = array(
47
        "Name",
48
        "BaseQuantity",
49
        "LastEdited"
50
    );
51
52
    private static $indexes = array(
53
        "BuyableID" => true,
54
        'BaseQuantity' => true,
55
        "BuyableClassName" => true
56
    );
57
58
    private static $default_sort = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
59
        'BuyableClassName' => 'ASC',
60
        'BaseQuantity' => 'DESC',
61
        'ID' => 'ASC'
62
    ];
63
64
    private static $singular_name = "Stock Calculated Quantity";
65
66
    private static $plural_name = "Stock Calculated Quantities";
67
68
    private static $calculation_done = array();
69
70
    public function canCreate($member = null)
71
    {
72
        return false;
73
    }
74
75
    public function canEdit($member = null)
76
    {
77
        return false;
78
    }
79
80
    public function canDelete($member = null)
81
    {
82
        return false;
83
    }
84
85
    public function canView($member = null)
86
    {
87
        return $this->canDoAnything();
88
    }
89
90
    public function Link($action = "update")
91
    {
92
        return "/update-stock/".$action."/".$this->ID."/";
93
    }
94
95
    public function HistoryLink()
96
    {
97
        return $this->Link("history");
98
    }
99
100
    public function Buyable()
101
    {
102
        return $this->getBuyable();
103
    }
104
    public function getBuyable()
105
    {
106
        if ($this->BuyableID && class_exists($this->BuyableClassName)) {
107
            $className = $this->BuyableClassName;
108
            return $className::get()->byID($this->BuyableID);
109
        }
110
    }
111
112
    public function UnlimitedStock()
113
    {
114
        return $this->geUnlimitedStock();
115
    }
116
    public function getUnlimitedStock()
117
    {
118
        if ($buyable = $this->getBuyable()) {
119
            return $buyable->UnlimitedStock;
120
        }
121
    }
122
123
    public function Name()
124
    {
125
        return $this->getName();
126
    }
127
    public function getName()
128
    {
129
        if ($buyable = $this->getBuyable()) {
130
            return $buyable->getTitle();
131
        }
132
        return "no name";
133
    }
134
135
    protected function canDoAnything($member = null)
136
    {
137
        if ($buyable = $this->getBuyable()) {
138
            if ($buyable->canEdit($member)) {
139
                return true;
140
            }
141
        }
142
        Security::permissionFailure($this, _t('Security.PERMFAILURE', ' This page is secured and you need administrator rights to access it. Enter your credentials below and we will send you right along.'));
143
    }
144
145
    public static function get_quantity_by_buyable($buyable)
146
    {
147
        $value = 0;
148
        $item = self::get_by_buyable($buyable);
149
        if ($item) {
150
            $value = $item->calculatedBaseQuantity();
151
            if ($value < 0) {
152
                $value = 0;
153
            }
154
        }
155
        return $value;
156
    }
157
158
    public static function get_by_buyable($buyable)
159
    {
160
        $obj = BuyableStockCalculatedQuantity::get()
161
                        ->filter(
162
                            array(
163
                                'BuyableID' => $buyable->ID,
164
                                'BuyableClassName' => $buyable->ClassName
165
                            )
166
                        )
167
                        ->First();
168
        if ($obj) {
169
            //do nothing
170
        } else {
171
            $obj = new BuyableStockCalculatedQuantity();
172
            $obj->BuyableID = $buyable->ID;
173
            $obj->BuyableClassName = $buyable->ClassName;
174
        }
175
        if ($obj) {
176
            if (isset($obj->ID) && $obj->exists() && $obj->UnlimitedStock == $buyable->UnlimitedStock) {
177
                //do nothing
178
            } else {
179
                $obj->UnlimitedStock = $buyable->UnlimitedStock;
180
                //we must write here to calculate quantities
181
                $obj->write();
182
            }
183
            return $obj;
184
        }
185
        user_error("Could not find / create BuyableStockCalculatedQuantity for buyable with ID / ClassName ".$buyableID."/".$buyableClassName, E_WARNING);
186
    }
187
188
    public function calculatedBaseQuantity()
189
    {
190
        if (!$this->ID) {
191
            return 0;
192
        }
193
        $actualQuantity = $this->workoutActualQuantity();
194
        if ($actualQuantity != $this->BaseQuantity) {
195
            $this->BaseQuantity = $actualQuantity;
196
            $this->write();
197
            return $actualQuantity;
198
        } else {
199
            return $this->getField("BaseQuantity");
200
        }
201
    }
202
203
    protected function calculatedBaseQuantities($buyables = null)
204
    {
205
        if ($buyables) {
206
            foreach ($buyables as $buyable) {
207
                $buyableStockCalculatedQuantity = BuyableStockCalculatedQuantity::get_by_buyable($buyable);
208
                if ($buyableStockCalculatedQuantity) {
209
                    $buyableStockCalculatedQuantity->calculatedBaseQuantity();
210
                }
211
            }
212
        }
213
    }
214
215
    /**
216
     * TODO: change to submitted from CustomerCanEdit criteria
217
     */
218
219
220
    protected function workoutActualQuantity()
221
    {
222
        $actualQuantity = 0;
223
        if ($buyable = $this->getBuyable()) {
224
            $query = Order::get()
225
                ->where('
226
                    "OrderItem"."BuyableID" = '.(intval($this->BuyableID) - 0).'
227
                    AND
228
                    "OrderItem"."BuyableClassName" = \''.$this->BuyableClassName.'\'
229
                    AND
230
                    "OrderStep"."CustomerCanEdit" = 0
231
                    AND
232
                    "Order"."ID" <> '.ShoppingCart::current_order()->ID.'
233
                ')
234
                ->innerJoin('OrderAttribute', '"OrderAttribute"."OrderID" = "Order"."ID"')
235
                ->innerJoin('OrderItem', '"OrderAttribute"."ID" = "OrderItem"."ID"')
236
                ->innerJoin('OrderStep', '"OrderStep"."ID" = "Order"."StatusID"');
237
            $amountPerOrder = array();
238
            if($query->count()) {
239
                foreach ($query as $row) {
240
                    if(!isset($amountPerOrder[$row->OrderID])) {
241
                        $amountPerOrder[$row->OrderID] = 0;
242
                    }
243
                    $amountPerOrder[$row->OrderID] += $row->Quantity;
244
                }
245
                foreach($amountPerOrder as $orderID => $sum) {
246
                    if ($orderID && $sum) {
247
                        $buyableStockOrderEntry = BuyableStockOrderEntry::get()
248
                            ->filter(
249
                                array(
250
                                    'OrderID' => $orderID,
251
                                    'ParentID' => $this->ID
252
                                )
253
                            )
254
                            ->First();
255
                        if ($buyableStockOrderEntry) {
256
                            //do nothing
257
                        } else {
258
                            $buyableStockOrderEntry = new BuyableStockOrderEntry();
259
                            $buyableStockOrderEntry->OrderID = $orderID;
260
                            $buyableStockOrderEntry->ParentID = $this->ID;
261
                            $buyableStockOrderEntry->IncludeInCurrentCalculation = 1;
262
                            $buyableStockOrderEntry->Quantity = 0;
263
                        }
264
                        if ($buyableStockOrderEntry->Quantity != $sum) {
265
                            $buyableStockOrderEntry->Quantity = $sum;
266
                            $buyableStockOrderEntry->write();
267
                        }
268
                    }
269
                }
270
            }
271
            //find last adjustment
272
            $latestManualUpdate = BuyableStockManualUpdate::get()
273
                                                            ->filter(array('ParentID' => $this->ID))
274
                                                            ->sort(array('LastEdited' => 'DESC'))
275
                                                            ->First();
276
            //nullify order quantities that were entered before last adjustment
277
            if ($latestManualUpdate) {
278
                $latestManualUpdateQuantity = $latestManualUpdate->Quantity;
279
                DB::query("
280
                    UPDATE \"BuyableStockOrderEntry\"
281
                    SET \"IncludeInCurrentCalculation\" = 0
282
                    WHERE
283
                    \"LastEdited\" < '".$latestManualUpdate->LastEdited."'
284
                        AND
285
                        \"ParentID\" = ".$this->ID
286
                );
287
            } else {
288
                $latestManualUpdateQuantity = 0;
289
            }
290
            //work out additional purchases
291
            $orderQuantityToDeduct = BuyableStockOrderEntry::get()
292
                                        ->filter(
293
                                            array(
294
                                                'ParentID' => $this->ID,
295
                                                'IncludeInCurrentCalculation' => 1
296
                                            )
297
                                        )->sum('Quantity');
298
            if (!$orderQuantityToDeduct) {
299
                $orderQuantityToDeduct = 0;
300
            }
301
            //work out base total
302
            $actualQuantity = $latestManualUpdateQuantity - $orderQuantityToDeduct;
303
            if (isset($_GET["debug"])) {
304
                echo "<hr />";
305
                echo $this->Name;
306
                echo " | Manual SUM: ".$latestManualUpdateQuantity;
307
                echo " | Order SUM: ".$orderQuantityToDeduct;
308
                echo " | Total SUM: ".$this->BaseQuantity;
309
                echo "<hr />";
310
            }
311
        }
312
        return $actualQuantity;
313
    }
314
}
315