From 1c5fee2815e580a0932d025eae179030b0a59675 Mon Sep 17 00:00:00 2001 From: Vladimir Fomichev Date: Wed, 17 Dec 2025 13:22:27 +0300 Subject: [PATCH] =?utf8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D1=8F=D0=B5=D0=BC=20aft?= =?utf8?q?erSave=20=D0=B8=D0=B7=20Admin.php=20=D0=B8=20=D0=BF=D0=B5=D1=80?= =?utf8?q?=D0=B5=D0=BD=D0=BE=D1=81=D0=B8=D0=BC=20=D0=BB=D0=BE=D0=B3=D0=B8?= =?utf8?q?=D0=BA=D1=83=20=D0=B8=D1=81=D1=82=D0=BE=D1=80=D0=B8=D0=B8=20?= =?utf8?q?=D0=B3=D1=80=D0=B5=D0=B9=D0=B4=D0=B0=20=D0=B2=20=D0=BC=D0=B5?= =?utf8?q?=D1=82=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/actions/grade/AdminUpdateAction.php | 10 ++++ erp24/actions/grade/UpdateAction.php | 8 ++- erp24/records/Admin.php | 67 ----------------------- erp24/records/EmployeePositionStatus.php | 55 +++++++++++++++++++ 4 files changed, 71 insertions(+), 69 deletions(-) diff --git a/erp24/actions/grade/AdminUpdateAction.php b/erp24/actions/grade/AdminUpdateAction.php index ada50c09..34d1a1d4 100644 --- a/erp24/actions/grade/AdminUpdateAction.php +++ b/erp24/actions/grade/AdminUpdateAction.php @@ -12,6 +12,7 @@ use yii_app\records\AdminStores; 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; @@ -144,8 +145,17 @@ class AdminUpdateAction extends Action } 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]); diff --git a/erp24/actions/grade/UpdateAction.php b/erp24/actions/grade/UpdateAction.php index 2e091236..af5ff376 100755 --- a/erp24/actions/grade/UpdateAction.php +++ b/erp24/actions/grade/UpdateAction.php @@ -96,9 +96,13 @@ class UpdateAction extends Action } } - // Сохраняем 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', 'Данные успешно сохранены'); diff --git a/erp24/records/Admin.php b/erp24/records/Admin.php index c0e39f83..42d5651b 100755 --- a/erp24/records/Admin.php +++ b/erp24/records/Admin.php @@ -766,71 +766,4 @@ class Admin extends ActiveRecord implements IdentityInterface 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 - } - } - } } diff --git a/erp24/records/EmployeePositionStatus.php b/erp24/records/EmployeePositionStatus.php index 89c44c98..da5655a9 100755 --- a/erp24/records/EmployeePositionStatus.php +++ b/erp24/records/EmployeePositionStatus.php @@ -46,4 +46,59 @@ class EmployeePositionStatus extends \yii\db\ActiveRecord '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; + } + } } -- 2.39.5