return $weeks;
}
+ public static function calculateFactFormula($motivationDataTableSort, $year, $month) {
+ // Определяем последний день месяца
+ $lastDayOfMonth = date('t', strtotime("$year-$month-01"));
+
+ $indMap = [];
+ foreach ($motivationDataTableSort as $ind => $row) {
+ if (!key_exists('code', $row)) {
+ continue;
+ }
+ $indMap[intval($row['code'])] = $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[self::CODE_SALE_OF_GOODS]][$column] = // "Продажа товара"
+ $motivationDataTableSort[$indMap[self::CODE_OFFLINE_SALES]][$column] + // "Оффлайн продажи"
+ $motivationDataTableSort[$indMap[self::CODE_ONLINE_SALES]][$column] + 0; // "Онлайн продажи"
+
+ $motivationDataTableSort[$indMap[self::CODE_OTHER_SERVICES]][$column] = // "Прочие услуги"
+ $motivationDataTableSort[$indMap[self::CODE_ASSEMBLY_SERVICES]][$column] + // "Услуги по сборке"
+ $motivationDataTableSort[$indMap[self::CODE_DELIVERY_SERVICES]][$column] + 0; // "Услуги по доставке"
+
+ $motivationDataTableSort[$indMap[self::CODE_REVENUE_FROM_SALES]][$column] = // "Выручка от реализации"
+ $motivationDataTableSort[$indMap[self::CODE_SALE_OF_GOODS]][$column] + // "Продажа товара"
+ $motivationDataTableSort[$indMap[self::CODE_OTHER_SERVICES]][$column] + 0; // "Прочие услуги"
+
+ $motivationDataTableSort[$indMap[self::CODE_COST_PRICE_OF_GOODS]][$column] = // "Себестоимость товара"
+ $motivationDataTableSort[$indMap[self::CODE_COSTS_OF_GOODS]][$column] + 0; // "Стоимость товара"
+
+ $motivationDataTableSort[$indMap[self::CODE_AGENT_SERVICES_EXPENSES_FOR_PURCHASING_STORING_DELIVERING_GOODS]][$column] = // "Услуги агентов (Расходы на закупку, хранение, доставку товара)"
+ (
+ $motivationDataTableSort[$indMap[self::CODE_COST_PRICE_OF_GOODS]][$column] + // "Себестоимость товара"
+ $motivationDataTableSort[$indMap[self::CODE_DEFECT_RESORTING]][$column] // "Брак, пересорт"
+ ) *
+ $motivationDataTableSort[$indMap[self::CODE_AGENT_SERVICES_TARIFF]][$column]; // "Услуги агентов (тариф)"
+
+ $motivationDataTableSort[$indMap[self::CODE_DEFECT_RESORTING]][$column] = // "Брак, пересорт"
+ $motivationDataTableSort[$indMap[self::CODE_DELIVERY_DEFECTS]][$column] + // "Брак с поставки"
+ $motivationDataTableSort[$indMap[self::CODE_WRITE_OFF_ILLIQUID_GOODS_SPOOLAGE_EXPIRATION_OF_SHELF_LIFE]][$column] + // "Списание неликвидного товара: порча, истечение срока годности"
+ $motivationDataTableSort[$indMap[self::CODE_EQUIPMENT_FAILURE_DEFECT]][$column] + // "Брак из-за поломки оборудования"
+ $motivationDataTableSort[$indMap[self::CODE_REGRADING]][$column] + 0; // "Пересорт"
+
+ $motivationDataTableSort[$indMap[self::CODE_DIRECT_SELLING_COSTS]][$column] = // "Прямые расходы на продажу"
+ $motivationDataTableSort[$indMap[self::CODE_COST_PRICE_OF_GOODS]][$column] + // "Себестоимость товара"
+ $motivationDataTableSort[$indMap[self::CODE_AGENT_SERVICES_EXPENSES_FOR_PURCHASING_STORING_DELIVERING_GOODS]][$column] + // "Услуги агентов (Расходы на закупку, хранение, доставку товара)"
+ $motivationDataTableSort[$indMap[self::CODE_DEFECT_RESORTING]][$column] + // "Брак, пересорт"
+ $motivationDataTableSort[$indMap[self::CODE_CONSUMABLES_SALES_SUPPORT]][$column] + 0; // "Расходные материалы (обеспечение продаж)"
+
+ $motivationDataTableSort[$indMap[self::CODE_MARGINAL_INCOME]][$column] = // "Маржинальный доход"
+ $motivationDataTableSort[$indMap[self::CODE_REVENUE_FROM_SALES]][$column] - // "Выручка от реализации"
+ $motivationDataTableSort[$indMap[self::CODE_DIRECT_SELLING_COSTS]][$column] + 0; // "Прямые расходы на продажу"
+
+ $motivationDataTableSort[$indMap[self::CODE_PAYMENT]][$column] = // "Оплата труда"
+ $motivationDataTableSort[$indMap[self::CODE_PAYROLL_FUND]][$column] + 0; // "Фонд оплаты труда персонала"
+
+ $motivationDataTableSort[$indMap[self::CODE_MAINTENANCE_OF_PRIMISES]][$column] = // "Содержание помещения"
+ $motivationDataTableSort[$indMap[self::CODE_RENT]][$column] + // "Аренда"
+ $motivationDataTableSort[$indMap[self::CODE_PUBLIC_SERVICES]][$column] + // "Коммунальные услуги"
+ $motivationDataTableSort[$indMap[self::CODE_SECURITY]][$column] + // "Охрана"
+ $motivationDataTableSort[$indMap[self::CODE_CLEANING_SERVICES_FOR_PREMISES_AND_TERRITORY]][$column] + 0; // "Услуги по уборке помещений и территории"
+
+ $motivationDataTableSort[$indMap[self::CODE_DELIVERY_COST]][$column] = // "Расходы по доставке"
+ $motivationDataTableSort[$indMap[self::CODE_DELIVERY_TO_CLIENT_CURRIER]][$column] + // "Доставка до клиента курьер"
+ $motivationDataTableSort[$indMap[self::CODE_DELIVERY_TO_CLIENT_TAXI]][$column] + 0; // "Доставка до клиента такси"
+
+ $motivationDataTableSort[$indMap[self::CODE_MAINTENANCE_AND_SERVICE_OF_FIXED_ASSETS_AND_INTANGIBLE_ASSETS]][$column] = // "Содержание и обслуживание ОС и НМА"
+ $motivationDataTableSort[$indMap[self::CODE_REFRIGERATION_EQUIPMENT_REPAIR_MAINTANANCE]][$column] + // "Холодильное оборудование (ремонт, содержание, ТО)"
+ $motivationDataTableSort[$indMap[self::CODE_COSTS_FOR_MAINTENANCE_AND_REPAIR_OF_OFFICE_EQUIPMENT_INCLUDING_CONSUMABLES]][$column] + // "Расходы на содержание и ремонт оргтехники, в т.ч. расходные материалы"
+ $motivationDataTableSort[$indMap[self::CODE_EXPENSES_FOR_MAINENANCE_AND_REPAIR_OF_OTHER_FIXED_ASSETS]][$column] + // "Расходы на содержание и ремонт прочих ОС"
+ $motivationDataTableSort[$indMap[self::CODE_MAINTENANCE_OF_CASH_REGISTERS]][$column] + 0; // "Техническое обслуживание кассовых аппаратов"
+
+ $motivationDataTableSort[$indMap[self::CODE_COMMUNICATION_SERVICES]][$column] = // "Услуги связи"
+ $motivationDataTableSort[$indMap[self::CODE_INTERNET]][$column] + 0; // "Интернет"
+
+ $motivationDataTableSort[$indMap[self::CODE_OTHER_OPERATING_EXPENSES]][$column] = // "Прочие операционные расходы"
+ $motivationDataTableSort[$indMap[self::CODE_HOUSEHOLD_GOODS]][$column] + // "Хозяйственные товары"
+ $motivationDataTableSort[$indMap[self::CODE_STATIONARY]][$column] + // "Канцтовары"
+ $motivationDataTableSort[$indMap[self::CODE_DRINKING_WATER]][$column] + 0; // "Вода питьевая"
+
+ $motivationDataTableSort[$indMap[self::CODE_OPERATIONAL_EXPANSES_COST]][$column] = // "Операционные расходы (Себестоимость)"
+ $motivationDataTableSort[$indMap[self::CODE_PAYMENT]][$column] + // "Оплата труда"
+ $motivationDataTableSort[$indMap[self::CODE_MAINTENANCE_OF_PRIMISES]][$column] + // "Содержание помещения"
+ $motivationDataTableSort[$indMap[self::CODE_DELIVERY_COST]][$column] + // "Расходы по доставке"
+ $motivationDataTableSort[$indMap[self::CODE_MARKETPLACE_SERVICES]][$column] + // "Услуги маркетплейсов"
+ $motivationDataTableSort[$indMap[self::CODE_MAINTENANCE_AND_SERVICE_OF_FIXED_ASSETS_AND_INTANGIBLE_ASSETS]][$column] + // "Содержание и обслуживание ОС и НМА"
+ $motivationDataTableSort[$indMap[self::CODE_COMMUNICATION_SERVICES]][$column] + // "Услуги связи"
+ $motivationDataTableSort[$indMap[self::CODE_OTHER_OPERATING_EXPENSES]][$column] + 0; // "Прочие операционные расходы"
+
+ $motivationDataTableSort[$indMap[self::CODE_GROSS_PROFIT]][$column] = // "Валовая прибыль"
+ $motivationDataTableSort[$indMap[self::CODE_MARGINAL_INCOME]][$column] - // "Маржинальный доход"
+ $motivationDataTableSort[$indMap[self::CODE_OPERATIONAL_EXPANSES_COST]][$column] + 0; // "Операционные расходы (Себестоимость)"
+
+ $motivationDataTableSort[$indMap[self::CODE_ACCOUNTING_AND_FINANCE]][$column] = // "Бухгалтерия и финансы"
+ $motivationDataTableSort[$indMap[self::CODE_ACCOUNTING_SERVICES_SETTING_UP_AND_MAINTAINING_ACCOUNTING_AND_TAX_RECORDS]][$column] + 0; // "Бухгалтерские услуги: постановка и ведение БУ и НУ"
+
+ $motivationDataTableSort[$indMap[self::CODE_LEGAL_SUPPORT]][$column] = // "Юридическое сопровождение"
+ $motivationDataTableSort[$indMap[self::CODE_LEGAL_SERVICES]][$column] + 0; // "Юридические услуги"
+
+ $motivationDataTableSort[$indMap[self::CODE_HR_SERVICES]][$column] = // "HR- услуги"
+ $motivationDataTableSort[$indMap[self::CODE_PERSONAL_ADMINISTRATION_LABOR_PROTECTION]][$column] + // "Кадровое администрирование, охрана труда"
+ $motivationDataTableSort[$indMap[self::CODE_RECRUITMENT_SERVICES]][$column] + 0; // "Услуги по подбору персонала"
+
+ $motivationDataTableSort[$indMap[self::CODE_IT_SERVICES]][$column] = // "IT услуги"
+ $motivationDataTableSort[$indMap[self::CODE_ADMINISTRATION_OF_IT_INFRASTRUCTURE_CONNECTIONS_TO_DATABASES_SOFTWARE_MAIL_INTERNET]][$column] + // "Администрирование ИТ инфраструктуры (подключения к базам данных, ПО, почта, интернет)"
+ $motivationDataTableSort[$indMap[self::CODE_SOFTWARE_LICENSE_ERP_SYSTEM]][$column] + 0; // "Лицензия на ПО: ERP система"
+
+ $motivationDataTableSort[$indMap[self::CODE_GENERAL_BUSINESS_EXPENSES]][$column] = // "Общехозяйственные расходы"
+ $motivationDataTableSort[$indMap[self::CODE_ACCOUNTING_AND_FINANCE]][$column] + // "Бухгалтерия и финансы"
+ $motivationDataTableSort[$indMap[self::CODE_LEGAL_SUPPORT]][$column] + // "Юридическое сопровождение"
+ $motivationDataTableSort[$indMap[self::CODE_HR_SERVICES]][$column] + // "HR- услуги"
+ $motivationDataTableSort[$indMap[self::CODE_IT_SERVICES]][$column] + // "IT услуги"
+ $motivationDataTableSort[$indMap[self::CODE_PROMOTION_AND_SALE_OF_GOODS_THROUGH_THE_WEBSITE]][$column] + 0; // "Продвижение и продажа товара через сайт"
+
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT]][$column] = // "Чистая прибыль"
+ $motivationDataTableSort[$indMap[self::CODE_GROSS_PROFIT]][$column] - // "Валовая прибыль"
+ $motivationDataTableSort[$indMap[self::CODE_GENERAL_BUSINESS_EXPENSES]][$column] + 0; // "Общехозяйственные расходы"
+
+ $c5 = $motivationDataTableSort[$indMap[self::CODE_REVENUE_FROM_SALES]][$column]; // "Выручка от реализации"
+ if ($c5 != 0) {
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_MARGIN_PERCENT]][$column] = // "Рентабельность по чистой прибыли, %"
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT]][$column] / $c5; // "Чистая прибыль"
+ }
+
+ if ($ind == 0) {
+ $b62 = $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT]]["plan"]; // "Чистая прибыль"
+ if ($b62 > 0) {
+ $b64 = $b62 * 0.9;
+ } else {
+ $b64 = $b62 * 1.1;
+ }
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_THRESHOLD_RUB]]["plan"] = $b64; // "Минимальный порог Чистой прибыли, руб."
+ }
+ if ($ind >= 1 && $ind <= 4) {
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_THRESHOLD_RUB]][$column] = // "Минимальный порог Чистой прибыли, руб."
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_THRESHOLD_RUB]]["plan"] / $lastDayOfMonth * 7; // "Минимальный порог Чистой прибыли, руб."
+ }
+ if ($ind == 5) {
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_THRESHOLD_RUB]][$column] = // "Минимальный порог Чистой прибыли, руб."
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_THRESHOLD_RUB]]["plan"] / $lastDayOfMonth * ($lastDayOfMonth - 4 * 7); // "Минимальный порог Чистой прибыли, руб."
+ }
+ if ($ind == 7) {
+ $sum = 0;
+ foreach (range(1, 5) as $index) {
+ $sum += $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_THRESHOLD_RUB]]['week' . $index]; // "Минимальный порог Чистой прибыли, руб."
+ }
+ $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_THRESHOLD_RUB]][$column] = $sum; // "Минимальный порог Чистой прибыли, руб."
+ }
+
+ // if ($ind > 0) {
+ // $c62 = $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT]][$column]; // "Чистая прибыль"
+ // $b64 = $motivationDataTableSort[$indMap[self::CODE_NET_PROFIT_THRESHOLD_RUB]][$ind == 6 ? "plan" : $column]; // "Минимальный порог Чистой прибыли, руб."
+ //
+ // $j66 = 0;
+ // if ($c62 >= $b64) {
+ // $formula = $c62 - $c5 * $motivationDataTableSort[$indMap[self::CODE_THRESHOLD_COEFFICIENT]]["plan"]; // "Пороговый коэффициент"
+ // if ($formula > 0) {
+ // $j66 = $formula;
+ // }
+ // }
+ // $motivationDataTableSort[$indMap[self::CODE_CALCULATION_OF_PREMIUM]][$column] = $j66 + 0; // "Расчет премии"
+ // }
+ }
+
+ // Отклонение
+ $deviationFunc = function ($code) use(&$motivationDataTableSort, &$indMap) {
+ if ($motivationDataTableSort[$indMap[$code]]["plan"] != 0) {
+ $motivationDataTableSort[$indMap[$code]]["deviation"] =
+ $motivationDataTableSort[$indMap[$code]]["fact"] /
+ $motivationDataTableSort[$indMap[$code]]["plan"];
+ }
+ };
+ // $names = [
+ // "Выручка от реализации",
+ // "Продажа товара",
+ // "Оффлайн продажи",
+ // "Онлайн продажи",
+ // "Прочие услуги",
+ // "Услуги по сборке",
+ // "Услуги по доставке",
+ // "Прямые расходы на продажу",
+ // "Себестоимость товара",
+ // "Услуги агентов (Расходы на закупку, хранение, доставку товара)",
+ // "Брак, пересорт",
+ // "Брак с поставки",
+ // "Списание неликвидного товара: порча, истечение срока годности",
+ // "Брак из-за поломки оборудования",
+ // "Пересорт",
+ // "Расходные материалы (обеспечение продаж)",
+ // "Маржинальный доход",
+ // "Операционные расходы (Себестоимость)",
+ // "Оплата труда",
+ // "Фонд оплаты труда персонала",
+ // "Содержание помещения",
+ // "Аренда",
+ // "Коммунальные услуги",
+ // "Охрана",
+ // "Услуги по уборке помещений и территории",
+ // "Расходы по доставке",
+ // "Доставка до клиента курьер",
+ // "Доставка до клиента такси",
+ // "Услуги маркетплейсов",
+ // "Содержание и обслуживание ОС и НМА",
+ // "Холодильное оборудование (ремонт, содержание, ТО)",
+ // "Расходы на содержание и ремонт оргтехники, в т.ч. расходные материалы",
+ // "Расходы на содержание и ремонт прочих ОС",
+ // "Техническое обслуживание кассовых аппаратов",
+ // "Услуги связи",
+ // "Интернет",
+ // "Прочие операционные расходы",
+ // "Хозяйственные товары",
+ // "Канцтовары",
+ // "Вода питьевая",
+ // "Валовая прибыль",
+ // "Общехозяйственные расходы",
+ // "Бухгалтерия и финансы",
+ // "Бухгалтерские услуги: постановка и ведение БУ и НУ",
+ // "Юридическое сопровождение",
+ // "Юридические услуги",
+ // "HR- услуги",
+ // "Кадровое администрирование, охрана труда",
+ // "Услуги по подбору персонала",
+ // "IT услуги",
+ // "Администрирование ИТ инфраструктуры (подключения к базам данных, ПО, почта, интернет)",
+ // "Лицензия на ПО: ERP система",
+ // "Продвижение и продажа товара через сайт",
+ // "Чистая прибыль",
+ // "Рентабельность по чистой прибыли, %"
+ // ];
+ foreach (array_keys($indMap) as $code) {
+ $deviationFunc($code);
+ }
+
+ return $motivationDataTableSort;
+ }
++
+ /**
+ * Вычисление суммы себестоимости товара по магазину в определенный промежуток дат.
+ *
+ * @param string $startDate Дата начала недели (Y-m-d format).
+ * @param string $endDate Дата конца недели (Y-m-d format).
+ * @param int $storeId ID магазина.
+ * @return float Суммарная себестоимость товаров по магазину в определенный промежуток дат.
+ */
+ public static function getSelfCostSumByStore($startDate, $endDate, $storeId)
+ {
+
+ $storeId = (int)$storeId;
+ $startDate = date('Y-m-d', strtotime($startDate));
+ $endDate = date('Y-m-d', strtotime($endDate));
+
+
+ $sum = (float)SelfCostProduct::find()
+ ->where(['store_id' => $storeId])
+ ->andWhere(['>=', 'date', $startDate])
+ ->andWhere(['<=', 'date', $endDate])
+ ->sum('price');
+
+ return $sum;
+ }
+
+ /**
+ * Сохраняет мотивацию по себестоимости товара для указанного магазина и месяца.
+ *
+ * @param int $year Год в формате YYYY.
+ * @param int $month Месяц в формате MM.
+ * @param int $storeId ID магазина.
+ * @return bool Возвращает true в случае успешного сохранения данных, иначе false.
+ */
+ public static function saveCostMotivation($storeId, $year, $month )
+ {
+ // Получаем идентификатор мотивации
+ $motivation = Motivation::find()
+ ->where([
+ 'store_id' => $storeId,
+ 'year' => $year,
+ 'month' => $month
+ ])
+ ->one();
+
+ if (!$motivation) {
+ Yii::error("Мотивация не найдена для store_id=$storeId, year=$year, month=$month");
+ return false; // мотивация не найдена, завершаем выполнение
+ }
+
+ $motivationId = $motivation->id;
+
+ // Вычисляем начало и конец месяца
+ $monthStart = date("Y-m-d 00:00:00", strtotime("$year-$month-01"));
+ $monthEnd = date("Y-m-t 23:59:59", strtotime("$year-$month-01"));
+
+ // Переменная для отслеживания успешности сохранений
+ $success = true;
+
+ // Проходим по каждой неделе (максимум 5 недель в месяце)
+ foreach (range(1, 5) as $weekIndex) {
+ // Вычисляем начало и конец недели
+ $weekStart = date("Y-m-d 00:00:00", strtotime("+" . (($weekIndex - 1) * 7) . " days", strtotime($monthStart)));
+ $weekEnd = date("Y-m-d 23:59:59", strtotime("+" . ($weekIndex * 7 - 1) . " days", strtotime($monthStart)));
+
+ // Если конец недели выходит за пределы месяца, ограничиваем его концом месяца
+ if ($weekEnd > $monthEnd) {
+ $weekEnd = $monthEnd;
+ }
+
+ Yii::info("Вычисляем себестоимость для недели $weekIndex: с $weekStart по $weekEnd");
+
+ // Рассчитываем сумму себестоимости за эту неделю
+ $costSum = self::getSelfCostSumByStore($weekStart, $weekEnd, $storeId);
+
+ Yii::info("Сумма себестоимости для недели $weekIndex: $costSum");
+
+ // Определяем alias группы для недели (week1, week2 и т.д.)
+ $groupAlias = 'week' . $weekIndex;
+
+ // Сохраняем или обновляем значение мотивации
+ $saveResult = self::saveOrUpdateMotivationValue(
+ $motivationId,
+ $groupAlias,
+ self::CODE_COSTS_OF_GOODS, // константа для себестоимости
+ 'float',
+ $costSum
+ );
+
+ if ($saveResult === false) {
+ Yii::error("Ошибка при сохранении значения для недели $weekIndex: $costSum");
+ $success = false;
+ }
+
+ // Прерываем цикл, если достигнут конец месяца
+ if ($weekEnd == $monthEnd) {
+ break;
+ }
+ }
+
+ return $success;
+ }
+
}