code fixes

This commit is contained in:
Dennis Eichhorn 2024-03-15 20:24:39 +00:00
parent 77b345864f
commit a87884f3a9
32 changed files with 340 additions and 312 deletions

View File

@ -57,5 +57,5 @@ return [
'state' => PermissionCategory::ORDER, 'state' => PermissionCategory::ORDER,
], ],
], ],
] ],
]; ];

View File

@ -84,6 +84,17 @@ final class ApiController extends Controller
$this->createStandardBackgroundResponse($request, $response, []); $this->createStandardBackgroundResponse($request, $response, []);
} }
/**
* Returns data from an order suggestion element.
*
* This also re-calculates a lot of values because some depend on the current stock amounts, prices etc.
*
* @param OrderSuggestionElement[] $elements Elements of our order
*
* @return array<int, array{singlePrice:FloatInt, totalPrice:FloatInt, stock:FloatInt, reserved:FloatInt, ordered:FloatInt, minquantity:FloatInt, minstock:FloatInt, quantitystep:FloatInt, avgsales:FloatInt, range_stock:float, range_reserved:float, range_ordered:float}>
*
* @since 1.0.0
*/
public function getOrderSuggestionElementData(array $elements) : array public function getOrderSuggestionElementData(array $elements) : array
{ {
if (empty($elements)) { if (empty($elements)) {
@ -111,7 +122,7 @@ final class ApiController extends Controller
// @todo A lot of the code below is mirrored in the CliController for ALL items. // @todo A lot of the code below is mirrored in the CliController for ALL items.
// Pull out some of the code so we only need to maintain one version // Pull out some of the code so we only need to maintain one version
foreach ($elements as $element) { foreach ($elements as $element) {
$maxHistoryDuration = $element->item->getAttribute('order_suggestion_history_duration')->value->getValue() ?? 12; $maxHistoryDuration = $element->item->getAttribute('order_suggestion_history_duration')->value->valueInt ?? 12;
$salesForecast = []; $salesForecast = [];
@ -179,15 +190,15 @@ final class ApiController extends Controller
// -> see SD HTS (depending on other shipments -> not delivered even if available) // -> see SD HTS (depending on other shipments -> not delivered even if available)
// -> maybe it's possible to consider the expected delivery time? // -> maybe it's possible to consider the expected delivery time?
$minimumStockQuantity = $element->item->getAttribute('minimum_stock_quantity')->value->getValue() ?? 0; $minimumStockQuantity = $element->item->getAttribute('minimum_stock_quantity')->value->valueInt ?? 0;
$minimumStockQuantity = (int) \round($minimumStockQuantity * 1000); $minimumStockQuantity = (int) \round($minimumStockQuantity * FloatInt::DIVISOR); // @bug why? shouldn't the value already be 10,000?
$minimumStockRange = $avgMonthlySales === 0 ? 0 : $minimumStockQuantity / $avgMonthlySales; $minimumStockRange = $avgMonthlySales === 0 ? 0 : $minimumStockQuantity / $avgMonthlySales;
$minimumStockQuantity = (int) \round($minimumStockRange * $avgMonthlySales); $minimumStockQuantity = (int) \round($minimumStockRange * $avgMonthlySales);
$minimumOrderQuantity = $element->item->getAttribute('minimum_order_quantity')->value->getValue() ?? 0; $minimumOrderQuantity = $element->item->getAttribute('minimum_order_quantity')->value->valueInt ?? 0;
$minimumOrderQuantity = (int) \round($minimumOrderQuantity * FloatInt::DIVISOR); $minimumOrderQuantity = (int) \round($minimumOrderQuantity * FloatInt::DIVISOR);
$orderQuantityStep = $element->item->getAttribute('order_quantity_steps')->value->getValue() ?? 1; $orderQuantityStep = $element->item->getAttribute('order_quantity_steps')->value->valueInt ?? 1;
$orderQuantityStep = (int) \round($orderQuantityStep * FloatInt::DIVISOR); $orderQuantityStep = (int) \round($orderQuantityStep * FloatInt::DIVISOR);
$orderQuantity = $element->quantity->value; $orderQuantity = $element->quantity->value;

View File

@ -84,6 +84,15 @@ final class CliController extends Controller
return $view; return $view;
} }
/**
* Create a suggestion
*
* @param RequestAbstract $request Request
*
* @return OrderSuggestion
*
* @since 1.0.0
*/
public function createSuggestionFromRequest(RequestAbstract $request) : OrderSuggestion public function createSuggestionFromRequest(RequestAbstract $request) : OrderSuggestion
{ {
$showIrrelevant = $request->getDataBool('-irrelevant') ?? false; $showIrrelevant = $request->getDataBool('-irrelevant') ?? false;
@ -99,6 +108,7 @@ final class CliController extends Controller
// @question Consider to save suggestion as model in db // @question Consider to save suggestion as model in db
// This would allow users to work on it for a longer time // This would allow users to work on it for a longer time
// It would also allow for an easier approval process // It would also allow for an easier approval process
/** @var \Modules\ItemManagement\Models\Item[] $items */
$items = ItemMapper::getAll() $items = ItemMapper::getAll()
->with('container') ->with('container')
->with('l11n') ->with('l11n')
@ -150,13 +160,13 @@ final class CliController extends Controller
->where('type/name', 'business_year_start') ->where('type/name', 'business_year_start')
->execute(); ->execute();
$businessStart = $unitAttribute->id === 0 ? 1 : $unitAttribute->value->getValue(); $businessStart = $unitAttribute->id === 0 ? 1 : $unitAttribute->value->valueInt;
$historyStart = (int) $start->format('m'); $historyStart = (int) $start->format('m');
$historyEnd = (int) $end->format('m'); $historyEnd = (int) $end->format('m');
foreach ($items as $item) { foreach ($items as $item) {
$maxHistoryDuration = $item->getAttribute('order_suggestion_history_duration')->value->getValue() ?? 12; $maxHistoryDuration = $item->getAttribute('order_suggestion_history_duration')->value->valueInt ?? 12;
$salesForecast = []; $salesForecast = [];
@ -225,24 +235,24 @@ final class CliController extends Controller
// -> maybe it's possible to consider the expected delivery time? // -> maybe it's possible to consider the expected delivery time?
// Get minimum range we want // Get minimum range we want
$wantedStockRange = $item->getAttribute('minimum_stock_range')->value->getValue() ?? 1; $wantedStockRange = $item->getAttribute('minimum_stock_range')->value->valueInt ?? 1;
$minimumStockQuantity = $item->getAttribute('minimum_stock_quantity')->value->getValue() ?? 0; $minimumStockQuantity = $item->getAttribute('minimum_stock_quantity')->value->valueInt ?? 0;
$minimumStockQuantity = (int) \round($minimumStockQuantity * FloatInt::DIVISOR); $minimumStockQuantity = (int) \round($minimumStockQuantity * FloatInt::DIVISOR);
$minimumStockRange = $avgMonthlySales === 0 ? 0 : $minimumStockQuantity / $avgMonthlySales; $minimumStockRange = $avgMonthlySales === 0 ? 0 : $minimumStockQuantity / $avgMonthlySales;
$minimumStockQuantity = (int) \round($minimumStockRange * $avgMonthlySales); $minimumStockQuantity = (int) \round($minimumStockRange * $avgMonthlySales);
$minimumOrderQuantity = $item->getAttribute('minimum_order_quantity')->value->getValue() ?? 0; $minimumOrderQuantity = $item->getAttribute('minimum_order_quantity')->value->valueInt ?? 0;
$minimumOrderQuantity = (int) \round($minimumOrderQuantity * FloatInt::DIVISOR); $minimumOrderQuantity = (int) \round($minimumOrderQuantity * FloatInt::DIVISOR);
$orderQuantityStep = $item->getAttribute('order_quantity_steps')->value->getValue() ?? 1; $orderQuantityStep = $item->getAttribute('order_quantity_steps')->value->valueInt ?? 1;
$orderQuantityStep = (int) \round($orderQuantityStep * FloatInt::DIVISOR); $orderQuantityStep = (int) \round($orderQuantityStep * FloatInt::DIVISOR);
$leadTime = $item->getAttribute('lead_time')->value->getValue() ?? 3; // in days $leadTime = $item->getAttribute('lead_time')->value->valueInt ?? 3; // in days
// @todo Business hours don't have to be 8 hours // @todo Business hours don't have to be 8 hours
// we assume 10 seconds per item if nothing is defined for (invoice handling, stock handling) // we assume 10 seconds per item if nothing is defined for (invoice handling, stock handling)
$adminTime = ($item->getAttribute('admin_time')->value->getValue() ?? 10) / (8 * 60 * 60); // from seconds -> days $adminTime = ($item->getAttribute('admin_time')->value->valueInt ?? 10) / (8 * 60 * 60); // from seconds -> days
// Overhead time in days by estimating at least 1 week worth of order quantity // Overhead time in days by estimating at least 1 week worth of order quantity
$estimatedOverheadTime = $leadTime + $adminTime * \max($minimumOrderQuantity, $avgMonthlySales / 4) / FloatInt::DIVISOR; $estimatedOverheadTime = $leadTime + $adminTime * \max($minimumOrderQuantity, $avgMonthlySales / 4) / FloatInt::DIVISOR;

View File

@ -36,14 +36,30 @@ class OrderSuggestion
public \DateTimeImmutable $createdAt; public \DateTimeImmutable $createdAt;
/**
* Order elements
*
* @var OrderSuggestionElement[]
* @since 1.0.0
*/
public array $elements = []; public array $elements = [];
/**
* Constructor.
*/
public function __construct() public function __construct()
{ {
$this->createdBy = new NullAccount(); $this->createdBy = new NullAccount();
$this->createdAt = new \DateTimeImmutable('now'); $this->createdAt = new \DateTimeImmutable('now');
} }
/**
* Calculate total costs of order
*
* @return FloatInt
*
* @since 1.0.0
*/
public function getTotalCosts() : FloatInt public function getTotalCosts() : FloatInt
{ {
$total = new FloatInt(); $total = new FloatInt();

View File

@ -53,6 +53,11 @@ class OrderSuggestionElement
public FloatInt $costs; public FloatInt $costs;
/**
* Constructor.
*
* @since 1.0.0
*/
public function __construct() public function __construct()
{ {
$this->modifiedBy = new NullAccount(); $this->modifiedBy = new NullAccount();

View File

@ -21,14 +21,14 @@ use Modules\SupplierManagement\Models\SupplierMapper;
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
/** /**
* Client mapper class. * OrderSuggestionElement mapper class.
* *
* @package Modules\Purchase\Models\OrderSuggestion * @package Modules\Purchase\Models\OrderSuggestion
* @license OMS License 2.0 * @license OMS License 2.0
* @link https://jingga.app * @link https://jingga.app
* @since 1.0.0 * @since 1.0.0
* *
* @template T of Client * @template T of OrderSuggestionElement
* @extends DataMapperFactory<T> * @extends DataMapperFactory<T>
*/ */
final class OrderSuggestionElementMapper extends DataMapperFactory final class OrderSuggestionElementMapper extends DataMapperFactory

View File

@ -18,14 +18,14 @@ use Modules\Admin\Models\AccountMapper;
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
/** /**
* Client mapper class. * OrderSuggestion mapper class.
* *
* @package Modules\Purchase\Models\OrderSuggestion * @package Modules\Purchase\Models\OrderSuggestion
* @license OMS License 2.0 * @license OMS License 2.0
* @link https://jingga.app * @link https://jingga.app
* @since 1.0.0 * @since 1.0.0
* *
* @template T of Client * @template T of OrderSuggestion
* @extends DataMapperFactory<T> * @extends DataMapperFactory<T>
*/ */
final class OrderSuggestionMapper extends DataMapperFactory final class OrderSuggestionMapper extends DataMapperFactory

View File

@ -33,5 +33,4 @@ abstract class OrderSuggestionOptimizationType extends Enum
public const COST = 2; // Suggestion focuses on creating better prices if volume discounts exist. public const COST = 2; // Suggestion focuses on creating better prices if volume discounts exist.
public const JUST_IN_TIME = 3; // Suggestion focuses on calculating minimum stock quantities. public const JUST_IN_TIME = 3; // Suggestion focuses on calculating minimum stock quantities.
} }

View File

@ -15,10 +15,8 @@ declare(strict_types=1);
return ['Navigation' => [ return ['Navigation' => [
'Analysis' => 'Analysis', 'Analysis' => 'Analysis',
'Articles' => 'Articles', 'Articles' => 'Articles',
'Create' => 'Create',
'Invoice' => 'Invoice', 'Invoice' => 'Invoice',
'Invoices' => 'Invoices', 'Invoices' => 'Invoices',
'List' => 'List',
'OrderSuggestions' => 'Order Suggestions', 'OrderSuggestions' => 'Order Suggestions',
'PendingOrders' => 'Pending Orders', 'PendingOrders' => 'Pending Orders',
'Profile' => 'Profile', 'Profile' => 'Profile',

View File

@ -16,11 +16,8 @@ return ['Purchase' => [
'Created' => 'Erstellt', 'Created' => 'Erstellt',
'Creator' => 'Ersteller', 'Creator' => 'Ersteller',
'Order' => 'Befehl', 'Order' => 'Befehl',
'Ordered' => 'Bestellt',
'Price' => 'Preis', 'Price' => 'Preis',
'Status' => 'Status', 'Status' => 'Status',
'Stock' => 'Aktie',
'Order' => 'Bestellen',
'Item' => 'Artikel', 'Item' => 'Artikel',
'Supplier' => 'Lieferant', 'Supplier' => 'Lieferant',
'Stock' => 'Lager', 'Stock' => 'Lager',
@ -35,7 +32,6 @@ return ['Purchase' => [
'Steps' => 'Schritte', 'Steps' => 'Schritte',
'Ordering' => 'Bestellen', 'Ordering' => 'Bestellen',
'NewRange' => 'Neue Reichw.', 'NewRange' => 'Neue Reichw.',
'Price' => 'Preis',
'Total' => 'Gesamt', 'Total' => 'Gesamt',
'Costs' => 'Kosten', 'Costs' => 'Kosten',
':SuggestionStatus-1' => 'Entwurf', ':SuggestionStatus-1' => 'Entwurf',
@ -53,7 +49,6 @@ return ['Purchase' => [
'Section' => 'Sparte', 'Section' => 'Sparte',
'SalesGroup' => 'Umsatzgruppe', 'SalesGroup' => 'Umsatzgruppe',
'ProductGroup' => 'Produktgruppe', 'ProductGroup' => 'Produktgruppe',
'MinRange' => 'Min.-Reichweite',
'OrderSuggestions' => 'Bestellvorschläge', 'OrderSuggestions' => 'Bestellvorschläge',
'OrderSuggestion' => 'Bestellvorschlag', 'OrderSuggestion' => 'Bestellvorschlag',
'Suggestions' => 'Vorschläge', 'Suggestions' => 'Vorschläge',

View File

@ -15,12 +15,7 @@ declare(strict_types=1);
return ['Purchase' => [ return ['Purchase' => [
'Created' => 'Created', 'Created' => 'Created',
'Creator' => 'Creator', 'Creator' => 'Creator',
'Order' => 'Order',
'Ordered' => 'Ordered',
'Price' => 'Price',
'Status' => 'Status', 'Status' => 'Status',
'Stock' => 'Stock',
'Supplier' => 'Supplier',
'Order' => 'Order', 'Order' => 'Order',
'Item' => 'Item', 'Item' => 'Item',
'Supplier' => 'Supplier', 'Supplier' => 'Supplier',
@ -32,7 +27,6 @@ return ['Purchase' => [
'Range2' => 'Range 2', 'Range2' => 'Range 2',
'MinStock' => 'Min. Stock', 'MinStock' => 'Min. Stock',
'MinOrder' => 'Min. Order', 'MinOrder' => 'Min. Order',
'MinRange' => 'Min. Range',
'Steps' => 'Steps', 'Steps' => 'Steps',
'Ordering' => 'Ordering', 'Ordering' => 'Ordering',
'NewRange' => 'New Range', 'NewRange' => 'New Range',

View File

@ -125,7 +125,7 @@ echo $this->data['nav']->render();
(int) ($months = ($this->data['suggestion_data'][$element->item->id]['range_ordered'] (int) ($months = ($this->data['suggestion_data'][$element->item->id]['range_ordered']
+ $this->data['suggestion_data'][$element->item->id]['range_reserved'])), + $this->data['suggestion_data'][$element->item->id]['range_reserved'])),
(int) (($months - ((int) $months)) * 30)) (int) (($months - ((int) $months)) * 30))
->format('Y-m-d') ->format('Y-m-d');
?> ?>
<td><?= $this->data['suggestion_data'][$element->item->id]['singlePrice']->getAmount(); ?> <td><?= $this->data['suggestion_data'][$element->item->id]['singlePrice']->getAmount(); ?>
<td><?= $element->costs->getAmount(); ?> <td><?= $element->costs->getAmount(); ?>