]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Перенос методов
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 22 May 2025 09:14:53 +0000 (12:14 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 22 May 2025 09:14:53 +0000 (12:14 +0300)
erp24/services/AutoPlannogrammaService.php

index 05ed880f346904846460a91ee52365929e368b1c..f483248f5e632461b19ad2cf5ff90fa9e17f13c8 100644 (file)
@@ -130,128 +130,7 @@ var_dump($totals); die();
         return $result;
     }
 
-    /**
-     * Считает взвешенную долю категорий за целевой месяц:
-     * берёт три предыдущих к «месяцу-цели» месяца (пропуская сразу предыдущий),
-     * присваивает им веса 3, 2 и 1 (самый старый месяц — 3, самый свежий — 1),
-     * суммирует взвешенные итоги по каждой категории и выдаёт их доли
-     * от общего взвешенного итога.
-     *
-     * @param string $month Целевой месяц в формате 'YYYY-MM'
-     * @param array|null $filters ['store_id'=>…, …]
-     * @param array|null $productFilter Опционально: [product_id, …]
-     * @param string $type 'sales' или 'writeOffs'
-     * @return array      [
-     *   <store_id> => [
-     *     ['category'=>string, 'total_sum'=>float, 'share_of_total'=>float],
-     *     …
-     *   ],
-     *   …
-     * ]
-     */
-    public function getMonthCategoryShareOrWriteOffWeighted(
-        string $month,
-        ?array $filters = null,
-        ?array $productFilter = null,
-        string $type = 'sales'
-    ): array
-    {
-        $stores = $this->getVisibleStores();
-        $storeIds = array_map(fn($s) => $s->id, $stores);
-        if (!empty($filters['store_id'])) {
-            $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
-        }
-        if (empty($storeIds)) {
-            return [];
-        }
-
-        $baseMonth = strtotime("{$month}-01");
-
-        $monthOffsets = [3, 4, 5];
-        $monthWeights = [3, 2, 1];
-
-
-        $weightedSums = [];
-        $monthStoreTotalsWeighted = [];
-
-        foreach ($monthOffsets as $idx => $offsetMonths) {
-            $w = $monthWeights[$idx];
-            $start = date('Y-m-01 00:00:00', strtotime("-{$offsetMonths} months", $baseMonth));
-            $end = date('Y-m-t 23:59:59', strtotime($start));
-
-
-
-            $q = (new Query())
-                ->select([
-                    'store_id' => 'ex.entity_id',
-                    'category' => 'p1c.category',
-                    'month_sum' => new Expression('SUM(CAST(wop.summ AS NUMERIC))'),
-                ])
-                ->from(['w' => 'write_offs']);
-
-            if ($type === 'writeOffs') {
-                $q->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = w.store_id')
-                    ->leftJoin(['wop' => 'write_offs_products'], 'wop.write_offs_id = w.id')
-                    ->leftJoin(['p1c' => 'products_1c_nomenclature'], 'p1c.id = wop.product_id')
-                    ->andWhere(['>=', 'w.date', $start])
-                    ->andWhere(['<=', 'w.date', $end]);
-
-                if ($productFilter !== null) {
-                    $q->andWhere(['wop.product_id' => $productFilter]);
-                }
-            } else {
-                $q->leftJoin(['sp' => 'sales_products'], 'sp.check_id = s.id')
-                    ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = s.store_id_1c')
-                    ->leftJoin(['p1c' => 'products_1c_nomenclature'], 'p1c.id = sp.product_id')
-                    ->andWhere(['>=', 's.date', $start])
-                    ->andWhere(['<=', 's.date', $end]);
-
-                if ($productFilter !== null) {
-                    $q->andWhere(['sp.product_id' => $productFilter]);
-                }
-            }
-
-            $q->andWhere(['ex.entity_id' => $storeIds])
-               // ->andWhere(['<>', 'p1c.category', ''])
-                ->groupBy(['ex.entity_id', 'p1c.category']);
-
-            $rows = $q->all();
-
-            foreach ($rows as $r) {
-                $sid = $r['store_id'];
-                $cat = $r['category'];
-                $sum = (float)$r['month_sum'] * $w;
-                $weightedSums[$sid][$cat] = ($weightedSums[$sid][$cat] ?? 0) + $sum;
-            }
-
-        }
-
-        $result = [];
-        foreach ($weightedSums as $storeId => $cats) {
-            $grand = array_sum($cats) ?: 1;
-
-            $unlabeledSum = $cats[''] ?? 0;
-            $labeledCount = count($cats) - (isset($cats['']) ? 1 : 0);
-
-            $distribution = $labeledCount > 0 ? $unlabeledSum / $labeledCount : 0;
-
-            foreach ($cats as $category => $weightedSum) {
-                if ($category === '') {
-                    continue;
-                }
-
-                $adjustedSum = $weightedSum + $distribution;
 
-                $result[$storeId][] = [
-                    'category' => $category,
-                    'total_sum_cat' => $weightedSum,
-                    'total_sum_store' => $grand,
-                    'share_of_total' => round($adjustedSum / $grand, 4),
-                ];
-            }
-        }
-        return $result;
-    }
 
     public function getMonthCategoryGoal(array $categoryShare, $datePlan, $filters): array
     {
@@ -350,96 +229,7 @@ var_dump($totals); die();
         return $result;
     }
 
