From: Alexander Smirnov Date: Fri, 16 Aug 2024 13:26:26 +0000 (+0300) Subject: Merge branch 'develop' into feature_smirnov_erp-105_final_fact_motivation X-Git-Tag: 1.4~20^2~6 X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=5b79e12576044195503bc8eda33db97fc8487a39;p=erp24_rep%2Fyii-erp24%2F.git Merge branch 'develop' into feature_smirnov_erp-105_final_fact_motivation # Conflicts: # erp24/services/MotivationService.php --- 5b79e12576044195503bc8eda33db97fc8487a39 diff --cc erp24/services/MotivationService.php index 42fe9ead,d2eecf56..016d6df4 --- a/erp24/services/MotivationService.php +++ b/erp24/services/MotivationService.php @@@ -520,210 -551,460 +551,667 @@@ class MotivationServic } } + public static function calculateSales($store_id, $year, $month) { + $monthStart = date("Y-m-d 00:00:00", strtotime($year . '-' . $month . '-1')); + $monthEnd = date("Y-m-t 23:59:59", strtotime($year . '-' . $month . '-1')); + + foreach (range(1, 5) as $ind) { + $weekStart = date("Y-m-d 00:00:00", strtotime("+" . (($ind - 1) * 7) . ' days', strtotime($monthStart))); + $weekEnd = date("Y-m-d 23:59:59", strtotime("+" . ($ind * 7 - 1) . ' days', strtotime($monthStart))); + if ($weekEnd > $monthEnd) { + $weekEnd = $monthEnd; + } + + $sales = Sales::find() + ->where(['between', 'date', $weekStart, $weekEnd]) + ->andWhere(['store_id' => $store_id]) + ->andWhere(['operation' => Sales::OPERATION_SALE]) + ->asArray()->all(); + $salesIds = ArrayHelper::getColumn($sales, 'id'); + + // Ищем чеки-возврат на текущие чеки + $returnSales = Sales::find()->where(['operation' => Sales::OPERATION_RETURN, 'sales_check' => $salesIds])->all(); + $returnSalesIds = ArrayHelper::getColumn($returnSales, 'sales_check'); + + // Offline sales + $salesOffline = Sales::find()->select(['SUM(summ) as total']) + ->where(['between', 'date', $weekStart, $weekEnd]) + ->andWhere(['order_id' => ['', '0']]) + ->andWhere(['store_id' => $store_id]) + ->andWhere(['operation' => Sales::OPERATION_SALE]) + ->andWhere(['NOT IN', 'id', $returnSalesIds]) + ->asArray()->one(); + + // Online sales + $salesOnline = Sales::find()->select(['SUM(summ) as total']) + ->where(['between', 'date', $weekStart, $weekEnd]) + ->andWhere(['NOT IN', 'order_id', ['', '0']]) + ->andWhere(['store_id' => $store_id]) + ->andWhere(['operation' => Sales::OPERATION_SALE]) + ->andWhere(['NOT IN', 'id', $returnSalesIds]) + ->asArray()->one(); + + $motivation = Motivation::find()->where(['store_id' => $store_id, 'year' => $year, 'month' => $month])->one(); + $motivationValueGroup = MotivationValueGroup::find()->where(['alias' => 'week' . $ind])->one(); + foreach (['Оффлайн продажи', 'Онлайн продажи'] as $topicInd => $topic) { + $motivationCostsItem = MotivationCostsItem::find()->where(['name' => $topic])->one(); + /** @var $motivationCostsItem MotivationCostsItem */ + if ($motivation) { + $motivationValue = MotivationValue::find()->where(['motivation_id' => $motivation->id, + 'motivation_group_id' => $motivationValueGroup->id, 'value_id' => $motivationCostsItem->code])->one(); + if (!$motivationValue) { + $motivationValue = new MotivationValue; + $motivationValue->motivation_id = $motivation->id; + $motivationValue->motivation_group_id = $motivationValueGroup->id; + $motivationValue->value_id = $motivationCostsItem->code; + $motivationValue->value_type = $motivationCostsItem->data_type; + } + $motivationValue->value_float = [$salesOffline, $salesOnline][$topicInd]['total']; + $motivationValue->save(); + if ($motivationValue->getErrors()) { + throw new \Exception(Json::encode($motivationValue->getErrors())); + } + } + } + } + } + + public static function calculateMonthForecast($store_id, $year, $month) { + $motivationCostsItem = MotivationCostsItem::find()->all(); + $motivationCostsItemCodes = ArrayHelper::getColumn($motivationCostsItem, 'code'); + $additionalItemsKeys = array_keys(self::$additionalItems); + + $items = array_merge($motivationCostsItemCodes, $additionalItemsKeys); + + $motivationValueGroups = []; + foreach (range(1, 5) as $ind) { + $motivationValueGroups []= MotivationValueGroup::find()->where(['alias' => 'week' . $ind])->one(); + } + /** @var $motivationValueGroups MotivationValueGroup[] */ + + $motivationValueGroupForecast = MotivationValueGroup::find()->where(['alias' => 'forecast'])->one(); + /** @var $motivationValueGroupForecast MotivationValueGroup */ + + $motivation = Motivation::find()->where(['store_id' => $store_id, 'year' => $year, 'month' => $month])->one(); + foreach ($items as $code) { + if ($motivation) { + $motivationValue = MotivationValue::find()->where(['motivation_id' => $motivation->id, + 'motivation_group_id' => $motivationValueGroupForecast->id, 'value_id' => $code])->one(); + $sum = 0; + $sum_type = MotivationCostsItem::DATA_TYPE_INT; + foreach (range(1, 5) as $ind) { + $mv = MotivationValue::find()->where(['motivation_id' => $motivation->id, + 'motivation_group_id' => $motivationValueGroups[$ind-1]->id, 'value_id' => $code])->one(); + /** @var $mv MotivationValue */ + if ($mv) { + switch ($mv->value_type) { + case MotivationCostsItem::DATA_TYPE_INT: { $sum += $mv->value_int; break; } + case MotivationCostsItem::DATA_TYPE_FLOAT: { $sum += $mv->value_float; $sum_type = MotivationCostsItem::DATA_TYPE_FLOAT; break; } + case MotivationCostsItem::DATA_TYPE_STRING: { throw new \Exception(Json::encode("Тип string невозможен для вычисления прогноза")); } + }; + } + } + if (!$motivationValue) { + $motivationValue = new MotivationValue; + $motivationValue->motivation_id = $motivation->id; + $motivationValue->motivation_group_id = $motivationValueGroupForecast->id; + $motivationValue->value_id = $code; + } + $motivationValue->value_type = $sum_type; + switch ($sum_type) { + case MotivationCostsItem::DATA_TYPE_INT: { $motivationValue->value_int = $sum; break; } + default: { $motivationValue->value_float = $sum; break; } + } + $motivationValue->save(); + if ($motivationValue->getErrors()) { + throw new \Exception(Json::encode($motivationValue->getErrors())); + } + } + } + } + + /** + * Получение записей по фактическому количеству смен в магазине за указанный период. + * + * @param string $startDate Дата начала периода в формате 'YYYY-MM-DD'. + * @param string $endDate Дата окончания периода в формате 'YYYY-MM-DD'. + * @param int $storeId Идентификатор магазина. + * + * @return array|null Возвращает массив записей из модели TimetableFactModel, соответствующих условиям, + * или null, если записи не найдены. + */ + public static function getTimetableFactRecordsByDateAndStore($startDate, $endDate, $storeId) + { + + + // Делаем запрос к TimetableFactModel + $records = TimetableFactModel::find() + ->where(['store_id' => $storeId]) + ->andWhere(['between', 'date_shift', $startDate, $endDate]) + ->all(); + + return $records; + } + + /** + * Получение суммы отпускных для магазина за определенный период. + * + * Этот метод выполняет поиск записей в таблице Timetable с `slot_type_id` равным 2, + * которые соответствуют указанному магазину и попадают в заданный период. + * Для каждой записи находится самая последняя запись о платеже сотрудника, + * и её значение используется для расчета суммы отпускных. + * + * @param string $startDate Дата начала периода в формате 'YYYY-MM-DD'. + * @param string $endDate Дата окончания периода в формате 'YYYY-MM-DD'. + * @param int $storeId Идентификатор магазина. + * + * @return float Возвращает общую сумму отпускных за указанный период. + * Если записей не найдено, возвращается 0.0. + */ + public static function getVacationsSum($startDate, $endDate, $storeId):float + { + // Делаем запрос к таблице Timetable для получения записей с slot_type_id = 2 (Timetable::TIMESLOT_VACATION) + $records = Timetable::find() + + ->where(['store_id' => $storeId]) + ->andWhere(['between', 'date', $startDate, $endDate]) + ->andWhere(['slot_type_id' => Timetable::TIMESLOT_VACATION]) + ->all(); + + // Проверяем, есть ли записи + if (empty($records)) { + return 0.0; // Возвращаем 0, если записей нет + } + + $dailyPayments = self::getEmployeePayments($endDate); + + $vacationsSum = 0.0; + foreach ($records as $record) { + $dailyPayment = isset($dailyPayments[$record->admin_id]) ? $dailyPayments[$record->admin_id] : 0.0; + + + $vacationsSum += $dailyPayment; + + } + + return $vacationsSum; + } + + /** + * Получение суммы всех недель для определенного `value_id` из таблицы `motivation_value`. + * + * Метод выполняет запрос к модели `MotivationValue`, чтобы получить все записи, + * соответствующие указанным `motivation_id` и `value_id`, и принадлежащие одной из + * определенных групп мотивации. Затем метод суммирует значения поля `value_float` + * из всех полученных записей. + * + * @param int $id Идентификатор мотивации (`motivation_id`). + * @param int $valueId Идентификатор значения (`value_id`). + * + * @return float Возвращает сумму всех значений `value_float` для заданного `value_id`. + * Если записи не найдены, возвращается 0. + */ + public static function getMonthSum($id, $valueId) + { + $aliases = ['week1', 'week2', 'week3', 'week4', 'week5']; + $groupIds = MotivationCostsItem::find() + ->select('id') + ->where(['alias' => $aliases]) + ->column(); + // Запрос к модели MotivationValue для получения записей с заданными условиями + $records = MotivationValue::find() + ->where(['motivation_id' => $id, 'value_id' => $valueId]) + ->andWhere(['in', 'motivation_group_id', $groupIds]) + ->all(); + + // Получаем массив значений value_float + $values = array_map(function($record) { + return $record->value_float; + }, $records); + + // Суммируем значения value_float + $totalSum = array_sum($values); + + return $totalSum; + } + + /** + * Создает или обновляет запись в таблице `motivation_value`. + * + * Метод пытается найти существующую запись в таблице `motivation_value` по заданным + * `motivation_id`, `group_id` и `value_id`. Если запись не найдена, создается новая. + * В зависимости от типа значения (`value_type`), метод устанавливает соответствующее поле + * (`value_float`, `value_int`, `value_string`) и сохраняет запись. + * Если сохранение успешно, возвращается ID записи. В случае ошибки возвращается `false`. + * + * @param int $motivationId Идентификатор мотивации. + * @param string $groupAlias Алиас группы мотивации. + * @param int $valueId Идентификатор значения. + * @param string $valueType Тип значения, который может быть 'float', 'int' или 'string'. + * @param string $value_string Значение, которое нужно сохранить (string). + * @param int $value_int Значение, которое нужно сохранить (int). + * @param float $value_float Значение, которое нужно сохранить (float). + * + * @return int|false Возвращает ID сохраненной или обновленной записи, либо `false` в случае ошибки. + * @throws \InvalidArgumentException Если передан некорректный тип значения. + */ + public static function saveOrUpdateMotivationValue($motivationId, $groupAlias, $valueId, $valueType, $value) + { + + // Найти id по алиасу в таблице MotivationValueGroup + $group = MotivationValueGroup::findOne(['alias' => $groupAlias]); + + if ($group === null) { + throw new \InvalidArgumentException("Неверный алиас группы: $groupAlias"); + } + + $groupId = $group->id; + // Найти существующую запись + $motivationValue = MotivationValue::findOne([ + 'motivation_id' => $motivationId, + 'motivation_group_id' => $groupId, + 'value_id' => $valueId, + ]); + + // Если запись не найдена, создать новую + if ($motivationValue === null) { + $motivationValue = new MotivationValue(); + $motivationValue->motivation_id = $motivationId; + $motivationValue->motivation_group_id = $groupId; + $motivationValue->value_id = $valueId; + $motivationValue->value_type = $valueType; + } + + // Установить значение в зависимости от типа + switch ($valueType) { + case 'float': + $motivationValue->value_float = $value; + break; + case 'int': + $motivationValue->value_int = $value; + break; + case 'string': + $motivationValue->value_string = $value; + break; + default: + throw new \InvalidArgumentException("Неправильное значение типа: $valueType"); + } + + // Сохранить запись и вернуть id или false + if ($motivationValue->save()) { + return $motivationValue->id; + } else { + Yii::error("Не удалось сохранить значение: " . json_encode($motivationValue->errors)); + return false; + } + } + + /** + * Определяет номер недели в месяце для указанной даты. + * + * Метод вычисляет номер недели в месяце на основе дня месяца, переданного в объекте `DateTime`. + * Недели считаются следующим образом: + * - 1-я неделя: с 1-го по 7-й день месяца. + * - 2-я неделя: с 8-го по 14-й день месяца. + * - 3-я неделя: с 15-го по 21-й день месяца. + * - 4-я неделя: с 22-го по 28-й день месяца. + * - 5-я неделя: с 29-го дня месяца и до конца месяца. + * + * @param string $date Строка даты в формате 'Y-m-d', для которой нужно определить номер недели. + * + * @return int Номер недели в месяце (от 1 до 5). + */ + public static function getWeekOfMonth($date) + { + // Преобразуем строку даты в день месяца + $dayOfMonth = intval(date('j', strtotime($date))); + + // Определяем номер недели в месяце + if ($dayOfMonth <= 7) { + return 1; + } elseif ($dayOfMonth <= 14) { + return 2; + } elseif ($dayOfMonth <= 21) { + return 3; + } elseif ($dayOfMonth <= 28) { + return 4; + } else { + return 5; + } + } + + /** + * Возвращает дату начала указанной недели месяца для заданной даты. + * + * Метод определяет начало недели в месяце на основе номера недели, переданного в параметре `$weekOfMonth`. + * Возвращается объект `DateTime`, представляющий первый день этой недели. Если номер недели выходит за пределы + * допустимых значений (1-5), по умолчанию возвращается начало первой недели месяца. + * + * @param string $date Строка даты в формате 'Y-m-d', представляющая дату, для которой определяется неделя. + * @param int $weekOfMonth Номер недели в месяце (от 1 до 5). + * + * @return string Дата начала указанной недели месяца в формате 'Y-m-d'. + */ + public static function getStartOfWeek($date, $weekOfMonth) + { + // Извлекаем год и месяц из строки даты + $year = date('Y', strtotime($date)); + $month = date('m', strtotime($date)); + + switch ($weekOfMonth) { + case 1: + return sprintf("%s-%s-01", $year, $month); + case 2: + return sprintf("%s-%s-08", $year, $month); + case 3: + return sprintf("%s-%s-15", $year, $month); + case 4: + return sprintf("%s-%s-22", $year, $month); + case 5: + return sprintf("%s-%s-29", $year, $month); + default: + throw new \InvalidArgumentException("Некорректное значение для недели месяца: $weekOfMonth"); + } + } + + /** + * Вычисление общей суммы фонда оплаты труда (ФОТ) - зарплаты и отпускных для магазина за указанный период. + * + * Этот метод сначала получает записи о сменах сотрудников за указанный период и сумму отпускных. + * Затем для каждой записи определяется соответствующая дневная зарплата, и она добавляется к общей сумме. + * Если в записи уже указана зарплата за смену, она используется вместо дневной зарплаты. + * В конце к общей сумме зарплат добавляется сумма отпускных. + * + * @param string $startDate Дата начала периода в формате 'YYYY-MM-DD'. + * @param string $endDate Дата окончания периода в формате 'YYYY-MM-DD'. + * @param int $storeId Идентификатор магазина. + * + * @return float Возвращает общую сумму фонда оплаты труда за указанный период, + * включая зарплаты и отпускные. + */ + public static function calculateTotalSalary($startDate, $endDate, $storeId):float + { + $records = self::getTimetableFactRecordsByDateAndStore($startDate, $endDate, $storeId); + $vacationSum = self::getVacationsSum($startDate, $endDate, $storeId); + $dailyPayments = self::getEmployeePayments($endDate); + $totalSalary = 0.0; + foreach ($records as $record) { + $dailyPayment = isset($dailyPayments[$record->admin_id]) ? $dailyPayments[$record->admin_id] : 0.0; + + if (!empty($record->salary_shift)) { + $totalSalary += $record->salary_shift; + } else { + $totalSalary += $dailyPayment; + } + } + + return $totalSalary + $vacationSum; + } + + + /** + * @param $currentDate + * @return array + */ + public static function getEmployeePayments($currentDate): array + { + // Запрос для получения всех записей, но только с учетом последней даты для каждого admin_id + $employeePayments = EmployeePayment::find() + ->where(['<=', 'date', $currentDate]) + ->orderBy(['admin_id' => SORT_ASC, 'date' => SORT_DESC]) + ->all(); + + // Преобразование результатов в массив admin_id => daily_payment + $dailyPayments = []; + foreach ($employeePayments as $payment) { + if (!isset($dailyPayments[$payment->admin_id])) { + $dailyPayments[$payment->admin_id] = $payment->daily_payment; + } + } + return $dailyPayments; + } + + /** + * Возвращает массив, представляющий недели месяца (с указаниме начала и конца недели) для указанного года и месяца. + * + * @param int|string $year Год в виде числа или строки (например, `2024` или `'2024'`). + * @param int|string $month Месяц в виде числа или строки (например, `2` или `'02'`). + * + * @return array Массив недель месяца, где каждая неделя представлена ассоциативным массивом с ключами 'start' и 'end'. + * Пример возвращаемого массива: + * [ + * ['start' => 1, 'end' => 7], + * ['start' => 8, 'end' => 14], + * ['start' => 15, 'end' => 21], + * ['start' => 22, 'end' => 28], + * ['start' => 29, 'end' => 30] // если месяц имеет больше 28 дней + * ] + */ + public static function getWeeksOfMonthArray($year, $month): array + { + // Массив недель, изначально включающий первые четыре недели + $weeks = [ + ['start' => 1, 'end' => 7], + ['start' => 8, 'end' => 14], + ['start' => 15, 'end' => 21], + ['start' => 22, 'end' => 28], + ]; + + // Определяем последний день месяца + $lastDayOfMonth = date('t', strtotime("$year-$month-01")); + + // Если в месяце больше 28 дней, добавляем пятую неделю + if ($lastDayOfMonth > 28) { + $weeks[] = ['start' => 29, 'end' => $lastDayOfMonth]; + } + + return $weeks; + } ++ + public static function calculateFactFormula(&$motivationDataTableSort) { + $indMap = []; + foreach ($motivationDataTableSort as $ind => $row) { + if (!key_exists('name', $row)) { + continue; + } + $indMap[$row['name']] = $ind; + } + foreach (range(0,7) as $ind) { + switch ($ind) { + case 0: { $column = 'plan'; break; } + case 6: { $column = 'fact'; break; } + case 7: { $column = 'forecast'; break; } + default: { $column = 'week' . $ind; break; } + } + + $motivationDataTableSort[$indMap["Продажа товара"]][$column] = + $motivationDataTableSort[$indMap["Оффлайн продажи"]][$column] + + $motivationDataTableSort[$indMap["Онлайн продажи"]][$column]; + + $motivationDataTableSort[$indMap["Прочие услуги"]][$column] = + $motivationDataTableSort[$indMap["Услуги по сборке"]][$column] + + $motivationDataTableSort[$indMap["Услуги по доставке"]][$column]; + + $motivationDataTableSort[$indMap["Выручка от реализации"]][$column] = + $motivationDataTableSort[$indMap["Продажа товара"]][$column] + + $motivationDataTableSort[$indMap["Прочие услуги"]][$column]; + + $motivationDataTableSort[$indMap["Услуги агентов (Расходы на закупку, хранение, доставку товара)"]][$column] = + ( + $motivationDataTableSort[$indMap["Стоимость товара"]][$column] + + $motivationDataTableSort[$indMap["Брак, пересорт"]][$column] + ) * + $motivationDataTableSort[$indMap["Услуги агентов (тариф)"]][$column]; + + $motivationDataTableSort[$indMap["Брак, пересорт"]][$column] = + $motivationDataTableSort[$indMap["Брак с поставки"]][$column] + + $motivationDataTableSort[$indMap["Списание неликвидного товара: порча, истечение срока годности"]][$column] + + $motivationDataTableSort[$indMap["Брак из-за поломки оборудования"]][$column] + + $motivationDataTableSort[$indMap["Пересорт"]][$column]; + + $motivationDataTableSort[$indMap["Прямые расходы на продажу"]][$column] = + $motivationDataTableSort[$indMap["Стоимость товара"]][$column] + + $motivationDataTableSort[$indMap["Услуги агентов (Расходы на закупку, хранение, доставку товара)"]][$column] + + $motivationDataTableSort[$indMap["Брак, пересорт"]][$column] + + $motivationDataTableSort[$indMap["Расходные материалы (обеспечение продаж)"]][$column]; + + $motivationDataTableSort[$indMap["Маржинальный доход"]][$column] = + $motivationDataTableSort[$indMap["Выручка от реализации"]][$column] - + $motivationDataTableSort[$indMap["Прямые расходы на продажу"]][$column]; + + $motivationDataTableSort[$indMap["Оплата труда"]][$column] = + $motivationDataTableSort[$indMap["Фонд оплаты труда персонала"]][$column]; + + $motivationDataTableSort[$indMap["Содержание помещения"]][$column] = + $motivationDataTableSort[$indMap["Аренда"]][$column] + + $motivationDataTableSort[$indMap["Коммунальные услуги"]][$column] + + $motivationDataTableSort[$indMap["Охрана"]][$column] + + $motivationDataTableSort[$indMap["Услуги по уборке помещений и территории"]][$column]; + + $motivationDataTableSort[$indMap["Расходы по доставке"]][$column] = + $motivationDataTableSort[$indMap["Доставка до клиента курьер"]][$column] + + $motivationDataTableSort[$indMap["Доставка до клиента такси"]][$column]; + + $motivationDataTableSort[$indMap["Содержание и обслуживание ОС и НМА"]][$column] = + $motivationDataTableSort[$indMap["Холодильное оборудование (ремонт, содержание, ТО)"]][$column] + + $motivationDataTableSort[$indMap["Расходы на содержание и ремонт оргтехники, в т.ч. расходные материалы"]][$column] + + $motivationDataTableSort[$indMap["Расходы на содержание и ремонт прочих ОС"]][$column] + + $motivationDataTableSort[$indMap["Техническое обслуживание кассовых аппаратов"]][$column]; + + $motivationDataTableSort[$indMap["Услуги связи"]][$column] = + $motivationDataTableSort[$indMap["Интернет"]][$column]; + + $motivationDataTableSort[$indMap["Прочие операционные расходы"]][$column] = + $motivationDataTableSort[$indMap["Хозяйственные товары"]][$column] + + $motivationDataTableSort[$indMap["Канцтовары"]][$column] + + $motivationDataTableSort[$indMap["Вода питьевая"]][$column]; + + $motivationDataTableSort[$indMap["Операционные расходы (Себестоимость)"]][$column] = + $motivationDataTableSort[$indMap["Оплата труда"]][$column] + + $motivationDataTableSort[$indMap["Содержание помещения"]][$column] + + $motivationDataTableSort[$indMap["Расходы по доставке"]][$column] + + $motivationDataTableSort[$indMap["Услуги маркетплейсов"]][$column] + + $motivationDataTableSort[$indMap["Содержание и обслуживание ОС и НМА"]][$column] + + $motivationDataTableSort[$indMap["Услуги связи"]][$column] + + $motivationDataTableSort[$indMap["Прочие операционные расходы"]][$column]; + + $motivationDataTableSort[$indMap["Валовая прибыль"]][$column] = + $motivationDataTableSort[$indMap["Маржинальный доход"]][$column] - + $motivationDataTableSort[$indMap["Операционные расходы (Себестоимость)"]][$column]; + + $motivationDataTableSort[$indMap["Бухгалтерия и финансы"]][$column] = + $motivationDataTableSort[$indMap["Бухгалтерские услуги: постановка и ведение БУ и НУ"]][$column]; + + $motivationDataTableSort[$indMap["Юридическое сопровождение"]][$column] = + $motivationDataTableSort[$indMap["Юридические услуги"]][$column]; + + $motivationDataTableSort[$indMap["HR- услуги"]][$column] = + $motivationDataTableSort[$indMap["Кадровое администрирование, охрана труда"]][$column] + + $motivationDataTableSort[$indMap["Услуги по подбору персонала"]][$column]; + + $motivationDataTableSort[$indMap["IT услуги"]][$column] = + $motivationDataTableSort[$indMap["Администрирование ИТ инфраструктуры (подключения к базам данных, ПО, почта, интернет)"]][$column] + + $motivationDataTableSort[$indMap["Лицензия на ПО: ERP система"]][$column]; + + $motivationDataTableSort[$indMap["Общехозяйственные расходы"]][$column] = + $motivationDataTableSort[$indMap["Бухгалтерия и финансы"]][$column] + + $motivationDataTableSort[$indMap["Юридическое сопровождение"]][$column] + + $motivationDataTableSort[$indMap["HR- услуги"]][$column] + + $motivationDataTableSort[$indMap["IT услуги"]][$column] + + $motivationDataTableSort[$indMap["Продвижение и продажа товара через сайт"]][$column]; + + $motivationDataTableSort[$indMap["Чистая прибыль"]][$column] = + $motivationDataTableSort[$indMap["Валовая прибыль"]][$column] - + $motivationDataTableSort[$indMap["Общехозяйственные расходы"]][$column]; + + $c5 = $motivationDataTableSort[$indMap["Выручка от реализации"]][$column]; + if ($c5 != 0) { + $motivationDataTableSort[$indMap["Рентабельность по чистой прибыли, %"]][$column] = + $motivationDataTableSort[$indMap["Чистая прибыль"]][$column] / $c5; + } + + if ($ind > 0) { + $c62 = $motivationDataTableSort[$indMap["Чистая прибыль"]][$column]; + $b64 = $motivationDataTableSort[$indMap["Минимальный порог Чистой прибыли, руб."]][$ind == 6 ? "plan" : $column]; + + $j66 = 0; + if ($c62 >= $b64) { + $formula = $c62 - $c5 * $motivationDataTableSort[$indMap["Пороговый коэффициент"]]["plan"]; + if ($formula > 0) { + $j66 = $formula; + } + } + $motivationDataTableSort[$indMap["Расчет премии"]][$column] = $j66; + } + } + + // Отклонение + $deviationFunc = function ($name) use(&$motivationDataTableSort, &$indMap) { + if ($motivationDataTableSort[$indMap[$name]]["plan"] != 0) { + $motivationDataTableSort[$indMap[$name]]["deviation"] = + $motivationDataTableSort[$indMap[$name]]["fact"] / + $motivationDataTableSort[$indMap[$name]]["plan"]; + } + }; + $names = [ + "Выручка от реализации", + "Продажа товара", + "Оффлайн продажи", + "Онлайн продажи", + "Прочие услуги", + "Услуги по сборке", + "Услуги по доставке", + "Прямые расходы на продажу", + "Стоимость товара", + "Услуги агентов (Расходы на закупку, хранение, доставку товара)", + "Брак, пересорт", + "Брак с поставки", + "Списание неликвидного товара: порча, истечение срока годности", + "Брак из-за поломки оборудования", + "Пересорт", + "Расходные материалы (обеспечение продаж)", + "Маржинальный доход", + "Операционные расходы (Себестоимость)", + "Оплата труда", + "Фонд оплаты труда персонала", + "Содержание помещения", + "Аренда", + "Коммунальные услуги", + "Охрана", + "Услуги по уборке помещений и территории", + "Расходы по доставке", + "Доставка до клиента курьер", + "Доставка до клиента такси", + "Услуги маркетплейсов", + "Содержание и обслуживание ОС и НМА", + "Холодильное оборудование (ремонт, содержание, ТО)", + "Расходы на содержание и ремонт оргтехники, в т.ч. расходные материалы", + "Расходы на содержание и ремонт прочих ОС", + "Техническое обслуживание кассовых аппаратов", + "Услуги связи", + "Интернет", + "Прочие операционные расходы", + "Хозяйственные товары", + "Канцтовары", + "Вода питьевая", + "Валовая прибыль", + "Общехозяйственные расходы", + "Бухгалтерия и финансы", + "Бухгалтерские услуги: постановка и ведение БУ и НУ", + "Юридическое сопровождение", + "Юридические услуги", + "HR- услуги", + "Кадровое администрирование, охрана труда", + "Услуги по подбору персонала", + "IT услуги", + "Администрирование ИТ инфраструктуры (подключения к базам данных, ПО, почта, интернет)", + "Лицензия на ПО: ERP система", + "Продвижение и продажа товара через сайт", + "Чистая прибыль", + "Рентабельность по чистой прибыли, %" + ]; + foreach ($names as $name) { + $deviationFunc($name); + } + } }