]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Merge branch 'develop' into feature_fomichev_erp_104_agents_services_computation
authorVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 29 Aug 2024 14:29:29 +0000 (17:29 +0300)
committerVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 29 Aug 2024 14:29:29 +0000 (17:29 +0300)
# Conflicts:
# erp24/services/MotivationService.php

1  2 
erp24/actions/motivation/IndexAction.php
erp24/services/MotivationService.php

index e34eb6bbf98a13bae21c3841c5e5ff19102a4681,dad9f984a2f4633096e88db4f08ea093f8c3b0b6..15c21924f46604c6129b061643d009d34c959266
@@@ -1105,106 -1106,243 +1105,346 @@@ class MotivationServic
          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;
 +    }
 +
  }