]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Расчет целей недели
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Mon, 12 May 2025 14:55:23 +0000 (17:55 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Mon, 12 May 2025 14:55:23 +0000 (17:55 +0300)
erp24/controllers/AutoPlannogrammaController.php
erp24/services/AutoPlannogrammaService.php
erp24/views/auto-plannogramma/control-species.php

index 5c099c2a530f20f4ddf847303254de1cf0d521ad..ffd94059fd552094f26ed42d3cf964964448f348 100644 (file)
@@ -285,7 +285,7 @@ class AutoPlannogrammaController extends BaseController
                 $monthSpeciesGoalsMap[$monthSpeciesGoal['store_id']]
                 [$monthSpeciesGoal['category']]
                 [$monthSpeciesGoal['subcategory']]
-                [$monthSpeciesGoal['product_name']] = $monthSpeciesGoal['goal'] ; // переименовать
+                [$monthSpeciesGoal['species']] = $monthSpeciesGoal['goal'] ; // переименовать
             }
 
 
@@ -345,8 +345,11 @@ class AutoPlannogrammaController extends BaseController
             }
 
             $weeksShareResult = $service->getHistoricalWeeklySpeciesShare($model->month, $filters, null, 'writeOffs');
-            $weeksData = $weeksShareResult['weeksData'];
-           // $weeksShareResult = $weeksShareResult['weeksShare'];
+            $weeksData = $service->calculateWeeklySpeciesGoals($weeksShareResult['weeksData'], $monthSpeciesGoals) ;
+
+
+
+
 
 
 
@@ -358,7 +361,7 @@ class AutoPlannogrammaController extends BaseController
             'model'           => $model,
             'result'          => $monthResult,
             'weeksData'       => $weeksData,
-           // 'weeksShareResult' => $weeksShareResult,
+           //'monthGoal' => $monthSpeciesGoalsMap,
            // 'weeksGoalResult' => $weeksGoalResult,
             'totals'          => $totals,
             'storeList'       => $storeList,
@@ -368,4 +371,6 @@ class AutoPlannogrammaController extends BaseController
     }
 
 
+
+
 }
index 74de0b0ef3957af39922faa2b91f08fd63c4f7df..5926c844c2516db356a8c5ff0eae6e4e9562df8b 100644 (file)
@@ -165,7 +165,7 @@ var_dump($totals); die();
 
         $baseMonth = strtotime("{$month}-01");
 
-        $monthOffsets = [2, 3, 4];
+        $monthOffsets = [3, 4, 5];
         $monthWeights = [3, 2, 1];
 
 
@@ -365,12 +365,7 @@ var_dump($totals); die();
             return [];
         }
 
-        $startYear = 2020;
-        $endYear   = $year - 1;
-        if ($endYear < $startYear) {
-            return [];
-        }
-        $years = range($startYear, $endYear);
+        $years = [$year - 2, $year - 1];
 
         $query = (new Query())
             ->select([
@@ -550,7 +545,7 @@ var_dump($totals); die();
                 $result[] = [
                     'category' => $species['category'],
                     'subcategory' => $species['subcategory'],
-                    'product_name' => $species['species'],
+                    'species' => $species['species'],
                     'store_id' => $species['store_id'],
                     'store_name' => $storeNamesMap[$species['store_id']] ?? 'Неизвестный магазин', // Добавляем название магазина
                     'goal' => round($species['percent_of_month'] * $goal, 2),
@@ -567,16 +562,20 @@ var_dump($totals); die();
         $dateFromForCategory = (new \DateTime($datePlan))->modify('-12 months')->format('Y-m-d');
         $dateFrom = (new \DateTime($datePlan))->modify('-3 months')->format('Y-m-d');
 
-        //$monthCategoryShare = $this->getMonthCategoryShareOrWriteOff($dateFromForCategory, $filters);
         $monthCategoryShare = $this->getMonthCategoryShareOrWriteOffWeighted($datePlan, $filters, null, $filters['type']);
         $monthCategoryGoal = $this->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters);
-
-       // $monthSubcategoryShare = $this->getMonthSubcategoryShareOrWriteOff($dateFrom, $filters);
         $monthSubcategoryShare = $this->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, $filters['type']);
         $monthSubcategoryGoal = $this->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal);
 
