use yii\web\Controller;
use yii\web\Response;
use yii_app\helpers\HtmlHelper;
+use yii_app\jobs\RebuildAutoplannogramJob;
use yii_app\records\CategoryPlan;
use yii_app\records\CityStore;
use yii_app\records\CityStoreParams;
));
}
+
+ if (Yii::$app->request->get('rebuild') === '1'
+ ) {
+ Yii::$app->queue->push(new RebuildAutoplannogramJob([
+ 'year' => (int)$model->year,
+ 'month' => (int)$model->month,
+ 'storeId' => (int)$model->store_id,
+ ]));
+ $params = [
+ 'DynamicModel' => $model->attributes,
+ ];
+
+ $params['DynamicModel'] = array_filter($params['DynamicModel'], function($v) {
+ return $v !== null && $v !== '';
+ });
+
+ return $this->redirect(array_merge(
+ ['index'],
+ $params
+ ));
+ }
+
$currentDate = new \DateTime(date($model->year . '-' . $model->month . '-01'));
$startDate = (clone $currentDate)->modify('-4 months')->modify('first day of this month');
--- /dev/null
+<?php
+
+namespace yii_app\jobs;
+
+use DateTime;
+use Throwable;
+use yii\base\BaseObject;
+use yii\helpers\ArrayHelper;
+use yii\queue\JobInterface;
+use yii_app\records\Autoplannogramma;
+use yii_app\records\CityStore;
+use yii_app\services\AutoPlannogrammaService;
+
+class RebuildAutoplannogramJob extends BaseObject implements JobInterface
+{
+ public int $year;
+ public int $month;
+ public int $storeId;
+
+ public function execute($queue)
+ {
+ $service = new AutoPlannogrammaService();
+
+ $date = (new DateTime())->setDate($this->year, $this->month, 1);
+ $planDate = $date->format('Y-m-01');
+
+ /** @var CityStore $store */
+ $store = CityStore::findOne($this->storeId);
+ if (!$store) {
+ throw new \RuntimeException("Store #{$this->storeId} not found");
+ }
+
+ try {
+
+ $forecastParams = [
+ 'month' => $this->month,
+ 'year' => $this->year,
+ 'type' => AutoPlannogrammaService::TYPE_SALES,
+ 'store_id' => $this->storeId,
+ 'category' => null,
+ 'subcategory' => null,
+ 'species' => null,
+ 'plan_date' => $planDate,
+ ];
+ $forecast = $service->calculateFullForecastForWeek($forecastParams);
+ $writeOffsForecast = $service->getWeeklyProductsWriteoffsForecast(
+ $this->month, $this->year, $forecast, $this->storeId
+ );
+ $salesForecast = $service->getWeeklyBouquetProductsSalesForecast(
+ $this->month, $this->year, $this->storeId
+ );
+
+ $existing = Autoplannogramma::find()
+ ->where([
+ 'store_id' => $this->storeId,
+ 'year' => $this->year,
+ 'month' => $this->month,
+ 'week' => array_unique(ArrayHelper::getColumn($forecast, 'week')),
+ ])
+ ->indexBy(fn($m) => $m->week . '_' . $m->product_id)
+ ->all();
+
+ foreach ($forecast as $item) {
+ $key = $item['week'] . '_' . $item['product_id'];
+ $model = $existing[$key] ?? new Autoplannogramma();
+ $quantity = (float)($item['forecast_week_pieces'] ?? 0);
+ $details = [];
+ $total = $quantity;
+
+ if (!empty($writeOffsForecast[$item['product_id']][$item['week']]['writeOffs'])) {
+ $w = $writeOffsForecast[$item['product_id']][$item['week']]['writeOffs'];
+ $details['writeOffs']['quantity'] = $w;
+ $total += is_array($w) ? array_sum($w) : (float)$w;
+ }
+
+ foreach (['offline','online','marketplace'] as $t) {
+ $block = ['share'=>0,'quantity'=>0,'groups'=>[]];
+ if (!empty($salesForecast[$this->storeId][$item['product_id']][$t])) {
+ $share = $salesForecast[$this->storeId][$t]['share'] ?? 0;
+ $block['share'] = $share;
+ $block['quantity'] = round($quantity * $share,2);
+ $total += $block['quantity'];
+
+ foreach ($salesForecast[$this->storeId][$item['product_id']][$t] as $k=>$v) {
+ $block['groups'][$k] = (float)$v;
+ $total += (float)$v;
+ }
+ }
+ $details[$t] = $block;
+ }
+
+ $details['forecast'] = ['quantity' => $quantity];
+ $total = (float) sprintf('%.2f', $total);
+
+ $needsUpdate = $model->isNewRecord
+ || $model->calculate != $quantity
+ || ceil($model->total) != ceil($total)
+ || json_encode($model->details, JSON_UNESCAPED_UNICODE)
+ !== json_encode($details, JSON_UNESCAPED_UNICODE);
+
+ if ($needsUpdate) {
+ $model->setAttributes([
+ 'year' => $this->year,
+ 'month' => $this->month,
+ 'week' => $item['week'],
+ 'product_id' => $item['product_id'],
+ 'store_id' => $this->storeId,
+ 'is_archive' => false,
+ 'capacity_type' => 1,
+ 'details' => json_encode($details, JSON_UNESCAPED_UNICODE),
+ 'calculate' => $quantity,
+ 'modify' => ceil($total),
+ 'total' => ceil($total),
+ ]);
+ if (!$model->save()) {
+ \Yii::error(
+ 'Ошибка сохранения Autoplannogramma: '
+ . json_encode($model->getErrors(), JSON_UNESCAPED_UNICODE),
+ __METHOD__
+ );
+ }
+ }
+ }
+ } catch (Throwable $e) {
+ \Yii::error("Job failed: " . $e->getMessage(), __METHOD__);
+ }
+ }
+}
\ No newline at end of file
use yii\db\Query;
use yii\helpers\ArrayHelper;
use yii_app\records\BouquetComposition;
+use yii_app\records\CategoryPlan;
use yii_app\records\CityStore;
use yii_app\records\PricesDynamic;
use yii_app\records\Products1cNomenclature;
$datePlan = $filters['plan_date'];
$dateFromForCategory = (new \DateTime($datePlan))->modify('-' . (self::CATEGORY_LOOKBACK_MONTHS + self::LOOKBACK_MONTHS) . ' months')->format('Y-m-d');
- $monthCategoryShare = $this->getMonthCategoryShareOrWriteOff($dateFromForCategory, $filters);
- $monthCategoryGoal = $this->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters['type']);
+ //$monthCategoryShare = $this->getMonthCategoryShareOrWriteOff($dateFromForCategory, $filters);
+ //$monthCategoryGoal = $this->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters['type']);
+ $monthCategoryGoal = [];
+ $categoryPlan = CategoryPlan::find()->where(['year' => date('Y', strtotime($datePlan)), 'month' => date('m', strtotime($datePlan)), 'store_id' => $filters['store_id']])->indexBy('category')->asArray()->all();
+ foreach ($categoryPlan as $category) {
+ if ($category['category'] === 'Матрица') {
+ continue;
+ }
+ if ($filters['type'] == AutoPlannogrammaService::TYPE_SALES) {
+ $fullGoal = $category['offline'] + $category['internet_shop'] + $category['marketplace'];
+ } else {
+ $fullGoal = $category['write_offs'];
+ }
+
+ $monthCategoryGoal[] = [
+ 'category' => $category['category'],
+ 'store_id' => $filters['store_id'],
+ 'goal' => $fullGoal
+ ];
+
+ }
$monthSubcategoryShare = $this->getMonthSubcategoryShareOrWriteOff($datePlan, $filters);
$monthSubcategoryGoal = $this->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal);