}
/**
- * После сохранения проверяем, изменилась ли должность, и создаем запись EmployeePayment
- * А также синхронизируем историю в EmployeePositionStatus (гибридный подход)
+ * Обработка после сохранения записи сотрудника (Admin)
+ *
+ * Метод выполняет две критически важные автоматические синхронизации:
+ *
+ * 1. **ИСТОРИЯ ГРЕЙДОВ (EmployeePositionStatus)** - Гибридный подход
+ * - Отслеживает изменения должности сотрудника во времени
+ * - При изменении employee_position_id:
+ * a) Закрывает все активные записи истории (closed_at = NOW)
+ * b) Создаёт новую запись с текущей должностью (closed_at = NULL)
+ * - Важно: история НЕ создаётся при первом сохранении нового сотрудника,
+ * так как нет предыдущей позиции для закрытия
+ *
+ * 2. **СИНХРОНИЗАЦИЯ ЗАРПЛАТЫ (EmployeePayment)**
+ * - Автоматически создаёт запись о зарплате через SalarySyncService
+ * - Срабатывает когда:
+ * a) Установлена должность (employee_position_id не NULL)
+ * b) Должность изменилась ИЛИ создаётся новая запись ($insert)
+ * c) Сотрудник не уволен (group_id != AdminGroup::GROUP_FIRED)
+ * - В отличие от истории грейдов, работает и для новых записей
+ *
+ * **Обработка ошибок:**
+ * - Все операции обёрнуты в try-catch
+ * - Ошибки логируются, но НЕ прерывают сохранение Admin
+ * - Это гарантирует, что основная операция (сохранение сотрудника) всегда завершится
+ *
+ * **Логирование:**
+ * - История грейдов: категория 'grade-sync'
+ * - Синхронизация зарплаты: категория 'salary-sync'
+ *
+ * @param bool $insert Флаг вставки новой записи (true) или обновления (false)
+ * @param array $changedAttributes Массив изменённых атрибутов в формате ['attribute' => 'oldValue']
+ * @return void
+ *
+ * @see EmployeePositionStatus Модель для истории должностей
+ * @see SalarySyncService Сервис синхронизации зарплаты
+ * @see EmployeePayment Модель зарплатных данных
*/
public function afterSave($insert, $changedAttributes)
{
parent::afterSave($insert, $changedAttributes);
- // Проверяем, изменилось ли поле employee_position_id
- // Важно: проверяем как через changedAttributes, так и через сравнение значений
+ // Создаём историю только если должность реально изменилась и новая должность установлена
$oldPositionId = $changedAttributes['employee_position_id'] ?? null;
$newPositionId = $this->employee_position_id;
- // Если это новая запись или изменилось employee_position_id
- if ($insert || ($oldPositionId != $newPositionId && array_key_exists('employee_position_id', $changedAttributes))) {
- // === ГИБРИДНЫЙ ПОДХОД: Ведение истории в EmployeePositionStatus ===
- if ($newPositionId && $oldPositionId != $newPositionId) {
- 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(false)) {
- 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
+ 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
}
}
- // === СИНХРОНИЗАЦИЯ ЗАРПЛАТЫ ===
- // Если должность была добавлена или изменена, и сотрудник не уволен
- $oldPositionIdForSalary = $changedAttributes['employee_position_id'] ?? null;
- $newPositionIdForSalary = $this->employee_position_id;
- if ($newPositionIdForSalary && ($oldPositionIdForSalary != $newPositionIdForSalary || $insert) && $this->group_id != AdminGroup::GROUP_FIRED) {
+ if ($newPositionId && ($oldPositionId != $newPositionId || $insert) && $this->group_id != AdminGroup::GROUP_FIRED) {
try {
$syncService = new SalarySyncService();
$result = $syncService->createPaymentFromPosition($this->id);
if ($result) {
- Yii::info("Автоматически создана запись EmployeePayment для admin_id={$this->id}, position_id={$newPositionIdForSalary}", 'salary-sync');
+ Yii::info("Автоматически создана запись EmployeePayment для admin_id={$this->id}, position_id={$newPositionId}", 'salary-sync');
}
} catch (\Exception $e) {
Yii::error("Ошибка автоматической синхронизации оклада для admin_id={$this->id}: " . $e->getMessage(), 'salary-sync');