use Exception;
use Yii;
+use yii\base\DynamicModel;
use yii\db\Expression;
use yii\filters\AccessControl;
use yii\helpers\ArrayHelper;
{
$request = Yii::$app->request;
- $month = $request->post('month') ? (int)$request->post('month') : 5;
- $year = $request->post('year') ? (int)$request->post('year') : 2025;
+ $month = $request->post('month', 5);
+ $year = $request->post('year', 2025);
+
+ $model = DynamicModel::validateData(
+ ['month' => $month, 'year' => $year],
+ [
+ [['month', 'year'], 'required'],
+ ['month', 'integer', 'min' => 1, 'max' => 12],
+ ['year', 'integer', 'min' => 2000, 'max' => 2100],
+ ]
+ );
+
+ if ($request->isPost && $model->validate()) {
+ $month = $model->month;
+ $year = $model->year;
+ } else {
+ $model->month = $month;
+ $model->year = $year;
+ }
$result = StorePlanService::getBouquetSpiecesMonthGoal($month, $year);
$storesMap = ArrayHelper::map($stores, 'id', 'name');
return $this->render('month-goal', [
+ 'model' => $model, // передаём динамическую модель
'result' => $result,
'storesMap' => $storesMap,
'month' => $month,
}
- public static function getBouquetSpiecesMonthGoal($month, $year) {
- $stores = ArrayHelper::map(CityStore::find()->select(['id'])->where(['visible' => CityStore::IS_VISIBLE])->asArray()->all(), 'id', 'id');
- $storesParams = ArrayHelper::map(CityStoreParams::find()
- ->select(['store_id', 'address_region'])
- ->where(['store_id' => array_keys($stores)])
- ->andWhere(['not', ['address_region' => '']])
- ->asArray()
- ->indexBy('store_id')
- ->all(), 'store_id', 'address_region');
+ public static function getBouquetSpiecesMonthGoal($month, $year)
+ {
+ $stores = ArrayHelper::map(
+ CityStore::find()
+ ->select(['id'])
+ ->where(['visible' => CityStore::IS_VISIBLE])
+ ->asArray()
+ ->all(),
+ 'id',
+ 'id'
+ );
+ $storesParams = ArrayHelper::map(
+ CityStoreParams::find()
+ ->select(['store_id', 'address_region'])
+ ->where(['store_id' => array_keys($stores)])
+ ->andWhere(['not', ['address_region' => '']])
+ ->asArray()
+ ->indexBy('store_id')
+ ->all(),
+ 'store_id',
+ 'address_region'
+ );
$matrixTypesIds = StorePlanService::getActiveMatrixTypes();
-
$storesForecasts = [];
- // получаем букеты из матрицы
+
if ($matrixTypesIds) {
foreach ($matrixTypesIds as $matrixTypeId) {
$bouquetsArray = StorePlanService::getBouqetsByDate($month, $year, $matrixTypeId);
- $forecasts = ArrayHelper::getColumn($bouquetsArray , 'bouquetForecast');
+ $forecasts = ArrayHelper::getColumn($bouquetsArray, 'bouquetForecast');
foreach ($forecasts as $forecastArray) {
if (is_array($forecastArray)) {
foreach ($forecastArray as $fc) {
$bouquetPrice = 0;
- if(isset($storesParams[$fc["type_sales_id"]])){
- $bouquetPrice = BouquetCompositionPrice::find()
+ if (isset($storesParams[$fc["type_sales_id"]])) {
+ $priceModel = BouquetCompositionPrice::find()
->where(['bouquet_id' => $fc['bouquet_id']])
- ->andWhere(['region_id' => $storesParams[$fc["type_sales_id"]]])->one()->price;
+ ->andWhere(['region_id' => $storesParams[$fc["type_sales_id"]]])
+ ->one();
+ if ($priceModel !== null) {
+ $bouquetPrice = $priceModel->price;
+ }
}
-
- $storesForecasts[] = array_merge($fc, ['matrixTypeId' => $matrixTypeId], ['price' => $bouquetPrice] );
-
+ $storesForecasts[] = array_merge($fc, [
+ 'matrixTypeId' => $matrixTypeId,
+ 'price' => $bouquetPrice
+ ]);
}
}
}
}
}
- $resultArr = [];
+
+ $resultData = [];
+ $debugData = [];
+ //var_dump($storesParams);die();
foreach ($storesForecasts as $storeForecast) {
$products = ArrayHelper::toArray(BouquetCompositionProducts::getCompositionProducts($storeForecast['bouquet_id']));
$productGuids = array_filter(array_column($products, 'product_guid'));
- $pricesData = ArrayHelper::map(PricesDynamic::find()
- ->where(['product_id' => $productGuids])
- ->andWhere(['active' => 1])
- ->andWhere(['region_id' => $storesParams[$fc["type_sales_id"]]])
- ->select(['price', 'product_id'])->asArray()->all(), 'product_id', 'price') ;
- // var_dump($products); die();
+ $pricesData = ArrayHelper::map(
+ PricesDynamic::find()
+ ->where(['product_id' => $productGuids])
+ ->andWhere(['active' => 1])
+ ->andWhere(['region_id' => $storesParams[$storeForecast["type_sales_id"]]])
+ ->select(['price', 'product_id'])
+ ->asArray()
+ ->all(),
+ 'product_id',
+ 'price'
+ );
foreach ($products as $product) {
- $species = Products1cNomenclature::find()->where(['id' => $product['product_guid']])->one()->species;
- $productCost = round($pricesData[$product['product_guid']] * $product['count'] * 1.15, 2);
- if (!isset($resultArr[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]])) {
- $resultArr[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]] = 0;
+ $productModel = Products1cNomenclature::find()->where(['id' => $product['product_guid']])->one();
+ $species = ($productModel !== null) ? $productModel->species : 'Неизвестно';
+ $basePrice = isset($pricesData[$product['product_guid']]) ? $pricesData[$product['product_guid']] : 0;
+ $rawCalculation = $basePrice * $product['count'] * $storeForecast["type_sales_value"];
+ $productCost = round($rawCalculation * 1.15, 2);
+
+ if (!isset($resultData[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]])) {
+ $resultData[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]] = 0;
}
- $resultArr[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]] += $productCost;
-
+ $resultData[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]] += $productCost;
+
+ $debugData[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]][] = [
+ 'product_guid' => $product['product_guid'],
+ 'price' => $basePrice,
+ 'count' => $product['count'],
+ 'forecast' => $storeForecast["type_sales_value"],
+ 'bouquet_id' => $storeForecast["bouquet_id"],
+ 'raw_calculation' => $rawCalculation,
+ 'rounded' => $productCost,
+ ];
}
-
}
+
$finalResult = [];
- foreach ($resultArr as $typeSalesId => $speciesData) {
+ foreach ($resultData as $typeSalesId => $speciesData) {
foreach ($speciesData as $species => $salesDetails) {
$finalResult[$typeSalesId][$species] = round(array_sum($salesDetails), 2);
}
}
- return ['detail' => $resultArr, 'final' => $finalResult];
-
-
-
+ return ['detail' => $resultData, 'final' => $finalResult, 'debug' => $debugData];
}
- public static function getBouquetGoal($bouquet) {
- $onlineStores = BouquetForecast::getStoresList(
- $bouquet['id'],
- BouquetForecast::ONLINE_STORES,
- CityStore::class,
- ['visible' => CityStore::IS_VISIBLE],
- $bouquet['forecast_month'],
- $bouquet['forecast_year']
- );
- $marketplaceStores = BouquetForecast::getStoresList(
- $bouquet['id'],
- BouquetForecast::MARKETPLACE,
- CityStore::class,
- ['visible' => CityStore::IS_VISIBLE],
- $bouquet['forecast_month'],
- $bouquet['forecast_year']
- );
- $offlineStores = BouquetForecast::getStoresList(
- $bouquet['id'],
- BouquetForecast::OFFLINE_STORES,
- StoreType::class,
- [],
- $bouquet['forecast_month'],
- $bouquet['forecast_year']
- );
- return [
- 'onlineStores' => $onlineStores,
- 'marketplaceStores' => $marketplaceStores,
- 'offlineStores' => $offlineStores,
-
- ];
-}
-
-
}
<?php
+
+use kartik\grid\GridView;
use yii\helpers\Html;
use yii\helpers\Url;
+use yii\widgets\ActiveForm;
+
+use yii\data\ArrayDataProvider;
+
+/* @var $model yii\base\DynamicModel */
+/* @var $result array ['final', 'detail', 'debug'] */
+/* @var $storesMap array */
+/* @var $month int */
+/* @var $year int */
-/* @var $result array // Массив с ключами 'detail' и 'final'
-@var $storesMap array // Маппинг [store_id => store_name]
-@var $month integer
-@var $year integer
- */
+$saleTypesLabels = [
+ 1 => 'Оффлайн',
+ 2 => 'Интернет магазины',
+ 3 => 'Маркетплейсы',
+];
?>
-<h1>Цели на месяц: <?= $month ?>/<?= $year ?></h1>
-
-<!-- Форма для выбора месяца и года -->
-<?= Html::beginForm(Url::to(['bouquet/month-goal']), 'post') ?>
-<label for="month">Месяц:</label>
-<?= Html::input('number', 'month', $month, ['min' => 1, 'max' => 12]) ?>
-
-<label for="year">Год:</label>
-<?= Html::input('number', 'year', $year, ['min' => 2000, 'max' => 2100]) ?>
-
-<?= Html::submitButton('Показать', ['class' => 'btn btn-primary']) ?>
-<?= Html::endForm() ?>
-
-<hr>
-
-<!-- Таблица для итоговых сумм ('final') -->
-<h2>Итоговые суммы по магазинам и видам продукции</h2>
-<table border="1" cellpadding="5" cellspacing="0">
- <thead>
- <tr>
- <th>Магазин</th>
- <th>Вид продукции</th>
- <th>Сумма</th>
- </tr>
- </thead>
- <tbody>
- <?php if (!empty($result['final'])): ?>
- <?php foreach ($result['final'] as $storeId => $speciesData): ?>
- <?php
- // Если имя магазина отсутствует в mapping'е – выводим id
- $storeName = isset($storesMap[$storeId]) ? $storesMap[$storeId] : $storeId;
- ?>
- <?php foreach ($speciesData as $species => $sum): ?>
- <tr>
- <td><?= Html::encode($storeName) ?></td>
- <td><?= Html::encode($species) ?></td>
- <td><?= $sum ?></td>
- </tr>
- <?php endforeach; ?>
- <?php endforeach; ?>
- <?php else: ?>
- <tr>
- <td colspan="3">Нет данных для отображения.</td>
- </tr>
- <?php endif; ?>
- </tbody>
-</table>
-
-<!-- Таблица для подробного расчёта ('detail') -->
-<h2>Подробный расчёт по магазинам</h2>
-<table border="1" cellpadding="5" cellspacing="0">
- <thead>
- <tr>
- <th>Магазин</th>
- <th>Вид продукции</th>
- <th>Тип продаж</th>
- <th>Сумма</th>
- </tr>
- </thead>
- <tbody>
- <?php if (!empty($result['detail'])): ?>
- <?php foreach ($result['detail'] as $storeId => $speciesData): ?>
- <?php
- // Определяем имя магазина по mapping'у
- $storeName = isset($storesMap[$storeId]) ? $storesMap[$storeId] : $storeId;
- ?>
- <?php foreach ($speciesData as $species => $saleTypes): ?>
- <?php foreach ($saleTypes as $saleType => $amount): ?>
- <tr>
- <td><?= Html::encode($storeName) ?></td>
- <td><?= Html::encode($species) ?></td>
- <td><?= Html::encode($saleType) ?></td>
- <td><?= $amount ?></td>
- </tr>
- <?php endforeach; ?>
+<div class="show-goal p-4">
+ <h1>Цели букета на месяц: <?= Html::encode($month) ?>/<?= Html::encode($year) ?></h1>
+
+ <div class="month-goal-form col-4">
+
+ <?php $form = ActiveForm::begin([
+ 'action' => Url::to(['your-controller/month-goal']),
+ 'method' => 'post',
+ ]); ?>
+
+ <?= $form->field($model, 'month')->input('number')->label('Месяц') ?>
+ <?= $form->field($model, 'year')->input('number')->label('Год') ?>
+
+ <div class="form-group">
+ <?= Html::submitButton('Показать', ['class' => 'btn btn-primary']) ?>
+ </div>
+
+ <?php ActiveForm::end(); ?>
+ </div>
+
+ <hr>
+
+ <h2>Финальные суммы по магазинам</h2>
+<?php if (!empty($result['final'])): ?>
+ <?php foreach ($result['final'] as $storeId => $speciesData): ?>
+ <h3>Магазин: <?= Html::encode($storesMap[$storeId] ?? $storeId) ?></h3>
+ <?= GridView::widget([
+ 'dataProvider' => new ArrayDataProvider([
+ 'allModels' => array_map(fn($species, $sum) => [
+ 'species' => $species,
+ 'sum' => $sum,
+ ], array_keys($speciesData), $speciesData),
+ 'pagination' => false,
+ ]),
+ 'columns' => [
+ ['attribute' => 'species', 'label' => 'Вид продукции'],
+ ['attribute' => 'sum', 'label' => 'Сумма'],
+ ],
+ ]) ?>
+ <?php endforeach; ?>
+<?php else: ?>
+ <p>Нет данных.</p>
+<?php endif; ?>
+
+ <hr>
+
+ <h2>Детальный расчёт по магазинам</h2>
+<?php if (!empty($result['detail'])): ?>
+ <?php foreach ($result['detail'] as $storeId => $speciesData): ?>
+ <h3>Магазин: <?= Html::encode($storesMap[$storeId] ?? $storeId) ?></h3>
+ <?= GridView::widget([
+ 'dataProvider' => new ArrayDataProvider([
+ 'allModels' => array_map(function ($species, $salesTypes) use ($saleTypesLabels) {
+ $sum = array_sum($salesTypes);
+ return [
+ 'species' => $species,
+ 'offline' => $salesTypes[1] ?? 0,
+ 'online' => $salesTypes[2] ?? 0,
+ 'marketplace' => $salesTypes[3] ?? 0,
+ 'sum' => $sum,
+ ];
+ }, array_keys($speciesData), $speciesData),
+ 'pagination' => false,
+ ]),
+ 'columns' => [
+ ['attribute' => 'species', 'label' => 'Вид продукции'],
+ ['attribute' => 'offline', 'label' => 'Оффлайн'],
+ ['attribute' => 'online', 'label' => 'Интернет'],
+ ['attribute' => 'marketplace', 'label' => 'Маркетплейсы'],
+ ['attribute' => 'sum', 'label' => 'Итого'],
+ ],
+ ]) ?>
+ <?php endforeach; ?>
+<?php else: ?>
+ <p>Нет данных.</p>
+<?php endif; ?>
+
+ <hr>
+
+ <h2>Детальный дебаг по магазинам</h2>
+<?php if (!empty($result['debug'])): ?>
+ <?php foreach ($result['debug'] as $storeId => $speciesData): ?>
+ <h3>Магазин: <?= Html::encode($storesMap[$storeId] ?? $storeId) ?></h3>
+ <?php foreach ($speciesData as $species => $saleTypes): ?>
+ <h4>Вид продукции: <?= Html::encode($species) ?></h4>
+ <?php foreach ($saleTypes as $saleType => $entries): ?>
+ <h5>Тип продаж: <?= Html::encode($saleTypesLabels[$saleType] ?? $saleType) ?></h5>
+ <?= GridView::widget([
+ 'dataProvider' => new ArrayDataProvider([
+ 'allModels' => $entries,
+ 'pagination' => false,
+ ]),
+ 'columns' => [
+ ['attribute' => 'product_guid',
+ 'value' => function ($model, $key, $index, $widget) {
+ return \yii_app\records\Products1c::find()->where(['id' => $model['product_guid']])->one()->name;
+ },
+ 'label' => 'Product GUID'],
+ ['attribute' => 'bouquet_id',
+ 'value' => function ($model, $key, $index, $widget) {
+ return \yii_app\records\BouquetComposition::find()->where(['id' => $model['bouquet_id']])->one()->name;
+ },
+ 'label' => 'Букет'],
+ ['attribute' => 'price', 'label' => 'Цена'],
+ ['attribute' => 'count', 'label' => 'Кол-во'],
+ ['attribute' => 'forecast', 'label' => 'Прогноз'],
+ ['attribute' => 'raw_calculation', 'label' => 'Базовый расчёт'],
+ ['attribute' => 'rounded', 'label' => 'С наценкой за сборку'],
+ ],
+ ]) ?>
<?php endforeach; ?>
<?php endforeach; ?>
- <?php else: ?>
- <tr>
- <td colspan="4">Нет данных для отображения.</td>
- </tr>
- <?php endif; ?>
- </tbody>
-</table>
\ No newline at end of file
+ <?php endforeach; ?>
+<?php else: ?>
+ <p>Нет данных.</p>
+<?php endif; ?>
+
+</div>