Files
grocy/services/StockService.php

186 lines
6.0 KiB
PHP
Raw Normal View History

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;
}
2018-04-11 19:49:35 +02:00
public function AddMissingProductsToShoppingList()
{
2018-04-11 19:49:35 +02:00
$missingProducts = $this->GetMissingProducts();
foreach ($missingProducts as $missingProduct)
{
2018-04-11 19:49:35 +02:00
$product = $this->Database->products()->where('id', $missingProduct->id)->fetch();
$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();
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(
'product_id' => $missingProduct->id,
'amount_autoadded' => $amount
));
$shoppinglistRow->save();
}
}
}
2017-04-16 23:11:03 +02:00
}