]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Перерасчет в задании
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Fri, 27 Jun 2025 15:08:47 +0000 (18:08 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Fri, 27 Jun 2025 15:08:47 +0000 (18:08 +0300)
erp24/commands/CronController.php
erp24/controllers/CategoryPlanController.php
erp24/jobs/RebuildAutoplannogramJob.php [new file with mode: 0644]
erp24/services/AutoPlannogrammaService.php

index 480b37897896d9d4dc7902fe3b4fa8cbb5f4220c..f8a88280b1e5340dc055fe5e9e0c699b07b926fe 100644 (file)
@@ -1591,7 +1591,7 @@ class CronController extends Controller
     public function actionAutoplannogrammaCalculate(): void
     {
         $date = new DateTime();
-        $date->modify('+2 months');
+        $date->modify('+2 months'); // TODO модифицировать
         $planDate = $date->format('Y-m-01');
         $month = (int)$date->format('m');
         $year = (int)$date->format('Y');
index dacf448d28de53e4912349b329d870b6c242b9e5..7805e95dfeb00d64d886b2f121b990c24e04b953 100644 (file)
@@ -10,6 +10,7 @@ use yii\helpers\Json;
 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;
@@ -498,6 +499,28 @@ class CategoryPlanController extends Controller {
                 ));
 
             }
+
+            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');
diff --git a/erp24/jobs/RebuildAutoplannogramJob.php b/erp24/jobs/RebuildAutoplannogramJob.php
new file mode 100644 (file)
index 0000000..afa66d4
--- /dev/null
@@ -0,0 +1,128 @@
+<?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
index 0f56cfb30c3bc6bbd0a0a819152812e791d51498..b44fc7ff6c5a16426e1f8f77f8ac4a0c22b60225 100644 (file)
@@ -9,6 +9,7 @@ use yii\db\mssql\PDO;
 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;
@@ -847,9 +848,28 @@ class AutoPlannogrammaService
         $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);