-        //$monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOff($dateFrom, $filters);
-        //$monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOffDate($dateFrom, $datePlan, $filters, null, $filters['type']);
+        if ($filters['type'] == 'writeOffs') {
+            $monthCategorySalesShare = $this->getMonthCategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales');
+            $monthCategorySalesGoal = $this->getMonthCategoryGoal($monthCategorySalesShare, $datePlan, $filters);
+            $monthSubcategorySalesShare = $this->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales');
+            $monthSubcategorySalesGoal = $this->getMonthSubcategoryGoal($monthSubcategorySalesShare, $monthCategorySalesGoal);
+
+
+        }
+
         $monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOffWeighted($datePlan, $datePlan, $filters, null, $filters['type']);
         $monthSpeciesGoal = $this->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal);
       // var_dump($monthSpeciesGoal);
@@ -743,12 +742,7 @@ var_dump($totals); die();
             return [];
         }
 
-        $startYear = 2020;
-        $endYear   = $year - 1;
-        if ($endYear < $startYear) {
-            return [];
-        }
-        $years = range($startYear, $endYear);
+        $years = [$year - 2, $year - 1];
 
         $query = (new Query())->select([
             'store_id'   => 'ex.entity_id',
@@ -928,8 +922,8 @@ var_dump($totals); die();
         $month = (int)$monthStr;
         $year  = (int)$yearStr;
 
-        $dateFrom = sprintf('%04d-%02d-01 00:00:00', $year, $month);
-        $dateTo   = date('Y-m-d H:i:s', strtotime($dateFrom . ' +1 month -1 second'));
+        $dateFrom = strtotime(sprintf('%04d-%02d-01 00:00:00', $year, $month));
+        $dateTo   = strtotime('+1 month -1 second', $dateFrom);
         //var_dump($monthYear); die();
         $stores   = $this->getVisibleStores();
         $storeIds = array_map(fn($s)=>$s->id, $stores);
@@ -940,20 +934,36 @@ var_dump($totals); die();
             return [];
         }
 
-        $result = [];
+        $dayOfWeek = (int)date('N', $dateFrom);
+        $firstMonday = $dayOfWeek === 1
+            ? $dateFrom
+            : strtotime('next monday', $dateFrom);
 
-        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;
+
+        $weekRanges = [];
+        for ($wkStart = $firstMonday; $wkStart <= $dateTo; $wkStart += 7 * 86400) {
+            $wkEnd = $wkStart + 6 * 86400;
+            if ($wkEnd > $dateTo) {
+                $wkEnd = $dateTo;
             }
-            if ($weekStart > $dateTo) {
-                continue;
+            $periodStart = max($wkStart, $dateFrom);
+            $periodEnd   = min($wkEnd, $dateTo);
+            $daysInMonth   = floor(($periodEnd - $periodStart) / 86400) + 1;
+            if ($daysInMonth >= 4) {
+                $weekRanges[] = [
+                    'index' => (int)date('W', $wkStart),
+                    'start' => date('Y-m-d H:i:s', $wkStart),
+                    'end'   => date('Y-m-d H:i:s', $wkEnd),
+                ];
             }
+        }
+
+        $result = [];
 
+        foreach ($weekRanges as $range) {
+            $exprWeek = new Expression((string)$range['index']);
             $query = (new Query())->select([
-                'week' => new Expression((string)$ind),
+                'week'        => $exprWeek,
                 'store_id'    => 'ex.entity_id',
                 'category'    => 'p1c.category',
                 'subcategory' => 'p1c.subcategory',
@@ -966,22 +976,22 @@ var_dump($totals); die();
             ]);
 
             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(['>=', 'w.date', $weekStart])
-                    ->andWhere(['<=', 'w.date', $weekEnd]);
+                $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(['>=', 'w.date', $range['start']])
+                    ->andWhere(['<=', 'w.date', $range['end']]);
                 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(['>=', 's.date', $weekStart])
-                    ->andWhere(['<=', 's.date', $weekEnd]);
+                    ->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', $range['start']])
+                    ->andWhere(['<=', 's.date', $range['end']]);
                 if ($productFilter !== null) {
                     $query->andWhere(['sp.product_id' => $productFilter]);
                 }
@@ -994,7 +1004,7 @@ var_dump($totals); die();
             $rows = $query->all();
             foreach ($rows as $row) {
                 $result[] = [
-                    'week'        => $ind,
+                    'week'        => $row['week'],
                     'store_id'    => $row['store_id'],
                     'category'    => $row['category'],
                     'subcategory' => $row['subcategory'],
@@ -1008,16 +1018,18 @@ var_dump($totals); die();
     }
 
     /**
-     * Исторический недельный отчёт и доли по видам без вложенных циклов.
+     * Исторический недельный отчёт и доли по видам с учётом store_id.
      *
      * @param string      $monthYear     месяц-год в формате MM-YYYY
      * @param array|null  $filters
      * @param array|null  $productFilter
      * @param string      $type
-     * @return array{
-     *   'historicalWeekly' => array<int, array>,
-     *   'weeklyShare'      => array<int, array>
-     * }
+     * @return array{ 'weeksData': array<int, array> }
+     *   возвращает плоский список строк:
+     *   [
+     *     ['week'=>1, 'store_id'=>2, 'category'=>'...', 'subcategory'=>'...', 'species'=>'...', 'percent'=>0.32],
+     *     ...
+     *   ]
      */
     public function getHistoricalWeeklySpeciesShare(
         string $monthYear,
@@ -1025,82 +1037,161 @@ var_dump($totals); die();
         ?array $productFilter = null,
         string $type = 'sales'
     ): array {
-
         [$monthStr, $yearStr] = explode('-', $monthYear);
         $month = (int)$monthStr;
         $year  = (int)$yearStr;
 
         $historical = [];
-        for ($yr = 2020; $yr < $year; $yr++) {
+        for ($yr = $year-2; $yr < $year; $yr++) {
             $mYear = sprintf('%02d-%d', $month, $yr);
-            $weeklyData = $this->getWeeklySpeciesDataForMonth($mYear, $filters, $productFilter, $type);
+            $weeklyData = $this->getWeeklySpeciesDataForMonth(
+                $mYear, $filters, $productFilter, $type
+            );
             foreach ($weeklyData as $row) {
-                $idx  = $row['week'];
-                $historical[$idx] ??= [];
-                $cat  = $row['category'];
-                $sub  = $row['subcategory'];
-                $spec = $row['species'];
-
-                $historical[$idx][$cat][$sub][$spec] =
-                    ($historical[$idx][$cat][$sub][$spec] ?? 0) + $row['sum'];
+                $week     = $row['week'];
+                $sid      = $row['store_id'];
+                $cat      = $row['category'];
+                $sub      = $row['subcategory'];
+                $spec     = $row['species'];
+                $sumWeek  = $row['sum'];
+
+                $historical[$week]           ??= [];
+                $historical[$week][$sid]     ??= [];
+                $historical[$week][$sid][$cat]   ??= [];
+                $historical[$week][$sid][$cat][$sub] ??= [];
+                $historical[$week][$sid][$cat][$sub][$spec] =
+                    ($historical[$week][$sid][$cat][$sub][$spec] ?? 0) + $sumWeek;
             }
         }
 
-
         $dateFrom = sprintf('%04d-%02d-01 00:00:00', $year, $month);
-        $dateTo   = date('Y-m-d H:i:s', strtotime($dateFrom . ' +1 month -1 second'));
+        $dateTo   = date('Y-m-d H:i:s', strtotime("$dateFrom +1 month -1 second"));
         $monthWeighted = $this->getMonthSpeciesShareOrWriteOffWeighted(
             $dateFrom, $dateTo, $filters, $productFilter, $type
         );
-
         $monthMap = [];
         foreach ($monthWeighted as $m) {
-            $cat  = $m['category'];
-            $sub  = $m['subcategory'];
-            $spec = $m['species'];
-            $monthMap[$cat][$sub][$spec] = ($monthMap[$cat][$sub][$spec] ?? 0) + $m['total_sum'];
+            $sid      = $m['store_id'];
+            $cat      = $m['category'];
+            $sub      = $m['subcategory'];
+            $spec     = $m['species'];
+            $sumMonth = $m['total_sum'];
+
+            $monthMap[$sid]            ??= [];
+            $monthMap[$sid][$cat]      ??= [];
+            $monthMap[$sid][$cat][$sub]??= [];
+            $monthMap[$sid][$cat][$sub][$spec] =
+                ($monthMap[$sid][$cat][$sub][$spec] ?? 0) + $sumMonth;
         }
 
 
-        $flatRows = [];
-        foreach ($historical as $week => $byCat) {
+        $weeksList = array_keys($historical);
+        sort($weeksList, SORT_NUMERIC);
+
+        $speciesList = [];
+        foreach ($monthMap as $sid => $byCat) {
             foreach ($byCat as $cat => $bySub) {
                 foreach ($bySub as $sub => $bySpec) {
-                    foreach ($bySpec as $spec => $sumWeek) {
-                        $sumMonth = $monthMap[$cat][$sub][$spec] ?? 0;
-                        if ($sumMonth <= 0) {
-                            continue;
-                        }
-                        $flatRows[] = [
-                            'week'        => $week,
-                            'category'    => $cat,
-                            'subcategory' => $sub,
-                            'species'     => $spec,
-                            'percent'     => round($sumWeek / $sumMonth, 4),
-                        ];
+                    foreach ($bySpec as $spec => $_) {
+                        $speciesList[] = compact('sid','cat','sub','spec');
                     }
                 }
             }
         }
-        usort($flatRows, function(array $a, array $b): int {
-            $cmp = strcmp($a['category'], $b['category']);
-            if ($cmp !== 0) {
-                return $cmp;
+
+
+        $rows = [];
+        foreach ($speciesList as $comb) {
+            $sid  = $comb['sid'];
+            $cat  = $comb['cat'];
+            $sub  = $comb['sub'];
+            $spec = $comb['spec'];
+
+            $sumMonth = $monthMap[$sid][$cat][$sub][$spec] ?? 0;
+            if ($sumMonth <= 0) {
+                continue; // нет месячного итога
             }
-            $cmp = strcmp($a['subcategory'], $b['subcategory']);
-            if ($cmp !== 0) {
-                return $cmp;
+
+            foreach ($weeksList as $week) {
+                $sumWeek = $historical[$week][$sid][$cat][$sub][$spec] ?? 0;
+                $percent = $sumWeek > 0 ? round($sumWeek / $sumMonth, 4) : null;
+
+                $rows[] = [
+                    'week'        => $week,
+                    'store_id'    => $sid,
+                    'category'    => $cat,
+                    'subcategory' => $sub,
+                    'species'     => $spec,
+                    'percent'     => $percent,
+                ];
             }
-            $cmp = strcmp($a['species'], $b['species']);
-            if ($cmp !== 0) {
-                return $cmp;
+        }
+
+        return ['weeksData' => $rows];
+    }
+
+
+    /**
+     * Рассчитывает недельную цель для каждого вида (species) по данным недельных долей
+     * и целям месяца.
+     * @param array $weeksShareData
+     * @param array $monthSpeciesGoals
+     * @return array
+     *   Плоский массив строк с полями: week, store_id, category, subcategory,
+     *   species, percent, monthly_goal, weekly_goal
+     */
+    public function calculateWeeklySpeciesGoals(
+        array $weeksShareData,
+        array $monthSpeciesGoals
+    ): array {
+        $monthSpeciesGoalsMap = [];
+        foreach ($monthSpeciesGoals as $monthSpeciesGoal) {
+            $monthSpeciesGoalsMap[$monthSpeciesGoal['store_id']]
+            [$monthSpeciesGoal['category']]
+            [$monthSpeciesGoal['subcategory']]
+            [$monthSpeciesGoal['species']] = $monthSpeciesGoal['goal'] ;
+        }
+        $result = [];
+        foreach ($weeksShareData as $row) {
+            $week   = $row['week'];
+            $sid    = $row['store_id'];
+            $cat    = $row['category'];
+            $sub    = $row['subcategory'];
+            $spec   = $row['species'];
+            $percent = $row['percent'];
+
+            $monthlyGoal = $monthSpeciesGoalsMap[$sid][$cat][$sub][$spec] ?? null;
+
+            $weeklyGoal = 0;
+            if ($monthlyGoal !== null && $percent !== null) {
+                $weeklyGoal =  round($percent * $monthlyGoal, 4);
             }
-            return $a['week'] <=> $b['week'];
-        });
 
-        return [
-            'weeksData' => $flatRows,
-        ];
+            $result[] = [
+                'week'         => $week,
+                'store_id'     => $sid,
+                'category'     => $cat,
+                'subcategory'  => $sub,
+                'species'      => $spec,
+                'percent'      => $percent,
+                'monthly_goal' => $monthlyGoal,
+                'weekly_goal'  => $weeklyGoal,
+            ];
+        }
+        return $result;
+    }
+
+    /**
+     * Возвращает дату понедельника ISO-недели в формате YYYY-MM-DD
+     *
+     * @param int $year  ISO-год (может отличаться от календарного в границах года)
+     * @param int $week  номер ISO-недели (1–53)
+     * @return string    дата понедельника, например '2025-03-10'
+     */
+    public static function getIsoWeekStart(int $year, int $week): string
+    {
+        $iso = $year . 'W' . str_pad($week, 2, '0', STR_PAD_LEFT) . '1';
+        return date('Y-m-d', strtotime($iso));
     }
 
 
index b73ac84fa2476ffcd4f117585ecd35370c9aecc8..a92d97b244d41cc610959e91b80095d71cf6cc5a 100644 (file)
@@ -101,6 +101,8 @@ use yii_app\records\Products1c;
                 <th>Вид</th>
                 <th>Неделя</th>
                 <th>Доля (%)</th>
+                <th>Цель месяца</th>
+                <th>Цель недели</th>
             </tr>
             </thead>
             <tbody>
@@ -109,8 +111,16 @@ use yii_app\records\Products1c;
                     <td><?= Html::encode($r['category']) ?></td>
                     <td><?= Html::encode($r['subcategory']) ?></td>
                     <td><?= Html::encode($r['species']) ?></td>
-                    <td><?= $r['week'] ?></td>
-                    <td><?= Yii::$app->formatter->asPercent($r['percent'], 1) ?></td>
+                    <td><?= $r['week'] ?>  неделя - начало <?= \yii_app\services\AutoPlannogrammaService::getIsoWeekStart((int)date('Y'), (int)$r['week']) ?></td>
+                    <td>
+                        <?php if ($r['percent'] === null): ?>
+                            &mdash;
+                        <?php else: ?>
+                            <?= Yii::$app->formatter->asPercent($r['percent'], 1) ?>
+                        <?php endif; ?>
+                    </td>
+                    <td><?= $r['monthly_goal'] ?> </td>
+                    <td><?= $r['weekly_goal'] ?>  </td>
                 </tr>
             <?php endforeach; ?>
             </tbody>