]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Merge branch 'develop' into feature_fomichev_erp-91_create_method_sum_salary
authorJoySystem_v <fvv2011@gmail.com>
Wed, 14 Aug 2024 08:32:05 +0000 (11:32 +0300)
committerJoySystem_v <fvv2011@gmail.com>
Wed, 14 Aug 2024 08:32:05 +0000 (11:32 +0300)
# Conflicts:
# erp24/services/MotivationService.php

1  2 
erp24/services/MotivationService.php

index e5321606a069e267a9e4be5cf28582723e446c77,0ada3d5e52587793fd22291d954eac4179dee154..c689de43360e01306f117b64920977042166ad23
@@@ -2,20 -2,20 +2,28 @@@
  
  namespace yii_app\services;
  
 +use Yii;
  use PhpOffice\PhpSpreadsheet\IOFactory;
+ use yii\helpers\Json;
+ use yii_app\records\ExportImportTable;
+ use yii\helpers\ArrayHelper;
  use yii_app\records\Motivation;
  use yii_app\records\MotivationValue;
  use yii_app\records\MotivationValueGroup;
  use yii_app\records\CityStore;
  use yii_app\records\MotivationCostsItem;
- use yii\helpers\ArrayHelper;
+ use yii_app\records\WriteOffs;
+ use yii_app\records\Products1c;
+ use yii_app\records\ProductsClass;
+ use yii_app\records\Sales;
+ use yii_app\records\SalesProducts;
 +use yii_app\records\TimetableFactModel;
 +use yii_app\records\Timetable;
 +use DateTime;
 +
++
 +
 +use yii_app\records\EmployeePayment;
  
  
  class MotivationService
          return compact('errors');
      }
  
