· 8 years ago · Jun 01, 2018, 04:58 AM
1<?php
2/**
3 * Payment_Gateway_Model lists the payment gateways available
4 */
5
6class Product_Model extends ORM
7{
8 // Relationships
9 protected $belongs_to = array( 'creator' => 'user', 'modifier' => 'user');
10 protected $has_many = array( 'product_skus', 'product_sku_stocks', 'product_images', 'featured_products', 'product_details' );
11 protected $has_one = array( );
12 protected $has_and_belongs_to_many = array( 'notes', 'permissions', 'categories', 'payment_gateways' );
13
14 // Public facing skus
15 public $available_skus;
16
17 // All skus
18// public $product_skus;
19
20 // Publically visible
21 public $visible;
22
23 // Product image
24 public $product_image;
25
26 // Product cheapest price
27 public $from_price;
28
29 /**
30 * Constructor
31 * @return
32 * @param $id Object[optional]
33 * @param $country_region_id Object[optional]
34 */
35 public function __construct( $id = FALSE, $country_region_id = FALSE )
36 {
37 // If there is an id
38 if( $id )
39 {
40 // load this model from the parent
41 parent::__construct( $id );
42
43 // If there is a country_region_id
44 if( $country_region_id )
45 {
46 $this->available_skus = $this->load_public_skus( $country_region_id );
47 }
48 else
49 {
50 $this->available_skus = FALSE;
51 }
52 // Load all skus for administration use
53// $this->product_skus = $this->load_all_skus();
54
55 // Load a product image
56 $this->product_image = $this->load_product_image();
57
58 // Get lowest from price
59 $this->from_price = $this->calculate_lowest_price();
60
61 // Setup the product visibility
62 $this->visible = $this->visibility();
63 }
64 else
65 {
66 // otherwise, just load a new model
67 parent::__construct();
68 }
69
70 return TRUE;
71 }
72
73 /**
74 * Find all the payment gateways payable for a currency.
75 *
76 * @return ORM_Iterator payment_gateways
77 */
78 public function find_payment_gateways()
79 {
80 // Load payment gateway
81 $payment_gateway = ORM::factory('Payment_Gateway');
82
83 // Get the join table name
84 $join_table = $payment_gateway->join_table($this->table_name);
85
86 // This will generate the following SQL, based on the currency id
87 //
88 // SELECT payment_gateways.*
89 // FROM payment_gateways
90 // JOIN payment_gateways_products ON payment_gateways_products.payment_gateway_id = payment_gateways.id
91 // WHERE payment_gateways_products.product_id = PRODUCT_ID
92 //
93
94 // Load all payment gateway for this currency
95 return $payment_gateway
96 ->join($join_table, $payment_gateway->foreign_key(NULL, $join_table), $payment_gateway->foreign_key(TRUE))
97 ->where($this->foreign_key(NULL, $join_table), $this->id)
98 ->find_all();
99 }
100
101 /**
102 * Find the main category of a product.
103 *
104 * @return Category_Model
105 */
106 public function main_category()
107 {
108 // Load the category
109 $category = ORM::factory('category');
110
111 // Joining table of products and categories
112 $join_table = $this->join_table($category->table_name);
113
114 // SELECT categories.*
115 // FROM categories
116 // JOIN categories_products ON categories_products.category_id = categories.id
117 // WHERE categories_products.product_id = PRODUCT_ID
118 // AND categories.category_parent_id IS NULL
119
120 return $category
121 ->join($join_table, $category->foreign_key(NULL, $join_table), $category->foreign_key(TRUE))
122 ->where($this->foreign_key(NULL, $join_table), $this->id)
123 ->where($category->table_name.'.category_parent_id', NULL)
124 ->limit(1)
125 ->find();
126 }
127
128 /**
129 * Loads a product image
130 * @return
131 */
132 public function load_product_image()
133 {
134 // Setup the result
135 $result = FALSE;
136
137 // Load all related product images
138 $product_image = $this->product_images;
139
140 // If this is a product image
141 if( $product_image instanceof ORM_Iterator and $product_image->count() > 0 )
142 {
143 $result = $product_image->current();
144 }
145 elseif( $product_image instanceof Product_Image_Model )
146 {
147 $result = $product_image;
148 }
149 else
150 {
151 $result = FALSE;
152 }
153
154 // Return the result
155 return $result;
156 }
157
158 /**
159 * Loads all the publically available SKU's
160 * @return
161 * @param $country_region_id Object[optional]
162 */
163 public function load_public_skus( $country_region_id = FALSE )
164 {
165 $result = FALSE;
166 // If there is a country_region_id
167
168 if( $country_region_id )
169 {
170 $pool = array();
171 //Get the region model
172 $country_region = ORM::factory( 'Country_Region', $country_region_id );
173 //Get the national region belonging to the region
174 $national_region = ORM::factory('Country_Region')
175 ->where( array( 'country_id' => $country_region->country_id, 'is_national' => 1 ) )
176 ->find();
177
178
179 // Create the IN clause based on this country region setting
180 if( $country_region->is_national == 0 )
181 {
182 // If this product is national
183 $in = array(
184 $national_region->id,
185 $country_region->id
186 );
187 }
188 else
189 {
190 // If this product is regional
191 $in = array(
192 $country_region->id
193 );
194 }
195
196 //Get available sku by region
197 $available_skus = ORM::factory( 'Product_Sku' )
198 ->where( array
199 (
200 'display_visible' => 1,
201 'product_id' => $this->id
202 ) )
203 ->in( 'repository_id', $in )
204 ->orderby( 'retail_value', 'ASC' )
205 ->find_all();
206
207 foreach( $available_skus as $product_sku )
208 { // Foreach sku found
209 if( $product_sku->stock_level > $product_sku->stock_warning_level OR $product_sku->display_sold_out == 1 )
210 { // Check to see if there is stock available, or if this sku is to be visible if sold out
211 $pool[] = $product_sku;
212 }
213 }
214
215 $result = $pool;
216 }
217 return $result;
218 }
219
220 /**
221 * Discovers if this product is publicly visible
222 *
223 * @return void
224 * @author Sam Clark
225 */
226 protected function visibility()
227 {
228 // Setup the result
229 $result = FALSE;
230
231 // If this model is loaded
232 if( $this->loaded )
233 {
234 // If this model is set to visible and has publicly available skus
235 if( $this->display_visible == 1 AND count( $this->available_skus ) > 0 )
236 {
237 // set the result to TRUE
238 $result = TRUE;
239 }
240 }
241 // Return the result
242 return $result;
243 }
244
245 /**
246 * Calculate the cheapest possible price from the available SKU's
247 * @return float or FALSE
248 */
249 protected function calculate_lowest_price()
250 {
251 $result = FALSE;
252
253 if( $this->product_skus )
254 {
255 // Foreach publically available SKU
256 foreach( $this->product_skus as $sku )
257 {
258 // If lowest price is FALSE or $sku is lower than current price
259 if( $result == FALSE or $sku->retail_value < $result )
260 {
261 // Apply this price
262 $result = $sku->retail_value;
263 }
264 }
265 }
266 return $result;
267 }
268
269 /**
270 * Load all SKU's
271 * @return
272 */
273 public function load_all_skus()
274 {
275 // Load all skus for administration use
276 return $this->product_skus;
277 }
278
279 /**
280 * Redefines save method
281 * @return
282 */
283 public function save()
284 {
285 if( $this->date_created < 1 )
286 {
287 $this->date_created = time();
288 }
289
290 $this->date_modified = time();
291
292 parent::save();
293 }
294
295 /**
296 * Allows a model to be loaded by username or email address.
297 */
298 public function unique_key($id = NULL)
299 {
300 if ( ! empty($id) && is_string($id) && ! ctype_digit($id))
301 {
302 return 'shortname';
303 }
304
305 return parent::unique_key($id);
306 }
307
308 /**
309 * Ensures that shortname is lowercase always
310 * @return
311 * @param $key Object
312 * @param $value Object
313 */
314 public function __set( $key, $value )
315 {
316 if( $key === 'shortname' )
317 {
318 $value = strtolower( $value );
319 }
320 return parent::__set($key, $value);
321 }
322
323
324 /**
325 * Returns the product pickup locations for standard products
326 *
327 * @param Country_Region_Model/integer $country Either a Country_Region_Model or the id/unique name for a country region model
328 * @return void
329 * @author Sam Clark
330 */
331 public function find_product_locations( $country = FALSE )
332 {
333 // Setup the result
334 $result = FALSE;
335
336 // If there is a country and this product exists
337 if( $country AND $this->id > 0 )
338 {
339 // Discover country type
340 if( !( $country instanceof Country_Model ) )
341 {
342 $country = ORM::factory( 'Country', $country );
343 }
344
345 // If the country is real
346 if( $country->loaded )
347 {
348 // Initialise a db instance
349 $db = Database::instance();
350
351 // Setup the orwhere array
352 $orwhere = array();
353
354 // Foreach country region
355 foreach ( $country->country_regions->as_array() as $region )
356 {
357 // add this to the orwhere array
358 $orwhere[] = array( 'country_region_id' => $region->id );
359 }
360
361 $query = FALSE;
362
363 // If there is an orwhere component
364 if( !empty( $orwhere ) )
365 {
366 // Start the query
367 $query = "SELECT `id` FROM `product_skus` WHERE `product_id` = {$this->id}";
368
369 // Start the AND part of the query
370 $query .= " AND ( ";
371
372 // Setup $i to monitor first iteration
373 $i = TRUE;
374
375 // Foreach part
376 foreach( $orwhere as $part )
377 {
378 // Load the key
379 $key = array_keys( $part );
380 $key = $key[0];
381
382 // Load the value
383 $value = $part[$key];
384
385 // Add the query
386 $query .= $i ? "`{$key}` = '{$value}' " : "OR `{$key}` = '{$value}' ";
387
388 // Make the iteration false so that it knows one pass has happened
389 $i = FALSE;
390 }
391 // Close the query
392 $query .= ") GROUP BY `country_region_id` ORDER BY `id` ASC";
393 }
394
395 // If there is a query to execute
396 if( $query )
397 {
398 // Execute the query
399 $country_regions = $db->query( $query );
400
401 // If there are product SKUs available
402 if( $country_regions->count() > 0 )
403 {
404 // Create a new array object
405 $locations = new ArrayObject();
406
407 // Foreach returned region
408 foreach( $country_regions as $region )
409 {
410 // Add it to the locations array
411 $locations->append( ORM::factory( 'Country_Region', $region->id ) );
412 }
413 // Set the locations to the result
414 $result = $locations->getIterator();
415 }
416 }
417 }
418 }
419 // Return the result
420 return $result;
421 }
422
423/**
424 * Finds the stock levels for products in repositories specified in array and returns them in associative array
425 *
426 * @param array $products Products to load, if empty then loads default products. Must use product shortcodes
427 * @param array $repositories Repositories to check stock levels for. Will always check main repository as well
428 * @return array Associative array of stock levels for relevant repositories
429 * @author Leo Allem
430 *
431 */
432 public function get_stocks( $products = FALSE, $repositories = NULL, $country_id = NULL )
433 {
434 //Set up result
435 $result = array();
436
437 // Load up default products if nothing set
438 if( ! $products )
439 {
440 $products = array
441 (
442 'Petrol' => 'fuelreg',
443 'Diesel' => 'fueldsl',
444 'Food' => 'foodpack',
445 'Geza' => 'gezawashpack',
446 'SIMs' => 'ecosim'
447 );
448 }
449
450 //Set up a row array that will be unset at the end of each loop
451 $row = array();
452
453 //For each of the products get all the sku_stocks in the right repositories
454 foreach( $products as $product_name => $product )
455 {
456
457 // Get product from database
458 $product = $this->unique_key( $product );
459
460 // Get all of the skus relating to that product
461 $product_sku_stocks = $product->product_sku_stocks;
462
463 //Reset total counter
464 $stock_total = 0;
465
466 //Get the stock level in the warehouse (from sku_stocks) for relevant product
467
468 $warehouse_stock_level = 0;
469
470 //Loop through for stock levels whilst simultaneously loading up product_sku_stock_items
471 foreach( $product_sku_stocks as $product_sku_stock )
472 {
473 if( $product_sku_stock->stock_status === 'A' )
474 {
475 $product_sku_stocks_items_array[] = $product_sku_stock->product_sku_stock_items;
476 $warehouse_stock_level += (int) $product_sku_stock->stock_level;
477 }
478
479 }
480
481 //Load warehouse stock levels into row
482 $stock_total = $row['warehouse'] = $warehouse_stock_level;
483
484 //Check whether there are any repositories
485 if( is_array( $repositories ) )
486 {
487 // Find for each repository all the products related to it
488 foreach( $repositories as $repository => $repository_id )
489 {
490 //Make sure variables are reset to 0
491 $available = $collection = $payment = 0;
492
493 // Loop through stock_items array
494 foreach( $product_sku_stocks_items_array as $product_sku_stocks_items )
495 {
496 // Drill down to next level to loop through each sku_stock_itemss result
497 foreach( $product_sku_stocks_items as $stocks_item )
498 {
499 // For each item check if it matches the repository id
500 if( $stocks_item->repository_id === $repository_id )
501 {
502 // If so check if value is not null and therefore stock item is available
503 if( is_null( $stocks_item->order_item_id ) )
504 {
505 $available += 1;
506 }
507 elseif( $stocks_item->order_item->order->authorised === 1 )
508 {
509 $collection += 1;
510 }
511 else
512 {
513 $payment += 1;
514 }
515 }
516 }
517 }
518
519 // Load values into array
520 $stock_total += $row[$repository]['available'] = $available;
521
522 $row[$repository]['collection'] = $collection;
523
524 $row[$repository]['payment'] = $payment;
525
526 }
527 }
528
529
530 //Load total into array
531
532 $row['total'] = $stock_total;
533
534 //Get row information for array key
535 $key = $product_name.'.'.$product->id;
536
537 // Pass row array into main result array
538 $result[ $key ] = $row;
539
540 // Unset array for next iteration
541 unset( $row );
542 }
543 return $result;
544 }
545}