From dc706b95966f0a40c4b98db9996351e320b8f2a5 Mon Sep 17 00:00:00 2001 From: Vladimir Fomichev Date: Mon, 8 Sep 2025 12:13:49 +0300 Subject: [PATCH] =?utf8?q?=D0=94=D0=B5=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=B0?= =?utf8?q?=D1=86=D0=B8=D1=8F=20=D0=B8=20=D0=BC=D1=8F=D0=B3=D0=BA=D0=BE?= =?utf8?q?=D0=B5=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/controllers/MatrixTypeController.php | 104 +++++++++++++++++- ...tid_active_fields_to_matrix_type_table.php | 20 ++++ erp24/records/MatrixType.php | 11 +- erp24/records/MatrixTypeSearch.php | 5 +- erp24/views/matrix-type/index.php | 3 +- erp24/web/js/matrix-type/index.js | 12 +- 6 files changed, 136 insertions(+), 19 deletions(-) diff --git a/erp24/controllers/MatrixTypeController.php b/erp24/controllers/MatrixTypeController.php index c1ebd629..9d346ce6 100644 --- a/erp24/controllers/MatrixTypeController.php +++ b/erp24/controllers/MatrixTypeController.php @@ -2,6 +2,7 @@ namespace app\controllers; +use Throwable; use Yii; use yii_app\records\MatrixType; use yii\data\ActiveDataProvider; @@ -123,20 +124,54 @@ class MatrixTypeController extends Controller public function actionToggleActive() { Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $id = Yii::$app->request->post('id'); - $active = Yii::$app->request->post('active'); + $id = (int) Yii::$app->request->post('id'); + $active = (int) Yii::$app->request->post('active'); $model = $this->findModel($id); if (!$model) { return ['success' => false, 'message' => 'Не найдено']; } - $model->active = (int)$active; - if ($model->save(false)) { + $db = Yii::$app->db; + $transaction = $db->beginTransaction(); + + try { + $model->active = $active; + if (!$model->save(false)) { + throw new \RuntimeException('Не удалось сохранить узел'); + } + + if ($active === 0 && empty($model->parent_id)) { + $this->deactivateChildren($model); + } + + $transaction->commit(); return ['success' => true]; + + } catch (Throwable $e) { + $transaction->rollBack(); + Yii::error($e->getMessage(), 'matrix-type.toggle-active'); + return ['success' => false, 'message' => 'Ошибка при сохранении']; + } + } + + protected function deactivateChildren(MatrixType $parent): void + { + $children = MatrixType::find() + ->where(['parent_id' => $parent->id]) + ->all(); + + foreach ($children as $child) { + if ($child->active != 0) { + $child->active = 0; + if(!$child->save()) { + Yii::error("Ошибка сохранения " . json_encode($child->getErrors(), JSON_UNESCAPED_UNICODE)); + } + } + $this->deactivateChildren($child); } - return ['success' => false, 'message' => 'Не удалось сохранить']; } + /** * Deletes an existing MatrixType model. * If deletion is successful, the browser will be redirected to the 'index' page. @@ -163,6 +198,63 @@ class MatrixTypeController extends Controller $model->delete(); } + public function actionSoftDelete($id) + { + $model = $this->findModel($id); + if (!$model) { + return $this->redirect(['index']); + } + + $db = Yii::$app->db; + $transaction = $db->beginTransaction(); + + try { + $now = date('Y-m-d H:i:s'); + $uid = Yii::$app->user->id ?? null; + + $this->markDeleted($model, $uid, $now); + + if (empty($model->parent_id)) { + $this->markDeletedChildren($model, $uid, $now); + } + + $transaction->commit(); + return $this->redirect(['index']); + } catch (\Throwable $e) { + $transaction->rollBack(); + Yii::error($e->getMessage(), 'matrix-type.soft-delete'); + return $this->redirect(['index']); + } + } + + protected function markDeleted(MatrixType $matrixType, $uid, $now): void + { + if (!empty($matrixType->deleted_at)) return; + + $result = $matrixType->updateAttributes([ + 'deleted_by' => $uid, + 'deleted_at' => $now, + 'active' => 0, + ]); + if ($result === false) { + Yii::error("Ошибка сохранения " . json_encode($matrixType->getErrors(), JSON_UNESCAPED_UNICODE)); + throw new \RuntimeException("Не удалось пометить удалённым ID={$matrixType->id}"); + } + } + + protected function markDeletedChildren(MatrixType $parent, $uid, $now): void + { + $children = MatrixType::find() + ->where(['parent_id' => $parent->id]) + ->all(); + + foreach ($children as $child) { + $this->markDeleted($child, $uid, $now); + $this->markDeletedChildren($child, $uid, $now); + } + } + + /** * Finds the MatrixType model based on its primary key value. * @param int $id @@ -171,7 +263,7 @@ class MatrixTypeController extends Controller */ protected function findModel($id) { - if (($model = MatrixType::findOne($id)) !== null) { + if (($model = MatrixType::find()->where(['id' => $id])->andWhere(['deleted_at' => null])->one()) !== null) { return $model; } diff --git a/erp24/migrations/m250905_090045_add_parentid_active_fields_to_matrix_type_table.php b/erp24/migrations/m250905_090045_add_parentid_active_fields_to_matrix_type_table.php index e2b70c18..08db8437 100644 --- a/erp24/migrations/m250905_090045_add_parentid_active_fields_to_matrix_type_table.php +++ b/erp24/migrations/m250905_090045_add_parentid_active_fields_to_matrix_type_table.php @@ -29,6 +29,20 @@ class m250905_090045_add_parentid_active_fields_to_matrix_type_table extends Mig $this->tinyInteger()->notNull()->defaultValue(1)->comment('активность') ); } + if (!$this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('deleted_by')) { + $this->addColumn( + self::TABLE_NAME, + 'deleted_by', + $this->integer()->null()->comment('Кем удалено - id пользователя') + ); + } + if (!$this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('deleted_at')) { + $this->addColumn( + self::TABLE_NAME, + 'deleted_at', + $this->timestamp()->defaultValue(null)->comment('дата удаления') + ); + } } /** @@ -42,6 +56,12 @@ class m250905_090045_add_parentid_active_fields_to_matrix_type_table extends Mig if ($this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('active')) { $this->dropColumn(self::TABLE_NAME, 'active'); } + if ($this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('deleted_by')) { + $this->dropColumn(self::TABLE_NAME, 'deleted_by'); + } + if ($this->db->schema->getTableSchema(self::TABLE_NAME, true)->getColumn('deleted_at')) { + $this->dropColumn(self::TABLE_NAME, 'deleted_at'); + } } /* diff --git a/erp24/records/MatrixType.php b/erp24/records/MatrixType.php index 2904ff18..c0d46ff3 100644 --- a/erp24/records/MatrixType.php +++ b/erp24/records/MatrixType.php @@ -61,17 +61,10 @@ class MatrixType extends \yii\db\ActiveRecord ['name', 'parent_id'], 'unique', 'targetAttribute' => ['name', 'parent_id'], - 'message' => 'Подгруппа с таким названием уже существует в этой группе.', + 'message' => 'Группа с таким названием уже существует в этой группе.', ], - // Уникальность имени для групп (без parent_id) - [ - 'name', - 'unique', - 'targetAttribute' => 'name', - 'filter' => ['parent_id' => null], - 'message' => 'Группа с таким названием уже существует.', - ], + ]; } diff --git a/erp24/records/MatrixTypeSearch.php b/erp24/records/MatrixTypeSearch.php index 0aaffb53..a4fe7421 100644 --- a/erp24/records/MatrixTypeSearch.php +++ b/erp24/records/MatrixTypeSearch.php @@ -20,7 +20,10 @@ class MatrixTypeSearch extends MatrixType { $this->load($params, ''); - $allTypes = MatrixType::find()->orderBy(['parent_id' => SORT_ASC, 'id' => SORT_ASC])->all(); + $allTypes = MatrixType::find() + ->andWhere(['deleted_at' => null]) + ->orderBy(['parent_id' => SORT_ASC, 'id' => SORT_ASC]) + ->all(); if ($this->active === '' || $this->active === null) { return new ArrayDataProvider(['allModels' => $allTypes, 'pagination' => false, 'sort' => false]); diff --git a/erp24/views/matrix-type/index.php b/erp24/views/matrix-type/index.php index 0eba8940..49bfa268 100644 --- a/erp24/views/matrix-type/index.php +++ b/erp24/views/matrix-type/index.php @@ -193,6 +193,7 @@ $this->registerJsFile('/js/matrix-type/index.js', ['position' => \yii\web\View:: [ 'class' => 'js-toggle-active', 'data-id' => $model->id, + 'data-parent' => $model->parent_id ?? '', ] ); }, @@ -204,7 +205,7 @@ $this->registerJsFile('/js/matrix-type/index.js', ['position' => \yii\web\View:: 'contentOptions' => ['class' => 'text-center', 'style' => 'width:80px; min-width:50px; '], 'urlCreator' => function ($action, $model, $key, $index) { if ($action === 'delete') { - return Url::to(['delete', 'id' => $model->id]); + return Url::to(['soft-delete', 'id' => $model->id]); } return '#'; }, diff --git a/erp24/web/js/matrix-type/index.js b/erp24/web/js/matrix-type/index.js index 62036bd8..ec2408e0 100644 --- a/erp24/web/js/matrix-type/index.js +++ b/erp24/web/js/matrix-type/index.js @@ -89,9 +89,17 @@ _csrf: yii.getCsrfToken() }, success: function (res) { - if (!res.success) { - alert('Ошибка: ' + res.message); + if (!res || res.success !== true) { + alert('Ошибка: ' + (res && res.message ? res.message : 'Не удалось сохранить')); checkbox.prop('checked', !active); + return; + } + + if (active === 0 && checkbox.closest('tr').find('td:first').text().trim() !== '') { + var nodeId = id; + $('.js-toggle-active[data-parent="' + nodeId + '"]').each(function () { + $(this).prop('checked', false); + }); } }, error: function () { -- 2.39.5