-     
+     public static function calculateDefectCost($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'));
+         $motivation = Motivation::find()->where(['store_id' => $store_id, 'year' => $year, 'month' => $month])->one();
+         $exportImportTable = ExportImportTable::find()->select(['export_val'])->where(['entity' => 'city_store', 'entity_id' => $store_id, 'export_id' => 1])->one();
+         /** @var $exportImportTable ExportImportTable */
+         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;
+             }
+             if ($weekStart > $monthEnd) {
+                 continue;
+             }
+             $motivationValueGroup = MotivationValueGroup::find()->where(['alias' => 'week' . $ind])->one();
+             if ($exportImportTable) {
+                 $writeOffs = WriteOffs::find()->select(['sum(summ) as total', 'type'])
+                     ->where(['between', 'date', $weekStart, $weekEnd])
+                     ->andWhere(['store_id' => $exportImportTable->export_val])
+                     ->groupBy(['type'])
+                     ->indexBy('type')
+                     ->asArray()->all();
+                 foreach ($writeOffs as $key => $data) {
+                     $motivationItemType = MotivationCostsItem::writeOffsToMotivationItemMap($key);
+                     if (empty($motivationItemType)) {
+                         continue;
+                     }
+                     $motivationCostsItem = MotivationCostsItem::find()->where(['name' => $motivationItemType])->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 = $data['total'];
+                         $motivationValue->save();
+                         if ($motivationValue->getErrors()) {
+                             throw new \Exception(Json::encode($motivationValue->getErrors()));
+                         }
+                     }
+                 }
+             }
+         }
+         if ($motivation) {
+             $motivation->save();
+             if ($motivation->getErrors()) {
+                 throw new \Exception(Json::encode($motivation->getErrors()));
+             }
+         }
+     }
+     public static function calculateServiceAssemblyAndDeliveryCost($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'));
+         // Ищем каталог-гуиды категории services
+         $productsClass = ProductsClass::find()->select(['category_id', 'tip'])
+             ->where(['tip' => 'services'])
+             ->indexBy('category_id')
+             ->asArray()->all();
+         // Ищем продуктовые гуиды по каталог-гуидам категории service
+         $products1c = Products1c::find()->select(['id', 'parent_id', 'name'])
+             ->where(['parent_id' => array_keys($productsClass), 'tip' => 'products'])
+             ->indexBy(['id'])
+             ->asArray()->all();
+         // Ищем каталог-гуиды категории services_delivery
+         $productsClassDelivery = ProductsClass::find()->select(['category_id', 'tip'])
+             ->where(['tip' => 'services_delivery'])
+             ->indexBy('category_id')
+             ->asArray()->all();
+         // Ищем продуктовые гуиды по каталог-гуидам категории service_delivery
+         $products1cDelivery = Products1c::find()->select(['id', 'parent_id', 'name'])
+             ->where(['parent_id' => array_keys($productsClassDelivery), 'tip' => 'products'])
+             ->indexBy(['id'])
+             ->asArray()->all();
+         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;
+             }
+             // Ищем продажи по продуктовым гуидам по каталог-гуидам категории service
+             $sales = Sales::find()->alias('s')
+                 ->leftJoin('sales_products p', 'p.check_id = s.id')
+                 ->where(['between', 's.date', $weekStart, $weekEnd])
+                 ->andWhere(['p.product_id' => array_keys($products1c)])
+                 ->andWhere(['s.store_id' => $store_id])
+                 ->andWhere(['s.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');
+             // Ищем продукты из категории services
+             $salesProduct = SalesProducts::find()
+                 ->select(['SUM(summ) as total'])
+                 ->where(['check_id' => $salesIds])
+                 ->andWhere(['NOT IN', 'check_id', $returnSalesIds])
+                 ->andWhere(['product_id' => array_keys($products1c)])
+                 ->asArray()->one();
+             $motivationValueGroup = MotivationValueGroup::find()->where(['alias' => 'week' . $ind])->one();
+             $motivationCostsItem = MotivationCostsItem::find()->where(['name' => 'Услуги по сборке'])->one();
+             /** @var $motivationCostsItem MotivationCostsItem */
+             $motivation = Motivation::find()->where(['store_id' => $store_id, 'year' => $year, 'month' => $month])->one();
+             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 = $salesProduct['total'];
+                 $motivationValue->save();
+                 //var_dump($motivationValue); die;
+                 if ($motivationValue->getErrors()) {
+                     throw new \Exception(Json::encode($motivationValue->getErrors()));
+                 }
+             }
+             // Ищем продажи по продуктовым гуидам по каталог-гуидам категории service_delivery
+             $sales = Sales::find()->alias('s')
+                 ->leftJoin('sales_products p', 'p.check_id = s.id')
+                 ->where(['between', 's.date', $weekStart, $weekEnd])
+                 ->andWhere(['p.product_id' => array_keys($products1cDelivery)])
+                 ->andWhere(['s.store_id' => $store_id])
+                 ->andWhere(['s.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');
+             // Ищем продукты из категории services_delivery
+             $salesProduct = SalesProducts::find()
+                 ->select(['SUM(summ) as total'])
+                 ->where(['check_id' => $salesIds])
+                 ->andWhere(['NOT IN', 'check_id', $returnSalesIds])
+                 ->andWhere(['product_id' => array_keys($products1c)])
+                 ->asArray()->one();
+             $motivationCostsItem = MotivationCostsItem::find()->where(['name' => 'Услуги по доставке'])->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 = $salesProduct['total'];
+                 $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 $store_id Идентификатор магазина.
 +     *
 +     * @return array|null Возвращает массив записей из модели TimetableFactModel, соответствующих условиям,
 +     *                    или null, если записи не найдены.
 +     */
 +    public static function getTimetableFactRecordsByDateAndStore($startDate, $endDate, $store_id)
 +    {
 +
 +
 +        // Делаем запрос к TimetableFactModel
 +        $records = TimetableFactModel::find()
 +            ->where(['store_id' => $store_id])
 +            ->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 $store_id Идентификатор магазина.
 +     *
 +     * @return float Возвращает общую сумму отпускных за указанный период.
 +     *               Если записей не найдено, возвращается 0.0.
 +     */
 +    public static function getVacationsSum($startDate, $endDate, $store_id):float
 +    {
 +        // Делаем запрос к таблице Timetable для получения записей с slot_type_id = 2
 +        $records = Timetable::find()
 +
 +            ->where(['store_id' => $store_id])
 +            ->andWhere(['between', 'date', $startDate, $endDate])
 +            ->andWhere(['slot_type_id' => 2])
 +            ->all();
 +
 +        // Проверяем, есть ли записи
 +        if (empty($records)) {
 +            return 0.0; // Возвращаем 0, если записей нет
 +        }
 +
 +
 +
 +        $vacationsSum = 0.0;
 +        foreach ($records as $record) {
 +            // Находим самую позднюю запись по admin_id
 +            $payment = EmployeePayment::find()
 +                ->where(['admin_id' => $record->admin_id])
 +                ->orderBy(['date' => SORT_DESC])
 +                ->one();
 +            $dailyPayment = $payment ? $payment->daily_payment : 0.0;
 +
 +
 +            $vacationsSum += $dailyPayment;
 +
 +        }
 +
 +
 +
 +        return $vacationsSum;
 +    }
 +
 +    /**
 +     * Получение суммы всех недель для определенного `value_id` из таблицы `motivation_value`.
 +     *
 +     * Метод выполняет запрос к модели `MotivationValue`, чтобы получить все записи,
 +     * соответствующие указанным `motivation_id` и `value_id`, и принадлежащие одной из
 +     * определенных групп мотивации. Затем метод суммирует значения поля `value_float`
 +     * из всех полученных записей.
 +     *
 +     * @param int $id Идентификатор мотивации (`motivation_id`).
 +     * @param int $value_id Идентификатор значения (`value_id`).
 +     *
 +     * @return float Возвращает сумму всех значений `value_float` для заданного `value_id`.
 +     *               Если записи не найдены, возвращается 0.
 +     */
 +    public static function getMonthSum($id, $value_id)
 +    {
 +        // Запрос к модели MotivationValue для получения записей с заданными условиями
 +        $records = MotivationValue::find()
 +            ->where(['motivation_id' => $id, 'value_id' => $value_id])
 +            ->andWhere(['in', 'motivation_group_id', [1, 2, 3, 4, 5]])
 +            ->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 $motivation_id Идентификатор мотивации.
 +     * @param int $group_id Идентификатор группы мотивации.
 +     * @param int $value_id Идентификатор значения.
 +     * @param string $value_type Тип значения, который может быть '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($motivation_id, $group_id, $value_id, $value_type, $value)
 +    {
 +        // Найти существующую запись
 +        $motivationValue = MotivationValue::findOne([
 +            'motivation_id' => $motivation_id,
 +            'motivation_group_id' => $group_id,
 +            'value_id' => $value_id,
 +        ]);
 +
 +        // Если запись не найдена, создать новую
 +        if ($motivationValue === null) {
 +            $motivationValue = new MotivationValue();
 +            $motivationValue->motivation_id = $motivation_id;
 +            $motivationValue->motivation_group_id = $group_id;
 +            $motivationValue->value_id = $value_id;
 +            $motivationValue->value_type = $value_type;
 +        }
 +
 +        // Установить значение в зависимости от типа
 +        switch ($value_type) {
 +            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("Неправильное значение типа: $value_type");
 +        }
 +
 +        // Сохранить запись и вернуть 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 \DateTime $dateTime Объект `DateTime`, для которого нужно определить номер недели.
 +     *
 +     * @return int Номер недели в месяце (от 1 до 5).
 +     */
 +    public static function getWeekOfMonth($dateTime)
 +    {
 +        $dayOfMonth = intval($dateTime->format('j'));
 +        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 \DateTime $dateTime Объект `DateTime`, представляющий дату, для которой определяется неделя.
 +     * @param int $weekOfMonth Номер недели в месяце (от 1 до 5).
 +     *
 +     * @return \DateTime Дата начала указанной недели месяца.
 +     */
 +    public static function getStartOfWeek($dateTime, $weekOfMonth)
 +    {
 +        $year = $dateTime->format('Y');
 +        $month = $dateTime->format('m');
++
 +        switch ($weekOfMonth) {
 +            case 1:
 +                return new DateTime("$year-$month-01");
 +            case 2:
 +                return new DateTime("$year-$month-08");
 +            case 3:
 +                return new DateTime("$year-$month-15");
 +            case 4:
 +                return new DateTime("$year-$month-22");
 +            case 5:
 +                return new DateTime("$year-$month-29");
 +            default:
 +                return new DateTime("$year-$month-01");
 +        }
 +    }
 +
 +    /**
 +     * Вычисление общей суммы фонда оплаты труда (ФОТ) - зарплаты и отпускных для магазина за указанный период.
 +     *
 +     * Этот метод сначала получает записи о сменах сотрудников за указанный период и сумму отпускных.
 +     * Затем для каждой записи определяется соответствующая дневная зарплата, и она добавляется к общей сумме.
 +     * Если в записи уже указана зарплата за смену, она используется вместо дневной зарплаты.
 +     * В конце к общей сумме зарплат добавляется сумма отпускных.
 +     *
 +     * @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);
 +
 +        $totalSalary = 0.0;
 +        foreach ($records as $record) {
 +            // Находим самую позднюю запись по admin_id
 +            $payment = EmployeePayment::find()
 +                ->where(['admin_id' => $record->admin_id])
 +                ->orderBy(['date' => SORT_DESC])
 +                ->one();
 +            $dailyPayment = $payment ? $payment->daily_payment : 0.0;
 +
 +            if (!empty($record->salary_shift)) {
 +                $totalSalary += $record->salary_shift;
 +            } else {
 +                $totalSalary += $dailyPayment;
 +            }
 +        }
 +
 +        return $totalSalary + $vacationSum;
 +    }
  }