From: fomichev Date: Thu, 19 Jun 2025 11:39:41 +0000 (+0300) Subject: Merge branch 'refs/heads/develop' into feature_fomichev_erp_424_change_marketplace... X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=2865a390b6b973c11b193958887677f5821ec4e9;p=erp24_rep%2Fyii-erp24%2F.git Merge branch 'refs/heads/develop' into feature_fomichev_erp_424_change_marketplace+orders_statuses_1c_relations # Conflicts: # erp24/commands/CronController.php --- 2865a390b6b973c11b193958887677f5821ec4e9 diff --cc erp24/commands/CronController.php index 4ae41c10,db9af7bf..401457ef --- a/erp24/commands/CronController.php +++ b/erp24/commands/CronController.php @@@ -90,18 -98,7 +98,18 @@@ class CronController extends Controlle $statusesData = []; foreach ($statuses as $status) { /* @var $status MarketplaceOrder1cStatuses */ + $relftions = $status->relationsFrom; + $relationsToSend = []; + foreach ($relftions as $relation) { + $statusTo = MarketplaceOrder1cStatuses::find()->where(['id' => $relation['status_id_to']])->one(); + if(!$statusTo) { + continue; + } + $relationsToSend[] = + $statusTo->status_id; + } - $statusesData []= [ + $statusesData [] = [ + 'index_number' => $status->posit, 'status_name' => $status->status, 'hint' => $status->status_instruction, 'status_id' => $status->status_id, @@@ -1567,6 -1563,136 +1575,136 @@@ return ExitCode::OK; } + public function actionAutoplannogrammaCalculate(): void + { + $date = new DateTime(); + $date->modify('+2 months'); + $planDate = $date->format('Y-m-01'); + $month = (int)$date->format('m'); + $year = (int)$date->format('Y'); + + $service = new AutoPlannogrammaService(); + $stores = CityStore::find()->where(['visible' => CityStore::IS_VISIBLE])->all(); + + $this->stdout("Начало расчетов автопланограммы для $planDate\n", BaseConsole::FG_GREEN); + + foreach ($stores as $store) { + $this->stdout("Начало расчетов автопланограммы для магазина ID: {$store->id} ({$store->name})\n", BaseConsole::FG_YELLOW); + + try { + $forecastParams = [ + 'month' => $month, + 'year' => $year, + 'type' => AutoPlannogrammaService::TYPE_SALES, + 'store_id' => $store->id, + 'category' => null, + 'subcategory' => null, + 'species' => null, + 'plan_date' => $planDate + ]; + + $forecast = $service->calculateFullForecastForWeek($forecastParams); + $writeOffsForecast = $service->getWeeklyProductsWriteoffsForecast($month, $year, $forecast, $store->id); + $salesForecast = $service->getWeeklyBouquetProductsSalesForecast($month, $year, $store->id); + + $this->stdout("Рассчитана автопланограмма для магазина {$store->name}\n", BaseConsole::FG_GREEN); + + $existingRecords = Autoplannogramma::find() + ->where([ + 'month' => $month, + 'year' => $year, + 'store_id' => $store->id, + 'week' => array_unique(array_column($forecast, 'week')) + ]) + ->indexBy(fn($record) => $record->week . '_' . $record->product_id) + ->all(); + + foreach ($forecast as $item) { + $key = $item['week'] . '_' . $item['product_id']; + $model = $existingRecords[$key] ?? new Autoplannogramma(); + $productId = $item['product_id']; + $week = $item['week']; + $quantity = (float)($item['forecast_week_pieces'] ?? 0); + + $details = []; + $total = $quantity; + + if (!empty($writeOffsForecast[$productId][$week]['writeOffs'])) { + $writeOffs = $writeOffsForecast[$productId][$week]['writeOffs']; + $details['writeOffs']['quantity'] = $writeOffs; + $total += is_array($writeOffs) ? array_sum($writeOffs) : (float)$writeOffs; + } + + foreach (['offline', 'online', 'marketplace'] as $type) { + if (!isset($salesForecast[$store->id][$productId][$type])) { + $details[$type] = ['share' => 0, 'quantity' => 0, 'groups' => []]; + continue; + } + $data = $salesForecast[$store->id][$productId][$type]; + if (!is_array($data)) { + $details[$type] = ['share' => 0, 'quantity' => 0, 'groups' => []]; + continue; + } + $block = []; + $groups = []; + $share = isset($salesForecast[$store->id][$type]['share']) ? (float)$salesForecast[$store->id][$type]['share'] : 0; + $block['share'] = $share; + $block['quantity'] = (float) sprintf('%.2f', round($quantity * $share, 2)); // Нормализует -0 до 0 + $total += $block['quantity']; + foreach ($data as $k => $v) { + $val = (float)$v; + $groups[$k] = $val; + $total += $val; + } + $block['groups'] = !empty($groups) ? $groups : []; + $details[$type] = $block; + } + + $details['forecast'] = ['quantity' => $quantity]; + - $total = (float) sprintf('%.2f', $total); ++ $total = (float) sprintf('%.2f', $total); + + $needsUpdate = !$model->isNewRecord && ( + $model->calculate !== $quantity || + ceil((float)$model->total) !== ceil($total) || + json_encode($model->details, JSON_UNESCAPED_UNICODE) !== json_encode($details, JSON_UNESCAPED_UNICODE) + ); + + if ($model->isNewRecord || $needsUpdate) { + $model->setAttributes([ + 'month' => $month, + 'year' => $year, + 'week' => $week, + 'product_id' => $productId, + 'store_id' => $store->id, + 'is_archive' => false, + 'capacity_type' => 1, + 'details' => json_encode($details, JSON_UNESCAPED_UNICODE), + 'calculate' => $quantity, + 'modify' => ceil($total), + 'total' => ceil($total) + ]); + + if (!$model->save()) { + $errors = implode('; ', array_map( + fn($attr, $attrErrors) => "$attr: " . implode(', ', $attrErrors), + array_keys($model->getErrors()), + $model->getErrors() + )); + $this->stderr("Ошибка при сохранении модели: $errors\n", BaseConsole::FG_RED); + Yii::error("Ошибка сохранения Autoplannogramma: $errors", __METHOD__); + } + } + } + + $this->stdout("Сохранена автопланограмма для магазина {$store->name}\n", BaseConsole::FG_GREEN); + } catch (Throwable $e) { + $this->stderr("Ошибка при расчёте прогноза: {$e->getMessage()}\n", BaseConsole::FG_RED); + Yii::error("Ошибка при расчёте прогноза: " . $e->getMessage(), __METHOD__); + continue; + } + } + + $this->stdout("Расчет и сохранение автопланограммы завершены\n", BaseConsole::FG_GREEN); + } }