-    public function getMonthSubcategoryShareOrWriteOffWeighted(string $dateFrom, ?array $filters = null, ?array $productFilter = null, string $type = 'sales'): array
-    {
-        try {
-            $dt = new \DateTime($dateFrom);
-        } catch (\Exception $e) {
-            // Неверный формат даты
-            return [];
-        }
-        $month = (int)$dt->format('m');
-        $year  = (int)$dt->format('Y');
 
-        $stores  = $this->getVisibleStores();
-        $storeIds = array_map(fn($s) => $s->id, $stores);
-        if (!empty($filters['store_id'])) {
-            $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
-        }
-        if (empty($storeIds)) {
-            return [];
-        }
-
-        $years = [$year - 2, $year - 1];
-
-        $query = (new Query())
-            ->select([
-                'store_id'     => 'ex.entity_id',
-                'subcategory'  => 'p1c.subcategory',
-                'category'     => 'p1c.category',
-                'total_sum'    => new Expression(
-                    $type === 'writeOffs'
-                        ? 'SUM(CAST(wop.summ AS NUMERIC))'
-                        : 'SUM(sp.summ)'
-                ),
-            ]);
-
-        if ($type === 'writeOffs') {
-            $query->from(['w' => 'write_offs'])
-                ->leftJoin(['ex'  => 'export_import_table'],      'ex.export_val = w.store_id')
-                ->leftJoin(['wop'=> 'write_offs_products'],      'wop.write_offs_id = w.id')
-                ->leftJoin(['p1c'=> 'products_1c_nomenclature'], 'p1c.id = wop.product_id')
-
-                ->andWhere(['in', new Expression('EXTRACT(YEAR FROM w.date)'), $years])
-                ->andWhere(['=', new Expression('EXTRACT(MONTH FROM w.date)'), $month]);
-            if ($productFilter !== null) {
-                $query->andWhere(['wop.product_id' => $productFilter]);
-            }
-        } else {
-            $query->from(['s' => 'sales'])
-                ->leftJoin(['sp'  => 'sales_products'],         'sp.check_id = s.id')
-                ->leftJoin(['ex'  => 'export_import_table'],    'ex.export_val = s.store_id_1c')
-                ->leftJoin(['p1c' => 'products_1c_nomenclature'],'p1c.id = sp.product_id')
-                ->andWhere(['in', new Expression('EXTRACT(YEAR FROM s.date)'), $years])
-                ->andWhere(['=', new Expression('EXTRACT(MONTH FROM s.date)'), $month]);
-            if ($productFilter !== null) {
-                $query->andWhere(['sp.product_id' => $productFilter]);
-            }
-        }
-
-        $query->andWhere(['ex.entity_id' => $storeIds])
-            ->andWhere(['<>', 'p1c.subcategory', ''])
-            ->groupBy(['ex.entity_id', 'p1c.subcategory', 'p1c.category']);
-
-        $rows = $query->all();
-        if (empty($rows)) {
-            return [];
-        }
-
-        $sumByStoreCategory = [];
-        foreach ($rows as $r) {
-            $sid = $r['store_id'];
-            $cat = $r['category'];
-            $sumByStoreCategory[$sid][$cat] = ($sumByStoreCategory[$sid][$cat] ?? 0) + $r['total_sum'];
-        }
-
-
-        $result = [];
-        foreach ($rows as $r) {
-            $sid   = $r['store_id'];
-            $cat   = $r['category'];
-            $total = $sumByStoreCategory[$sid][$cat] ?: 1;
-            $result[] = [
-                'store_id'         => $sid,
-                'category'         => $cat,
-                'subcategory'      => $r['subcategory'],
-                'total_sum'        => $r['total_sum'],
-                'percent_of_month' => round($r['total_sum'] / $total, 4),
-            ];
-        }
-
-        return $result;
-    }
 
     public function getMonthSubcategoryGoal(array $subcategoryShare, array $categoryGoals): array
     {
@@ -663,28 +453,29 @@ var_dump($totals); die();
         return array_values($filtered);
     }
 
