From: fomichev Date: Tue, 20 May 2025 13:13:13 +0000 (+0300) Subject: Вывод 8 шага X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=633aab23a6855b610664b4fea5a9035247c519d3;p=erp24_rep%2Fyii-erp24%2F.git Вывод 8 шага --- diff --git a/erp24/controllers/AutoPlannogrammaController.php b/erp24/controllers/AutoPlannogrammaController.php index 76200530..dd5451fa 100644 --- a/erp24/controllers/AutoPlannogrammaController.php +++ b/erp24/controllers/AutoPlannogrammaController.php @@ -625,8 +625,8 @@ class AutoPlannogrammaController extends BaseController // Обработка даты на год и месяц if (!empty($filters['year']) && !empty($filters['month'])) { $filters['plan_date'] = $filters['year'] . '-' . str_pad($filters['month'], 2, '0', STR_PAD_LEFT) . '-01'; - // $service = new AutoPlannogrammaService(); - $histShare = StorePlanService::calculateHistoricalShare( + + $data = StorePlanService::calculateSpeciesForecastForProductsWithoutHistory( $filters['store_id'], $filters['month'], $filters['year'], @@ -635,8 +635,7 @@ class AutoPlannogrammaController extends BaseController $filters['species'], ); - $data = StorePlanService::calculateWeightedSalesForProductsWithoutHistory($filters['store_id'], $filters['month'], $filters['year'], $histShare); -var_dump($histShare); die(); + var_dump($data); die(); $flatData = array_filter($data, function ($row) use ($filters) { foreach ($filters as $key => $value) { if (empty($value)) continue; diff --git a/erp24/services/StorePlanService.php b/erp24/services/StorePlanService.php index 0ebf1cee..6068c9db 100755 --- a/erp24/services/StorePlanService.php +++ b/erp24/services/StorePlanService.php @@ -276,8 +276,8 @@ class StorePlanService */ private static function getPeriods($baseDate, $count, $withWeeks = false, $withWeights = false) { - $date = new \DateTime($baseDate); - $dateFrom = $date->modify('-3 month')->format('Y-m'); + $date = new \DateTime(date('Y-m-d', $baseDate)); + $dateFrom = strtotime($date->modify('-2 month')->format('Y-m')); $periods = []; for ($i = 1; $i <= $count; $i++) { $timestamp = strtotime("-{$i} month", $dateFrom); @@ -503,6 +503,208 @@ class StorePlanService return $weightedResults; } + /** + * Возвращает среднюю цену $productId для месяца, отстоящего на $offset месяцев + * от заданного $year-$month, в контексте $storeId. + * + * Если целевой месяц март 2025, при $offset = 2 будет рассчитана цена за январь 2025. + * + * @param string $productId + * @param int $year + * @param int $month + * @param int $storeId + * @param int $offset количество месяцев назад (по умолчанию 2) + * @return float + */ + public static function getPriceForProductAtOffsetMonth(string $productId, int $year, int $month, int $storeId, int $offset = 2): float + { + $date = new \DateTimeImmutable(sprintf('%04d-%02d-01', $year, $month)); + $target = $date->modify(sprintf('-%d months', $offset)); + $tYear = (int)$target->format('Y'); + $tMonth = (int)$target->format('m'); + + $monthStart = sprintf('%04d-%02d-01', $tYear, $tMonth); + $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $tMonth, $tYear); + $monthEnd = sprintf('%04d-%02d-%02d', $tYear, $tMonth, $daysInMonth); + + $priceRecords = PricesDynamic::find() + ->where(['product_id' => $productId]) + ->andWhere(['<=', 'date_from', $monthEnd]) + ->andWhere([ + 'or', + ['>=', 'date_to', $monthStart], + ['date_to' => '2100-01-01 03:00:00+03'] + ]) + ->all(); + + // определяем регион по городу + $cityId = CityStore::find()->select('city_id')->where(['id' => $storeId])->scalar(); + if ($cityId == 1342) { + $region = BouquetComposition::REGION_NN; + } elseif ($cityId == 1) { + $region = BouquetComposition::REGION_MSK; + } else { + $region = null; + } + + // ->andWhere(['or', ['region_id' => $region], ['region_id' => null]]) + + if (empty($priceRecords)) { + return 0.0; + } + + $total = 0; + $count = 0; + foreach ($priceRecords as $rec) { + if (isset($rec->price)) { + $total += $rec->price; + $count++; + } + } + + return $count > 0 ? ($total / $count) : 0.0; + } + + + /** + * Рассчитывает итоговые стоимости для товаров без истории, группируя суммы по + * category/subcategory/species. + * + * @param int $storeId + * @param int $selectedMonth + * @param int $selectedYear + * @param array $productsWithoutHistory массив ['guid' => …] + * @return array плоский массив строк: + * [ + * [ + * 'store_id' => …, + * 'category' => …, + * 'subcategory' => …, + * 'species' => …, + * 'month' => …, + * 'year' => …, + * 'sum' => …, + * ], + * … + * ] + */ + public static function calculateCostForProductsWithoutHistory( + int $storeId, + int $selectedMonth, + int $selectedYear, + array $weightedProductsWithoutHistory + ): array + { + + $accumulator = []; + + foreach ($weightedProductsWithoutHistory as $guid => $info) { + $quantity = (float)$info['weightedValue']; + if ($quantity <= 0) { + continue; + } + + // цена за два месяца назад + $price = self::getPriceForProductAtOffsetMonth( + $guid, + $selectedYear, + $selectedMonth, + $storeId, + 2 + ); + + $cost = $quantity * $price; + + /** @var Products1cNomenclature $nom */ + $nom = Products1cNomenclature::findOne($guid); + $cat = $nom->category ?? ''; + $sub = $nom->subcategory ?? ''; + $sp = $nom->species ?? ''; + + $key = implode('|', [$cat, $sub, $sp]); + if (!isset($accumulator[$key])) { + $accumulator[$key] = [ + 'store_id' => $storeId, + 'category' => $cat, + 'subcategory' => $sub, + 'species' => $sp, + 'month' => $selectedMonth, + 'year' => $selectedYear, + 'sum' => 0.0, + ]; + } + $accumulator[$key]['sum'] += $cost; + } + + return array_values($accumulator); + } + + + /** + * Общий расчёт плана для заданной категории товаров без истории. + * + * @param int $storeId + * @param string $yearMonth строка "YYYY-MM", например "2025-03" + * @param string $category + * @param string|null $subcategory + * @param string|null $species + * @return array [ + * [ + * 'store_id' => …, + * 'category' => …, + * 'subcategory' => …, + * 'species' => …, + * 'month' => …, + * 'year' => …, + * 'sum' => …, + * ], + * … + * ] + * + */ + public static function calculateSpeciesForecastForProductsWithoutHistory( + int $storeId, + string $month, + string $year, + string $category, + ?string $subcategory, + ?string $species + ): array { + + $histResult = self::calculateHistoricalShare( + $storeId, + $month, + $year, + $category, + $subcategory, + $species + ); + $productsWithoutHistory = $histResult['without_history'] ?? []; + + if (empty($productsWithoutHistory)) { + return []; + } + + $weightedResults = self::calculateWeightedSalesForProductsWithoutHistory( + $storeId, + $month, + $year, + $productsWithoutHistory + ); + + if (empty($weightedResults)) { + return []; + } + + $costs = self::calculateCostForProductsWithoutHistory( + $storeId, + $month, + $year, + $weightedResults + ); + + return $costs; + } /** * Получает идентификаторы товаров, похожих на указанный товар,