Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions src/grid/ModelGridView.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
use hipanel\grid\RefColumn;
use hipanel\modules\stock\helpers\StockLocationsProvider;
use hipanel\modules\stock\models\Model;
use hipanel\modules\stock\models\VO\LocationItem;
use Yii;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\web\User;

Expand Down Expand Up @@ -115,15 +115,9 @@ public function columns()
private function generateStockColumns(): array
{
$result = [];
$locations = $this->locationsProvider->getAllLocations();
$locationIds = array_column($locations, 'id');
$locations = ArrayHelper::index($this->locationsProvider->getAllLocations(), 'id');
foreach ($this->locationsProvider->getLocations() as $key) {
$locationId = array_search($key, $locationIds, true);
if ($locationId === false) {
continue;
}
/** @var LocationItem $location */
$location = $locations[$locationId];
$location = $locations[$key];
$icon = Html::tag('span', null, [
'class' => "fa fa-fw " . $location->icon,
]
Expand Down
23 changes: 12 additions & 11 deletions src/models/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ function ($attribute) {
);
}
},
'on' => 'create'
'on' => 'create',
],

// Update
Expand Down Expand Up @@ -181,8 +181,10 @@ public function renderReserves(string $dc): string
$legendItems = (new ModelGridLegend($this))->items();
foreach ($legendItems as $state => $item) {
if (!empty($this->counters[$dc][$state])) {
$count = Html::tag('b',
Html::encode($this->counters[$dc][$state]), [
$count = Html::tag(
'b',
Html::encode($this->counters[$dc][$state]),
[
'style' => "color: {$item['color']}",
'title' => $item['label'],
]
Expand All @@ -191,14 +193,13 @@ public function renderReserves(string $dc): string
$perPage = $this->counters[$dc][$state] > 500
? 500
: ceil($this->counters[$dc][$state] / 50) * 50;

$link = Html::a($count,
Url::toSearch('part', [
'model_id' => $this->id,
'stock_location_in' => $dc,
'stock_location_state' => $state,
], 'index', ['per_page' => $perPage]),
);
$link = Html::a($count, Url::to([
'@part/index',
'PartSearch[model_id]' => $this->id,
'PartSearch[stock_location_in]' => $dc,
'PartSearch[stock_location_state]' => $state,
'per_page' => $perPage,
]));
$out .= $item['prefix'] . $link;
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/models/VO/LocationItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use hipanel\helpers\StringHelper;
use hipanel\modules\stock\enums\StockLocationCategory;
use hipanel\modules\stock\enums\StockLocationType;
use yii\helpers\Json;

final readonly class LocationItem
{
Expand All @@ -28,19 +29,21 @@ public function __construct(

public static function fromArray(array $data): self
{
$objects = Json::decode($data['objects'] ?? '[]');

return new self(
id: (string)$data['id'],
category: StockLocationCategory::from($data['category']),
type: StockLocationType::from($data['location_type']),
name: (string)$data['location_name'],
customers: (string)($data['customers'] ?? ''),
objects: (array)($data['objects'] ?? []),
objects: $objects,
);
}

private function getLabel(): string
{
if ($this->id === 'chwbox' || $this->id === 'stock:ANY') {
if ($this->id === 'chwbox' || $this->id === 'stock:ANY' || str_starts_with($this->id, 'alias_group')) {
return $this->name;
}

Expand Down
6 changes: 1 addition & 5 deletions src/repositories/StockLocationsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ public function getLocations(): array
self::CACHE_DURATION
);

// todo: remove filter when API will return correct type and category without nulls
return array_map(
static fn(array $item) => LocationItem::fromArray($item),
array_filter($data, static fn(array $datum) => !in_array(null, $datum, true))
);
return array_map(fn(array $item) => LocationItem::fromArray($item), $data);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find LocationItem and StockLocationsRepository files
find . -type f -name "*.php" | grep -E "(LocationItem|StockLocationsRepository)" | head -20

Repository: hiqdev/hipanel-module-stock

Length of output: 151


🏁 Script executed:

#!/bin/bash
# 1. First, locate the relevant files
git ls-files | grep -E "(LocationItem|StockLocationsRepository)" | head -20

Repository: hiqdev/hipanel-module-stock

Length of output: 147


🏁 Script executed:

#!/bin/bash
# 2. Check the LocationItem::fromArray implementation
if git ls-files | grep -q "LocationItem"; then
  files=$(git ls-files | grep -i "locationitem")
  for file in $files; do
    echo "=== File: $file ==="
    cat -n "$file" 2>/dev/null || echo "Could not read $file"
  done
fi

Repository: hiqdev/hipanel-module-stock

Length of output: 3755


🏁 Script executed:

#!/bin/bash
# 3. Check StockLocationsRepository for the current code
if git ls-files | grep -q "StockLocationsRepository"; then
  file=$(git ls-files | grep "StockLocationsRepository")
  echo "=== File: $file ==="
  wc -l "$file"
fi

Repository: hiqdev/hipanel-module-stock

Length of output: 179


🏁 Script executed:

#!/bin/bash
# Read the StockLocationsRepository file
cat -n src/repositories/StockLocationsRepository.php

Repository: hiqdev/hipanel-module-stock

Length of output: 1219


🏁 Script executed:

#!/bin/bash
# Check git history to see if there was a previous filter that was removed
cd $(git rev-parse --show-toplevel)
git log --oneline -20 src/repositories/StockLocationsRepository.php

Repository: hiqdev/hipanel-module-stock

Length of output: 281


🏁 Script executed:

#!/bin/bash
# Get the diff of recent changes to this file
git log -p -2 src/repositories/StockLocationsRepository.php | head -100

Repository: hiqdev/hipanel-module-stock

Length of output: 53


🏁 Script executed:

#!/bin/bash
# Check if enum::from() throws on null values by looking at PHP docs behavior
# Also check if there's any type that allows null in the data structure
rg -B5 -A5 'stock-locations-list' src/

Repository: hiqdev/hipanel-module-stock

Length of output: 1741


🏁 Script executed:

#!/bin/bash
# Get the actual diff showing what was removed
git show 3fd9676 -- src/repositories/StockLocationsRepository.php

Repository: hiqdev/hipanel-module-stock

Length of output: 1588


🏁 Script executed:

#!/bin/bash
# Check the enums to understand their behavior
cat -n src/enums/StockLocationCategory.php src/enums/StockLocationType.php 2>/dev/null | head -50

Repository: hiqdev/hipanel-module-stock

Length of output: 1583


🏁 Script executed:

#!/bin/bash
# Search for Model::perform to understand what data structure it returns
rg -A10 'function perform' src/models/Model.php

Repository: hiqdev/hipanel-module-stock

Length of output: 53


🏁 Script executed:

#!/bin/bash
# Find and read the Model class
git ls-files | grep -E "models/Model\.php" | xargs cat -n

Repository: hiqdev/hipanel-module-stock

Length of output: 9494


🏁 Script executed:

#!/bin/bash
# Check previous version before the commit to see what was removed
git show 3fd9676~1:src/repositories/StockLocationsRepository.php 2>/dev/null || echo "Previous version not found or file didn't exist"

Repository: hiqdev/hipanel-module-stock

Length of output: 118


🏁 Script executed:

#!/bin/bash
# Search for any previous usage patterns in git history that might show null filtering
git log --all --source --remotes -S "array_filter" -- "*StockLocations*" 2>/dev/null | head -20

Repository: hiqdev/hipanel-module-stock

Length of output: 1091


🏁 Script executed:

#!/bin/bash
# Look for any other files that might have had this code before the refactor
git show 3fd9676~1:src/ 2>/dev/null | grep -i "location" || echo "Checking other locations..."
git log --name-status --oneline 3fd9676~1 | grep -i location

Repository: hiqdev/hipanel-module-stock

Length of output: 305


🏁 Script executed:

#!/bin/bash
# Check if there's a commit message or PR info mentioning what was refactored
git show 3fd9676 --stat

Repository: hiqdev/hipanel-module-stock

Length of output: 16979


🏁 Script executed:

#!/bin/bash
# Check the parent commit to see the state before this change
git show aa66070104fb038eb0277d567b55a25ec41956e4:src/repositories/StockLocationsRepository.php 2>/dev/null | head -50

Repository: hiqdev/hipanel-module-stock

Length of output: 1179


Flag the assumption that the API now guarantees non-null category and location_type values.

Removing the null filter means LocationItem::fromArray will receive all items from $data. Since the method uses StockLocationCategory::from() and StockLocationType::from() without null checks, passing null for category or location_type will throw a TypeError at runtime. The previous code's TODO comment indicates this filter was temporary until the API returned correct types. Verify that the API contract now guarantees these fields are never null before removing this filter.

Additionally, the arrow function doesn't use $this or capture parent scope variables, so marking it static fn(array $item) would clarify intent and align with the previous implementation.

}
}
63 changes: 52 additions & 11 deletions src/widgets/StockLocationsListTreeSelect.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use hipanel\widgets\VueTreeSelectInput;
use Yii;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Json;
use yii\helpers\Url;
Expand Down Expand Up @@ -42,7 +43,7 @@ public function init(): void
{
$this->useStorage = !$this->hasModel();
$this->value = $this->hasModel()
? StringHelper::explode($this->model->{$this->attribute} ?? '')
? (empty($this->model->{$this->attribute}) ? [] : StringHelper::explode($this->model->{$this->attribute}))
: $this->provider->getLocations();

parent::init();
Expand Down Expand Up @@ -217,34 +218,74 @@ private function buildOptions(): array
{
$stockLocationsList = $this->provider->getAllLocations();

$dcTree = $this->buildDataCentersTree($stockLocationsList);
$aliasGroupTree = $this->buildAliasGroupTree($stockLocationsList);
// $anyStockTree = $this->buildDataCentersTree($stockLocationsList); // todo: legacy tree, may not be useful in the future
$chwTree = $this->buildCHWTree($stockLocationsList);
$rackTree = $this->buildRacksTree($stockLocationsList);

return array_merge(
$dcTree,
$aliasGroupTree,
$chwTree,
$rackTree,
);
}

private function buildAliasGroupTree(array $stockLocationsList): array
{
$result = [];
$locations = array_filter($stockLocationsList, static fn(LocationItem $l) => $l->category->value === 'alias_group_by_stock_state');
$stocks = ArrayHelper::index(array_filter(
$stockLocationsList,
static fn(LocationItem $l) => $l->category->value === 'stock' && $l->id !== 'stock:ANY'
), 'id');

foreach ($locations as $l) {
if (str_ends_with($l->id, ':ANY')) {
$result[$l->type->value]['id'] = $l->id;
$result[$l->type->value]['label'] = $l->label;
} else {
$item = ['id' => $l->id, 'label' => $l->label];
foreach ($l->objects as $objName) {
$item['children'][$objName] = ['id' => $objName, 'label' => $objName];
if (isset($stocks[$objName])) {
$item['children'][$objName]['label'] = $stocks[$objName]->label;
}
}
$result[$l->type->value]['children'][$l->id] = $item;
}
}
$sortedResult = array_merge(array_flip([
'alias_group_stock',
'alias_group_used',
'alias_group_rma',
'alias_group_for-test',
]), $result);

return [
[
'id' => 'alias_group',
'label' => Yii::t('hipanel:stock', 'Alias groups'),
'children' => $this->removeKeysRecursively(array_values($sortedResult)),
],
];
}

/**
* @param $stockLocationsList LocationItem[]
*/
private function buildRacksTree(array $stockLocationsList): array
{
$filterByLocationType = function (array $list, string $type) {
return array_filter($list, function (LocationItem $item) use ($type) {
return str_starts_with($item->category->value, 'location') && $item->type->value === $type;
});
};
$filterByLocationType = fn(array $list, string $type) => array_filter(
$list,
fn(LocationItem $item) => str_starts_with($item->category->value, 'location') && $item->type->value === $type
);

$dcs = $filterByLocationType($stockLocationsList, 'dc');
$buildings = $filterByLocationType($stockLocationsList, 'building');
$cages = $filterByLocationType($stockLocationsList, 'cage');
$racks = $filterByLocationType($stockLocationsList, 'rack');

$result = $this->nestRackTreeChildren([$dcs, $buildings, $cages, $racks]);
$result = $this->nestTreeChildren([$dcs, $buildings, $cages, $racks]);

return [
[
Expand All @@ -255,7 +296,7 @@ private function buildRacksTree(array $stockLocationsList): array
];
}

private function nestRackTreeChildren($dataOrders, string $parent_location = null): array|null
private function nestTreeChildren($dataOrders, string $parent_location = null): array|null
{
$children = [];
if ($dataOrders === []) {
Expand All @@ -270,7 +311,7 @@ private function nestRackTreeChildren($dataOrders, string $parent_location = nul
'label' => $item->label,
];

$nested = $this->nestRackTreeChildren($dataOrders, $item->name);
$nested = $this->nestTreeChildren($dataOrders, $item->name);
if ($nested !== null) {
$children[$item->name]['children'] = $nested;
}
Expand Down