use yii_app\records\CityStore;
use yii_app\records\Companies;
use yii_app\records\EmployeePosition;
+use yii_app\records\EmployeePositionStatus;
use yii_app\records\ExportImportTable;
use yii_app\services\HistoryService;
}
if (!$raiseError) {
+ // Сохраняем старое значение employee_position_id для отслеживания изменений
+ $oldEmployeePositionId = $model->getOldAttribute('employee_position_id');
+
if ($model->save(false)) {
HistoryService::setHistoryUserInfo($model);
+
+ // Создаём историю грейда если employee_position_id изменился
+ $newEmployeePositionId = $model->employee_position_id;
+ if ($newEmployeePositionId && $oldEmployeePositionId != $newEmployeePositionId) {
+ EmployeePositionStatus::updateHistory($model->id, $oldEmployeePositionId, $newEmployeePositionId);
+ }
AdminStores::deleteAll(['admin_id' => $model->id]);
}
}
- // Сохраняем Admin, что автоматически вызовет afterSave
- // и создаст запись в EmployeePositionStatus для истории
+ // Сохраняем Admin и создаём историю грейда
if ($admin->save(false)) {
+ // Создаём историю только если должность реально изменилась и новая должность установлена
+ if ($modelPosition->position_id && $oldPositionId != $modelPosition->position_id) {
+ EmployeePositionStatus::updateHistory($admin->id, $oldPositionId, $modelPosition->position_id);
+ }
+
if ($oldPositionId == $modelPosition->position_id) {
// Если ничего не изменилось - показываем стандартное сообщение
Yii::$app->session->setFlash('success', 'Данные успешно сохранены');
return false;
}
- /**
- * Обработка после сохранения записи сотрудника (Admin)
- *
- * Метод выполняет две критически важные автоматические синхронизации:
- *
- * 1. **ИСТОРИЯ ГРЕЙДОВ (EmployeePositionStatus)** - Гибридный подход
- * - Отслеживает изменения должности сотрудника во времени
- * - При изменении employee_position_id:
- * a) Закрывает все активные записи истории (closed_at = NOW)
- * b) Создаёт новую запись с текущей должностью (closed_at = NULL)
- * - Важно: история НЕ создаётся при первом сохранении нового сотрудника,
- * так как нет предыдущей позиции для закрытия *
- *
- * **Обработка ошибок:**
- * - Все операции обёрнуты в try-catch
- * - Ошибки логируются, но НЕ прерывают сохранение Admin
- * - Это гарантирует, что основная операция (сохранение сотрудника) всегда завершится
- *
- * **Логирование:**
- * - История грейдов: категория 'grade-sync'
- * - Синхронизация зарплаты: категория 'salary-sync'
- *
- * @param bool $insert Флаг вставки новой записи (true) или обновления (false)
- * @param array $changedAttributes Массив изменённых атрибутов в формате ['attribute' => 'oldValue']
- * @return void
- *
- * @see EmployeePositionStatus Модель для истории должностей
- * @see EmployeePayment Модель зарплатных данных
- */
- public function afterSave($insert, $changedAttributes)
- {
- parent::afterSave($insert, $changedAttributes);
-
- // Создаём историю только если должность реально изменилась и новая должность установлена
- $oldPositionId = $changedAttributes['employee_position_id'] ?? null;
- $newPositionId = $this->employee_position_id;
-
- if ($newPositionId && $oldPositionId != $newPositionId && array_key_exists('employee_position_id', $changedAttributes)) {
- try {
- // Закрыть ВСЕ активные записи для этого админа (на случай если есть несколько активных)
- $closedCount = EmployeePositionStatus::updateAll(
- ['closed_at' => date('Y-m-d H:i:s')],
- ['admin_id' => $this->id, 'closed_at' => null]
- );
-
- Yii::info("Закрыто активных записей для admin_id={$this->id}: {$closedCount}", 'grade-sync');
-
- // Создать новую запись об истории (даже если возвращаемся к предыдущей должности)
- $positionStatus = new EmployeePositionStatus();
- $positionStatus->admin_id = $this->id;
- $positionStatus->position_id = $newPositionId;
- $positionStatus->created_at = date('Y-m-d H:i:s');
- $positionStatus->closed_at = null; // Явно устанавливаем null для новой активной записи
-
- if (!$positionStatus->save()) {
- Yii::error("Не удалось сохранить EmployeePositionStatus для admin_id={$this->id}, position_id={$newPositionId}. Ошибки: " .
- implode(', ', $positionStatus->getFirstErrors()), 'grade-sync');
- } else {
- Yii::info("История грейда обновлена для admin_id={$this->id}, position_id={$newPositionId} (старая: " . ($oldPositionId ?? 'null') . ")", 'grade-sync');
- }
- } catch (\Exception $e) {
- Yii::error("Ошибка при обновлении истории EmployeePositionStatus для admin_id={$this->id}: " . $e->getMessage() .
- " Trace: " . $e->getTraceAsString(), 'grade-sync');
- // Не бросаем исключение, чтобы не прервать сохранение Admin
- }
- }
- }
}
'closed_at' => 'Closed At',
];
}
+
+ /**
+ * Обновляет историю должностей для сотрудника
+ *
+ * Метод выполняет следующие операции:
+ * 1. Закрывает все активные записи истории для данного сотрудника (closed_at = NOW)
+ * 2. Создаёт новую запись с новой должностью (closed_at = NULL)
+ * 3. Логирует все операции в категорию 'grade-sync'
+ *
+ * Метод безопасен - все ошибки логируются, но не прерывают выполнение.
+ *
+ * @param int $adminId ID администратора/сотрудника
+ * @param int|null $oldPositionId Предыдущая должность (для логирования)
+ * @param int $newPositionId Новая должность
+ * @return bool true в случае успеха, false в случае ошибки
+ */
+ public static function updateHistory($adminId, $oldPositionId, $newPositionId)
+ {
+ // Проверяем, что новая должность действительно указана
+ if (!$newPositionId) {
+ Yii::warning("Попытка обновить историю грейда для admin_id={$adminId} без указания новой должности", 'grade-sync');
+ return false;
+ }
+
+ try {
+ // Закрыть ВСЕ активные записи для этого админа (на случай если есть несколько активных)
+ $closedCount = self::updateAll(
+ ['closed_at' => date('Y-m-d H:i:s')],
+ ['admin_id' => $adminId, 'closed_at' => null]
+ );
+
+ Yii::info("Закрыто активных записей для admin_id={$adminId}: {$closedCount}", 'grade-sync');
+
+ // Создать новую запись об истории (даже если возвращаемся к предыдущей должности)
+ $positionStatus = new self();
+ $positionStatus->admin_id = $adminId;
+ $positionStatus->position_id = $newPositionId;
+ $positionStatus->created_at = date('Y-m-d H:i:s');
+ $positionStatus->closed_at = null; // Явно устанавливаем null для новой активной записи
+
+ if (!$positionStatus->save()) {
+ Yii::error("Не удалось сохранить EmployeePositionStatus для admin_id={$adminId}, position_id={$newPositionId}. Ошибки: " .
+ implode(', ', $positionStatus->getFirstErrors()), 'grade-sync');
+ return false;
+ }
+
+ Yii::info("История грейда обновлена для admin_id={$adminId}, position_id={$newPositionId} (старая: " . ($oldPositionId ?? 'null') . ")", 'grade-sync');
+ return true;
+
+ } catch (\Exception $e) {
+ Yii::error("Ошибка при обновлении истории EmployeePositionStatus для admin_id={$adminId}: " . $e->getMessage() .
+ " Trace: " . $e->getTraceAsString(), 'grade-sync');
+ return false;
+ }
+ }
}