From: Alexander Smirnov Date: Wed, 7 Aug 2024 07:41:22 +0000 (+0300) Subject: Merge branch 'develop' into feature_smirnov_erp-88_upload_xlsx X-Git-Tag: 1.4~45^2~1 X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=d8c02cfcfe07e8abd4050c0fb1a16e087471cd6f;p=erp24_rep%2Fyii-erp24%2F.git Merge branch 'develop' into feature_smirnov_erp-88_upload_xlsx # Conflicts: # erp24/actions/motivation/IndexAction.php # erp24/composer.json # erp24/services/MotivationService.php # erp24/uploads/template_plan.xlsx --- d8c02cfcfe07e8abd4050c0fb1a16e087471cd6f diff --cc erp24/actions/motivation/IndexAction.php index 6c5dab83,f745fa06..aa76a282 --- a/erp24/actions/motivation/IndexAction.php +++ b/erp24/actions/motivation/IndexAction.php @@@ -31,13 -40,17 +40,20 @@@ class IndexAction extends Actio } } + $model = DynamicModel::validateData([ - 'store_id' => null, 'year' => null, 'month' => null - ], [ - [['store_id', 'year', 'month'], 'safe'] - ]); - $model->load(Yii::$app->request->get()); + 'store_id' => $currentUser->store_id, // Устанавливаем store_id текущего пользователя + 'year' => $currentDate->format('Y'), // Текущий год + 'month' => $currentDate->format('n') // Текущий месяц (1-12) + ], [ + [['store_id', 'year', 'month'], 'safe'] + ]); + + + ++ $motivations = Motivation::find()->all(); ++ $possibleStoreIds = ArrayHelper::getColumn($motivations, 'store_id'); + $motivations = Motivation::find()->all(); $possibleStoreIds = ArrayHelper::getColumn($motivations, 'store_id'); @@@ -50,12 -63,129 +66,129 @@@ $years = array_filter(range(2023, 20100), function ($k) use ($possibleYears) { return in_array($k, $possibleYears); }); + $years = array_combine($years, $years); $possibleMonth = ArrayHelper::getColumn($motivations, 'month'); - $months = array_filter(['Январь',' Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'], function ($k, $v) use ($possibleMonth) { - return in_array($v + 1, $possibleMonth); + $months = array_filter([1 => 'Январь', 2 => 'Февраль', 3 => 'Март', 4 => 'Апрель', 5 => 'Май', 6 => 'Июнь', 7 => 'Июль', 8 => 'Август', 9 => 'Сентябрь', 10 => 'Октябрь', 11 => 'Ноябрь', 12 => 'Декабрь'], function ($k, $v) use ($possibleMonth) { + return in_array($v, $possibleMonth); }, ARRAY_FILTER_USE_BOTH); - return $this->controller->render('index', - compact('model', 'stores', 'years', 'months')); + + // Загружаем данные из GET-запроса, если они есть + if (Yii::$app->request->get()) { + $model->load(Yii::$app->request->get()); + + if ($model->year === '') { + $model->year = $currentDate->format('Y'); + } else { + $model->year = intval($model->year); + // Проверяем, что год находится в допустимом диапазоне + if (!in_array($model->year, $years)) { + $model->year = $currentDate->format('Y'); + } + } + + $model->month = intval($model->month); + } + + $showTable = false; + $motivationData = []; + $daysInMonth = null; + $daysInLastWeek = null; + $week5Header = 'Неделя 5'; + + + + if ($model->store_id !== null && $model->year !== null && $model->month !== null) { + $showTable = true; + $motivationService = new MotivationService(); + + + // получаем данные из таблицы + $motivationDataTableSort = $motivationService->getMotivationDataTableSort($model->store_id, $model->year, $model->month); + + // Получаем количество дней в месяце + $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $model->month, $model->year); + + // Определяем, сколько дней в последней неделе + $daysInLastWeek = $daysInMonth - 28; + + // Список названий строк, для которых нужно выполнить расчет + $calculateFor = [ - ++ + '12', //'Аренда' + '13', //'Коммунальные услуги' + '14', //'Охрана' + '16', //'Доставка до клиента курьер' + '17', //'Доставка до клиента такси' + '22', //'Техническое обслуживание кассовых аппаратов' + '23', //'Интернет' + '27', // 'Бухгалтерские услуги: постановка и ведение БУ и НУ' + '28', //'Юридические услуги' + '29', //'Кадровое администрирование, охрана труда' + '30', //'Услуги по подбору персонала' + '31', //'Администрирование ИТ инфраструктуры (подключения к базам данных, ПО, почта, интернет)' + '32', //'Лицензия на ПО: ERP система' + '33' //'Продвижение и продажа товара через сайт' + + ]; + + + + // Проходим по всем данным и выполняем расчеты для каждой недели + foreach ($motivationDataTableSort as &$row) { + if (!key_exists('code', $row)){ + continue; + } + if (in_array($row['code'], $calculateFor)) { + $row['week1'] = $this->calculateWeekValue($row['plan'], $daysInMonth, 7); + $row['week2'] = $this->calculateWeekValue($row['plan'], $daysInMonth, 7); + $row['week3'] = $this->calculateWeekValue($row['plan'], $daysInMonth, 7); + $row['week4'] = $this->calculateWeekValue($row['plan'], $daysInMonth, 7); + + // Для 5-й недели используем оставшиеся дни + if ($daysInLastWeek > 0) { + $row['week5'] = $this->calculateWeekValue($row['plan'], $daysInMonth, $daysInLastWeek); + } else { + $row['week5'] = null; + } + } + } + + + + // Формируем заголовок для 5-й недели + if ($model->month == 2) { // Февраль + if ($daysInMonth == 29) { + $week5Header = 'Неделя 5
(29)'; + } + } else if ($daysInLastWeek > 0) { + $week5Header = "Неделя 5
(29-{$daysInMonth})"; + } + } + + + + + return $this->controller->render( + 'index', + compact('model', 'stores', 'years', 'months', 'motivationDataTableSort', 'showTable', + 'daysInMonth', 'daysInLastWeek', 'week5Header') + ); + } + + // Функция для расчета значения недели + private function calculateWeekValue($plan, $daysInMonth, $daysInWeek) + { + // Проверяем, является ли план пустым или нулевым + if (empty($plan) || $plan === null) { + return null; + } + + // Проверяем, чтобы избежать деления на ноль + if ($daysInMonth == 0) { + return null; + } + + return ($plan / $daysInMonth) * $daysInWeek; } - } + } diff --cc erp24/views/motivation/index.php index 5a91c905,38386efc..b2e27e42 --- a/erp24/views/motivation/index.php +++ b/erp24/views/motivation/index.php @@@ -9,7 -10,13 +10,15 @@@ use yii_app\records\MotivationCostsItem /** @var $stores array */ /** @var $years array */ /** @var $months array */ + /** @var $motivationDataTableSort array */ + /** @var $showTable bool */ + /** @var $daysInMonth int */ + /** @var $daysInLastWeek int */ + /** @var $week5Header string */ + + ++$this->registerJsFile('/js/motivation/index.js', ['position' => \yii\web\View::POS_END]); + $this->registerJsFile('/js/motivation/index.js', ['position' => \yii\web\View::POS_END]); ?> @@@ -40,29 -50,302 +52,306 @@@ 'pluginOptions' => [ 'allowClear' => true ], - ])->label(false) ?> + ])->label(false) ?> + +
+
Месяц:
+
+ field($model, 'month')->widget(Select2::class, [ + 'data' => $months, + 'language' => 'ru', + 'options' => ['placeholder' => 'Месяц...'], + 'pluginOptions' => [ + 'allowClear' => true + ], + ])->label(false) ?> +
+
+
+
'btn btn-secondary btn-sm']) ?>
+
-
Месяц:
-
field($model, 'month')->widget(Select2::class, [ - 'data' => $months, - 'language' => 'ru', - 'options' => ['placeholder' => 'Месяц...'], - 'pluginOptions' => [ - 'allowClear' => true - ], - ])->label(false) ?>
-
-
-
'btn btn-secondary btn-sm'])?>
+
'btn btn-success btn-sm', + 'onclick' => 'openUploadDictionary();'])?>
+
+
'btn btn-success btn-sm', + 'onclick' => 'openUploadDictionary();'])?>
+
- - + + + + +
Данные отсутствуют
+ + + + +

