'Подан отзыв документов', DraftsManager::REASON_SENT => 'Подано на проверку', DraftsManager::REASON_DECLINED => 'Отклонено модератором', DraftsManager::REASON_REJECTED_BY_1C => 'Отклонено 1С', DraftsManager::REASON_APPROVED => 'Принято', DraftsManager::REASON_ACTUAL_UPDATED_FROM_1C => 'Чистовик обновлён из 1С', DraftsManager::REASON_UPDATED_FROM_1C => 'Обновлено из 1С', DraftsManager::REASON_MASS_REMOVAL_ADMINISTRATOR => 'Массовое удаление администратором', ]; public static $attributes_to_ignore = [ 'id', 'created_at', 'updated_at', ]; public static function makeCopy( ActiveRecord $from, ActiveRecord $to = null, bool $wrap_in_transaction = true, Closure $additional_model_attributes_provider = null ): ActiveRecord { $class = get_class($from); if (!$to) { $to = new $class(); } $processed_attributes = DraftsManager::excludeIgnoredProps($from); $transaction = null; if ($wrap_in_transaction) { $transaction = Yii::$app->db->beginTransaction(); } try { $additional_model_attributes = []; if ($additional_model_attributes_provider) { $additional_model_attributes = $additional_model_attributes_provider($to); } $processed_attributes = ArrayHelper::merge($processed_attributes, $additional_model_attributes); DraftsManager::setModelAttributes($to, $processed_attributes); if ($to instanceof IHaveCallbackAfterDraftCopy && $from instanceof FileToSendInterface) { $to->afterDraftCopy($from); } if ($from instanceof IHasRelations) { foreach ($from->getRelationsInfo() as $relationInfo) { $attrs = $relationInfo->copyRelatedRecords($to); $processed_attributes = ArrayHelper::merge($processed_attributes, $attrs); DraftsManager::setModelAttributes($to, $processed_attributes); } } } catch (\Throwable $e) { if ($wrap_in_transaction) { $transaction->rollBack(); } Yii::error("Ошибка клонирования {$class}: {$e->getMessage()}", 'cloning'); throw $e; } if ($wrap_in_transaction) { $transaction->commit(); } return $to; } public static function SuspendHistory($model) { if ($model instanceof ModelWithChangeHistoryHandlerInterface) { $handler = $model->getChangeHistoryHandler(); if ($handler) { $handler->setDisabled(true); } } } public static function excludeIgnoredProps(Model $model_with_attributes) { $props = $model_with_attributes->attributes; $ignored_attributes = DraftsManager::$attributes_to_ignore; if ($model_with_attributes instanceof IHaveIgnoredOnCopyingAttributes) { $ignored_attributes = $model_with_attributes->getIgnoredOnCopyingAttributes(); } foreach ($ignored_attributes as $attr) { if (array_key_exists($attr, $props)) { unset($props[$attr]); } } return $props; } public static function setModelAttributes(ActiveRecord $model, array $attributes) { foreach ($attributes as $key => $value) { if ($model->hasAttribute($key)) { $model->{$key} = $value; } } DraftsManager::SuspendHistory($model); $model ->loadDefaultValues() ->save(false); } public static function ensurePersisted(ActiveRecord $model): ActiveRecord { if ($model->getIsNewRecord()) { DraftsManager::SuspendHistory($model); $model ->loadDefaultValues() ->save(false); } return $model; } public static function getOrCreateApplicationDraftByOtherDraft(BachelorApplication $from, int $draft_status): BachelorApplication { if ($from->draft_status == $draft_status) { return $from; } $draft = DraftsManager::getApplicationDraftByOtherDraft($from, $draft_status); if ($draft && $draft->status != $from->status) { $draft ->setArchiveInitiator(Yii::$app->user->identity) ->setArchiveReason(DraftsManager::REASON_DECLINED) ->archive(); $draft = null; } if (!$draft) { $draft = DraftsManager::createApplicationDraftByOtherDraft($from, $draft_status); } return $draft; } public static function getApplicationDraftByOtherDraft(BachelorApplication $application, int $draft_status): ?BachelorApplication { if (!$application->user) { return null; } if ($application->draft_status == $draft_status) { return $application; } return DraftsManager::getApplicationDraft($application->user, $application->type, $draft_status); } public static function createApplicationDraftByOtherDraft(BachelorApplication $from, int $to_draft_status, int $status = null): BachelorApplication { $from_draft_status = $from->draft_status; $transaction = Yii::$app->db->beginTransaction(); $new_app = null; try { $new_app = DraftsManager::makeCopy($from); $new_app->draft_status = $to_draft_status; if (is_null($status)) { $new_app = DraftsManager::setupApplicationStatus($new_app); } else { $new_app->status = $status; } $new_app->synced_with_1C_at = ($from->draft_status == IDraftable::DRAFT_STATUS_APPROVED ? max($from->approved_at, $from->sent_at, $from->synced_with_1C_at) : $from->synced_with_1C_at); $new_app ->loadDefaultValues() ->setParentDraft($from) ->save(false); $new_app = ApplicationAndQuestionaryLinker::setUpQuestionaryLink($new_app); if ($to_draft_status != $from_draft_status && !$from->isArchive()) { $from_questionary = $from->getAbiturientQuestionary()->one(); if ($to_draft_status == IDraftable::DRAFT_STATUS_APPROVED) { ApplicationAndQuestionaryLinker::copyQuestionaryToActual($from_questionary); } elseif ($from_draft_status == IDraftable::DRAFT_STATUS_APPROVED && $to_draft_status == IDraftable::DRAFT_STATUS_CREATED) { ApplicationAndQuestionaryLinker::copyQuestionaryToDraft($from_questionary); } } $transaction->commit(); } catch (Throwable $e) { $transaction->rollBack(); throw $e; } return $new_app; } public static function getApplicationDraftQuery(User $user, ApplicationType $applicationType, int $draft_status): ActiveQuery { $tn = BachelorApplication::tableName(); return BachelorApplication::find() ->active() ->andWhere([ "{$tn}.user_id" => $user->id, "{$tn}.type_id" => $applicationType->id, ]) ->andWhere([ "{$tn}.draft_status" => $draft_status, ]) ->orderBy(["{$tn}.updated_at" => SORT_DESC]); } public static function getApplicationDraft(User $user, ApplicationType $applicationType, int $draft_status): ?BachelorApplication { return DraftsManager::getApplicationDraftQuery($user, $applicationType, $draft_status) ->limit(1) ->one(); } public static function createArchivePoint(BachelorApplication $entity, string $reason, int $draft_status, $initiator = null): BachelorApplication { if (!$initiator) { $initiator = Yii::$app->user->identity; } $new_entity = DraftsManager::createApplicationDraftByOtherDraft($entity, $draft_status, $entity->status); $entity ->setArchiveInitiator($initiator) ->setArchiveReason($reason) ->archive(); return $new_entity; } public static function getOrCreateApplicationDraft(User $user, ApplicationType $applicationType, int $draft_status): array { $created = false; $app = DraftsManager::getApplicationDraft($user, $applicationType, $draft_status); if (!$app) { $created = true; $app = new BachelorApplication(); $app->user_id = $user->id; $app->type_id = $applicationType->id; $app->draft_status = $draft_status; $app = DraftsManager::setupApplicationStatus($app); $app ->loadDefaultValues() ->save(false); ApplicationAndQuestionaryLinker::setUpQuestionaryLink($app); } return [$app, $created]; } private static function setupApplicationStatus(BachelorApplication $app) { $draft_status = $app->draft_status; if ($draft_status == IDraftable::DRAFT_STATUS_APPROVED) { $app->status = ApplicationInterface::STATUS_APPROVED; } elseif ($draft_status == IDraftable::DRAFT_STATUS_CREATED) { $app->status = ApplicationInterface::STATUS_CREATED; } elseif (!$app->moderationAllowedByStatus()) { $app->status = ApplicationInterface::STATUS_SENT; } return $app; } public static function getActualApplication(User $user, ApplicationType $type, bool $forced = false): ?BachelorApplication { $needs_update = false; $isIn1C = $user->hasAppInOneS($type); $application = DraftsManager::getApplicationDraft($user, $type, IDraftable::DRAFT_STATUS_APPROVED); if ($isIn1C && $application && !$forced) { [$needs_update, $_] = NeedBlockAndUpdateProcessor::getProcessedNeedBlockAndUpdate($application); } $transaction = Yii::$app->db->beginTransaction(); try { if (!$isIn1C && $application) { $application->delete(); $application = null; } if ($isIn1C) { if (!$application) { [$application, $_] = DraftsManager::getOrCreateApplicationDraft($user, $type, IDraftable::DRAFT_STATUS_APPROVED); $forced = true; } if ($forced || $needs_update) { $application->fullUpdateFrom1C(); if ($application->type->archive_actual_app_on_update) { $application = DraftsManager::createArchivePoint( $application, DraftsManager::REASON_ACTUAL_UPDATED_FROM_1C, IDraftable::DRAFT_STATUS_APPROVED ); } } } if ($application) { $application->status = ApplicationInterface::STATUS_APPROVED; if (EmptyCheck::isEmpty($application->approver_id)) { $application->approver_id = 1; } $application ->loadDefaultValues() ->save(false); } $transaction->commit(); } catch (Throwable $e) { $transaction->rollBack(); throw $e; } return $application; } public static function getActualQuestionary(User $user, bool $update = false): ?AbiturientQuestionary { if (!$user->userRef) { return null; } $questionary = $user->getActualAbiturientQuestionary()->one(); $transaction = Yii::$app->db->beginTransaction(); try { if (!$questionary) { $editable_questionary = $user->getAbiturientQuestionary()->one(); if ($editable_questionary) { $questionary = DraftsManager::makeCopy($editable_questionary); } else { $questionary = new AbiturientQuestionary(); $questionary->user_id = $user->id; $questionary->loadDefaultValues(); } $questionary->status = AbiturientQuestionary::STATUS_CREATE_FROM_1C; $questionary->draft_status = IDraftable::DRAFT_STATUS_APPROVED; $update = true; } if ($update) { if (!$questionary->getFrom1CWithParents()) { throw new UserException('Не удалось получить данные из Информационной системы вуза'); } } $transaction->commit(); } catch (\Throwable $e) { $transaction->rollBack(); Yii::error($e->getMessage(), 'ActualUpdate'); Yii::$app->session->setFlash('alert', [ 'body' => $e->getMessage(), 'options' => ['class' => 'alert-danger'] ]); return null; } return $questionary; } public static function clearOldModerations(BachelorApplication $app, IEntrantManager $initiator, string $reason) { if ($app->isArchive()) { throw new UserException("Вы работаете с устаревшей версией заявления"); } $old_moderating_apps = BachelorApplication::find() ->active() ->andWhere(['draft_status' => IDraftable::DRAFT_STATUS_MODERATING]) ->andWhere(['not', ['id' => $app->id]]) ->andWhere([ 'user_id' => $app->user->id, 'type_id' => $app->type->id, ]) ->all(); foreach ($old_moderating_apps as $old_moderating_app) { $old_moderating_app ->setArchiveInitiator($initiator) ->setArchiveReason($reason) ->archive(); } } public static function clearOldSendings(BachelorApplication $app, IEntrantManager $initiator, string $reason) { if ($app->isArchive()) { throw new UserException("Вы работаете с устаревшей версией заявления"); } $old_apps = BachelorApplication::find() ->active() ->andWhere(['draft_status' => IDraftable::DRAFT_STATUS_SENT]) ->andWhere([ 'user_id' => $app->user->id, 'type_id' => $app->type->id, ]) ->andWhere(['not', ['id' => $app->id]]) ->all(); foreach ($old_apps as $old_app) { $old_app ->setArchiveInitiator($initiator) ->setArchiveReason($reason) ->archive(); } } public static function removeOldApproved(BachelorApplication $app, IEntrantManager $initiator, string $reason) { if ($app->isArchive()) { throw new UserException("Вы работаете с устаревшей версией заявления"); } $provide_real_deletion = !$app->type->archive_actual_app_on_update; $old_apps = BachelorApplication::find() ->active() ->andWhere(['draft_status' => IDraftable::DRAFT_STATUS_APPROVED]) ->andWhere([ 'user_id' => $app->user->id, 'type_id' => $app->type->id, ]) ->andWhere(['not', ['id' => $app->id]]) ->all(); foreach ($old_apps as $old_app) { if ($provide_real_deletion) { $old_app->delete(); } else { $old_app ->setArchiveInitiator($initiator) ->setArchiveReason($reason) ->archive(); } } } }