From d0bd038f3a422aa7e195a0de44f1ca9e1d4d1a59 Mon Sep 17 00:00:00 2001 From: Vladimir Fomichev Date: Wed, 18 Jun 2025 17:18:55 +0300 Subject: [PATCH] =?utf8?q?=D0=A0=D0=B0=D1=81=D1=87=D0=B5=D1=82=20=D0=B7?= =?utf8?q?=D0=B0=203=20=D0=BC=D0=B5=D1=81=D1=8F=D1=86=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/services/AutoPlannogrammaService.php | 163 ++++++++++++--------- 1 file changed, 91 insertions(+), 72 deletions(-) diff --git a/erp24/services/AutoPlannogrammaService.php b/erp24/services/AutoPlannogrammaService.php index 86a1bc2e..bcb43f2e 100644 --- a/erp24/services/AutoPlannogrammaService.php +++ b/erp24/services/AutoPlannogrammaService.php @@ -3058,11 +3058,11 @@ class AutoPlannogrammaService $grouped = []; foreach ($weeksProductForecast as $item) { - $productId = $item['product_guid']; + $productId = $item['product_id']; $week = $item['week']; $type = 'writeOffs'; $forecastValue = $item['forecast']; - $grouped[$productId][$week][$type] = $forecastValue; + $grouped[$productId][$week][$type] = ceil($forecastValue); } @@ -3070,59 +3070,55 @@ class AutoPlannogrammaService } /** - * Рассчитывает долю списания (write-offs) каждого товара за заданный месяц - * на основании исторических данных за два предыдущих года. + * Рассчитывает долю списания товаров за целевой месяц, + * используя взвешенные данные за три предыдущих месяца. * - * @param array $productList Список товаров: + * @param array $productList Список товаров с ключами: + * ['store_id', 'category', 'subcategory', 'species', 'product_id', …] + * @param int $month Целевой месяц (1–12) + * @param int $year Целевой год (например, 2025) + * @return array [ * [ - * [ - * 'week' => int, - * 'store_id' => int, - * 'category' => string, - * 'subcategory' => string, - * 'species' => string, - * 'product_id' => string, - * 'forecast_month_pieces' => float, - * 'forecast_week_pieces' => float, - * ], … - * ] - * @param string $monthYear Формат 'YYYY-MM' - * @return array [ - * [ - * 'store_id' => int, - * 'month' => int, - * 'year' => int, - * 'category' => string, - * 'subcategory'=> string, - * 'species' => string, - * 'product_id' => string, - * 'writeoff_qty' => float, - * 'species_qty' => float, - * 'share' => float, // доля - * ], … - * ] + * 'store_id', 'month', 'year', + * 'category','subcategory','species', + * 'product_id','writeoff_qty','species_qty','share' + * ], + * … + * ] */ - public function calculateWriteOffShareByMonth(array $productList, int $month, int $year ): array - { - $year = $year ?? (int)date("Y"); - $month = $month ?? (int)date("m", strtotime( "+ 2 month" . date('Y-m-01'))); - - $storeIds = array_unique(array_column($productList, 'store_id')); + public function calculateWriteOffShareByMonth( + array $productList, + int $month, + int $year + ): array { + $targetDate = strtotime(sprintf('%04d-%02d-01', $year, $month)); + $storeIds = array_unique(array_column($productList, 'store_id')); if (empty($storeIds)) { return []; } - $productQtyMap = []; // [store][cat][sub][spec][pid] => qty - $speciesQtyMap = []; // [store][cat][sub][spec] => total qty + $periods = StorePlanService::getPeriods($targetDate, 3, false, true); + + $productQtyMap = []; // [store][cat][sub][spec][pid] => weighted qty + $speciesQtyMap = []; // [store][cat][sub][spec] => weighted total + + $storeJoin = 'ex.export_val = w.store_id'; - $storeJoinCondition = 'ex.export_val = w.store_id'; - for ($offset = 2; $offset >= 1; $offset--) { - $histYear = $year - $offset; - $start = sprintf('%04d-%02d-01 00:00:00', $histYear, $month); - $end = date('Y-m-d 23:59:59', strtotime("$start +1 month -1 second")); + foreach ($periods as $period) { + $histYear = (int)$period['year']; + $histMonth = (int)$period['month']; + $weight = (float)$period['weight']; + + + $start = sprintf('%04d-%02d-01 00:00:00', $histYear, $histMonth); + $end = date( + 'Y-m-d 23:59:59', + strtotime("$start +1 month -1 second") + ); + - $productsWriteOffs = (new Query()) + $productsWriteOffs = (new \yii\db\Query()) ->select([ 'store_id' => 'ex.entity_id', 'category' => 'p1c.category', @@ -3131,46 +3127,68 @@ class AutoPlannogrammaService 'product_id' => 'wp.product_id', 'qty' => new \yii\db\Expression('SUM(wp.quantity)'), ]) - ->from(['w' => 'write_offs']) - ->innerJoin(['wp' => 'write_offs_products'], 'wp.write_offs_id = w.id') - ->leftJoin('export_import_table ex', $storeJoinCondition) - ->leftJoin('products_1c_nomenclature p1c', 'p1c.id = wp.product_id') - ->leftJoin('products_1c p1', 'p1.id = wp.product_id') - + ->from(['w' => 'write_offs']) + ->innerJoin( + ['wp' => 'write_offs_products'], + 'wp.write_offs_id = w.id' + ) + ->leftJoin( + 'export_import_table ex', + $storeJoin + ) + ->leftJoin( + 'products_1c_nomenclature p1c', + 'p1c.id = wp.product_id' + ) + ->leftJoin( + 'products_1c p1', + 'p1.id = wp.product_id' + ) ->andWhere(['ex.entity_id' => $storeIds]) ->andWhere(['>=', 'w.date', $start]) ->andWhere(['<=', 'w.date', $end]) - ->andWhere(['p1.components' => '']) - ->andWhere(['not in', 'p1c.category', ['', 'букет', 'сборка', 'сервис']]) - ->groupBy(['ex.entity_id','p1c.category','p1c.subcategory','p1c.species','wp.product_id']) + ->andWhere([ + 'not in', + 'p1c.category', + ['', 'букет', 'сборка', 'сервис'] + ]) + ->groupBy([ + 'ex.entity_id', + 'p1c.category', + 'p1c.subcategory', + 'p1c.species', + 'wp.product_id', + ]) ->all(); + foreach ($productsWriteOffs as $productsWriteOffsItem) { $sid = (int)$productsWriteOffsItem['store_id']; $cat = $productsWriteOffsItem['category']; $sub = $productsWriteOffsItem['subcategory']; $spec = $productsWriteOffsItem['species']; $pid = $productsWriteOffsItem['product_id']; - $qty = (float)$productsWriteOffsItem['qty']; - + $qty = (float)$productsWriteOffsItem['qty'] * $weight; $productQtyMap[$sid][$cat][$sub][$spec][$pid] = - ($productQtyMap[$sid][$cat][$sub][$spec][$pid] ?? 0.0) + $qty; + ($productQtyMap[$sid][$cat][$sub][$spec][$pid] ?? 0.0) + + $qty; $speciesQtyMap[$sid][$cat][$sub][$spec] = - ($speciesQtyMap[$sid][$cat][$sub][$spec] ?? 0.0) + $qty; + ($speciesQtyMap[$sid][$cat][$sub][$spec] ?? 0.0) + + $qty; } } - $seen = []; + $seen = []; $result = []; foreach ($productList as $item) { - $pid = $item['product_id']; $sid = $item['store_id']; $cat = $item['category']; $sub = $item['subcategory']; $spec = $item['species']; + $pid = $item['product_id']; $key = "{$sid}|{$cat}|{$sub}|{$spec}|{$pid}"; if (isset($seen[$key])) { @@ -3179,26 +3197,27 @@ class AutoPlannogrammaService $seen[$key] = true; $productsQty = $productQtyMap[$sid][$cat][$sub][$spec][$pid] ?? 0.0; - $speciesQty = $speciesQtyMap[$sid][$cat][$sub][$spec] ?? 0.0; - $share = $speciesQty > 0 + $speciesQty = $speciesQtyMap[$sid][$cat][$sub][$spec] ?? 0.0; + $share = $speciesQty > 0.0 ? round($productsQty / $speciesQty, 4) : 0.0; $result[] = [ - 'store_id' => $sid, - 'month' => $month, - 'year' => $year, - 'category' => $cat, - 'subcategory' => $sub, - 'species' => $spec, - 'product_id' => $pid, - 'writeoff_qty' => $productsQty, - 'species_qty' => $speciesQty, - 'share' => $share, + 'store_id' => $sid, + 'month' => $month, + 'year' => $year, + 'category' => $cat, + 'subcategory' => $sub, + 'species' => $spec, + 'product_id' => $pid, + 'writeoff_qty' => round($productsQty, 4), + 'species_qty' => round($speciesQty, 4), + 'share' => $share, ]; } return $result; } + } \ No newline at end of file -- 2.39.5