2017-04-16 23:11:03 +02:00
< ? php
2018-04-11 19:49:35 +02:00
namespace Grocy\Services ;
class StockService extends BaseService
2017-04-16 23:11:03 +02:00
{
2017-04-19 21:09:28 +02:00
const TRANSACTION_TYPE_PURCHASE = 'purchase' ;
const TRANSACTION_TYPE_CONSUME = 'consume' ;
const TRANSACTION_TYPE_INVENTORY_CORRECTION = 'inventory-correction' ;
2018-04-11 19:49:35 +02:00
public function GetCurrentStock ()
2017-04-16 23:11:03 +02:00
{
2017-04-21 13:21:09 +02:00
$sql = 'SELECT * from stock_current' ;
2018-04-11 19:49:35 +02:00
return $this -> DatabaseService -> ExecuteDbQuery ( $sql ) -> fetchAll ( \PDO :: FETCH_OBJ );
2017-04-21 13:21:09 +02:00
}
2018-04-11 19:49:35 +02:00
public function GetMissingProducts ()
2017-04-21 13:21:09 +02:00
{
$sql = 'SELECT * from stock_missing_products' ;
2018-04-11 19:49:35 +02:00
return $this -> DatabaseService -> ExecuteDbQuery ( $sql ) -> fetchAll ( \PDO :: FETCH_OBJ );
2017-04-16 23:11:03 +02:00
}
2018-04-11 19:49:35 +02:00
public function GetProductDetails ( int $productId )
2017-04-16 23:11:03 +02:00
{
2018-04-11 19:49:35 +02:00
$product = $this -> Database -> products ( $productId );
$productStockAmount = $this -> Database -> stock () -> where ( 'product_id' , $productId ) -> sum ( 'amount' );
$productLastPurchased = $this -> Database -> stock_log () -> where ( 'product_id' , $productId ) -> where ( 'transaction_type' , self :: TRANSACTION_TYPE_PURCHASE ) -> max ( 'purchased_date' );
$productLastUsed = $this -> Database -> stock_log () -> where ( 'product_id' , $productId ) -> where ( 'transaction_type' , self :: TRANSACTION_TYPE_CONSUME ) -> max ( 'used_date' );
$quPurchase = $this -> Database -> quantity_units ( $product -> qu_id_purchase );
$quStock = $this -> Database -> quantity_units ( $product -> qu_id_stock );
2017-04-16 23:11:03 +02:00
return array (
'product' => $product ,
'last_purchased' => $productLastPurchased ,
'last_used' => $productLastUsed ,
'stock_amount' => $productStockAmount ,
'quantity_unit_purchase' => $quPurchase ,
'quantity_unit_stock' => $quStock
);
}
2018-04-11 19:49:35 +02:00
public function AddProduct ( int $productId , int $amount , string $bestBeforeDate , $transactionType )
2017-04-19 21:09:28 +02:00
{
2017-04-20 17:10:21 +02:00
if ( $transactionType === self :: TRANSACTION_TYPE_CONSUME || $transactionType === self :: TRANSACTION_TYPE_PURCHASE || $transactionType === self :: TRANSACTION_TYPE_INVENTORY_CORRECTION )
{
$stockId = uniqid ();
2018-04-11 19:49:35 +02:00
$logRow = $this -> Database -> stock_log () -> createRow ( array (
2017-04-20 17:10:21 +02:00
'product_id' => $productId ,
'amount' => $amount ,
'best_before_date' => $bestBeforeDate ,
'purchased_date' => date ( 'Y-m-d' ),
'stock_id' => $stockId ,
'transaction_type' => $transactionType
));
$logRow -> save ();
2018-04-11 19:49:35 +02:00
$stockRow = $this -> Database -> stock () -> createRow ( array (
2017-04-20 17:10:21 +02:00
'product_id' => $productId ,
'amount' => $amount ,
'best_before_date' => $bestBeforeDate ,
'purchased_date' => date ( 'Y-m-d' ),
'stock_id' => $stockId ,
));
$stockRow -> save ();
return true ;
}
else
{
2018-04-10 20:30:11 +02:00
throw new Exception ( " Transaction type $transactionType is not valid (StockService.AddProduct) " );
2017-04-20 17:10:21 +02:00
}
2017-04-19 21:09:28 +02:00
}
2018-04-11 19:49:35 +02:00
public function ConsumeProduct ( int $productId , int $amount , bool $spoiled , $transactionType )
2017-04-16 23:11:03 +02:00
{
2017-04-20 17:10:21 +02:00
if ( $transactionType === self :: TRANSACTION_TYPE_CONSUME || $transactionType === self :: TRANSACTION_TYPE_PURCHASE || $transactionType === self :: TRANSACTION_TYPE_INVENTORY_CORRECTION )
2017-04-16 23:11:03 +02:00
{
2018-04-11 19:49:35 +02:00
$productStockAmount = $this -> Database -> stock () -> where ( 'product_id' , $productId ) -> sum ( 'amount' );
$potentialStockEntries = $this -> Database -> stock () -> where ( 'product_id' , $productId ) -> orderBy ( 'best_before_date' , 'ASC' ) -> orderBy ( 'purchased_date' , 'ASC' ) -> fetchAll (); //First expiring first, then first in first out
2017-04-16 23:11:03 +02:00
2017-04-20 17:10:21 +02:00
if ( $amount > $productStockAmount )
2017-04-16 23:11:03 +02:00
{
2017-04-20 17:10:21 +02:00
return false ;
2017-04-16 23:11:03 +02:00
}
2017-04-20 17:10:21 +02:00
foreach ( $potentialStockEntries as $stockEntry )
2017-04-16 23:11:03 +02:00
{
2017-04-20 17:10:21 +02:00
if ( $amount == 0 )
{
break ;
}
if ( $amount >= $stockEntry -> amount ) //Take the whole stock entry
{
2018-04-11 19:49:35 +02:00
$logRow = $this -> Database -> stock_log () -> createRow ( array (
2017-04-20 17:10:21 +02:00
'product_id' => $stockEntry -> product_id ,
'amount' => $stockEntry -> amount * - 1 ,
'best_before_date' => $stockEntry -> best_before_date ,
'purchased_date' => $stockEntry -> purchased_date ,
'used_date' => date ( 'Y-m-d' ),
'spoiled' => $spoiled ,
'stock_id' => $stockEntry -> stock_id ,
'transaction_type' => $transactionType
));
$logRow -> save ();
$amount -= $stockEntry -> amount ;
$stockEntry -> delete ();
}
else //Stock entry amount is > than needed amount -> split the stock entry resp. update the amount
{
2018-04-11 19:49:35 +02:00
$logRow = $this -> Database -> stock_log () -> createRow ( array (
2017-04-20 17:10:21 +02:00
'product_id' => $stockEntry -> product_id ,
'amount' => $amount * - 1 ,
'best_before_date' => $stockEntry -> best_before_date ,
'purchased_date' => $stockEntry -> purchased_date ,
'used_date' => date ( 'Y-m-d' ),
'spoiled' => $spoiled ,
'stock_id' => $stockEntry -> stock_id ,
'transaction_type' => $transactionType
));
$logRow -> save ();
$restStockAmount = $stockEntry -> amount - $amount ;
$amount = 0 ;
$stockEntry -> update ( array (
'amount' => $restStockAmount
));
}
2017-04-16 23:11:03 +02:00
}
2017-04-20 17:10:21 +02:00
return true ;
}
else
{
2018-04-10 20:30:11 +02:00
throw new Exception ( " Transaction type $transactionType is not valid (StockService.ConsumeProduct) " );
2017-04-20 17:10:21 +02:00
}
}
2018-04-11 19:49:35 +02:00
public function InventoryProduct ( int $productId , int $newAmount , string $bestBeforeDate )
2017-04-20 17:10:21 +02:00
{
2018-04-11 19:49:35 +02:00
$productStockAmount = $this -> Database -> stock () -> where ( 'product_id' , $productId ) -> sum ( 'amount' );
2017-04-20 17:10:21 +02:00
if ( $newAmount > $productStockAmount )
{
$amountToAdd = $newAmount - $productStockAmount ;
2018-04-11 19:49:35 +02:00
$this -> AddProduct ( $productId , $amountToAdd , $bestBeforeDate , self :: TRANSACTION_TYPE_INVENTORY_CORRECTION );
2017-04-20 17:10:21 +02:00
}
else if ( $newAmount < $productStockAmount )
{
$amountToRemove = $productStockAmount - $newAmount ;
2018-04-11 19:49:35 +02:00
$this -> ConsumeProduct ( $productId , $amountToRemove , false , self :: TRANSACTION_TYPE_INVENTORY_CORRECTION );
2017-04-16 23:11:03 +02:00
}
return true ;
}
2017-04-21 15:36:04 +02:00
2018-04-11 19:49:35 +02:00
public function AddMissingProductsToShoppingList ()
2017-04-21 15:36:04 +02:00
{
2018-04-11 19:49:35 +02:00
$missingProducts = $this -> GetMissingProducts ();
2017-04-21 15:36:04 +02:00
foreach ( $missingProducts as $missingProduct )
{
2018-04-11 19:49:35 +02:00
$product = $this -> Database -> products () -> where ( 'id' , $missingProduct -> id ) -> fetch ();
2017-04-21 15:36:04 +02:00
$amount = ceil ( $missingProduct -> amount_missing / $product -> qu_factor_purchase_to_stock );
2018-04-11 19:49:35 +02:00
$alreadyExistingEntry = $this -> Database -> shopping_list () -> where ( 'product_id' , $missingProduct -> id ) -> fetch ();
2017-04-21 15:36:04 +02:00
if ( $alreadyExistingEntry ) //Update
{
$alreadyExistingEntry -> update ( array (
'amount_autoadded' => $amount
));
}
else //Insert
{
2018-04-11 19:49:35 +02:00
$shoppinglistRow = $this -> Database -> shopping_list () -> createRow ( array (
2017-04-21 15:36:04 +02:00
'product_id' => $missingProduct -> id ,
'amount_autoadded' => $amount
));
$shoppinglistRow -> save ();
}
}
}
2017-04-16 23:11:03 +02:00
}