mirror of
https://github.com/Karaka-Management/oms-Purchase.git
synced 2026-02-18 00:18:40 +00:00
code fixes
This commit is contained in:
parent
77b345864f
commit
a87884f3a9
|
|
@ -57,5 +57,5 @@ return [
|
||||||
'state' => PermissionCategory::ORDER,
|
'state' => PermissionCategory::ORDER,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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(); ?>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user