+
+
     /**
-     * Считает для каждого магазина месячную сумму и долю
-     * продаж или списаний по видам (species).
+     * Считает взвешенную долю категорий за целевой месяц:
+     * берёт три предыдущих к «месяцу-цели» месяца (пропуская сразу предыдущий),
+     * присваивает им веса 3, 2 и 1 (самый старый месяц — 3, самый свежий — 1),
+     * суммирует взвешенные итоги по каждой категории и выдаёт их доли
+     * от общего взвешенного итога.
      *
-     * @param string $dateFrom Дата начала периода (Y-m-d)
-     * @param array|null $filters Фильтры (например ['store_id'=>...])
-     * @param array|null $productFilter Опциональный фильтр по product_id
+     * @param string $month Целевой месяц в формате 'YYYY-MM'
+     * @param array|null $filters ['store_id'=>…, …]
+     * @param array|null $productFilter Опционально: [product_id, …]
      * @param string $type 'sales' или 'writeOffs'
-     * @return array       [
-     *   [
-     *     'store_id'        => (int),
-     *     'category'        => (string),
-     *     'subcategory'     => (string),
-     *     'species'         => (string),  // теперь — именно вид
-     *     'total_sum'       => (float),
-     *     'percent_of_month'=> (float),  // доля от общего объёма магазина
-     *   ], …
+     * @return array      [
+     *   <store_id> => [
+     *     ['category'=>string, 'total_sum'=>float, 'share_of_total'=>float],
+     *     …
+     *   ],
+     *   …
      * ]
      */
