return $grouped;
}
- public function getWeeklyProductsWriteoffsForecast($month, $year, $storeId = null, $weekNumber = null)
+ public function getWeeklyProductsWriteoffsForecast($month, $year, $weeklySalesForecast, $storeId = null, $weekNumber = null)
{
$weeksProductForecast = [];
$filters = [];
[$monthSpeciesGoal['subcategory']]
[$monthSpeciesGoal['species']] = $monthSpeciesGoal['goal'];
}
+ $productsWriteoffsShare = $this->calculateWriteOffShareByMonth($weeklySalesForecast, $month, $year);
+ $productsWriteoffsShareMap = [];
+ foreach ($productsWriteoffsShare as $productWriteoffs) {
+ $productsWriteoffsShareMap[$productWriteoffs['store_id']][$productWriteoffs['product_id']] = $productWriteoffs['share'];
+ }
$weeksShareResult = $this->getHistoricalWeeklySpeciesShare($monthYear, $filters, null, 'writeOffs');
$weeksData = $this->calculateWeeklySpeciesGoals($weeksShareResult, $monthSpeciesGoals);
$forecasts = $this->calculateWeekForecastSpeciesProducts($r['category'], $r['subcategory'], $r['species'], $r['store_id'], $r['weekly_goal']);
foreach ($forecasts as $forecast) {
+ $productWriteoffsForecastSpeciesShare = $productsWriteoffsShareMap[$r['store_id']][$forecast['product_id']] ?? 0;
+ $productWriteoffsForecast = $productWriteoffsForecastSpeciesShare * $forecast['forecast'];
$weeksProductForecast[] = [
'category' => $forecast['category'] ?? '',
'subcategory' => $forecast['subcategory'] ?? '',
'name' => $forecast['name'] ?? '',
'price' => $forecast['price'] ?? '',
'goal' => $forecast['goal'] ?? 0,
- 'forecast' => $forecast['forecast'] ?? 0,
+ 'forecast' => $productWriteoffsForecast ?? 0,
'week' => $r['week'],
];
}
return $weeksProductForecast;
}
+ /**
+ * Рассчитывает долю списания (write-offs) каждого товара за заданный месяц
+ * на основании исторических данных за два предыдущих года.
+ *
+ * @param array $productList Список товаров:
+ * [
+ * [
+ * '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, // доля
+ * ], …
+ * ]
+ */
+ 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'));
+ if (empty($storeIds)) {
+ return [];
+ }
+
+ $productQtyMap = []; // [store][cat][sub][spec][pid] => qty
+ $speciesQtyMap = []; // [store][cat][sub][spec] => total qty
+
+ $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"));
+
+ $productsWriteOffs = (new Query())
+ ->select([
+ 'store_id' => 'ex.entity_id',
+ 'category' => 'p1c.category',
+ 'subcategory' => 'p1c.subcategory',
+ 'species' => 'p1c.species',
+ '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')
+
+ ->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'])
+ ->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'];
+
+
+ $productQtyMap[$sid][$cat][$sub][$spec][$pid] =
+ ($productQtyMap[$sid][$cat][$sub][$spec][$pid] ?? 0.0) + $qty;
+
+ $speciesQtyMap[$sid][$cat][$sub][$spec] =
+ ($speciesQtyMap[$sid][$cat][$sub][$spec] ?? 0.0) + $qty;
+ }
+ }
+
+ $seen = [];
+ $result = [];
+ foreach ($productList as $item) {
+ $pid = $item['product_id'];
+ $sid = $item['store_id'];
+ $cat = $item['category'];
+ $sub = $item['subcategory'];
+ $spec = $item['species'];
+
+ $key = "{$sid}|{$cat}|{$sub}|{$spec}|{$pid}";
+ if (isset($seen[$key])) {
+ continue;
+ }
+ $seen[$key] = true;
+
+ $productsQty = $productQtyMap[$sid][$cat][$sub][$spec][$pid] ?? 0.0;
+ $speciesQty = $speciesQtyMap[$sid][$cat][$sub][$spec] ?? 0.0;
+ $share = $speciesQty > 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,
+ ];
+ }
+
+ return $result;
+ }
+
+
}
\ No newline at end of file