From: marina Date: Tue, 15 Apr 2025 07:23:39 +0000 (+0300) Subject: ERP-359 Создать логику расчета на месяц - сумма продаж вида для автопм X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=a5d8edfe585d876a7a8755e352eb112b0aa5d829;p=erp24_rep%2Fyii-erp24%2F.git ERP-359 Создать логику расчета на месяц - сумма продаж вида для автопм --- diff --git a/erp24/controllers/AutoPlanogrammaController.php b/erp24/controllers/AutoPlanogrammaController.php index f1eacae6..02e7ae9a 100644 --- a/erp24/controllers/AutoPlanogrammaController.php +++ b/erp24/controllers/AutoPlanogrammaController.php @@ -2,11 +2,13 @@ namespace app\controllers; +use Yii; use yii\data\ArrayDataProvider; use yii\db\Expression; use yii\db\Query; use yii\helpers\ArrayHelper; use yii_app\records\CityStore; +use yii_app\records\Sales; use yii_app\services\AutoPlannogrammaService; class AutoPlanogrammaController extends BaseController @@ -29,7 +31,8 @@ class AutoPlanogrammaController extends BaseController } - public function actionIndex() { + public function actionIndex() + { $categories = (new Query()) ->select([ new Expression(" @@ -68,20 +71,22 @@ class AutoPlanogrammaController extends BaseController public function actionTestSales() { - $dateFrom = '2024-03-01'; - $datePlan = '2025-03-01'; - // Создаём экземпляр сервиса + $datePlan = '2025-04-01'; //плановое + $dateFromForCategory = date('Y-m-d', strtotime($datePlan . ' -12 months')); // смотрим 12 месяца назад + $dateFrom = date('Y-m-d', strtotime($datePlan . ' -3 months')); // смотрим 3 месяца назад + $autoPlannogrammaService = new AutoPlannogrammaService(); // Шаг 1: Получаем долю по категориям - $monthCategoryShare = $autoPlannogrammaService->getMonthCategoryShare($dateFrom); + $monthCategoryShare = $autoPlannogrammaService->getMonthCategoryShare($dateFromForCategory); // Шаг 2: Получаем цели по категориям $monthCategoryGoal = $autoPlannogrammaService->getMonthCategoryGoal($monthCategoryShare, $datePlan); // Шаг 3: Получаем долю по подкатегориям $monthSubcategoryShare = $autoPlannogrammaService->getMonthSubcategoryShare($dateFrom, $datePlan); - +// echo '
';var_dump($monthSubcategoryShare);
+//        echo '
';die(); // Шаг 4: Получаем цели по подкатегориям $monthSubcategoryGoal = $autoPlannogrammaService->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal); @@ -103,6 +108,38 @@ class AutoPlanogrammaController extends BaseController ]); } + public function actionSales() + { + + $query = Yii::$app->db->createCommand(" + select + c.name AS city_name, + p1.name AS product_name, + p1.category, + p1.subcategory, + SUM(sp.quantity) AS count, + SUM(sp.summ) AS sum + from sales s + left join sales_products sp on sp.check_id = s.id + left join products_1c_nomenclature p1 on p1.id = sp.product_id + left join export_import_table ex on ex.export_val = store_id_1c + left join city_store c on c.id = entity_id + where s.date >= '2024-04-01' + and p1.name = '' + group by p1.name, p1.category, p1.subcategory, c.name, c.id + order by c.id desc, category, subcategory, p1.name +")->queryAll(); + + $dataProvider = new ArrayDataProvider([ + 'allModels' => $query, + 'pagination' => ['pageSize' => 20], + ]); + + return $this->render('sales', [ + 'dataProvider' => $dataProvider + ]); + } + public function actionTestWriteOffs() { return $this->render('test-write-offs'); diff --git a/erp24/services/AutoPlannogrammaService.php b/erp24/services/AutoPlannogrammaService.php index 7fecad0f..9d0b2eb4 100644 --- a/erp24/services/AutoPlannogrammaService.php +++ b/erp24/services/AutoPlannogrammaService.php @@ -5,6 +5,7 @@ namespace yii_app\services; use yii\db\Expression; use yii\db\Query; +use yii_app\records\CityStore; use yii_app\records\Sales; use yii_app\records\SalesWriteOffsPlan; @@ -13,56 +14,75 @@ class AutoPlannogrammaService // шаг 1 public function getMonthCategoryShare($dateFrom) { - $subQuery = (new Query()) - ->from('sales') - ->leftJoin('sales_products sp2', 'sp2.check_id = sales.id') - ->where(['>=', 'date', $dateFrom]); - -// if ($isSales !== null) { -// $subQuery->andWhere(['sales.is_sale' => $isSales]); -// } - - $total = $subQuery->sum('sp2.summ'); - - $query = (new Query()) - ->select([ - 'category', - 'total_sum' => 'SUM(sp.summ)', - 'share_of_total' => new Expression( - 'ROUND(SUM(sp.summ) / :total, 4)', - [':total' => $total ?: 1] - ), - ]) - ->from('sales') - ->leftJoin('sales_products sp', 'sp.check_id = sales.id') - ->leftJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id') - ->andWhere(['>=', 'date', $dateFrom]) - ->andWhere(['<>', 'category', '']); + $percentArray = []; + + $stores = CityStore::findAll(['visible' => CityStore::IS_VISIBLE]); -// if ($isSales !== null) { -// $query->andWhere(['sales.is_sale' => $isSales]); -// } + foreach ($stores as $store) { + // Правильный подзапрос для totalSum с учётом связи с city_store + $totalSum = (new Query()) + ->select(['SUM(sp2.summ)']) + //вычитать возвраты + ->from('sales s2') + ->leftJoin('sales_products sp2', 'sp2.check_id = s2.id') + ->leftJoin('export_import_table ex2', 'ex2.export_val = s2.store_id_1c') + ->leftJoin('city_store cs', 'cs.id = ex2.entity_id') + ->where(['>=', 's2.date', $dateFrom]) + //границу 3 месяца + ->andWhere(['cs.id' => $store->id]) + ->scalar(); - $query->groupBy('category'); + $query = (new Query()) + ->select([ + 'p1c.category', + 'total_sum' => new Expression('SUM(sp.summ)'), + 'share_of_total' => new Expression('ROUND(SUM(sp.summ) / :total, 4)', [ + ':total' => $totalSum ?: 1, + ]), + ]) + ->from('sales s') + ->leftJoin('sales_products sp', 'sp.check_id = s.id') + ->leftJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id') + ->leftJoin('export_import_table ex', 'ex.export_val = s.store_id_1c') + ->leftJoin('city_store cs', 'cs.id = ex.entity_id') + ->where(['<>', 'p1c.category', '']) + ->andWhere(['cs.id' => $store->id]) + ->groupBy(['p1c.category']) + ->addParams([':total' => $totalSum ?: 1]); - return $query->all(); + $percentArray[$store->id] = $query->all(); + } + + return $percentArray; } // шаг 2 - public function getMonthCategoryGoal(array $monthCategoryShare, $datePlan) + public function getMonthCategoryGoal(array $monthCategoryShare, $datePlan) { $calculating = []; $timestamp = strtotime($datePlan); + $storesPlans = SalesWriteOffsPlan::find() - ->andWhere(['year' => date('Y', $timestamp), 'month' => date('m', $timestamp)]) + ->andWhere([ + 'year' => date('Y', $timestamp), + 'month' => date('m', $timestamp) + ]) ->asArray() ->all(); + foreach ($storesPlans as $storesPlan) { - foreach ($monthCategoryShare as $category) { + $storeId = $storesPlan['store_id']; + + // Проверяем, есть ли данные по категориям для этого магазина + if (!isset($monthCategoryShare[$storeId])) { + continue; + } + + foreach ($monthCategoryShare[$storeId] as $categoryData) { $calculating[] = [ - 'category' => $category['category'], - 'store_id' => $storesPlan['store_id'], - 'goal' => $category['share_of_total'] * $storesPlan['total_sales_plan'], + 'category' => $categoryData['category'], + 'store_id' => $storeId, + 'goal' => $categoryData['share_of_total'] * $storesPlan['total_sales_plan'], ]; } } @@ -70,158 +90,168 @@ class AutoPlannogrammaService return $calculating; } + // шаг 3 public function getMonthSubcategoryShare($dateFrom, $datePlan) { -// $dateFrom = $dateFrom ?? date('Y-m-d', strtotime('-1 year')); + $result = []; + $stores = CityStore::findAll(['visible' => CityStore::IS_VISIBLE]); + $month = date('m', strtotime($datePlan)); - $monthlySales = (new Query()) - ->select([ - 'month' => new Expression('EXTRACT(MONTH FROM s.date)'), - 'year' => new Expression('EXTRACT(YEAR FROM s.date)'), - 'total_sales' => new Expression('SUM(sp.summ)'), - ]) - ->from(['sp' => 'sales_products']) - ->leftJoin(['s' => 'sales'], 's.id = sp.check_id') - ->where(['IS NOT', 'sp.summ', null]) - ->andWhere(['>=', 's.date', $dateFrom]) - ->groupBy([ - new Expression('EXTRACT(YEAR FROM s.date)'), - new Expression('EXTRACT(MONTH FROM s.date)'), - ]); - - $subcategorySales = (new Query()) - ->select([ - 'month' => new Expression('EXTRACT(MONTH FROM s.date)'), - 'year' => new Expression('EXTRACT(YEAR FROM s.date)'), - 'category' => 'p1c.category', - 'subcategory' => 'p1c.subcategory', - 'total_sum' => new Expression('SUM(sp.summ)'), - ]) - ->from(['sp' => 'sales_products']) - ->leftJoin(['p1c' => 'products_1c_nomenclature'], 'p1c.id = sp.product_id') - ->leftJoin(['s' => 'sales'], 's.id = sp.check_id') - ->andWhere(['>=', 's.date', $dateFrom]) - ->groupBy([ - new Expression('EXTRACT(YEAR FROM s.date)'), - new Expression('EXTRACT(MONTH FROM s.date)'), - 'p1c.category', - 'p1c.subcategory', - ]); - - $query = (new Query()) - ->select([ - 'ss.category', - 'ss.subcategory', - 'ss.year', - 'ss.month', - 'ss.total_sum', - 'percent_of_month' => new Expression( - '(ROUND(SUM(ss.total_sum) OVER (PARTITION BY ss.month, ss.category, ss.subcategory) / SUM(ss.total_sum) OVER (PARTITION BY ss.month, ss.category)), 4)' - ), - ]) - ->from(['ss' => $subcategorySales]) - ->innerJoin(['ms' => $monthlySales], 'ss.month = ms.month AND ss.year = ms.year') - ->where(['ss.month' => date('m', strtotime($datePlan))]) - ->orderBy([ - 'ss.category' => SORT_ASC, - 'ss.subcategory' => SORT_ASC, - 'ss.month' => SORT_ASC, - 'ss.year' => SORT_ASC, - ]); - - return $query->all(); + foreach ($stores as $store) { + $storeId = $store->id; + + $monthlySales = (new Query()) + ->select([ + 'month' => new Expression('EXTRACT(MONTH FROM s.date)'), + 'year' => new Expression('EXTRACT(YEAR FROM s.date)'), + 'total_sales' => new Expression('SUM(sp.summ)'), + ]) + ->from(['sp' => 'sales_products']) + ->leftJoin(['s' => 'sales'], 's.id = sp.check_id') + ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = s.store_id_1c') + ->where(['>=', 's.date', $dateFrom]) + ->andWhere(['ex.entity_id' => $storeId]) + ->groupBy([ + new Expression('EXTRACT(YEAR FROM s.date)'), + new Expression('EXTRACT(MONTH FROM s.date)'), + ]); + + $subcategorySales = (new Query()) + ->select([ + 'month' => new Expression('EXTRACT(MONTH FROM s.date)'), + 'year' => new Expression('EXTRACT(YEAR FROM s.date)'), + 'category' => 'p1c.category', + 'subcategory' => 'p1c.subcategory', + 'total_sum' => new Expression('SUM(sp.summ)'), + ]) + ->from(['sp' => 'sales_products']) + ->leftJoin(['p1c' => 'products_1c_nomenclature'], 'p1c.id = sp.product_id') + ->leftJoin(['s' => 'sales'], 's.id = sp.check_id') + ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = s.store_id_1c') + ->where(['>=', 's.date', $dateFrom]) + ->andWhere(['ex.entity_id' => $storeId]) + ->groupBy([ + new Expression('EXTRACT(YEAR FROM s.date)'), + new Expression('EXTRACT(MONTH FROM s.date)'), + 'p1c.category', + 'p1c.subcategory', + ]); + + $query = (new Query()) + ->select([ + 'ss.category', + 'ss.subcategory', + 'ss.year', + 'ss.month', + 'ss.total_sum', + 'store_id' => new Expression($storeId), + 'percent_of_month' => new Expression('ROUND( + SUM(ss.total_sum) OVER (PARTITION BY ss.month, ss.category, ss.subcategory) + / NULLIF(SUM(ss.total_sum) OVER (PARTITION BY ss.month, ss.category), 0), + 4 + )' + ), + ]) + ->from(['ss' => $subcategorySales]) + ->innerJoin(['ms' => $monthlySales], 'ss.month = ms.month AND ss.year = ms.year') + ->where(['ss.month' => $month]); + + $result = array_merge($result, $query->all()); + } + + return $result; } + // шаг 4 - public function getMonthSubcategoryGoal(array $monthSubcategoryShare, array $monthCategoryGoal) + public function getMonthSubcategoryGoal(array $categoryShare, array $sales) { - $calculating = []; + $result = []; - $goalIndex = []; - foreach ($monthCategoryGoal as $goal) { - $goalIndex[$goal['category']][] = $goal; - } -// echo '
';
-//        var_dump($monthSubcategoryShare);
-//        echo '
'; -// die; - - foreach ($monthSubcategoryShare as $shareData) { - if (isset($shareData['category'], $shareData['subcategory'], $shareData['percent_of_month'])) { - if (isset($goalIndex[$shareData['category']])) { - foreach ($goalIndex[$shareData['category']] as $goal) { - if (isset($goal['store_id'], $goal['goal'])) { - $calculating[] = [ - 'category' => $shareData['category'], - 'subcategory'=> $shareData['subcategory'], - 'store_id' => $goal['store_id'], - 'goal' => $goal['goal'] * floatval(strtr($shareData['percent_of_month'], [',' => '.', '(' => '', ')' => ''])), - ]; - } - } + foreach ($categoryShare as $shareItem) { + foreach ($sales as $saleItem) { + if ( + $shareItem['category'] === $saleItem['category'] && + $shareItem['store_id'] == $saleItem['store_id'] + ) { + $percent = floatval(strtr($shareItem['percent_of_month'], [',' => '.', '(' => '', ')' => ''])); + $goal = $percent * floatval($saleItem['goal']); // исправлено тут + + $result[] = [ + 'category' => $shareItem['category'], + 'subcategory' => $shareItem['subcategory'], + 'store_id' => $saleItem['store_id'], + 'goal' => $goal, + ]; } } } - return $calculating; + return $result; } + // шаг 5 public function getMonthSpeciesShare($datePlan) { - $monthlySales = (new Query()) - ->select([ - 'month' => new Expression('EXTRACT(MONTH FROM s.date)'), - 'total_sales' => new Expression('SUM(sp.summ)'), - ]) - ->from(['sp' => 'sales_products']) - ->leftJoin(['s' => 'sales'], 's.id = sp.check_id') - ->where(['IS NOT', 'sp.summ', null]) - ->groupBy(new Expression('EXTRACT(MONTH FROM s.date)')); - - $subcategorySales = (new Query()) - ->select([ - 'category' => 'p1.category', - 'name' => 'p1.name', - 'subcategory' => 'p1.subcategory', - 'month' => new Expression('EXTRACT(MONTH FROM s.date)'), - 'total_sum' => new Expression('SUM(sp.summ)'), - ]) - ->from(['sp' => 'sales_products']) - ->leftJoin(['p1' => 'products_1c_nomenclature'], 'p1.id = sp.product_id') - ->leftJoin(['s' => 'sales'], 's.id = sp.check_id') - ->groupBy(['p1.category', 'p1.name', 'p1.subcategory', new Expression('EXTRACT(MONTH FROM s.date)')]); - - if ($datePlan !== null) { - $month = date('m', strtotime($datePlan)); - $monthlySales->andWhere(['EXTRACT(MONTH FROM s.date)' => $month]); - $subcategorySales->andWhere(['EXTRACT(MONTH FROM s.date)' => $month]); + $result = []; + $stores = CityStore::findAll(['visible' => CityStore::IS_VISIBLE]); + $month = date('m', strtotime($datePlan)); + + foreach ($stores as $store) { + $storeId = $store->id; + + $monthlySales = (new Query()) + ->select([ + 'month' => new Expression('EXTRACT(MONTH FROM s.date)'), + 'total_sales' => new Expression('SUM(sp.summ)'), + ]) + ->from(['sp' => 'sales_products']) + ->leftJoin(['s' => 'sales'], 's.id = sp.check_id') + ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = s.store_id_1c') + ->where(['ex.entity_id' => $storeId]) + ->andWhere(['EXTRACT(MONTH FROM s.date)' => $month]) + ->groupBy(new Expression('EXTRACT(MONTH FROM s.date)')); + + $speciesSales = (new Query()) + ->select([ + 'category' => 'p1.category', + 'name' => 'p1.name', + 'subcategory' => 'p1.subcategory', + 'month' => new Expression('EXTRACT(MONTH FROM s.date)'), + 'total_sum' => new Expression('SUM(sp.summ)'), + ]) + ->from(['sp' => 'sales_products']) + ->leftJoin(['p1' => 'products_1c_nomenclature'], 'p1.id = sp.product_id') + ->leftJoin(['s' => 'sales'], 's.id = sp.check_id') + ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = s.store_id_1c') + ->where(['ex.entity_id' => $storeId]) + ->andWhere(['EXTRACT(MONTH FROM s.date)' => $month]) + ->groupBy(['p1.category', 'p1.name', 'p1.subcategory', new Expression('EXTRACT(MONTH FROM s.date)')]); + + $query = (new Query()) + ->select([ + 'ss.category', + 'ss.name', + 'ss.subcategory', + 'ss.month', + 'ss.total_sum', + 'store_id' => new Expression($storeId), + 'percent_of_month' => new Expression( + 'ROUND(ss.total_sum / SUM(ss.total_sum) OVER (PARTITION BY ss.month), 6)' + ), + ]) + ->from(['ss' => $speciesSales]) + ->innerJoin(['ms' => $monthlySales], 'ss.month = ms.month'); + + $result = array_merge($result, $query->all()); } - $query = (new Query()) - ->select([ - 'ss.category', - 'ss.name', - 'ss.subcategory', - 'ss.month', - 'ss.total_sum', - 'percent_of_month' => new Expression( - 'ROUND(ss.total_sum / SUM(ss.total_sum) OVER (PARTITION BY ss.month), 6)' - ), - ]) - ->from(['ss' => $subcategorySales]) - ->innerJoin(['ms' => $monthlySales], 'ss.month = ms.month') - ->orderBy([ - 'ss.month' => SORT_ASC, - 'ss.category' => SORT_ASC, - 'ss.subcategory' => SORT_ASC, - 'ss.name' => SORT_ASC, - ]); - - return $query->all(); + return $result; } +//количество = делить сумму на розничную цену в зависимости от магазина где он находится // шаг 6 public function getMonthSpeciesGoalDirtry(array $monthSpeciesShare, array $monthSubcategoryGoal) { @@ -231,18 +261,19 @@ class AutoPlannogrammaService foreach ($monthSubcategoryGoal as $goalItem) { if ( $species['category'] === $goalItem['category'] && - $species['subcategory'] === $goalItem['subcategory'] + $species['subcategory'] === $goalItem['subcategory'] && + $species['store_id'] == $goalItem['store_id'] ) { $percent = floatval(strtr($species['percent_of_month'], [',' => '.', '(' => '', ')' => ''])); $goalValue = floatval($goalItem['goal']); $result = $percent * $goalValue; $calculating[] = [ - 'category' => $species['category'], + 'category' => $species['category'], 'subcategory' => $species['subcategory'], - 'species' => $species['name'], - 'store_id' => $goalItem['store_id'], - 'goal' => $result, + 'species' => $species['name'], + 'store_id' => $goalItem['store_id'], + 'goal' => $result, ]; } } diff --git a/erp24/views/auto-planogramma/sales.php b/erp24/views/auto-planogramma/sales.php new file mode 100644 index 00000000..3c504913 --- /dev/null +++ b/erp24/views/auto-planogramma/sales.php @@ -0,0 +1,26 @@ + $dataProvider, + 'columns' => [ + [ + 'attribute' => 'city_name', + 'label' => 'Магазин', + ], + [ + 'attribute' => 'product_name', + 'label' => 'Товар', + ], + 'category', + 'subcategory', + [ + 'attribute' => 'count', + 'label' => 'Кол-во', + ], + [ + 'attribute' => 'sum', + 'label' => 'Сумма', + ], + ], +]); \ No newline at end of file diff --git a/erp24/views/auto-planogramma/test-sales.php b/erp24/views/auto-planogramma/test-sales.php index 609827b2..8111b6f9 100644 --- a/erp24/views/auto-planogramma/test-sales.php +++ b/erp24/views/auto-planogramma/test-sales.php @@ -1,82 +1,8 @@ select('category')->distinct()->all(), 'category', 'category'); -////$subcategories = $categories = ArrayHelper::map(Products1cNomenclature::find()->select('category')->distinct()->all(), 'category', 'subcategory'); -////$speciesList = $categories = ArrayHelper::map(Products1cNomenclature::find()->select('category')->distinct()->all(), 'category', 'species'); -////$stores = $categories = ArrayHelper::map(Products1cNomenclature::find()->select('category')->distinct()->all(), 'category', 'category'); -// -////$filter = Yii::$app->request->get('filter', []); -//// -////$filteredData = array_filter($monthSpeciesGoalDirtry, function ($item) use ($filter) { -//// foreach ($filter as $key => $value) { -//// if ($value !== '' && $item[$key] != $value) { -//// return false; -//// } -//// } -//// return true; -////}); -// -//$dataProvider = new ArrayDataProvider([ -// 'allModels' => $filteredData, -// 'pagination' => [ -// 'pageSize' => 20, -// ], -//]); -// -//?> - - 'get']); ?> - - - 'filter[category]', -// 'value' => $filter['category'] ?? '', -// 'data' => ArrayHelper::map($categories, fn($v) => $v, fn($v) => $v), -// 'options' => ['placeholder' => 'Категория'], -// 'pluginOptions' => ['allowClear' => true], -// ]) ?> - - 'filter[subcategory]', -// 'value' => $filter['subcategory'] ?? '', -// 'data' => ArrayHelper::map($subcategories, fn($v) => $v, fn($v) => $v), -// 'options' => ['placeholder' => 'Подкатегория'], -// 'pluginOptions' => ['allowClear' => true], -// ]) ?> - - 'filter[species]', -// 'value' => $filter['species'] ?? '', -// 'data' => ArrayHelper::map($speciesList, fn($v) => $v, fn($v) => $v), -// 'options' => ['placeholder' => 'Вид'], -// 'pluginOptions' => ['allowClear' => true], -// ]) ?> - - 'filter[store_id]', -// 'value' => $filter['store_id'] ?? '', -// 'data' => ArrayHelper::map($stores, fn($v) => $v, fn($v) => 'Магазин ' . $v), -// 'options' => ['placeholder' => 'Магазин'], -// 'pluginOptions' => ['allowClear' => true], -// ]) ?> - - 'btn btn-primary']) ?> - 'btn btn-default']) ?> - - - - - $dataProvider, 'columns' => [ 'category', @@ -85,7 +11,7 @@ use yii_app\records\Products1cNomenclature; 'store_id', [ 'attribute' => 'goal', - 'format' => ['decimal', 4], +// 'format' => ['decimal', 4], 'label' => 'План закупок(RUB)', ], ],