--- /dev/null
+<?php
+namespace app\controllers;
+
+use Yii;
+use yii\helpers\ArrayHelper;
+use yii\web\Controller;
+use yii_app\records\BouquetComposition;
+use yii_app\records\BouquetCompositionProducts;
+use yii_app\records\Products1c;
+
+/**
+ * Контроллер для управления букетами и их составами.
+ */
+class BouquetController extends Controller
+{
+ public function actionIndex() {
+ return $this->render('index');
+ }
+
+ public function actionView() {
+ return $this->render('view');
+ }
+
+ public function actionUpdate() {
+ return $this->render('update');
+ }
+
+ public function actionGetList()
+ {
+ \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+
+ $products = Products1c::find()
+ ->where(['tip' => Products1c::TYPE_PRODUCTS, 'view' => Products1c::IS_VISIBLE])
+ ->asArray()
+ ->all();
+
+ return ArrayHelper::map($products, 'id', 'name');
+ }
+}
--- /dev/null
+<?php
+namespace yii_app\records;
+
+use Yii;
+use yii\db\ActiveRecord;
+use yii_app\records\BouquetCompositionProducts;
+
+/**
+ * This is the model class for table "erp24.bouquet_composition".
+ *
+ * @property int $id
+ * @property string|null $guid
+ * @property string $name
+ * @property int|null $matrix_type_id
+ * @property int|null $photo_id
+ * @property int|null $video_id
+ * @property string|null $created_at
+ * @property string|null $updated_at
+ * @property int|null $created_by
+ * @property int|null $updated_by
+ *
+ * @property BouquetCompositionProducts[] $bouquetCompositionProducts
+ */
+class BouquetComposition extends ActiveRecord
+{
+ public static function tableName()
+ {
+ return 'erp24.bouquet_composition';
+ }
+
+ public function rules()
+ {
+ return [
+ [['name'], 'required'],
+ [['matrix_type_id', 'photo_id', 'video_id', 'created_by', 'updated_by'], 'integer'],
+ [['created_at', 'updated_at'], 'safe'],
+ [['guid', 'name'], 'string', 'max' => 255],
+ ];
+ }
+
+ public function attributeLabels()
+ {
+ return [
+ 'id' => 'ID',
+ 'guid' => 'GUID букета',
+ 'name' => 'Название букета',
+ 'matrix_type_id' => 'ИД типа матрицы',
+ 'photo_id' => 'ИД фото',
+ 'video_id' => 'ИД видео',
+ 'created_at' => 'Дата создания',
+ 'updated_at' => 'Дата обновления',
+ 'created_by' => 'ID создателя записи',
+ 'updated_by' => 'ID обновителя записи',
+ ];
+ }
+
+ public function getBouquetCompositionProducts()
+ {
+ return $this->hasMany(BouquetCompositionProducts::class, ['bouquet_id' => 'id']);
+ }
+}
--- /dev/null
+<?php
+namespace yii_app\records;
+
+use Yii;
+use yii\db\ActiveRecord;
+
+/**
+ * This is the model class for table "erp24.bouquet_composition_products".
+ *
+ * @property int $id
+ * @property int $bouquet_id
+ * @property string $product_guid
+ * @property float|null $count
+ * @property string|null $created_at
+ * @property string|null $updated_at
+ * @property int|null $created_by
+ * @property int|null $updated_by
+ *
+ * @property BouquetComposition $bouquet
+ */
+class BouquetCompositionProducts extends ActiveRecord
+{
+ public static function tableName()
+ {
+ return 'erp24.bouquet_composition_products';
+ }
+
+ public function rules()
+ {
+ return [
+ [['bouquet_id', 'product_guid'], 'required'],
+ [['bouquet_id', 'created_by', 'updated_by'], 'integer'],
+ [['count'], 'number'],
+ [['created_at', 'updated_at'], 'safe'],
+ [['product_guid'], 'string', 'max' => 255],
+ [['bouquet_id'], 'exist', 'skipOnError' => true, 'targetClass' => BouquetComposition::class, 'targetAttribute' => ['bouquet_id' => 'id']],
+ ];
+ }
+
+ public function attributeLabels()
+ {
+ return [
+ 'id' => 'ID',
+ 'bouquet_id' => 'ID букета',
+ 'product_guid' => 'GUID продукта',
+ 'count' => 'Количество продукта',
+ 'created_at' => 'Дата создания',
+ 'updated_at' => 'Дата обновления',
+ 'created_by' => 'ID создателя записи',
+ 'updated_by' => 'ID обновителя записи',
+ ];
+ }
+
+ public function getBouquet()
+ {
+ return $this->hasOne(BouquetComposition::class, ['id' => 'bouquet_id']);
+ }
+}
class Products1c extends \yii\db\ActiveRecord
{
+ public const TYPE_PRODUCTS = 'products';
+ public const IS_VISIBLE = 1;
+
const PRODUCT1C_FIELDS = ['id', 'parent_id', 'tip', 'code', 'name', 'articule', 'view', 'components', 'AdditionCharacteristics'];
/**
--- /dev/null
+<?php
+
+use yii\helpers\Html;
+
+/** @var yii\web\View $this */
+
+$this->title = 'Составы букетов';
+?>
+<div class="bouquet-index">
+ <h1><?= Html::encode($this->title) ?></h1>
+
+ <p>
+ <?= Html::a('Добавить букет', ['create'], ['class' => 'btn btn-success']) ?>
+ </p>
+
+</div>
--- /dev/null
+<?php
+
+use app\widgets\DualList;
+use yii\helpers\ArrayHelper;
+use yii\helpers\Html;
+use yii_app\records\Products1c;
+
+/** @var yii\web\View $this */
+/** @var yii_app\records\BouquetComposition $model */
+
+$this->title = 'Три гладиолуса';
+$this->params['breadcrumbs'][] = ['label' => 'Букеты', 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+<div class="bouquet-update p-4">
+ <?= Html::label('Редактирование букета', null, ['class' => 'h4']) ?>
+ <h1 class="ms-3"><?= Html::encode($this->title) ?></h1>
+
+ <div class="row">
+ <div class="col-md-3 p-3 border rounded ms-3">
+ <div class="row mb-2">
+ <?= Html::dropDownList('category', null, [], ['class' => 'form-select', 'prompt' => 'Выберите категорию']) ?>
+ </div>
+ <div class="row mb-2">
+ <?= Html::dropDownList('species', null, [], ['class' => 'form-select', 'prompt' => 'Выберите вид']) ?>
+ </div>
+ <div class="row mb-2">
+ <?= Html::dropDownList('type', null, [], ['class' => 'form-select', 'prompt' => 'Выберите тип']) ?>
+ </div>
+ <div class="row mb-2">
+ <?= Html::dropDownList('size', null, [], ['class' => 'form-select', 'prompt' => 'Выберите размер']) ?>
+ </div>
+ <div class="row mb-3">
+ <?= Html::dropDownList('color', null, [], ['class' => 'form-select', 'prompt' => 'Выберите цвет']) ?>
+ </div>
+
+ <?= Html::button('Применить', ['class' => 'btn btn-primary w-100 mb-3', 'id' => 'apply-button']) ?>
+
+ <div class="border-top pt-2">
+ <p class="mb-1"><strong>Себестоимость:</strong> <span class="cost-value">0</span> ₽</p>
+ <p class="mb-1"><strong>Наценка:</strong> <span class="markup-value">0</span> %</p>
+ <p class="mb-0"><strong>Цена:</strong> <span class="price-value">0</span> ₽</p>
+ </div>
+ </div>
+
+ <div class="col-md-8">
+ <?= DualList::widget([
+ 'name' => 'products',
+ 'items' => ArrayHelper::map(
+ Products1c::findAll(['tip' => Products1c::TYPE_PRODUCTS, 'view' => Products1c::IS_VISIBLE]),
+ 'id', 'name'
+ ),
+ 'ajaxUrl' => ['/bouquet/get-list'], // для асинхронной загрузки данных
+ 'options' => [
+ 'multiple' => true,
+ 'size' => 10,
+ ],
+ 'triggerButton' => 'apply-button', // Кнопка, которая будет обновлять список
+ 'clientOptions' => [
+ 'moveOnSelect' => false,
+ 'nonSelectedListLabel' => 'Выбор',
+ 'selectedListLabel' => 'Состав букета',
+ 'filterTextClear' => 'Показать всё',
+ 'filterPlaceHolder' => 'Фильтр...',
+ 'infoText' => 'Показано {0}',
+ 'infoTextFiltered' => '<span class="badge bg-info">{0}</span> из {1}',
+ 'infoTextEmpty' => 'Список пуст',
+ ],
+ ]); ?>
+ </div>
+ </div>
+</div>
--- /dev/null
+<?php
+
+use kartik\file\FileInput;
+use yii\helpers\ArrayHelper;
+use yii\helpers\Html;
+use yii\helpers\Url;
+use yii\widgets\DetailView;
+use yii_app\records\MatrixType;
+
+/** @var yii\web\View $this */
+/** @var yii_app\records\BouquetComposition $model
+ */
+
+//$this->title = $model->name;
+$this->title = 'Три гладиолуса';
+//$this->title = $model->name;
+$this->params['breadcrumbs'][] = ['label' => 'Букеты', 'url' => ['index']];
+$this->params['breadcrumbs'][] = $this->title;
+?>
+<div class="bouquet-view p-4">
+ <?= Html::label('Состав букета', null, ['class' => 'h4']) ?>
+ <h1 class="ms-5"><?= Html::encode($this->title) ?></h1>
+
+ <div class="row">
+ <div class="col-md-4">
+ Тут типа расчеты
+ </div>
+ <div class="col-md-8">
+ <div class="row">
+ <div class="col-md-4">
+ <?= Html::label('Фото', null, ['class' => 'h4']) ?>
+ <?= FileInput::widget([
+ 'name' => 'attachment_4',
+ 'disabled' => true
+ ]); ?>
+ </div>
+ <div class="col-md-7">
+ <div class="row border-bottom">
+ <div class="col-md-5">
+ <?= Html::label('Тип матрицы', null, ['class' => 'h4']) ?>
+ <br>
+ <a href="<?= Url::to('/matrix-type') ?>" class="text-decoration-none" target="_blank">Редактировать</a>
+ </div>
+ <div class="col-md-7 pt-2 mb-2">
+ <?= Html::dropDownList('matrix_type_id', null, ArrayHelper::map(MatrixType::find()->all(), 'id', 'name'), ['class' => 'form-control', 'prompt' => 'Тип матрицы']) ?>
+ </div>
+ </div>
+ <div class="row">
+ <?= Html::label('Прогноз продаж (мес.)', null, ['class' => 'h5 text-center pt-5']) ?>
+ </div>
+ <div class="row pt-2">
+ <div class="col-md-4">
+ <?= Html::label('Маркетплейсы', null, ['class' => 'h5 pt-2']) ?>
+ </div>
+ <div class="col-md-8">
+ <?= Html::input('number', 'marketplace', null, ['class' => 'form-control']) ?>
+ </div>
+ </div>
+ <div class="row pt-2">
+ <div class="col-md-4">
+ <?= Html::label('Интернет магазин', null, ['class' => 'h5 pt-2']) ?>
+ </div>
+ <div class="col-md-8">
+ <?= Html::input('number', 'online_stores', null, ['class' => 'form-control']) ?>
+ </div>
+ </div>
+ <div class="row">
+ <?= Html::label('Оффлайн магазины', null, ['class' => 'h5 text-center pt-5']) ?>
+ </div>
+ <div class="row">
+ <?php foreach (\yii_app\records\StoreType::find()->all() as $number => $store) { ?>
+ <div class="col-md-3 pt-1 d-flex align-items-center">
+ <span class="offline-stores me-3"><?= Html::encode($store->name) ?></span>
+ </div>
+ <div class="col-md-9 pt-1 d-flex align-items-center">
+ <?= Html::input('number', "offline-store-$store->id", null, ['class' => 'form-control']) ?>
+ </div>
+ <?php } ?>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
--- /dev/null
+<?php
+
+namespace app\widgets;
+
+use yii\helpers\Json;
+use yii\web\View;
+use yii\helpers\Url;
+use softark\duallistbox\DualListbox;
+class DualList extends DualListbox
+{
+ public $ajaxUrl; // URL для AJAX-запроса
+ public $triggerButton; // ID кнопки, которая будет запускать AJAX-запрос
+ protected function registerPlugin($id)
+ {
+ parent::registerPlugin($id);
+
+ $view = $this->getView();
+
+ // Проверяем, что URL и кнопка переданы
+ if (!$this->ajaxUrl || !$this->triggerButton) {
+ return;
+ }
+
+ // Получаем URL для AJAX-запроса и кнопки
+ $ajaxUrl = Url::to($this->ajaxUrl);
+ $triggerButton = $this->triggerButton ? '#' . $this->triggerButton : null;
+
+ // Скрипт для обработки клика по кнопке и отправки AJAX-запроса
+ $js = <<<JS
+ function loadDualListboxData() {
+ console.log('Запрос отправляется на:', '$ajaxUrl');
+
+ $.ajax({
+ url: '$ajaxUrl',
+ method: 'GET',
+ success: function(data) {
+ console.log('Данные получены:', data);
+ if (Array.isArray(data)) {
+ let options = data.map(item => `<option value="\${item.id}">\${item.name}</option>`);
+ $('#$id').html(options.join('')); // Очищаем список и добавляем новые данные
+ $('#$id').bootstrapDualListbox('refresh'); // Обновляем плагин
+ }
+ },
+ error: function(xhr) {
+ console.error('Ошибка загрузки данных:', xhr);
+ }
+ });
+ }
+
+ // Проверим, что кнопка существует
+ $(document).on('click', '$triggerButton', function() {
+ console.log('Кнопка с ID $triggerButton нажата!');
+ loadDualListboxData();
+ });
+
+ // Печать, чтобы проверить, загружен ли скрипт
+ console.log('JavaScript был загружен и привязан!');
+JS;
+
+ // Регистрируем JS скрипт
+ $view->registerJs($js);
+ }
+}
+
+
+