From: fomichev Date: Thu, 15 May 2025 12:25:43 +0000 (+0300) Subject: Расчет цели X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=32f18d6e023ee390dee722ed7113ffaa0e8cbba8;p=erp24_rep%2Fyii-erp24%2F.git Расчет цели --- diff --git a/erp24/controllers/BouquetController.php b/erp24/controllers/BouquetController.php index fdfd9ab0..6051e805 100644 --- a/erp24/controllers/BouquetController.php +++ b/erp24/controllers/BouquetController.php @@ -6,6 +6,7 @@ use Exception; use PhpOffice\PhpSpreadsheet\IOFactory; use Yii; use yii\base\DynamicModel; +use yii\data\ActiveDataProvider; use yii\data\ArrayDataProvider; use yii\db\Expression; use yii\filters\AccessControl; @@ -23,6 +24,7 @@ use yii_app\records\{BouquetComposition, CityStore, CityStoreParams, Files, + MatrixBouquetForecast, MatrixType, PricesDynamic, Products1c, @@ -305,8 +307,6 @@ class BouquetController extends Controller $model = new DynamicModel(['excelFile']); $model->addRule('excelFile', 'file', ['extensions' => ['xls', 'xlsx'], 'skipOnEmpty' => false]); - $data = []; - if (Yii::$app->request->isPost) { $model->excelFile = UploadedFile::getInstance($model, 'excelFile'); @@ -315,30 +315,82 @@ class BouquetController extends Controller $model->excelFile->saveAs($filePath); $spreadsheet = IOFactory::load($filePath); - $rows = $spreadsheet->getActiveSheet()->toArray(); - - $names = array_filter(array_column($rows, 2)); - - $products = Products1c::find() - ->select(['id', 'name']) - ->where(['name' => $names]) - ->asArray() - ->all(); - - $data = $products; - //var_dump($products); die(); - Yii::$app->session->set('productExportData', $data); + $rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, true); + + $header = array_shift($rows); + foreach ($rows as $row) { + $group = trim($row['B']); + $name = trim($row['C']); + $month = strtolower(trim($row['D'])); + $year = (int) date('Y'); + + $newValues = [ + 's_store' => (int)$row['E'], + 'm_store' => (int)$row['F'], + 'l_store' => (int)$row['G'], + 'xl_store' => (int)$row['H'], + 'marketplace' => (int)$row['I'], + 'internet' => (int)$row['J'], + ]; + + $forecast = MatrixBouquetForecast::find() + ->where([ + 'bouquet_name' => $name, + 'bouquet_group' => $group, + 'year' => $year, + 'month' => $month, + ]) + ->one(); + + if ($forecast) { + $changed = false; + foreach ($newValues as $field => $value) { + if ((int)$forecast->$field !== $value) { + $forecast->$field = $value; + $changed = true; + } + } + + if ($changed) { + $forecast->updated_at = date('Y-m-d H:i:s'); + if (!$forecast->save()) { + Yii::error("Ошибка обновления прогноза: " . json_encode($forecast->getErrors()), __METHOD__); + } + } + } else { + $forecast = new MatrixBouquetForecast(); + $forecast->bouquet_name = $name; + $forecast->bouquet_group = $group; + $forecast->year = $year; + $forecast->month = $month; + $forecast->created_at = date('Y-m-d H:i:s'); + $forecast->updated_at = date('Y-m-d H:i:s'); + + $product = Products1c::find()->select(['id'])->where(['name' => $name])->one(); + $forecast->bouquet_id = $product ? $product->id : null; + + foreach ($newValues as $field => $value) { + $forecast->$field = $value; + } + + if (!$forecast->save()) { + Yii::error("Ошибка создания прогноза: " . json_encode($forecast->getErrors()), __METHOD__); + } + } + } + + Yii::$app->session->setFlash('success', 'Импорт завершён: записи добавлены или обновлены.'); } } - $provider = new ArrayDataProvider([ - 'allModels' => $data, - 'pagination' => false, + $dataProvider = new ActiveDataProvider([ + 'query' => MatrixBouquetForecast::find()->orderBy(['id' => SORT_ASC]), + 'pagination' => ['pageSize' => 200], ]); return $this->render('get-guid-bouquet', [ 'model' => $model, - 'provider' => $provider, + 'dataProvider' => $dataProvider, ]); } diff --git a/erp24/migrations/m250514_143555_create_matrix_bouquet_forecast_table.php b/erp24/migrations/m250514_143555_create_matrix_bouquet_forecast_table.php index 213112e8..e730e398 100644 --- a/erp24/migrations/m250514_143555_create_matrix_bouquet_forecast_table.php +++ b/erp24/migrations/m250514_143555_create_matrix_bouquet_forecast_table.php @@ -18,7 +18,7 @@ class m250514_143555_create_matrix_bouquet_forecast_table extends Migration if (!isset($tableSchema)) { $this->createTable(self::TABLE_NAME, [ 'id' => $this->primaryKey(), - 'bouquet_id' => $this->integer()->comment('ИД букета'), + 'bouquet_id' => $this->string()->comment('ИД букета'), 'bouquet_name' => $this->string()->comment('Наименование букета'), 'bouquet_group' => $this->string()->comment('Матрица букета'), 'year' => $this->integer()->comment('Год'), diff --git a/erp24/records/MatrixBouquetForecast.php b/erp24/records/MatrixBouquetForecast.php index aed1e688..9ab692ed 100644 --- a/erp24/records/MatrixBouquetForecast.php +++ b/erp24/records/MatrixBouquetForecast.php @@ -8,7 +8,7 @@ use Yii; * This is the model class for table "matrix_bouquet_forecast". * * @property int $id - * @property int|null $bouquet_id ИД букета + * @property string|null $bouquet_id ИД букета * @property string|null $bouquet_name Наименование букета * @property string|null $bouquet_group Матрица букета * @property int|null $year Год @@ -41,10 +41,10 @@ class MatrixBouquetForecast extends \yii\db\ActiveRecord { return [ [['bouquet_id', 'bouquet_name', 'bouquet_group', 'year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet', 'created_at', 'updated_at'], 'default', 'value' => null], - [['bouquet_id', 'year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet'], 'default', 'value' => null], - [['bouquet_id', 'year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet'], 'integer'], + [['year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet'], 'default', 'value' => null], + [['year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet'], 'integer'], [['created_at', 'updated_at'], 'safe'], - [['bouquet_name', 'bouquet_group'], 'string', 'max' => 255], + [['bouquet_id', 'bouquet_name', 'bouquet_group'], 'string', 'max' => 255], ]; } diff --git a/erp24/services/StorePlanService.php b/erp24/services/StorePlanService.php index d7e3e411..97b8ddcc 100755 --- a/erp24/services/StorePlanService.php +++ b/erp24/services/StorePlanService.php @@ -12,6 +12,7 @@ use yii_app\records\BouquetCompositionProducts; use yii_app\records\BouquetForecast; use yii_app\records\CityStore; use yii_app\records\CityStoreParams; +use yii_app\records\MatrixBouquetForecast; use yii_app\records\Motivation; use yii_app\records\PricesDynamic; use yii_app\records\Products1cAdditionalCharacteristics; @@ -909,5 +910,131 @@ class StorePlanService } + public static function getBouquetSpiecesMonthGoalFromForecast($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' + ); + + // Все прогнозы на месяц + $forecasts = MatrixBouquetForecast::find() + ->where(['year' => $year, 'month' => $month]) + ->andWhere(['not', ['bouquet_id' => null]]) + ->asArray() + ->all(); + + $resultData = []; + $debugData = []; + + foreach ($forecasts as $forecast) { + $products = []; + + $product = Products1c::find() + ->select(['components']) + ->where(['id' => $forecast['bouquet_id']]) + ->asArray() + ->one(); + + if ($product && !empty($product['components'])) { + $components = json_decode($product['components'], true); + if (is_array($components)) { + foreach ($components as $productId => $count) { + $products[] = [ + 'product_guid' => $productId, + 'count' => (int)$count, + ]; + } + } + } + + $productGuids = array_filter(array_column($products, 'product_guid')); + + $types = [ + 's_store' => 'offline', + 'm_store' => 'offline', + 'l_store' => 'offline', + 'xl_store' => 'offline', + 'marketplace' => 'marketplace', + 'internet' => 'internet' + ]; + + foreach ($types as $field => $typeSales) { + $typeSalesValue = (int)$forecast[$field]; + if ($typeSalesValue <= 0) { + continue; + } + + foreach ($storesParams as $storeId => $regionId) { + $pricesData = ArrayHelper::map( + PricesDynamic::find() + ->where(['product_id' => $productGuids]) + ->andWhere(['active' => 1]) + ->andWhere(['region_id' => $regionId]) + ->select(['price', 'product_id']) + ->asArray() + ->all(), + 'product_id', + 'price' + ); + + foreach ($products as $product) { + $productModel = Products1cNomenclature::findOne($product['product_guid']); + $species = $productModel?->species ?? 'Неизвестно'; + $basePrice = $pricesData[$product['product_guid']] ?? 0; + $rawCalculation = $basePrice * $product['count'] * $typeSalesValue; + $productCost = round($rawCalculation * 1.15, 2); + + if (!isset($resultData[$storeId][$species][$typeSales])) { + $resultData[$storeId][$species][$typeSales] = 0; + } + $resultData[$storeId][$species][$typeSales] += $productCost; + + $debugData[$storeId][$species][$typeSales][] = [ + 'product_guid' => $product['product_guid'], + 'price' => $basePrice, + 'count' => $product['count'], + 'forecast' => $typeSalesValue, + 'bouquet_id' => $forecast['bouquet_id'], + 'raw_calculation' => $rawCalculation, + 'rounded' => $productCost, + ]; + } + } + } + } + + $finalResult = []; + foreach ($resultData as $storeId => $speciesData) { + foreach ($speciesData as $species => $salesData) { + $finalResult[$storeId][$species] = round(array_sum($salesData), 2); + } + } + + return [ + 'detail' => $resultData, + 'final' => $finalResult, + 'debug' => $debugData + ]; + } + + } diff --git a/erp24/views/bouquet/get-guid-bouquet.php b/erp24/views/bouquet/get-guid-bouquet.php index 4203b808..c5371230 100644 --- a/erp24/views/bouquet/get-guid-bouquet.php +++ b/erp24/views/bouquet/get-guid-bouquet.php @@ -5,31 +5,45 @@ use yii\helpers\Html; use kartik\grid\GridView; /** @var yii\web\View $this */ -/** @var yii\base\DynamicModel $model */ -/** @var yii\data\ArrayDataProvider $provider */ +/** @var yii\data\ActiveDataProvider $dataProvider */ +/** @var \yii\base\DynamicModel $model */ -$this->title = 'Импорт товаров из Excel'; +$this->title = 'Импорт прогноза по букетам'; ?> - +

title) ?>

- ['enctype' => 'multipart/form-data']]) ?> + ['enctype' => 'multipart/form-data']]); ?> field($model, 'excelFile')->fileInput() ?>
- 'btn btn-primary']) ?> + 'btn btn-success']) ?>
- + -getCount()): ?> -

Результаты поиска

+ $provider, + 'dataProvider' => $dataProvider, 'columns' => [ - ['class' => 'yii\grid\SerialColumn'], 'id', - 'name', + 'bouquet_id', + [ + 'attribute' => 'bouquet_group', + 'label' => 'Группа букета', + ], + [ + 'attribute' => 'bouquet_name', + 'label' => 'Название букета', + ], + 'year', + 'month', + 's_store', + 'm_store', + 'l_store', + 'xl_store', + 'marketplace', + 'internet', ], ]) ?> + - 'btn btn-success']) ?> - \ No newline at end of file +