+ Магазин: store_id] ?? '' ?>, + Год: year ?>, + Месяц: month] ?? '' ?> +

+ new \yii\data\ArrayDataProvider([ + 'allModels' => array_filter($motivationDataTableSort, function ($item) { + return !in_array($item['name'], [ + 'Услуги агентов (тариф)', + 'Кадровое администрирование, охрана труда (тариф)', + 'Базовая премия', + 'Размер бонуса', + 'Пороговый коэффициент' + ]); + }), + 'pagination' => false, + + ]), + 'floatHeader' => true, + + 'columns' => [ + [ + 'attribute' => 'name', + + + 'header' => 'Наименование статьи доходов/расходов', + ], + [ + 'attribute' => 'plan', + 'value' => function ($model) { + if ($model["plan"] !== null) { + return Yii::$app->formatter->asDecimal($model["plan"], 2); + } else { + return " "; + } + }, + 'header' => 'План', + ], + [ + 'attribute' => 'correction', + 'value' => function ($model) { + if ($model["correction"] !== null && $model["correction"] !== '') { + return Yii::$app->formatter->asDecimal($model["correction"], 2); + } else { + return " "; + } + }, + + 'header' => 'Корректировка', + ], + [ + 'attribute' => 'week1', + 'class' => 'kartik\grid\FormulaColumn', + + 'header' => 'Неделя 1
(1-7)', + 'value' => function ($model) { + if ($model["week1"] !== null) { + return Yii::$app->formatter->asDecimal($model["week1"], 2); + } else { + return " "; + } + }, + + ], + [ + 'attribute' => 'week2', + 'class' => 'kartik\grid\FormulaColumn', + + 'header' => 'Неделя 2
(8-14)', + 'value' => function ($model) { + if ($model["week2"] !== null) { + return Yii::$app->formatter->asDecimal($model["week2"], 2); + } else { + return " "; + } + }, + + ], + [ + 'attribute' => 'week3', + 'class' => 'kartik\grid\FormulaColumn', + + 'header' => 'Неделя 3
(15-21)', + 'value' => function ($model) { + if ($model["week3"] !== null) { + return Yii::$app->formatter->asDecimal($model["week3"], 2); + } else { + return " "; + } + }, + + ], + [ + 'attribute' => 'week4', + 'class' => 'kartik\grid\FormulaColumn', + + 'header' => 'Неделя 4
(22-28)', + 'value' => function ($model) { + if ($model["week4"] !== null) { + return Yii::$app->formatter->asDecimal($model["week4"], 2); + } else { + return " "; + } + }, + + ], + [ + 'attribute' => 'week5', + 'class' => 'kartik\grid\FormulaColumn', + + 'header' => $week5Header, + 'value' => function ($model) { + if ($model["week5"] !== null) { + return Yii::$app->formatter->asDecimal($model["week5"], 2); + } else { + return " "; + } + }, + + ], + [ + 'attribute' => 'forecast', + + + 'header' => 'Прогноз за месяц', + 'value' => function ($model) { + if ($model["forecast"] !== null) { + return Yii::$app->formatter->asDecimal($model["forecast"], 2); + } else { + return " "; + } + }, + + ], + [ + 'attribute' => 'fact', + + + 'header' => 'Факт за месяц', + 'value' => function ($model) { + if ($model["fact"] !== null) { + return Yii::$app->formatter->asDecimal($model["fact"], 2); + } else { + return " "; + } + }, + ], + [ + 'attribute' => 'deviation', + + 'header' => 'Отклонение, %', + 'value' => function ($model) { + if ($model["deviation"] !== null) { + return Yii::$app->formatter->asDecimal($model["deviation"], 2); + } else { + return " "; + } + }, + + ] + + ], + 'rowOptions' => function ($model, $key, $index, $grid) { + $style = ''; + $class = ''; + + switch ($model['name']) { + case 'Выручка от реализации': + $style = 'font-weight: 700; background-color:#f7caac;'; + $class = 'fw-bold'; + break; + case 'Прочие услуги': + case 'Продажа товара': + case 'Чистая прибыль': + case 'Рентабельность по чистой прибыли, %': + $style = 'font-weight: bold; background-color:#a5a5a5;'; + break; + case 'Прямые расходы на продажу': + case 'Операционные расходы (Себестоимость)': + case 'Общехозяйственные расходы': + $style = 'font-weight: bold; background-color:#ccffff;'; + break; + case 'Маржинальный доход': + case 'Валовая прибыль': + $style = 'font-weight: bold; background-color:#c5e0b3;'; + break; + // Добавьте другие case для остальных специфических строк + default: + // Проверка для остальных строк, которые должны иметь жирный шрифт + $boldRows = [ + 'Стоимость товара', 'Услуги агентов (Расходы на закупку, хранение, доставку товара)', + 'Брак, пересорт', 'Расходные материалы (обеспечение продаж)', 'Оплата труда', 'Содержание помещения', + 'Расходы по доставке', 'Услуги маркетплейсов', 'Содержание и обслуживание ОС и НМА', + 'Услуги связи', 'Прочие операционные расходы', 'Бухгалтерия и финансы', + 'Юридическое сопровождение', 'HR- услуги', 'IT услуги', + 'Продвижение и продажа товара через сайт', 'Минимальный порог Чистой прибыли, руб.', + 'Расчет премии' + ]; + if (in_array($model['name'], $boldRows)) { + $style = 'font-weight: 700;'; + } + break; + } + + return ['class' => $class, 'style' => $style]; + }, + 'responsive' => true, + 'hover' => true + ]); ?> + +

+ Основные единицы для расчета +

+ + new \yii\data\ArrayDataProvider([ + 'allModels' => array_filter($motivationDataTableSort, function ($item) { + return in_array($item['name'], [ + 'Услуги агентов (тариф)', + 'Кадровое администрирование, охрана труда (тариф)', + 'Базовая премия', + 'Размер бонуса', + 'Пороговый коэффициент' + ]); + }), + 'pagination' => false, + ]), + 'floatHeader' => true, + 'columns' => [ + [ + 'attribute' => 'name', + 'header' => 'Наименование', + 'value' => function ($model) { + $mappings = [ + 'Услуги агентов (тариф)' => 'Услуги агентов - тариф', + 'Кадровое администрирование, охрана труда (тариф)' => 'Кадровое администрирование, охрана труда', + ]; + return isset($mappings[$model['name']]) ? $mappings[$model['name']] : $model['name']; + }, + ], + [ + 'attribute' => 'plan', + 'header' => 'Значение', + 'format' => ['decimal', 2], + ], + ], + 'responsive' => true, + 'hover' => true + ]); ?> + + + - + +