->join('JOIN', new Expression('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(['<=', 'w.date', $dateTo])
->andWhere(['ex.entity_id' => $storeIds])
->andWhere(['<>', 'p1c.category', ''])
->groupBy(['ex.entity_id', 'p1c.category']);
->leftJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id')
->leftJoin('export_import_table ex', 'ex.export_val = s.store_id_1c')
->where(['>=', 's.date', $dateFrom])
- ->andWhere(['<=','w.date',$dateTo])
+ ->andWhere(['<=', 'w.date', $dateTo])
->andWhere(['ex.entity_id' => $storeIds])
->andWhere(['<>', 'p1c.category', ''])
->groupBy(['ex.entity_id', 'p1c.category']);
* суммирует взвешенные итоги по каждой категории и выдаёт их доли
* от общего взвешенного итога.
*
- * @param string $month Целевой месяц в формате 'YYYY-MM'
- * @param array|null $filters ['store_id'=>…, …]
- * @param array|null $productFilter Опционально: [product_id, …]
- * @param string $type 'sales' или 'writeOffs'
+ * @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 $filters = null,
?array $productFilter = null,
- string $type = 'sales'
+ string $type = 'sales'
): array
{
- $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']]);
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));
+ $end = date('Y-m-t 23:59:59', strtotime($start));
$monthStoreTotals = $this->getStoreTotals(
$storeIds, $start, $productFilter, $type, $end
$q = (new Query())
->select([
- 'store_id' => 'ex.entity_id',
- 'category' => 'p1c.category',
- 'month_sum' => new Expression(
- $type === 'writeOffs'
- ? "SUM(CAST(item->>'summ' AS NUMERIC))"
- : 'SUM(sp.summ)'
- )
+ 'store_id' => 'ex.entity_id',
+ 'category' => 'p1c.category',
+ 'month_sum' => new Expression('SUM(CAST(wop.summ AS NUMERIC))'),
])
- ->from($type === 'writeOffs' ? ['w'=>'write_offs'] : ['s'=>'sales']);
+ ->from(['w' => 'write_offs']);
if ($type === 'writeOffs') {
- $q->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\'')
- ->andWhere(['>=','w.date',$start])
- ->andWhere(['<=','w.date',$end]);
+ $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(["item->>'product_id'" => $productFilter]);
+ $q->andWhere(['wop.product_id' => $productFilter]);
}
} else {
- $q->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')
- ->andWhere(['>=','s.date',$start])
- ->andWhere(['<=','s.date',$end]);
+ $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']);
+ // ->andWhere(['<>', 'p1c.category', ''])
+ ->groupBy(['ex.entity_id', 'p1c.category']);
$rows = $q->all();
$grand = array_sum($cats) ?: 1;
foreach ($cats as $category => $weightedSum) {
$result[$storeId][] = [
- 'category' => $category,
+ 'category' => $category,
'total_sum_cat' => $weightedSum,
'total_sum_store' => $monthStoreTotalsWeighted[$storeId],
'query_total_sum_store' => $grand,
- 'share_of_total' => round($weightedSum / $grand, 4),
- 'share_of_total_control' => round($weightedSum / $monthStoreTotalsWeighted[$storeId], 4),
+ 'share_of_total' => round($weightedSum / $grand, 4),
+ 'share_of_total_control' => round($weightedSum / $monthStoreTotalsWeighted[$storeId], 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');
+
+ $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 [];
+ }
+
+ $startYear = 2020;
+ $endYear = $year - 1;
+ if ($endYear < $startYear) {
+ return [];
+ }
+ $years = range($startYear, $endYear);
+
+ $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
{
$indexedGoals = [];
//$monthCategoryShare = $this->getMonthCategoryShareOrWriteOff($dateFromForCategory, $filters);
$monthCategoryShare = $this->getMonthCategoryShareOrWriteOffWeighted($datePlan, $filters, null, $filters['type']);
$monthCategoryGoal = $this->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters);
- var_dump($monthCategoryShare); die();
- $monthSubcategoryShare = $this->getMonthSubcategoryShareOrWriteOff($dateFrom, $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']);
+ //$monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOffDate($dateFrom, $datePlan, $filters, null, $filters['type']);
+ $monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOffWeighted($datePlan, $datePlan, $filters, null, $filters['type']);
$monthSpeciesGoal = $this->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal);
-
+ // var_dump($monthSpeciesGoal);
+ // die();
$filtered = array_filter($monthSpeciesGoal, function ($row) use ($filters) {
foreach ($filters as $key => $value) {
if ($value === null || $value === '') {
* Считает для каждого магазина месячную сумму и долю
* продаж или списаний по видам (species).
*
- * @param string $dateFrom Дата начала периода (Y-m-d)
- * @param array|null $filters Фильтры (например ['store_id'=>...])
- * @param array|null $productFilter Опциональный фильтр по product_id
- * @param string $type 'sales' или 'writeOffs'
+ * @param string $dateFrom Дата начала периода (Y-m-d)
+ * @param array|null $filters Фильтры (например ['store_id'=>...])
+ * @param array|null $productFilter Опциональный фильтр по product_id
+ * @param string $type 'sales' или 'writeOffs'
* @return array [
* [
* 'store_id' => (int),
string $type = 'sales'
): array
{
- $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',
- 'category' => 'p1c.category',
+ 'store_id' => 'ex.entity_id',
+ 'category' => 'p1c.category',
'subcategory' => 'p1c.subcategory',
// Суммируем сразу по виду из колонки p1c.species
- 'species' => 'p1c.species',
- 'total_sum' => new Expression(
+ 'species' => 'p1c.species',
+ 'total_sum' => new Expression(
$type === 'writeOffs'
? "SUM(CAST(item->>'summ' AS NUMERIC))"
: 'SUM(sp.summ)'
}
- $rows = $query->all();
+ $rows = $query->all();
$result = [];
foreach ($rows as $row) {
$storeId = $row['store_id'];
- $total = $totals[$storeId] ?? 1;
+ $total = $totals[$storeId] ?? 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),
+ ];
+ }
+
+ return $result;
+ }
+
+ public function getMonthSpeciesShareOrWriteOffWeighted(
+ string $dateFrom,
+ string $dateTo,
+ ?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 [];
+ }
+
+ $startYear = 2020;
+ $endYear = $year - 1;
+ if ($endYear < $startYear) {
+ return [];
+ }
+ $years = range($startYear, $endYear);
+
+ $query = (new Query())->select([
+ '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')
+ ->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.species', ''])
+ ->groupBy([
+ 'ex.entity_id',
+ 'p1c.category',
+ 'p1c.subcategory',
+ 'p1c.species',
+ ]);
+
+ $rows = $query->all();
+ if (empty($rows)) {
+ return [];
+ }
+
+ $sumByStoreSubcategory = [];
+ foreach ($rows as $r) {
+ $sid = $r['store_id'];
+ $sub = $r['subcategory'];
+ $sumByStoreSubcategory[$sid][$sub] =
+ ($sumByStoreSubcategory[$sid][$sub] ?? 0) + $r['total_sum'];
+ }
+
+ $result = [];
+ foreach ($rows as $r) {
+ $sid = $r['store_id'];
+ $sub = $r['subcategory'];
+ $total = $sumByStoreSubcategory[$sid][$sub] ?: 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' => $r['category'],
+ 'subcategory' => $sub,
+ 'species' => $r['species'],
+ 'total_sum' => (float)$r['total_sum'],
+ 'percent_of_month' => round($r['total_sum'] / $total, 4),
];
}
* и рассчитывает для каждого вида (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'
+ * @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> // доля недели от месячного итога
}
return [
- 'weeksData' => $weeksData,
+ 'weeksData' => $weeksData,
'weeksShare' => $weeksShareResult,
];
}