]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Вывод 8 шага
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Tue, 20 May 2025 13:13:13 +0000 (16:13 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Tue, 20 May 2025 13:13:13 +0000 (16:13 +0300)
erp24/controllers/AutoPlannogrammaController.php
erp24/services/StorePlanService.php

index 762005308674dcbcef7f3c2632f966855beb4d5f..dd5451fafe4a61b19230924d6c6967f9a5be60a9 100644 (file)
@@ -625,8 +625,8 @@ class AutoPlannogrammaController extends BaseController
         // Обработка даты на год и месяц
         if (!empty($filters['year']) && !empty($filters['month'])) {
             $filters['plan_date'] = $filters['year'] . '-' . str_pad($filters['month'], 2, '0', STR_PAD_LEFT) . '-01';
-           // $service = new AutoPlannogrammaService();
-            $histShare = StorePlanService::calculateHistoricalShare(
+
+            $data = StorePlanService::calculateSpeciesForecastForProductsWithoutHistory(
                 $filters['store_id'],
                 $filters['month'],
                 $filters['year'],
@@ -635,8 +635,7 @@ class AutoPlannogrammaController extends BaseController
                 $filters['species'],
             );
 
-            $data = StorePlanService::calculateWeightedSalesForProductsWithoutHistory($filters['store_id'], $filters['month'], $filters['year'], $histShare);
-var_dump($histShare); die();
+            var_dump($data); die();
             $flatData = array_filter($data, function ($row) use ($filters) {
                 foreach ($filters as $key => $value) {
                     if (empty($value)) continue;
index 0ebf1ceecfe7faefb302d241b6d6553ba355bf49..6068c9db773bd93d0f2428d6bc11dc8175aa7e5f 100755 (executable)
@@ -276,8 +276,8 @@ class StorePlanService
      */
     private static function getPeriods($baseDate, $count, $withWeeks = false, $withWeights = false)
     {
-        $date = new \DateTime($baseDate);
-        $dateFrom = $date->modify('-3 month')->format('Y-m');
+        $date = new \DateTime(date('Y-m-d', $baseDate));
+        $dateFrom = strtotime($date->modify('-2 month')->format('Y-m'));
         $periods = [];
         for ($i = 1; $i <= $count; $i++) {
             $timestamp = strtotime("-{$i} month", $dateFrom);
@@ -503,6 +503,208 @@ class StorePlanService
         return $weightedResults;
     }
 
+    /**
+     * Возвращает среднюю цену $productId для месяца, отстоящего на $offset месяцев
+     * от заданного $year-$month, в контексте $storeId.
+     *
+     * Если целевой месяц март 2025, при $offset = 2 будет рассчитана цена за январь 2025.
+     *
+     * @param string $productId
+     * @param int    $year
+     * @param int    $month
+     * @param int    $storeId
+     * @param int    $offset  количество месяцев назад (по умолчанию 2)
+     * @return float
+     */
+    public static function getPriceForProductAtOffsetMonth(string $productId, int $year, int $month, int $storeId, int $offset = 2): float
+    {
+        $date = new \DateTimeImmutable(sprintf('%04d-%02d-01', $year, $month));
+        $target = $date->modify(sprintf('-%d months', $offset));
+        $tYear  = (int)$target->format('Y');
+        $tMonth = (int)$target->format('m');
+
+        $monthStart = sprintf('%04d-%02d-01', $tYear, $tMonth);
+        $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $tMonth, $tYear);
+        $monthEnd   = sprintf('%04d-%02d-%02d', $tYear, $tMonth, $daysInMonth);
+
+        $priceRecords = PricesDynamic::find()
+            ->where(['product_id' => $productId])
+            ->andWhere(['<=', 'date_from', $monthEnd])
+            ->andWhere([
+                'or',
+                ['>=', 'date_to',   $monthStart],
+                ['date_to' => '2100-01-01 03:00:00+03']
+            ])
+            ->all();
+
+        // определяем регион по городу
+        $cityId = CityStore::find()->select('city_id')->where(['id' => $storeId])->scalar();
+        if ($cityId == 1342) {
+            $region = BouquetComposition::REGION_NN;
+        } elseif ($cityId == 1) {
+            $region = BouquetComposition::REGION_MSK;
+        } else {
+            $region = null;
+        }
+
+        // ->andWhere(['or', ['region_id' => $region], ['region_id' => null]])
+
+        if (empty($priceRecords)) {
+            return 0.0;
+        }
+
+        $total = 0;
+        $count = 0;
+        foreach ($priceRecords as $rec) {
+            if (isset($rec->price)) {
+                $total += $rec->price;
+                $count++;
+            }
+        }
+
+        return $count > 0 ? ($total / $count) : 0.0;
+    }
+
+
+    /**
+     * Рассчитывает итоговые стоимости для товаров без истории, группируя суммы по
+     * category/subcategory/species.
+     *
+     * @param int   $storeId
+     * @param int   $selectedMonth
+     * @param int   $selectedYear
+     * @param array $productsWithoutHistory  массив ['guid' => …]
+     * @return array  плоский массив строк:
+     *   [
+     *     [
+     *       'store_id'    => …,
+     *       'category'    => …,
+     *       'subcategory' => …,
+     *       'species'     => …,
+     *       'month'       => …,
+     *       'year'        => …,
+     *       'sum'         => …,
+     *     ],
+     *     …
+     *   ]
+     */
+    public static function calculateCostForProductsWithoutHistory(
+        int   $storeId,
+        int   $selectedMonth,
+        int   $selectedYear,
+        array $weightedProductsWithoutHistory
+    ): array
+    {
+
+        $accumulator = [];
+
+        foreach ($weightedProductsWithoutHistory as $guid => $info) {
+            $quantity = (float)$info['weightedValue'];
+            if ($quantity <= 0) {
+                continue;
+            }
+
+            // цена за два месяца назад
+            $price = self::getPriceForProductAtOffsetMonth(
+                $guid,
+                $selectedYear,
+                $selectedMonth,
+                $storeId,
+                2
+            );
+
+            $cost = $quantity * $price;
+
+            /** @var Products1cNomenclature $nom */
+            $nom = Products1cNomenclature::findOne($guid);
+            $cat = $nom->category    ?? '';
+            $sub = $nom->subcategory ?? '';
+            $sp  = $nom->species     ?? '';
+
+            $key = implode('|', [$cat, $sub, $sp]);
+            if (!isset($accumulator[$key])) {
+                $accumulator[$key] = [
+                    'store_id'    => $storeId,
+                    'category'    => $cat,
+                    'subcategory' => $sub,
+                    'species'     => $sp,
+                    'month'       => $selectedMonth,
+                    'year'        => $selectedYear,
+                    'sum'         => 0.0,
+                ];
+            }
+            $accumulator[$key]['sum'] += $cost;
+        }
+
+        return array_values($accumulator);
+    }
+
+
+    /**
+     * Общий расчёт плана для заданной категории товаров без истории.
+     *
+     * @param int    $storeId
+     * @param string $yearMonth   строка "YYYY-MM", например "2025-03"
+     * @param string $category
+     * @param string|null $subcategory
+     * @param string|null $species
+     * @return array  [
+     *     [
+     *         'store_id'    => …,
+     *         'category'    => …,
+     *         'subcategory' => …,
+     *         'species'     => …,
+     *         'month'       => …,
+     *         'year'        => …,
+     *         'sum'         => …,
+     *     ],
+     *     …
+     * ]
+     *
+     */
+    public static function calculateSpeciesForecastForProductsWithoutHistory(
+        int    $storeId,
+        string $month,
+        string $year,
+        string $category,
+        ?string $subcategory,
+        ?string $species
+    ): array {
+
+        $histResult = self::calculateHistoricalShare(
+            $storeId,
+            $month,
+            $year,
+            $category,
+            $subcategory,
+            $species
+        );
+        $productsWithoutHistory = $histResult['without_history'] ?? [];
+
+        if (empty($productsWithoutHistory)) {
+            return [];
+        }
+
+        $weightedResults = self::calculateWeightedSalesForProductsWithoutHistory(
+            $storeId,
+            $month,
+            $year,
+            $productsWithoutHistory
+        );
+
+        if (empty($weightedResults)) {
+            return [];
+        }
+
+        $costs = self::calculateCostForProductsWithoutHistory(
+            $storeId,
+            $month,
+            $year,
+            $weightedResults
+        );
+
+        return $costs;
+    }
 
     /**
      * Получает идентификаторы товаров, похожих на указанный товар,