-    public function getMonthSpeciesShareOrWriteOffDate(
-        string $dateFrom,
-        string $dateTo,
+    public function getMonthCategoryShareOrWriteOffWeighted(
+        string $month,
         ?array $filters = null,
         ?array $productFilter = null,
         string $type = 'sales'
@@ -695,79 +486,183 @@ var_dump($totals); die();
         if (!empty($filters['store_id'])) {
             $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
         }
+        if (empty($storeIds)) {
+            return [];
+        }
 
-        $totals = $this->getStoreTotals($storeIds, $dateFrom, null, $type, $dateTo);
-        if (empty($totals)) {
+        $baseMonth = strtotime("{$month}-01");
+
+        $monthOffsets = [3, 4, 5];
+        $monthWeights = [3, 2, 1];
+
+
+        $weightedSums = [];
+        $monthStoreTotalsWeighted = [];
+
+        foreach ($monthOffsets as $idx => $offsetMonths) {
+            $w = $monthWeights[$idx];
+            $start = date('Y-m-01 00:00:00', strtotime("-{$offsetMonths} months", $baseMonth));
+            $end = date('Y-m-t 23:59:59', strtotime($start));
+
+
+
+            $q = (new Query())
+                ->select([
+                    'store_id' => 'ex.entity_id',
+                    'category' => 'p1c.category',
+                    'month_sum' => new Expression('SUM(CAST(wop.summ AS NUMERIC))'),
+                ])
+                ->from(['w' => 'write_offs']);
+
+            if ($type === 'writeOffs') {
+                $q->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = w.store_id')
+                    ->leftJoin(['wop' => 'write_offs_products'], 'wop.write_offs_id = w.id')
+                    ->leftJoin(['p1c' => 'products_1c_nomenclature'], 'p1c.id = wop.product_id')
+                    ->andWhere(['>=', 'w.date', $start])
+                    ->andWhere(['<=', 'w.date', $end]);
+
+                if ($productFilter !== null) {
+                    $q->andWhere(['wop.product_id' => $productFilter]);
+                }
+            } else {
+                $q->leftJoin(['sp' => 'sales_products'], 'sp.check_id = s.id')
+                    ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = s.store_id_1c')
+                    ->leftJoin(['p1c' => 'products_1c_nomenclature'], 'p1c.id = sp.product_id')
+                    ->andWhere(['>=', 's.date', $start])
+                    ->andWhere(['<=', 's.date', $end]);
+
+                if ($productFilter !== null) {
+                    $q->andWhere(['sp.product_id' => $productFilter]);
+                }
+            }
+
+            $q->andWhere(['ex.entity_id' => $storeIds])
+                // ->andWhere(['<>', 'p1c.category', ''])
+                ->groupBy(['ex.entity_id', 'p1c.category']);
+
+            $rows = $q->all();
+
+            foreach ($rows as $r) {
+                $sid = $r['store_id'];
+                $cat = $r['category'];
+                $sum = (float)$r['month_sum'] * $w;
+                $weightedSums[$sid][$cat] = ($weightedSums[$sid][$cat] ?? 0) + $sum;
+            }
+
+        }
+
+        $result = [];
+        foreach ($weightedSums as $storeId => $cats) {
+            $grand = array_sum($cats) ?: 1;
+
+            $unlabeledSum = $cats[''] ?? 0;
+            $labeledCount = count($cats) - (isset($cats['']) ? 1 : 0);
+
+            $distribution = $labeledCount > 0 ? $unlabeledSum / $labeledCount : 0;
+
+            foreach ($cats as $category => $weightedSum) {
+                if ($category === '') {
+                    continue;
+                }
+
+                $adjustedSum = $weightedSum + $distribution;
+
+                $result[$storeId][] = [
+                    'category' => $category,
+                    'total_sum_cat' => $weightedSum,
+                    'total_sum_store' => $grand,
+                    'share_of_total' => round($adjustedSum / $grand, 4),
+                ];
+            }
+        }
+        return $result;
+    }
+
+    public function getMonthSubcategoryShareOrWriteOffWeighted(string $dateFrom, ?array $filters = null, ?array $productFilter = null, string $type = 'sales'): array
+    {
+        try {
+            $dt = new \DateTime($dateFrom);
+        } catch (\Exception $e) {
+            // Неверный формат даты
             return [];
         }
+        $month = (int)$dt->format('m');
+        $year  = (int)$dt->format('Y');
 
-        $query = (new Query())->select([
-            'store_id' => 'ex.entity_id',
-            'category' => 'p1c.category',
-            'subcategory' => 'p1c.subcategory',
-            // Суммируем сразу по виду из колонки p1c.species
-            'species' => 'p1c.species',
-            'total_sum' => new Expression(
-                $type === 'writeOffs'
-                    ? "SUM(CAST(item->>'summ' AS NUMERIC))"
-                    : 'SUM(sp.summ)'
-            ),
-        ]);
+        $stores  = $this->getVisibleStores();
+        $storeIds = array_map(fn($s) => $s->id, $stores);
+        if (!empty($filters['store_id'])) {
+            $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
+        }
+        if (empty($storeIds)) {
+            return [];
+        }
+
+        $years = [$year - 2, $year - 1];
+
+        $query = (new Query())
+            ->select([
+                'store_id'     => 'ex.entity_id',
+                'subcategory'  => 'p1c.subcategory',
+                'category'     => 'p1c.category',
+                'total_sum'    => new Expression(
+                    $type === 'writeOffs'
+                        ? 'SUM(CAST(wop.summ AS NUMERIC))'
+                        : 'SUM(sp.summ)'
+                ),
+            ]);
 
         if ($type === 'writeOffs') {
             $query->from(['w' => 'write_offs'])
-                ->leftJoin('export_import_table ex', 'ex.export_val = w.store_id')
-                ->leftJoin(
-                    'LATERAL jsonb_array_elements(w.items::jsonb) AS item',
-                    'TRUE'
-                )
-                ->leftJoin('products_1c_nomenclature p1c', 'p1c.id = item->>\'product_id\'')
-                ->where(['>=', 'w.date', $dateFrom])
-                ->andWhere(['<=', 'w.date', $dateTo])
-                ->andWhere(['ex.entity_id' => $storeIds])
-                ->andWhere(['<>', 'p1c.species', ''])
-                ->groupBy([
-                    'ex.entity_id',
-                    'p1c.category',
-                    'p1c.subcategory',
-                    'p1c.species',
-                ]);
+                ->leftJoin(['ex'  => 'export_import_table'],      'ex.export_val = w.store_id')
+                ->leftJoin(['wop'=> 'write_offs_products'],      'wop.write_offs_id = w.id')
+                ->leftJoin(['p1c'=> 'products_1c_nomenclature'], 'p1c.id = wop.product_id')
+
+                ->andWhere(['in', new Expression('EXTRACT(YEAR FROM w.date)'), $years])
+                ->andWhere(['=', new Expression('EXTRACT(MONTH FROM w.date)'), $month]);
             if ($productFilter !== null) {
-                $query->andWhere(['item->>\'product_id\'' => $productFilter]);
+                $query->andWhere(['wop.product_id' => $productFilter]);
             }
         } else {
             $query->from(['s' => 'sales'])
-                ->leftJoin('sales_products sp', 'sp.check_id = s.id')
-                ->leftJoin('export_import_table ex', 'ex.export_val = s.store_id_1c')
-                ->leftJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id')
-                ->where(['>=', 's.date', $dateFrom])
-                ->andWhere(['<=', 's.date', $dateTo])
-                ->andWhere(['ex.entity_id' => $storeIds])
-                ->andWhere(['<>', 'p1c.species', ''])
-                ->groupBy([
-                    'ex.entity_id',
-                    'p1c.category',
-                    'p1c.subcategory',
-                    'p1c.species',
-                ]);
+                ->leftJoin(['sp'  => 'sales_products'],         'sp.check_id = s.id')
+                ->leftJoin(['ex'  => 'export_import_table'],    'ex.export_val = s.store_id_1c')
+                ->leftJoin(['p1c' => 'products_1c_nomenclature'],'p1c.id = sp.product_id')
+                ->andWhere(['in', new Expression('EXTRACT(YEAR FROM s.date)'), $years])
+                ->andWhere(['=', new Expression('EXTRACT(MONTH FROM s.date)'), $month]);
             if ($productFilter !== null) {
                 $query->andWhere(['sp.product_id' => $productFilter]);
             }
         }
 
+        $query->andWhere(['ex.entity_id' => $storeIds])
+            ->andWhere(['<>', 'p1c.subcategory', ''])
+            ->groupBy(['ex.entity_id', 'p1c.subcategory', 'p1c.category']);
 
         $rows = $query->all();
+        if (empty($rows)) {
+            return [];
+        }
+
+        $sumByStoreCategory = [];
+        foreach ($rows as $r) {
+            $sid = $r['store_id'];
+            $cat = $r['category'];
+            $sumByStoreCategory[$sid][$cat] = ($sumByStoreCategory[$sid][$cat] ?? 0) + $r['total_sum'];
+        }
+
+
         $result = [];
-        foreach ($rows as $row) {
-            $storeId = $row['store_id'];
-            $total = $totals[$storeId] ?? 1;
+        foreach ($rows as $r) {
+            $sid   = $r['store_id'];
+            $cat   = $r['category'];
+            $total = $sumByStoreCategory[$sid][$cat] ?: 1;
             $result[] = [
-                'store_id' => $storeId,
-                'category' => $row['category'],
-                'subcategory' => $row['subcategory'],
-                'species' => $row['species'],
-                'total_sum' => (float)$row['total_sum'],
-                'percent_of_month' => round(((float)$row['total_sum'] / $total), 4),
+                'store_id'         => $sid,
+                'category'         => $cat,
+                'subcategory'      => $r['subcategory'],
+                'total_sum'        => $r['total_sum'],
+                'percent_of_month' => round($r['total_sum'] / $total, 4),
             ];
         }
 
@@ -875,87 +770,8 @@ var_dump($totals); die();
         return $result;
     }
 
-    /**
-     * Для заданного магазина/фильтров получает по 1–5 неделям внутри месяца
-     * и рассчитывает для каждого вида (species) долю недельного объёма
-     * от месячного итога.
-     *
-     * @param string $dateFrom начало месяца (Y-m-d H:i:s)
-     * @param string $dateTo конец месяца  (Y-m-d H:i:s)
-     * @param array $filters ['store_id'=>..., 'category'=>..., ...]
-     * @param array|null $productFilter опц. фильтр по product_id
-     * @param string $type 'sales' или 'writeOffs'
-     * @return array{
-     *     weeksData: array<int, array>,      // сырые данные по каждой неделе
-     *     weeksShare: array<int, array>      // доля недели от месячного итога
-     * }
-     */
-    public function getWeeksSpeciesShareOrWriteOff(
-        string $dateFrom,
-        string $dateTo,
-        array  $filters,
-        ?array $productFilter,
-        string $type = 'writeOffs'
-    ): array
-    {
-        $monthResult = $this->getMonthSpeciesShareOrWriteOffDate(
-            $dateFrom, $dateTo, $filters, $productFilter, $type
-        );
-
-        $speciesMonthTotals = [];
-        foreach ($monthResult as $r) {
-            $speciesMonthTotals
-            [$r['store_id']]
-            [$r['category']]
-            [$r['subcategory']]
-            [$r['species']] = $r['total_sum'];
-        }
+    // Недельные расчеты
 
-        $weeksData = [];
-        $weeksShareResult = [];
-        foreach (range(1, 5) as $ind) {
-            $weekStart = date("Y-m-d 00:00:00", strtotime("+" . (($ind - 1) * 7) . ' days', strtotime($dateFrom)));
-            $weekEnd = date("Y-m-d 23:59:59", strtotime("+" . ($ind * 7 - 1) . ' days', strtotime($dateFrom)));
-            if ($weekEnd > $dateTo) {
-                $weekEnd = $dateTo;
-            }
-            if ($weekStart > $dateTo) {
-                continue;
-            }
-            $weekResult = $this->getMonthSpeciesShareOrWriteOffDate(
-                $weekStart,
-                $weekEnd,
-                $filters,
-                null,
-                $type
-            );
-            $weeksData[$ind] = $weekResult;
-            if (!$weekResult) {
-                continue;
-            }
-            $weeksShareResult[$ind] = [];
-            foreach ($weekResult as $weekRow) {
-                $monthSum = $speciesMonthTotals
-                [$weekRow['store_id']]
-                [$weekRow['category']]
-                [$weekRow['subcategory']]
-                [$weekRow['species']] ?? null;
-
-                if ($monthSum) {
-                    $weeksShareResult[$ind]
-                    [$weekRow['category']]
-                    [$weekRow['subcategory']]
-                    [$weekRow['species']] = $weekRow['total_sum'] / $monthSum;
-                }
-
-            }
-
-        }
-        return [
-            'weeksData' => $weeksData,
-            'weeksShare' => $weeksShareResult,
-        ];
-    }
 
     /**
      * Получает суммы продаж или списаний по видам (species) для каждой недели указанного месяца.