'percent' => new Expression('ROUND(CAST(main.total_sum AS DECIMAL) / NULLIF(totals.total, 0), 4)'),
'type' => new Expression(':type', ['type' => $type]),
'totals_total' => 'totals.total',
- 'products_list'=> 'main.products_list',
+ 'products_list' => 'main.products_list',
])
->from([
'main' => (new Query())
$type
);
- if(!$items) {
+ if (!$items) {
continue;
}
foreach ($items as $it) {
'new_total_store' => $newDenominator,
'base_total_store' => $baseTotal,
'products_list' => $r['products_list'],
- 'products_components_list' => implode(',',$allComponentsProductIds[$sid] ?? []),
+ 'products_components_list' => implode(',', $allComponentsProductIds[$sid] ?? []),
];
}
$productTableJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'wp.write_offs_id = w.id' : 'sp.check_id = s.id';
$field = $type === self::TYPE_WRITE_OFFS ? 'w.date' : 's.date';
- $base = new \DateTime($dateFrom);
+ $base = new \DateTime($dateFrom);
$months = [
[
(clone $base)->modify('-2 year'),
(clone $base)->modify('-1 year'),
];
- $componentAdds = [];
- $componentAddsSumAll = [];
+ $componentAdds = [];
+ $componentAddsSumAll = [];
$allComponentsProdIds = [];
foreach ($storeIds as $sid) {
$allComponentsProdIds[$sid] = array_unique($allComponentsProdIds[$sid]);
foreach ($sums as $sr) {
- $cat = $sr['category'];
+ $cat = $sr['category'];
$subcat = $sr['subcategory'];
- $val = $sr['sum'];
+ $val = $sr['sum'];
$componentAdds[$sid][$cat][$subcat] =
($componentAdds[$sid][$cat][$subcat] ?? 0) + $val;
$result = [];
foreach ($rows as $r) {
- $sid = $r['store_id'];
- $cat = $r['category'];
- $subcat = $r['subcategory'];
+ $sid = $r['store_id'];
+ $cat = $r['category'];
+ $subcat = $r['subcategory'];
- $subTotal = (float)$r['total_sum'];
- $catTotal = (float)$r['totals_total'];
+ $subTotal = (float)$r['total_sum'];
+ $catTotal = (float)$r['totals_total'];
- $addCat = $componentAddsSumAll[$sid][$cat] ?? 0.0;
- $addSubcat = $componentAdds[$sid][$cat][$subcat] ?? 0.0;
+ $addCat = $componentAddsSumAll[$sid][$cat] ?? 0.0;
+ $addSubcat = $componentAdds[$sid][$cat][$subcat] ?? 0.0;
$newCatTotal = $catTotal + $addCat;
: 0.0;
$result[] = [
- 'store_id' => $sid,
- 'category' => $cat,
- 'subcategory' => $subcat,
- 'old_total_sum' => $subTotal,
- 'total_sum' => $newSubTotal,
- 'percent' => $percent,
- 'type' => $type,
- 'new_total_store' => $newCatTotal,
- 'base_total_store' => $catTotal,
+ 'store_id' => $sid,
+ 'category' => $cat,
+ 'subcategory' => $subcat,
+ 'old_total_sum' => $subTotal,
+ 'total_sum' => $newSubTotal,
+ 'percent' => $percent,
+ 'type' => $type,
+ 'new_total_store' => $newCatTotal,
+ 'base_total_store' => $catTotal,
'products_components_list' => implode(',', $allComponentsProdIds[$sid] ?? []),
];
}
$productTableJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'wp.write_offs_id = w.id' : 'sp.check_id = s.id';
$field = $type === self::TYPE_WRITE_OFFS ? 'w.date' : 's.date';
- $base = new \DateTime($dateFrom);
+ $base = new \DateTime($dateFrom);
$months = [
[
'between',
(clone $base)->modify('-1 year'),
];
- $finalResult = [];
- $componentAdds = [];
- $componentAddsSumAll = [];
+ $finalResult = [];
+ $componentAdds = [];
+ $componentAddsSumAll = [];
$allComponentsProdIds = [];
foreach ($storeIds as $sid) {
$allComponentsProdIds[$sid] = array_unique($allComponentsProdIds[$sid]);
foreach ($sums as $sr) {
- $cat = $sr['category'];
+ $cat = $sr['category'];
$subcat = $sr['subcategory'];
$spec = $sr['species'];
- $val = $sr['sum'];
+ $val = $sr['sum'];
$componentAdds[$sid][$cat][$subcat][$spec] =
($componentAdds[$sid][$cat][$subcat][$spec] ?? 0) + $val;
foreach ($result as $r) {
- $sid = $r['store_id'];
- $cat = $r['category'];
- $subcat = $r['subcategory'];
- $spec = $r['species'];
+ $sid = $r['store_id'];
+ $cat = $r['category'];
+ $subcat = $r['subcategory'];
+ $spec = $r['species'];
- $specTotal = (float)$r['total_sum'];
- $subcatTotal = (float)$r['totals_total'];
+ $specTotal = (float)$r['total_sum'];
+ $subcatTotal = (float)$r['totals_total'];
- $addSubcat = $componentAddsSumAll[$sid][$cat][$subcat] ?? 0.0;
- $addSpecies = $componentAdds[$sid][$cat][$subcat][$spec] ?? 0.0;
+ $addSubcat = $componentAddsSumAll[$sid][$cat][$subcat] ?? 0.0;
+ $addSpecies = $componentAdds[$sid][$cat][$subcat][$spec] ?? 0.0;
$newSubcatTotal = $subcatTotal + $addSubcat;
: 0.0;
$finalResult[] = [
- 'store_id' => $sid,
- 'category' => $cat,
- 'subcategory' => $subcat,
- 'species' => $spec ,
- 'old_total_sum' => $specTotal,
- 'total_sum' => $newSpecTotal,
- 'percent' => $percent,
- 'type' => $type,
- 'new_total_store' => $newSubcatTotal,
- 'base_total_store' => $subcatTotal,
+ 'store_id' => $sid,
+ 'category' => $cat,
+ 'subcategory' => $subcat,
+ 'species' => $spec,
+ 'old_total_sum' => $specTotal,
+ 'total_sum' => $newSpecTotal,
+ 'percent' => $percent,
+ 'type' => $type,
+ 'new_total_store' => $newSubcatTotal,
+ 'base_total_store' => $subcatTotal,
'products_components_list' => implode(',', $allComponentsProdIds[$sid] ?? []),
];
}
/**
* Получает суммы продаж или списаний по видам (species) для каждой недели указанного месяца.
*
- * @param string $monthYear месяц-год в формате MM-YYYY, например '03-2025'
- * @param array|null $filters опциональные фильтры ['store_id'=>...]
- * @param array|null $productFilter опциональный фильтр по product_id
- * @param string $type 'sales' или 'writeOffs'
+ * @param string $monthYear месяц-год в формате MM-YYYY, например '03-2025'
+ * @param array|null $filters опциональные фильтры ['store_id'=>...]
+ * @param array|null $productFilter опциональный фильтр по product_id
+ * @param string $type 'sales' или 'writeOffs'
* @return array<int, array> массив строк: [
* ['week'=>1,'store_id'=>...,'category'=>...,'subcategory'=>...,'species'=>...,'sum'=>...],
* ...
?array $filters = null,
?array $productFilter = null,
string $type = 'sales'
- ): array {
- [$yearStr, $monthStr ] = explode('-', $monthYear);
+ ): array
+ {
+ [$yearStr, $monthStr] = explode('-', $monthYear);
$month = (int)$monthStr;
- $year = (int)$yearStr;
+ $year = (int)$yearStr;
$dateFrom = strtotime(sprintf('%04d-%02d-01 00:00:00', $year, $month));
- $dateTo = strtotime('+1 month -1 second', $dateFrom);
+ $dateTo = strtotime('+1 month -1 second', $dateFrom);
- $stores = $this->getVisibleStores();
- $storeIds = array_map(fn($s)=>$s->id, $stores);
+ $stores = $this->getVisibleStores();
+ $storeIds = array_map(fn($s) => $s->id, $stores);
if (!empty($filters['store_id'])) {
$storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
}
$wkEnd = $dateTo;
}
$periodStart = max($wkStart, $dateFrom);
- $periodEnd = min($wkEnd, $dateTo);
- $daysInMonth = floor(($periodEnd - $periodStart) / 86400) + 1;
+ $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 23:59:59', $wkEnd),
+ 'end' => date('Y-m-d 23:59:59', $wkEnd),
];
}
}
foreach ($weekRanges as $range) {
$exprWeek = new Expression((string)$range['index']);
$query = (new Query())->select([
- 'week' => $exprWeek,
- 'store_id' => 'ex.entity_id',
- 'category' => 'p1c.category',
+ 'week' => $exprWeek,
+ 'store_id' => 'ex.entity_id',
+ 'category' => 'p1c.category',
'subcategory' => 'p1c.subcategory',
- 'species' => 'p1c.species',
- 'total_sum' => new Expression(
+ 'species' => 'p1c.species',
+ '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')
+ $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) {
}
} 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')
+ ->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(['ex.entity_id' => $storeIds])
->andWhere(['<>', 'p1c.species', ''])
- ->groupBy(['week','ex.entity_id','p1c.category','p1c.subcategory','p1c.species']);
+ ->groupBy(['week', 'ex.entity_id', 'p1c.category', 'p1c.subcategory', 'p1c.species']);
$rows = $query->all();
foreach ($rows as $row) {
$result[] = [
- 'week' => $row['week'],
- 'store_id' => $row['store_id'],
- 'category' => $row['category'],
+ 'week' => $row['week'],
+ 'store_id' => $row['store_id'],
+ 'category' => $row['category'],
'subcategory' => $row['subcategory'],
- 'species' => $row['species'],
- 'sum' => (float)$row['total_sum'],
+ 'species' => $row['species'],
+ 'sum' => (float)$row['total_sum'],
];
}
}
* Возвращает диапазоны недель (index, start, end) для указанного года и месяца,
* взятые по правилу «неделя считается, если в неё входит ≥4 дня из этого месяца».
*
- * @param int $year Год (например, 2025)
+ * @param int $year Год (например, 2025)
* @param int $month Месяц (1–12)
* @return array<array{index:int, start:string, end:string}>
*/
public function getWeekRangesForMonth(int $year, int $month): array
{
$dateFrom = strtotime(sprintf('%04d-%02d-01 00:00:00', $year, $month));
- $dateTo = strtotime('+1 month -1 second', $dateFrom);
+ $dateTo = strtotime('+1 month -1 second', $dateFrom);
- $dayOfWeek = (int)date('N', $dateFrom);
+ $dayOfWeek = (int)date('N', $dateFrom);
$firstMonday = $dayOfWeek === 1
? $dateFrom
: strtotime('next monday', $dateFrom);
$wkEnd = $wkStart + 6 * 86400;
$periodStart = max($wkStart, $dateFrom);
- $periodEnd = min($wkEnd, $dateTo);
+ $periodEnd = min($wkEnd, $dateTo);
$daysInMonth = floor(($periodEnd - $periodStart) / 86400) + 1;
if ($daysInMonth >= 4) {
$ranges[] = [
'index' => (int)date('W', $wkStart), // ISO-неделя от $wkStart
'start' => date('Y-m-d H:i:s', $wkStart), // “год-месяц-день 00:00:00”
- 'end' => date('Y-m-d 23:59:59', $wkEnd), // “год-месяц-день 23:59:59”
+ 'end' => date('Y-m-d 23:59:59', $wkEnd), // “год-месяц-день 23:59:59”
];
}
}
}
public function getHistoricalSpeciesShareByWeek(
- string $monthYear,
- ?array $filters = null,
- string $type = self::TYPE_SALES
- ): array {
+ string $monthYear,
+ ?array $filters = null,
+ string $type = self::TYPE_SALES
+ ): array
+ {
[$yearStr, $monthStr, $day] = explode('-', $monthYear);
- $year = (int)$yearStr;
+ $year = (int)$yearStr;
$month = (int)$monthStr;
- $stores = $this->getVisibleStores();
+ $stores = $this->getVisibleStores();
$storeIds = array_map(fn($s) => $s->id, $stores);
if (!empty($filters['store_id'])) {
$storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
: 'SUM(sp.quantity)';
// Таблицы и условия join
- $fromTable = $type === self::TYPE_WRITE_OFFS ? ['w' => 'write_offs'] : ['s' => 'sales'];
- $alias = key($fromTable);
+ $fromTable = $type === self::TYPE_WRITE_OFFS ? ['w' => 'write_offs'] : ['s' => 'sales'];
+ $alias = key($fromTable);
- $productTableJoin = $type === self::TYPE_WRITE_OFFS ? ['wp' => 'write_offs_products'] : ['sp' => 'sales_products'];
- $productAlias = key($productTableJoin); // 'wp' или 'sp'
- $productTableJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'wp.write_offs_id = w.id' : 'sp.check_id = s.id';
- $storeJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'ex.export_val = w.store_id' : 'ex.export_val = s.store_id_1c';
+ $productTableJoin = $type === self::TYPE_WRITE_OFFS ? ['wp' => 'write_offs_products'] : ['sp' => 'sales_products'];
+ $productAlias = key($productTableJoin); // 'wp' или 'sp'
+ $productTableJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'wp.write_offs_id = w.id' : 'sp.check_id = s.id';
+ $storeJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'ex.export_val = w.store_id' : 'ex.export_val = s.store_id_1c';
$monthQtyBySpecies = [];
- $weekQtyByPos = [];
+ $weekQtyByPos = [];
foreach ([$year - 2, $year - 1] as $histYear) {
$histMonthStart = sprintf('%04d-%02d-01 00:00:00', $histYear, $month);
- $histMonthEnd = date('Y-m-d 23:59:59', strtotime("$histMonthStart +1 month -1 second"));
+ $histMonthEnd = date('Y-m-d 23:59:59', strtotime("$histMonthStart +1 month -1 second"));
$monthQuery = (new Query())
->select([
- 'store_id' => 'ex.entity_id',
- 'category' => 'p1c.category',
+ 'store_id' => 'ex.entity_id',
+ 'category' => 'p1c.category',
'subcategory' => 'p1c.subcategory',
- 'species' => 'p1c.species',
- 'month_sum' => new Expression($sumExpression),
+ 'species' => 'p1c.species',
+ 'month_sum' => new Expression($sumExpression),
])
->from($fromTable)
->leftJoin($productTableJoin, $productTableJoinCondition)
$monthRows = $monthQuery->all();
foreach ($monthRows as $row) {
- $sid = $row['store_id'];
- $cat = $row['category'];
- $sub = $row['subcategory'];
- $spec = $row['species'];
- $qty = (float)$row['month_sum'];
+ $sid = $row['store_id'];
+ $cat = $row['category'];
+ $sub = $row['subcategory'];
+ $spec = $row['species'];
+ $qty = (float)$row['month_sum'];
$monthQtyBySpecies[$sid][$cat][$sub][$spec] =
($monthQtyBySpecies[$sid][$cat][$sub][$spec] ?? 0.0) + $qty;
$weekQuery = (new Query())
->select([
- 'store_id' => 'ex.entity_id',
- 'category' => 'p1c.category',
+ 'store_id' => 'ex.entity_id',
+ 'category' => 'p1c.category',
'subcategory' => 'p1c.subcategory',
- 'species' => 'p1c.species',
- 'week_sum' => new Expression($sumExpression),
+ 'species' => 'p1c.species',
+ 'week_sum' => new Expression($sumExpression),
])
->from($fromTable)
->leftJoin($productTableJoin, $productTableJoinCondition)
"{$alias}.date BETWEEN :wstart AND :wend",
[
':wstart' => $startDt->format('Y-m-d H:i:s'),
- ':wend' => $endDt->format('Y-m-d H:i:s'),
+ ':wend' => $endDt->format('Y-m-d H:i:s'),
]
))
->groupBy(['ex.entity_id', 'p1c.category', 'p1c.subcategory', 'p1c.species']);
$weekRows = $weekQuery->all();
foreach ($weekRows as $row) {
- $sid = $row['store_id'];
- $cat = $row['category'];
- $sub = $row['subcategory'];
- $spec = $row['species'];
- $qty = (float)$row['week_sum'];
+ $sid = $row['store_id'];
+ $cat = $row['category'];
+ $sub = $row['subcategory'];
+ $spec = $row['species'];
+ $qty = (float)$row['week_sum'];
if (!isset($weekQtyByPos[$weekPos])) {
$weekQtyByPos[$weekPos] = [];
$result = [];
foreach ($targetRanges as $posIndex => $range) {
- $weekPos = $posIndex + 1;
+ $weekPos = $posIndex + 1;
$isoWeekNumber = $range['index'];
if (!isset($shareByPos[$weekPos])) {
foreach ($bySub as $sub => $bySpec) {
foreach ($bySpec as $spec => $share) {
$result[] = [
- 'store_id' => $sid,
- 'category' => $cat,
+ 'store_id' => $sid,
+ 'category' => $cat,
'subcategory' => $sub,
- 'species' => $spec,
- 'week' => $isoWeekNumber,
- 'share' => $share,
+ 'species' => $spec,
+ 'week' => $isoWeekNumber,
+ 'share' => $share,
'sumMonth' => $monthQtyBySpecies[$sid][$cat][$sub][$spec] ?? 0.0,
'sumWeek' => $weekQtyByPos[$weekPos][$sid][$cat][$sub][$spec] ?? 0.0
];
public function calculateWeeklyProductForecastPieces(
array $productForecastSpecies,
array $weeklySales
- ): array {
+ ): array
+ {
$forecastMap = [];
foreach ($productForecastSpecies as $item) {
- $sid = $item['store_id'];
- $cat = $item['category'];
- $sub = $item['subcategory'];
- $spec = $item['species'];
- $pid = $item['product_id'];
+ $sid = $item['store_id'];
+ $cat = $item['category'];
+ $sub = $item['subcategory'];
+ $spec = $item['species'];
+ $pid = $item['product_id'];
$piecesMon = (float)$item['product_sales_pieces'];
$forecastMap[$sid][$cat][$sub][$spec][$pid] = $piecesMon;
$result = [];
foreach ($weeklySales as $w) {
- $sid = $w['store_id'];
- $cat = $w['category'];
- $sub = $w['subcategory'];
- $spec = $w['species'];
- $week = $w['week'];
+ $sid = $w['store_id'];
+ $cat = $w['category'];
+ $sub = $w['subcategory'];
+ $spec = $w['species'];
+ $week = $w['week'];
$wShare = (float)$w['share'];
if (
- ! isset(
+ !isset(
$forecastMap[$sid],
$forecastMap[$sid][$cat],
$forecastMap[$sid][$cat][$sub],
$forecastWeekPieces = round($piecesMon * $wShare, 2);
$result[] = [
- 'week' => $week,
- 'store_id' => $sid,
- 'category' => $cat,
- 'subcategory' => $sub,
- 'species' => $spec,
- 'product_id' => $pid,
+ 'week' => $week,
+ 'store_id' => $sid,
+ 'category' => $cat,
+ 'subcategory' => $sub,
+ 'species' => $spec,
+ 'product_id' => $pid,
'forecast_month_pieces' => $piecesMon,
'forecast_week_pieces' => $forecastWeekPieces,
];
if (!isset($grouped[$key])) {
$grouped[$key] = [
- 'store_id' => $row['store_id'],
- 'category' => $row['category'],
- 'subcategory' => $row['subcategory'],
- 'species' => $row['species'],
- 'product_id' => $row['product_id'],
- 'forecast_month_pieces' => $row['forecast_month_pieces'],
+ 'store_id' => $row['store_id'],
+ 'category' => $row['category'],
+ 'subcategory' => $row['subcategory'],
+ 'species' => $row['species'],
+ 'product_id' => $row['product_id'],
+ 'forecast_month_pieces' => $row['forecast_month_pieces'],
];
}
- $weekNum = (int)$row['week'];
+ $weekNum = (int)$row['week'];
$fieldName = 'week' . $weekNum;
$grouped[$key][$fieldName] = (float)$row['forecast_week_pieces'];
}
$allWeekFields = array_keys($allWeekFields);
- foreach ($grouped as &$group) {
+ foreach ($grouped as &$group) {
foreach ($allWeekFields as $fieldName) {
- if (! isset($group[$fieldName])) {
+ if (!isset($group[$fieldName])) {
$group[$fieldName] = 0.0;
}
}
/**
* Исторический недельный отчёт и доли по видам с учётом store_id.
*
- * @param string $monthYear месяц-год в формате MM-YYYY
- * @param array|null $filters
- * @param array|null $productFilter
- * @param string $type
+ * @param string $monthYear месяц-год в формате MM-YYYY
+ * @param array|null $filters
+ * @param array|null $productFilter
+ * @param string $type
* @return array{ 'weeksData': array<int, array> }
* возвращает плоский список строк:
* [
?array $filters = null,
?array $productFilter = null,
string $type = 'sales'
- ): array {
+ ): array
+ {
[$monthStr, $yearStr] = explode('-', $monthYear);
$month = (int)$monthStr;
- $year = (int)$yearStr;
+ $year = (int)$yearStr;
$yearData = [];
$historical = [];
for ($yr = $year - 2; $yr < $year; $yr++) {
- $mYear = sprintf('%04d-%02d',$yr, $month);
+ $mYear = sprintf('%04d-%02d', $yr, $month);
$weeklyData = $this->getWeeklySpeciesDataForMonth(
$mYear, $filters, $productFilter, $type
);
$yearData[$mYear] = $weeklyData;
foreach ($weeklyData as $row) {
- $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] ??= [];
+ $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->getMonthSpeciesShareOrWriteOff(
$dateFrom, $filters, $type
);
$monthMap = [];
foreach ($monthWeighted as $m) {
- $sid = $m['store_id'];
- $cat = $m['category'];
- $sub = $m['subcategory'];
- $spec = $m['species'];
+ $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] ??= [];
+ $monthMap[$sid][$cat] ??= [];
+ $monthMap[$sid][$cat][$sub] ??= [];
$monthMap[$sid][$cat][$sub][$spec] =
($monthMap[$sid][$cat][$sub][$spec] ?? 0) + $sumMonth;
}
foreach ($byCat as $cat => $bySub) {
foreach ($bySub as $sub => $bySpec) {
foreach ($bySpec as $spec => $value) {
- $speciesList[] = compact('sid','cat','sub','spec');
+ $speciesList[] = compact('sid', 'cat', 'sub', 'spec');
}
}
}
$rows = [];
foreach ($speciesList as $comb) {
- $sid = $comb['sid'];
- $cat = $comb['cat'];
- $sub = $comb['sub'];
+ $sid = $comb['sid'];
+ $cat = $comb['cat'];
+ $sub = $comb['sub'];
$spec = $comb['spec'];
$sumMonth = $monthMap[$sid][$cat][$sub][$spec] ?? 0;
$percent = $sumWeek > 0 ? round($sumWeek / $sumMonth, 4) : null;
$rows[] = [
- 'week' => $week,
- 'store_id' => $sid,
- 'category' => $cat,
+ 'week' => $week,
+ 'store_id' => $sid,
+ 'category' => $cat,
'subcategory' => $sub,
- 'species' => $spec,
- 'sumWeek' => $sumWeek,
- 'percent' => $percent,
+ 'species' => $spec,
+ 'sumWeek' => $sumWeek,
+ 'percent' => $percent,
'sumMonth' => $sumMonth
];
}
}
}
- return $rows;
+ return $rows;
}
public function calculateWeeklySpeciesGoals(
array $weeksShareData,
array $monthSpeciesGoals
- ): array {
+ ): array
+ {
$monthSpeciesGoalsMap = [];
foreach ($monthSpeciesGoals as $monthSpeciesGoal) {
$monthSpeciesGoalsMap[$monthSpeciesGoal['store_id']]
[$monthSpeciesGoal['category']]
[$monthSpeciesGoal['subcategory']]
- [$monthSpeciesGoal['species']] = $monthSpeciesGoal['goal'] ;
+ [$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'];
+ $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);
+ $weeklyGoal = round($percent * $monthlyGoal, 4);
}
$result[] = [
- 'week' => $week,
- 'store_id' => $sid,
- 'category' => $cat,
- 'subcategory' => $sub,
- 'species' => $spec,
- 'percent' => $percent,
+ 'week' => $week,
+ 'store_id' => $sid,
+ 'category' => $cat,
+ 'subcategory' => $sub,
+ 'species' => $spec,
+ 'percent' => $percent,
'monthly_goal' => $monthlyGoal,
- 'weekly_goal' => $weeklyGoal,
+ 'weekly_goal' => $weeklyGoal,
];
}
return $result;
/**
* Возвращает дату понедельника ISO-недели в формате YYYY-MM-DD
*
- * @param int $year ISO-год (может отличаться от календарного в границах года)
- * @param int $week номер ISO-недели (1–53)
+ * @param int $year ISO-год (может отличаться от календарного в границах года)
+ * @param int $week номер ISO-недели (1–53)
* @return string дата понедельника, например '2025-03-10'
*/
public static function getIsoWeekStart(int $year, int $week): string
: BouquetComposition::REGION_NN;
}
- $pricesMap = self::buildPricesMap($productsIds, $region);
+ $pricesMap = self::buildPricesMap($productsIds, $region);
foreach ($pricesMap as $id => $price) {
if ($goal == 0 || (float)$price == 0) {
/**
* Общий расчёт плана для заданной категории товаров без истории.
*
- * @param int $storeId
- * @param string $yearMonth строка "YYYY-MM", например "2025-03"
+ * @param int $storeId
+ * @param string $yearMonth строка "YYYY-MM", например "2025-03"
* @param string $category
* @param string|null $subcategory
* @param string|null $species
return $result;
}
- public function mapGoalsBySpecies(array $rows): array {
+ public function mapGoalsBySpecies(array $rows): array
+ {
$mapped = [];
foreach ($rows as $row) {
$mapped[$row['store_id']][$row['category']][$row['subcategory']][$row['species']] = $row['goal'];
return $mapped;
}
- public function subtractSpeciesGoals(array $data, array $bouquetForecast, array $noHistory): array {
+ public function subtractSpeciesGoals(array $data, array $bouquetForecast, array $noHistory): array
+ {
$bouquetForecastMap = $this->mapGoalsBySpecies($bouquetForecast);
$noHistoryMap = $this->mapGoalsBySpecies($noHistory);
$result = [];
string $year,
array $speciesGoals,
array $productSalesShare
- ): array {
+ ): array
+ {
$result = [];
if (empty($speciesGoals)) {
: BouquetComposition::REGION_NN;
}
- $pricesMap = self::buildPricesMap(array_keys($productSalesShare), $region);
+ $pricesMap = self::buildPricesMap(array_keys($productSalesShare), $region);
foreach ($productSalesShare as $productId => $data) {
$share = $data['share'] ?? 0.0;
}
$storeId = $data['store_id'];
- $cat = $data['category'];
- $sub = $data['subcategory'];
- $spec = $data['species'];
- $price = $pricesMap[$productId];
+ $cat = $data['category'];
+ $sub = $data['subcategory'];
+ $spec = $data['species'];
+ $price = $pricesMap[$productId];
if (
- ! isset(
+ !isset(
$goalsMap[$storeId],
$goalsMap[$storeId][$cat],
$goalsMap[$storeId][$cat][$sub],
*
* Объединяет прогнозы без истории и с историей для полного списка товаров.
*
- * @param array $pieciesForecastProductsNoHistyory Результат calculateSpeciesForecastForProductsWithoutHistory
+ * @param array $pieciesForecastProductsNoHistyory Результат calculateSpeciesForecastForProductsWithoutHistory
* @param array $pieciesForecastProductWithHistory Результат calculateProductForecastInPiecesProductsWithHistory
* @return array
*/
public function calculateProductForecastShare(
array $pieciesForecastProductsNoHistyory,
array $pieciesForecastProductWithHistory
- ): array {
+ ): array
+ {
$groupedForecasts = [];
// Обработка товаров без истории
foreach ($pieciesForecastProductsNoHistyory as $group) {
$base = [
- 'store_id' => $group['store_id'],
- 'month' => $group['month'],
- 'year' => $group['year'],
- 'category' => $group['category'],
+ 'store_id' => $group['store_id'],
+ 'month' => $group['month'],
+ 'year' => $group['year'],
+ 'category' => $group['category'],
'subcategory' => $group['subcategory'],
- 'species' => $group['species'],
+ 'species' => $group['species'],
];
foreach ($group['forecasts'] as $productId => $forecast) {
$groupedForecasts[$key]['meta'] = $base;
$groupedForecasts[$key]['products'][$productId] = [
- 'product_id' => $productId,
- 'forecast_pieces' => (float)$forecast,
- 'history_status' => 'No history',
+ 'product_id' => $productId,
+ 'forecast_pieces' => (float)$forecast,
+ 'history_status' => 'No history',
];
}
}
]);
$groupedForecasts[$key]['meta'] = [
- 'store_id' => $item['store_id'],
- 'month' => $item['month'],
- 'year' => $item['year'],
- 'category' => $item['category'],
+ 'store_id' => $item['store_id'],
+ 'month' => $item['month'],
+ 'year' => $item['year'],
+ 'category' => $item['category'],
'subcategory' => $item['subcategory'],
- 'species' => $item['species'],
+ 'species' => $item['species'],
];
$groupedForecasts[$key]['products'][$item['product_id']] = [
- 'product_id' => $item['product_id'],
+ 'product_id' => $item['product_id'],
'forecast_pieces' => (float)$item['forecast_pieces'],
- 'history_status' => 'With history',
+ 'history_status' => 'With history',
];
}
$share = round($product['forecast_pieces'] / $totalForecast, 4);
$result[] = array_merge($meta, [
- 'product_id' => $product['product_id'],
+ 'product_id' => $product['product_id'],
'forecast_pieces' => $product['forecast_pieces'],
- 'share' => $share,
- 'history_status' => $product['history_status'],
+ 'share' => $share,
+ 'history_status' => $product['history_status'],
]);
}
}
/**
* Рассчитывает продажи по каждому товару внутри вида на основе долей и очищенной цели вида.
*
- * @param array $productShares Результат calculateProductForecastShare
- * @param array $speciesGoals Массив целей по видам с ключом 'goal'
+ * @param array $productShares Результат calculateProductForecastShare
+ * @param array $speciesGoals Массив целей по видам с ключом 'goal'
* @return array
*/
public static function calculateProductSalesBySpecies(
array $productShares,
array $speciesGoals
- ): array {
+ ): array
+ {
$result = [];
$goalsMap = [];
foreach ($speciesGoals as $item) {
->all();
$products = ArrayHelper::getColumn($productShares, 'product_id');
- $pricesMap = self::buildPricesMap($products);
+ $pricesMap = self::buildPricesMap($products);
foreach ($productShares as $shareItem) {
$storeId = $shareItem['store_id'];
$shareItem['species']
]);
- $cleanGoal = $goalsMap[$key] ?? 0;
+ $cleanGoal = $goalsMap[$key] ?? 0;
$productSales = $shareItem['share'] * $cleanGoal;
$productSalesPieces = round($productSales / $price, 2);
$result[] = [
- 'store_id' => $shareItem['store_id'],
- 'month' => $shareItem['month'] ?? null,
- 'year' => $shareItem['year'] ?? null,
- 'category' => $shareItem['category'],
- 'subcategory' => $shareItem['subcategory'],
- 'species' => $shareItem['species'],
- 'product_id' => $shareItem['product_id'],
+ 'store_id' => $shareItem['store_id'],
+ 'month' => $shareItem['month'] ?? null,
+ 'year' => $shareItem['year'] ?? null,
+ 'category' => $shareItem['category'],
+ 'subcategory' => $shareItem['subcategory'],
+ 'species' => $shareItem['species'],
+ 'product_id' => $shareItem['product_id'],
'forecast_pieces' => $shareItem['forecast_pieces'],
- 'share' => round($shareItem['share'], 4),
+ 'share' => round($shareItem['share'], 4),
'history_status' => $shareItem['history_status'],
'cleanGoal' => $cleanGoal,
'product_sales' => round($productSales, 2),
$monthStart = sprintf('%04d-%02d-01 00:00:00', $year, $month);
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
- $monthEnd = sprintf('%04d-%02d-%02d 23:59:59', $year, $month, $daysInMonth);
+ $monthEnd = sprintf('%04d-%02d-%02d 23:59:59', $year, $month, $daysInMonth);
$componentProducts = [];
if ($type == self::TYPE_SALES) {
['nom' => Products1cNomenclature::tableName()],
'nom.id = sp.product_id'
)
- ->andWhere(['s.store_id' => $storeId])
- ->andWhere(['not', ['s.operation' => ['Удален', 'Удаление']]])
+ ->andWhere(['s.store_id' => $storeId])
+ ->andWhere(['not', ['s.operation' => ['Удален', 'Удаление']]])
->andWhere(['between', 's.date', $monthStart, $monthEnd])
->andWhere(['not', ['p1c.components' => '']])
->asArray()
}
$componentGuids = [];
- $componentDataRecords = [];
+ $componentDataRecords = [];
foreach ($componentProducts as $cp) {
$js = trim($cp['components']);
if ($js === '' || $js[0] !== '{') {
}
$componentGuids[$guid] = true;
$componentDataRecords[] = [
- 'record_id' => $cp['id'] ?? $cp['write_off_id'],
- 'sale_date' => $cp['date'] ?? $cp['write_off_date'],
- 'product_id' => $cp['product_id'],
- 'product_name' => $cp['name'] ?? $cp['product_name'],
+ 'record_id' => $cp['id'] ?? $cp['write_off_id'],
+ 'sale_date' => $cp['date'] ?? $cp['write_off_date'],
+ 'product_id' => $cp['product_id'],
+ 'product_name' => $cp['name'] ?? $cp['product_name'],
'quantity_product' => $cp['quantity_product'],
- 'component_guid' => $guid,
- 'quantity' => $qty,
- 'type' => $type,
- 'operation' => $cp['operation'] ?? '',
+ 'component_guid' => $guid,
+ 'quantity' => $qty,
+ 'type' => $type,
+ 'operation' => $cp['operation'] ?? '',
];
}
}
->andWhere(['region_id' => $region])
->andWhere(['product_id' => array_values(ArrayHelper::getColumn($nomenclatures, 'id'))])
->andWhere(['<=', 'date_from', $monthEnd])
- ->andWhere(['>=', 'date_to', $monthStart])
+ ->andWhere(['>=', 'date_to', $monthStart])
->orderBy(['date_from' => SORT_DESC])
->all();
$result = [];
foreach ($componentDataRecords as $componentDataRecord) {
- $guid = $componentDataRecord['component_guid'];
- $nomenclatureData = $nomenclatures[$guid] ?? null;
- $productId = $nomenclatureData?->id;
+ $guid = $componentDataRecord['component_guid'];
+ $nomenclatureData = $nomenclatures[$guid] ?? null;
+ $productId = $nomenclatureData?->id;
$price = 0;
$dailyPrices = [];
foreach ($pricesByProduct[$productId] ?? [] as $priceRecordForProduct) {
if (defined($heliumConstant) && $productId == constant($heliumConstant)) {
$saleDay = (new \DateTime($componentDataRecord['sale_date']))->format('Y-m-d');
$fromDay = (new \DateTime($priceRecordForProduct->date_from))->modify('-1 day')->format('Y-m-d');
- $toDay = (new \DateTime($priceRecordForProduct->date_to ))->modify('+1 day')->format('Y-m-d');
+ $toDay = (new \DateTime($priceRecordForProduct->date_to))->modify('+1 day')->format('Y-m-d');
if ($fromDay <= $saleDay && $saleDay <= $toDay) {
$dailyPrices[] = (float)$priceRecordForProduct->price;
$costComponent = $componentDataRecord['quantity'] * $price;
$result[] = [
- 'store_id' => $storeId,
- 'record_id' => $componentDataRecord['record_id'],
- 'sale_date' => $componentDataRecord['sale_date'],
- 'product_id' => $componentDataRecord['product_id'],
- 'product_name' => $componentDataRecord['product_name'],
- 'quantity_product' => $componentDataRecord['quantity_product'],
- 'component_guid' => $guid,
- 'component_name' => $nomenclatureData?->name,
- 'component_category' => $nomenclatureData?->category,
- 'component_subcategory'=> $nomenclatureData?->subcategory,
- 'component_species' => $nomenclatureData?->species,
- 'quantity' => $componentDataRecord['quantity'],
- 'price' => $price,
- 'cost' => $cost,
- 'component_cost' => $costComponent,
- 'type' => $type,
- 'operation' => $componentDataRecord['operation'],
+ 'store_id' => $storeId,
+ 'record_id' => $componentDataRecord['record_id'],
+ 'sale_date' => $componentDataRecord['sale_date'],
+ 'product_id' => $componentDataRecord['product_id'],
+ 'product_name' => $componentDataRecord['product_name'],
+ 'quantity_product' => $componentDataRecord['quantity_product'],
+ 'component_guid' => $guid,
+ 'component_name' => $nomenclatureData?->name,
+ 'component_category' => $nomenclatureData?->category,
+ 'component_subcategory' => $nomenclatureData?->subcategory,
+ 'component_species' => $nomenclatureData?->species,
+ 'quantity' => $componentDataRecord['quantity'],
+ 'price' => $price,
+ 'cost' => $cost,
+ 'component_cost' => $costComponent,
+ 'type' => $type,
+ 'operation' => $componentDataRecord['operation'],
];
}
}
-
public function sumProductsComponentsByGroup(array $items, string $type, string $group = 'category'): array
{
$aggregated = [];
foreach ($items as $row) {
- $storeId = $row['store_id'];
- $category = $row['component_category'];
+ $storeId = $row['store_id'];
+ $category = $row['component_category'];
$subcategory = $row['component_subcategory'];
- $species = $row['component_species'];
- $operation = $row['operation'] ?? null;
+ $species = $row['component_species'];
+ $operation = $row['operation'] ?? null;
$cost = (float)$row['cost'];
if (!isset($aggregated[$key])) {
// базовая структура
$aggregated[$key] = [
- 'store_id' => $storeId,
- 'category' => $category,
- 'sum' => 0.0,
- 'type' => $type,
+ 'store_id' => $storeId,
+ 'category' => $category,
+ 'sum' => 0.0,
+ 'type' => $type,
];
if ($group === 'subcategory' || $group === 'species') {
$matrixGroups = array_unique(ArrayHelper::getColumn($matrixForecast, 'group'));
$bouquetForecast = StorePlanService::getBouquetSpiecesMonthGoalFromForecast($filters['month'], $filters['year'], $filters['store_id'], $matrixGroups);
$speciesData = $bouquetForecast['final'];
- foreach ($speciesData as $store_id => $categoryData) {
+ foreach ($speciesData as $store_id => $categoryData) {
foreach ($categoryData as $category => $subcategoryData) {
foreach ($subcategoryData as $subcategory => $species) {
foreach ($species as $speciesInd => $row) {
}
-
-
-
-
/**
* Считает взвешенную долю категорий за целевой месяц:
* берёт три предыдущих к «месяцу-цели» месяца (пропуская сразу предыдущий),
$end = date('Y-m-t 23:59:59', strtotime($start));
-
$q = (new Query())
->select([
'store_id' => 'ex.entity_id',
return [];
}
$month = (int)$dt->format('m');
- $year = (int)$dt->format('Y');
+ $year = (int)$dt->format('Y');
- $stores = $this->getVisibleStores();
+ $stores = $this->getVisibleStores();
$storeIds = array_map(fn($s) => $s->id, $stores);
if (!empty($filters['store_id'])) {
$storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
$query = (new Query())
->select([
- 'store_id' => 'ex.entity_id',
- 'subcategory' => 'p1c.subcategory',
- 'category' => 'p1c.category',
- 'total_sum' => new Expression(
+ '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')
-
+ ->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) {
}
} 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')
+ ->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) {
$result = [];
foreach ($rows as $r) {
- $sid = $r['store_id'];
- $cat = $r['category'];
+ $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'],
+ 'store_id' => $sid,
+ 'category' => $cat,
+ 'subcategory' => $r['subcategory'],
+ 'total_sum' => $r['total_sum'],
'percent_of_month' => round($r['total_sum'] / $total, 4),
];
}
): array
{
try {
- $dt = new \DateTime($dateFrom);
+ $dt = new \DateTime($dateFrom);
} catch (\Exception $e) {
return [];
}
$month = (int)$dt->format('m');
- $year = (int)$dt->format('Y');
+ $year = (int)$dt->format('Y');
- $stores = $this->getVisibleStores();
+ $stores = $this->getVisibleStores();
$storeIds = array_map(fn($s) => $s->id, $stores);
if (!empty($filters['store_id'])) {
$storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
$years = [$year - 2, $year - 1];
$query = (new Query())->select([
- 'store_id' => 'ex.entity_id',
- 'category' => 'p1c.category',
- 'subcategory'=> 'p1c.subcategory',
- 'species' => 'p1c.species',
- 'total_sum' => new Expression(
+ 'store_id' => 'ex.entity_id',
+ 'category' => 'p1c.category',
+ 'subcategory' => 'p1c.subcategory',
+ 'species' => 'p1c.species',
+ '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')
+ ->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]);
+ ->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')
+ ->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]);
+ ->andWhere(['=', new Expression('EXTRACT(MONTH FROM s.date)'), $month]);
if ($productFilter !== null) {
$query->andWhere(['sp.product_id' => $productFilter]);
}
$result = [];
foreach ($rows as $r) {
- $sid = $r['store_id'];
- $sub = $r['subcategory'];
+ $sid = $r['store_id'];
+ $sub = $r['subcategory'];
$total = $sumByStoreSubcategory[$sid][$sub] ?: 1;
$result[] = [
- 'store_id' => $sid,
- 'category' => $r['category'],
- 'subcategory' => $sub,
- 'species' => $r['species'],
- 'total_sum' => (float)$r['total_sum'],
+ 'store_id' => $sid,
+ 'category' => $r['category'],
+ 'subcategory' => $sub,
+ 'species' => $r['species'],
+ 'total_sum' => (float)$r['total_sum'],
'percent_of_month' => round($r['total_sum'] / $total, 4),
];
}
// Недельные расчеты
-
-
// альтернативные методы расчета списаний
public function calculateFullGoalChainWeighted(array $filters): array
$monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOff($datePlan, $filters);
if ($filters['type'] === 'writeOffs') {
- $salesSubShare = $this->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales');
- $salesSubGoal = $this->getMonthSubcategoryGoal($salesSubShare, $monthCategoryGoal);
+ $salesSubShare = $this->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales');
+ $salesSubGoal = $this->getMonthSubcategoryGoal($salesSubShare, $monthCategoryGoal);
$catGoalMap = [];
foreach ($monthCategoryGoal as $row) {
$writeShare = $row['percent_of_month'];
- $writeGoal = ($catGoalMap[$cat] ?? 0) * $writeShare;
- $saleGoal = $salesSubGoalMap[$cat][$sub] ?? 0;
+ $writeGoal = ($catGoalMap[$cat] ?? 0) * $writeShare;
+ $saleGoal = $salesSubGoalMap[$cat][$sub] ?? 0;
if ($saleGoal > 0 && $writeGoal > 0.1 * $saleGoal) {
$row['share'] = 0.1;
$monthSpeciesGoal = $this->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal);
if ($filters['type'] === 'writeOffs') {
$salesSpecShare = $this->getMonthSpeciesShareOrWriteOffWeighted($datePlan, $datePlan, $filters, null, 'sales');
- $salesSpecGoal = $this->getMonthSpeciesGoalDirty($salesSpecShare, $monthSubcategoryGoal);
+ $salesSpecGoal = $this->getMonthSpeciesGoalDirty($salesSpecShare, $monthSubcategoryGoal);
$subGoalMap = [];
foreach ($monthSubcategoryGoal as $row) {
}
foreach ($monthSpeciesShare as &$row) {
- $cat = $row['category'];
- $sub = $row['subcategory'];
+ $cat = $row['category'];
+ $sub = $row['subcategory'];
$spec = $row['species'];
$writeShare = $row['percent_of_month'];
- $writeGoal = ($subGoalMap[$cat][$sub] ?? 0) * $writeShare;
- $saleGoal = $salesSpecGoalMap[$cat][$sub][$spec] ?? 0;
+ $writeGoal = ($subGoalMap[$cat][$sub] ?? 0) * $writeShare;
+ $saleGoal = $salesSpecGoalMap[$cat][$sub][$spec] ?? 0;
if ($saleGoal > 0 && $writeGoal > 0.1 * $saleGoal) {
$row['share'] = 0.1;
/**
* Строит карту цен за последние 20 дней.
*
- * @param array $productIds Список product_id для выборки
- * @param int|null $regionId Если указан — один регион, иначе — несколько
+ * @param array $productIds Список product_id для выборки
+ * @param int|null $regionId Если указан — один регион, иначе — несколько
* @return array
*/
public static function buildPricesMap(
- array $productIds,
- ?int $regionId = null
- ): array {
+ array $productIds,
+ ?int $regionId = null
+ ): array
+ {
$heliumGuid = '2b72702a-792f-11e8-9edd-1c6f659fb563';
- $periodEnd = (new \DateTime())->format('Y-m-d H:i:s');
+ $periodEnd = (new \DateTime())->format('Y-m-d H:i:s');
$periodStart = (new \DateTime())->modify('-20 days')->format('Y-m-d H:i:s');
->select(['product_id', 'region_id', 'price', 'active', 'date_from', 'date_to'])
->where(['product_id' => $productIds])
->andWhere(['<=', 'date_from', $periodEnd])
- ->andWhere(['>=', 'date_to', $periodStart])
+ ->andWhere(['>=', 'date_to', $periodStart])
->orderBy(['date_from' => SORT_ASC]);
if ($regionId !== null) {
$multiRegion = $regionId === null;
foreach ($priceRecords as $priceRecord) {
- $productId = $priceRecord['product_id'];
+ $productId = $priceRecord['product_id'];
$price = (float)$priceRecord['price'];
- $regionId = $multiRegion
+ $regionId = $multiRegion
? $priceRecord['region_id']
: null;
}
$weeklyForecasts[] = [
- 'store_id' => $item['store_id'],
- 'category' => $item['category'],
- 'subcategory' => $item['subcategory'],
- 'species' => $item['species'],
- 'type' => $item['type'],
- 'product_guid' => $item['product_guid'],
- 'week_forecast' => round($item['full_forecast'] * $weekShare['share'], 2),
- 'full_forecast' => $item['full_forecast'],
- 'week' => $weekShare['week'],
- 'matrix_group' => $item['matrix_group'],
- 'month' => $item['month'],
- 'year' => $item['year'],
+ 'store_id' => $item['store_id'],
+ 'category' => $item['category'],
+ 'subcategory' => $item['subcategory'],
+ 'species' => $item['species'],
+ 'type' => $item['type'],
+ 'product_guid' => $item['product_guid'],
+ 'week_forecast' => round($item['full_forecast'] * $weekShare['share'], 2),
+ 'full_forecast' => $item['full_forecast'],
+ 'week' => $weekShare['week'],
+ 'matrix_group' => $item['matrix_group'],
+ 'month' => $item['month'],
+ 'year' => $item['year'],
];
}
}
- return $weeklyForecasts;
+ $grouped = [];
+ foreach ($weeklyForecasts as $item) {
+ $weekItem = (int)$item['week'];
+ $storeItem = (int)$item['store_id'];
+ $guid = (string)$item['product_guid'];
+ $group = (string)$item['matrix_group'];
+ $type = (string)$item['type'];
+ $forecastValue = (float)$item['week_forecast'];
+
+ $grouped[$weekItem][$storeItem][$guid][$type][$group] = $forecastValue;
+ }
+
+ return $grouped;
}
}
\ No newline at end of file