feat: add product interaction mode selector with three scenarios
- Add ItemToggleButton component for 3-way toggle in admin panel - Add product_interaction_mode setting with options: order, manager, browser - Add manager_username setting for Telegram manager contact - Remove store_enabled setting, replaced with product_interaction_mode - Create migration to automatically migrate store_enabled to product_interaction_mode - Update Product.vue to handle all three interaction modes - Update Dock.vue to show cart button only when product_interaction_mode is 'order' - Rename 'Магазин' tab to 'Витрина' in admin panel - Remove 'Разрешить покупки' option (now controlled via product_interaction_mode) - Set default product_interaction_mode to 'browser' - Update StoreDTO to remove enableStore field - Update SettingsHandler to return product_interaction_mode instead of store_enabled
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li :class="{active: route.name === 'store'}">
|
<li :class="{active: route.name === 'store'}">
|
||||||
<RouterLink :to="{name: 'store'}">Магазин</RouterLink>
|
<RouterLink :to="{name: 'store'}">Витрина</RouterLink>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li :class="{active: route.name === 'texts'}">
|
<li :class="{active: route.name === 'texts'}">
|
||||||
|
|||||||
51
frontend/admin/src/components/Settings/ItemToggleButton.vue
Normal file
51
frontend/admin/src/components/Settings/ItemToggleButton.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<SettingsItem :label="label">
|
||||||
|
<template #default>
|
||||||
|
<SelectButton
|
||||||
|
:modelValue="model"
|
||||||
|
:options="options"
|
||||||
|
optionLabel="label"
|
||||||
|
optionValue="value"
|
||||||
|
:allowEmpty="false"
|
||||||
|
@update:modelValue="updateValue"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #help>
|
||||||
|
<slot/>
|
||||||
|
</template>
|
||||||
|
</SettingsItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {computed} from "vue";
|
||||||
|
import SettingsItem from "@/components/SettingsItem.vue";
|
||||||
|
import SelectButton from "primevue/selectbutton";
|
||||||
|
|
||||||
|
const model = defineModel();
|
||||||
|
const props = defineProps({
|
||||||
|
items: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = computed(() => {
|
||||||
|
return Object.entries(props.items).map(([value, label]) => ({
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateValue(newValue) {
|
||||||
|
model.value = newValue;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -36,10 +36,11 @@ export const useSettingsStore = defineStore('settings', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
store: {
|
store: {
|
||||||
enable_store: true,
|
|
||||||
feature_coupons: true,
|
feature_coupons: true,
|
||||||
feature_vouchers: true,
|
feature_vouchers: true,
|
||||||
show_category_products_button: true,
|
show_category_products_button: true,
|
||||||
|
product_interaction_mode: 'browser',
|
||||||
|
manager_username: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
orders: {
|
orders: {
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<ItemBool label="Разрешить покупки" v-model="settings.items.store.enable_store">
|
<ItemToggleButton
|
||||||
<p>Если опция <strong>включена</strong> — пользователи смогут оформлять
|
label="Сценарий взаимодействия с товаром"
|
||||||
заказы прямо в Telegram-магазине. <br>
|
v-model="settings.items.store.product_interaction_mode"
|
||||||
Если <strong>выключена</strong> — оформление заказов будет недоступно. Вместо кнопки «Добавить
|
:items="productInteractionOptions"
|
||||||
в корзину» пользователи увидят кнопку «Перейти к товару», которая откроет страницу товара на
|
>
|
||||||
вашем сайте. В этом режиме Telecart работает как каталог.</p>
|
<p>Выберите, что будет происходить при нажатии на кнопку товара:
|
||||||
</ItemBool>
|
<br><strong>Создание заявки / заказа</strong> — Пользователи смогут добавить товар и оформить заявку на покупку прямо в Telegram. Заказ фиксируется в OpenCart, а дальнейшая работа с клиентом происходит вручную.
|
||||||
|
<br><strong>Кнопка связи с менеджером</strong> — пользователи увидят кнопку для связи с менеджером в Telegram. Менеджера можно указать в поле "Username менеджера" ниже.
|
||||||
|
<br><strong>Открытие товара на сайте</strong> — кнопка откроет страницу товара на основном сайте OpenCart во внешнем браузере.</p>
|
||||||
|
</ItemToggleButton>
|
||||||
|
|
||||||
|
<ItemInput
|
||||||
|
label="Username менеджера"
|
||||||
|
v-model="settings.items.store.manager_username"
|
||||||
|
placeholder="@username"
|
||||||
|
>
|
||||||
|
<p>Укажите username (например, @username) для связи с менеджером. Это может быть личный аккаунт или группа, куда покупатели могут писать. Используется только при выборе режима "Кнопка связи с менеджером".</p>
|
||||||
|
</ItemInput>
|
||||||
|
|
||||||
<ItemBool label="Промокоды" v-model="settings.items.store.feature_coupons">
|
<ItemBool label="Промокоды" v-model="settings.items.store.feature_coupons">
|
||||||
<p>
|
<p>
|
||||||
@@ -31,6 +42,8 @@
|
|||||||
import {useSettingsStore} from "@/stores/settings.js";
|
import {useSettingsStore} from "@/stores/settings.js";
|
||||||
import ItemBool from "@/components/Settings/ItemBool.vue";
|
import ItemBool from "@/components/Settings/ItemBool.vue";
|
||||||
import ItemSelect from "@/components/Settings/ItemSelect.vue";
|
import ItemSelect from "@/components/Settings/ItemSelect.vue";
|
||||||
|
import ItemInput from "@/components/Settings/ItemInput.vue";
|
||||||
|
import ItemToggleButton from "@/components/Settings/ItemToggleButton.vue";
|
||||||
import ItemProductsSelect from "@/components/Settings/ItemProductsSelect.vue";
|
import ItemProductsSelect from "@/components/Settings/ItemProductsSelect.vue";
|
||||||
import ItemCategoriesSelect from "@/components/Settings/ItemCategoriesSelect.vue";
|
import ItemCategoriesSelect from "@/components/Settings/ItemCategoriesSelect.vue";
|
||||||
|
|
||||||
@@ -42,5 +55,11 @@ const mainpage_categories_options = {
|
|||||||
featured: 'Избранные категории (задать в поле ниже)',
|
featured: 'Избранные категории (задать в поле ниже)',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const productInteractionOptions = {
|
||||||
|
order: 'Создание заявки / заказа',
|
||||||
|
manager: 'Кнопка связи с менеджером',
|
||||||
|
browser: 'Открытие товара на сайте',
|
||||||
|
};
|
||||||
|
|
||||||
const userToken = window.TeleCart.user_token;
|
const userToken = window.TeleCart.user_token;
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
<RouterLink
|
<RouterLink
|
||||||
v-if="settings.store_enabled"
|
v-if="settings.product_interaction_mode === 'order'"
|
||||||
:to="{name: 'cart'}"
|
:to="{name: 'cart'}"
|
||||||
:class="{'dock-active': route.name === 'cart'}"
|
:class="{'dock-active': route.name === 'cart'}"
|
||||||
@click="onDockItemClick"
|
@click="onDockItemClick"
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ export const useSettingsStore = defineStore('settings', {
|
|||||||
state: () => ({
|
state: () => ({
|
||||||
app_enabled: true,
|
app_enabled: true,
|
||||||
app_debug: false,
|
app_debug: false,
|
||||||
store_enabled: true,
|
product_interaction_mode: 'browser',
|
||||||
|
manager_username: null,
|
||||||
app_name: 'OpenCart Telegram магазин',
|
app_name: 'OpenCart Telegram магазин',
|
||||||
app_icon: '',
|
app_icon: '',
|
||||||
night_auto: true,
|
night_auto: true,
|
||||||
@@ -41,7 +42,8 @@ export const useSettingsStore = defineStore('settings', {
|
|||||||
this.ya_metrika_enabled = settings.ya_metrika_enabled;
|
this.ya_metrika_enabled = settings.ya_metrika_enabled;
|
||||||
this.app_enabled = settings.app_enabled;
|
this.app_enabled = settings.app_enabled;
|
||||||
this.app_debug = settings.app_debug;
|
this.app_debug = settings.app_debug;
|
||||||
this.store_enabled = settings.store_enabled;
|
this.product_interaction_mode = settings.product_interaction_mode ?? 'browser';
|
||||||
|
this.manager_username = settings.manager_username;
|
||||||
this.feature_coupons = settings.feature_coupons;
|
this.feature_coupons = settings.feature_coupons;
|
||||||
this.feature_vouchers = settings.feature_vouchers;
|
this.feature_vouchers = settings.feature_vouchers;
|
||||||
this.show_category_products_button = settings.show_category_products_button ?? true;
|
this.show_category_products_button = settings.show_category_products_button ?? true;
|
||||||
|
|||||||
@@ -123,7 +123,8 @@
|
|||||||
<div v-if="product.product_id"
|
<div v-if="product.product_id"
|
||||||
class="fixed bottom-fix px-4 pb-4 pt-4 left-0 w-full bg-base-100/95 backdrop-blur-md z-50 flex flex-col gap-3 border-t border-base-300 shadow-lg"
|
class="fixed bottom-fix px-4 pb-4 pt-4 left-0 w-full bg-base-100/95 backdrop-blur-md z-50 flex flex-col gap-3 border-t border-base-300 shadow-lg"
|
||||||
>
|
>
|
||||||
<template v-if="settings.store_enabled">
|
<!-- Режим: Создание заказа -->
|
||||||
|
<template v-if="settings.product_interaction_mode === 'order'">
|
||||||
<div v-if="error" class="alert alert-error alert-sm py-2">
|
<div v-if="error" class="alert alert-error alert-sm py-2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-4 w-4" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-4 w-4" fill="none"
|
||||||
viewBox="0 0 24 24">
|
viewBox="0 0 24 24">
|
||||||
@@ -175,7 +176,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<!-- Режим: Кнопка связи с менеджером -->
|
||||||
|
<template v-else-if="settings.product_interaction_mode === 'manager'">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-lg w-full shadow-md"
|
||||||
|
:disabled="!settings.manager_username"
|
||||||
|
@click="openManagerChat"
|
||||||
|
>
|
||||||
|
<template v-if="settings.manager_username">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
|
stroke="currentColor" class="w-5 h-5">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-5.74.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155"/>
|
||||||
|
</svg>
|
||||||
|
Связаться с менеджером
|
||||||
|
</template>
|
||||||
|
<template v-else>Менеджер недоступен</template>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Режим: Открытие товара на сайте -->
|
||||||
|
<template v-else-if="settings.product_interaction_mode === 'browser'">
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary btn-lg w-full shadow-md"
|
class="btn btn-primary btn-lg w-full shadow-md"
|
||||||
:disabled="! product.share"
|
:disabled="! product.share"
|
||||||
@@ -301,6 +322,27 @@ function openProductInMarketplace() {
|
|||||||
window.Telegram.WebApp.openLink(product.value.share, {try_instant_view: false});
|
window.Telegram.WebApp.openLink(product.value.share, {try_instant_view: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openManagerChat() {
|
||||||
|
if (!settings.manager_username) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Telegram.WebApp.HapticFeedback.selectionChanged();
|
||||||
|
|
||||||
|
// Формируем ссылку для открытия чата с менеджером
|
||||||
|
// manager_username должен быть username (например, @username)
|
||||||
|
const managerUsername = String(settings.manager_username).trim();
|
||||||
|
|
||||||
|
// Если username начинается с @, убираем @
|
||||||
|
const username = managerUsername.startsWith('@')
|
||||||
|
? managerUsername.substring(1)
|
||||||
|
: managerUsername;
|
||||||
|
|
||||||
|
const chatUrl = `https://t.me/${username}`;
|
||||||
|
|
||||||
|
window.Telegram.WebApp.openTelegramLink(chatUrl);
|
||||||
|
}
|
||||||
|
|
||||||
function setQuantity(newQuantity) {
|
function setQuantity(newQuantity) {
|
||||||
quantity.value = newQuantity;
|
quantity.value = newQuantity;
|
||||||
window.Telegram.WebApp.HapticFeedback.selectionChanged();
|
window.Telegram.WebApp.HapticFeedback.selectionChanged();
|
||||||
|
|||||||
@@ -52,10 +52,11 @@ HTML,
|
|||||||
],
|
],
|
||||||
|
|
||||||
'store' => [
|
'store' => [
|
||||||
'enable_store' => true,
|
|
||||||
'feature_coupons' => true,
|
'feature_coupons' => true,
|
||||||
'feature_vouchers' => true,
|
'feature_vouchers' => true,
|
||||||
'show_category_products_button' => true,
|
'show_category_products_button' => true,
|
||||||
|
'product_interaction_mode' => 'browser',
|
||||||
|
'manager_username' => null,
|
||||||
],
|
],
|
||||||
|
|
||||||
'texts' => [
|
'texts' => [
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Openguru\OpenCartFramework\Migrations\Migration;
|
||||||
|
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
|
||||||
|
use Openguru\OpenCartFramework\Support\Arr;
|
||||||
|
|
||||||
|
return new class extends Migration {
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$opencart = $this->app->get(OcRegistryDecorator::class);
|
||||||
|
$opencart->load->model('setting/setting');
|
||||||
|
|
||||||
|
$currentSettings = $opencart->model_setting_setting->getSetting('module_telecart');
|
||||||
|
|
||||||
|
if (! $currentSettings || ! isset($currentSettings['module_telecart_settings'])) {
|
||||||
|
$this->logger->info("Settings not found in database, migration skipped");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allSettings = $currentSettings['module_telecart_settings'];
|
||||||
|
|
||||||
|
// Проверяем наличие store.enable_store
|
||||||
|
$enableStore = Arr::get($allSettings, 'store.enable_store');
|
||||||
|
|
||||||
|
if ($enableStore !== null) {
|
||||||
|
// Определяем значение product_interaction_mode на основе store.enable_store
|
||||||
|
$productInteractionMode = filter_var($enableStore, FILTER_VALIDATE_BOOLEAN)
|
||||||
|
? 'order'
|
||||||
|
: 'browser';
|
||||||
|
|
||||||
|
// Устанавливаем product_interaction_mode, если его еще нет
|
||||||
|
if (!isset($allSettings['store']['product_interaction_mode'])) {
|
||||||
|
Arr::set($allSettings, 'store.product_interaction_mode', $productInteractionMode);
|
||||||
|
$this->logger->info("Migrated store.enable_store to product_interaction_mode: {$enableStore} -> {$productInteractionMode}");
|
||||||
|
} else {
|
||||||
|
$this->logger->info("product_interaction_mode already exists, skipping migration but removing store.enable_store");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем store.enable_store из настроек
|
||||||
|
Arr::unset($allSettings, 'store.enable_store');
|
||||||
|
$this->logger->info("Removed store.enable_store from settings");
|
||||||
|
|
||||||
|
// Сохраняем обновленные настройки через OpenCart модель
|
||||||
|
$opencart->model_setting_setting->editSetting('module_telecart', [
|
||||||
|
'module_telecart_settings' => $allSettings,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->logger->info("Successfully migrated store.enable_store to product_interaction_mode and removed store.enable_store from settings");
|
||||||
|
} else {
|
||||||
|
$this->logger->info("store.enable_store not found in settings, migration skipped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -4,37 +4,35 @@ namespace App\DTO\Settings;
|
|||||||
|
|
||||||
final class StoreDTO
|
final class StoreDTO
|
||||||
{
|
{
|
||||||
private bool $enableStore;
|
|
||||||
private bool $featureCoupons;
|
private bool $featureCoupons;
|
||||||
private bool $featureVouchers;
|
private bool $featureVouchers;
|
||||||
private bool $showCategoryProductsButton;
|
private bool $showCategoryProductsButton;
|
||||||
|
private string $productInteractionMode;
|
||||||
|
private ?string $managerUsername;
|
||||||
private string $ocDefaultCurrency;
|
private string $ocDefaultCurrency;
|
||||||
private bool $ocConfigTax;
|
private bool $ocConfigTax;
|
||||||
private int $ocStoreId;
|
private int $ocStoreId;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
bool $enableStore,
|
|
||||||
bool $featureCoupons,
|
bool $featureCoupons,
|
||||||
bool $featureVouchers,
|
bool $featureVouchers,
|
||||||
bool $showCategoryProductsButton,
|
bool $showCategoryProductsButton,
|
||||||
|
string $productInteractionMode,
|
||||||
|
?string $managerUsername,
|
||||||
string $ocDefaultCurrency,
|
string $ocDefaultCurrency,
|
||||||
bool $ocConfigTax,
|
bool $ocConfigTax,
|
||||||
int $ocStoreId
|
int $ocStoreId
|
||||||
) {
|
) {
|
||||||
$this->enableStore = $enableStore;
|
|
||||||
$this->featureCoupons = $featureCoupons;
|
$this->featureCoupons = $featureCoupons;
|
||||||
$this->featureVouchers = $featureVouchers;
|
$this->featureVouchers = $featureVouchers;
|
||||||
$this->showCategoryProductsButton = $showCategoryProductsButton;
|
$this->showCategoryProductsButton = $showCategoryProductsButton;
|
||||||
|
$this->productInteractionMode = $productInteractionMode;
|
||||||
|
$this->managerUsername = $managerUsername;
|
||||||
$this->ocDefaultCurrency = $ocDefaultCurrency;
|
$this->ocDefaultCurrency = $ocDefaultCurrency;
|
||||||
$this->ocConfigTax = $ocConfigTax;
|
$this->ocConfigTax = $ocConfigTax;
|
||||||
$this->ocStoreId = $ocStoreId;
|
$this->ocStoreId = $ocStoreId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEnableStore(): bool
|
|
||||||
{
|
|
||||||
return $this->enableStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isFeatureCoupons(): bool
|
public function isFeatureCoupons(): bool
|
||||||
{
|
{
|
||||||
return $this->featureCoupons;
|
return $this->featureCoupons;
|
||||||
@@ -50,6 +48,16 @@ final class StoreDTO
|
|||||||
return $this->showCategoryProductsButton;
|
return $this->showCategoryProductsButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getProductInteractionMode(): string
|
||||||
|
{
|
||||||
|
return $this->productInteractionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getManagerUsername(): ?string
|
||||||
|
{
|
||||||
|
return $this->managerUsername;
|
||||||
|
}
|
||||||
|
|
||||||
public function getOcDefaultCurrency(): string
|
public function getOcDefaultCurrency(): string
|
||||||
{
|
{
|
||||||
return $this->ocDefaultCurrency;
|
return $this->ocDefaultCurrency;
|
||||||
@@ -68,10 +76,12 @@ final class StoreDTO
|
|||||||
public function toArray(): array
|
public function toArray(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'enable_store' => $this->enableStore,
|
// enable_store больше не сериализуется, так как заменен на product_interaction_mode
|
||||||
'feature_coupons' => $this->featureCoupons,
|
'feature_coupons' => $this->featureCoupons,
|
||||||
'feature_vouchers' => $this->featureVouchers,
|
'feature_vouchers' => $this->featureVouchers,
|
||||||
'show_category_products_button' => $this->showCategoryProductsButton,
|
'show_category_products_button' => $this->showCategoryProductsButton,
|
||||||
|
'product_interaction_mode' => $this->productInteractionMode,
|
||||||
|
'manager_username' => $this->managerUsername,
|
||||||
'oc_default_currency' => $this->ocDefaultCurrency,
|
'oc_default_currency' => $this->ocDefaultCurrency,
|
||||||
'oc_config_tax' => $this->ocConfigTax,
|
'oc_config_tax' => $this->ocConfigTax,
|
||||||
'oc_store_id' => $this->ocStoreId,
|
'oc_store_id' => $this->ocStoreId,
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ class SettingsHandler
|
|||||||
'theme_dark' => $appConfig->getThemeDark(),
|
'theme_dark' => $appConfig->getThemeDark(),
|
||||||
'ya_metrika_enabled' => $this->settings->config()->getMetrics()->isYandexMetrikaEnabled(),
|
'ya_metrika_enabled' => $this->settings->config()->getMetrics()->isYandexMetrikaEnabled(),
|
||||||
'app_enabled' => $appConfig->isAppEnabled(),
|
'app_enabled' => $appConfig->isAppEnabled(),
|
||||||
'store_enabled' => $this->settings->config()->getStore()->isEnableStore(),
|
'product_interaction_mode' => $this->settings->config()->getStore()->getProductInteractionMode(),
|
||||||
|
'manager_username' => $this->settings->config()->getStore()->getManagerUsername(),
|
||||||
'feature_coupons' => $this->settings->config()->getStore()->isFeatureCoupons(),
|
'feature_coupons' => $this->settings->config()->getStore()->isFeatureCoupons(),
|
||||||
'feature_vouchers' => $this->settings->config()->getStore()->isFeatureVouchers(),
|
'feature_vouchers' => $this->settings->config()->getStore()->isFeatureVouchers(),
|
||||||
'show_category_products_button' => $this->settings->config()->getStore()->isShowCategoryProductsButton(),
|
'show_category_products_button' => $this->settings->config()->getStore()->isShowCategoryProductsButton(),
|
||||||
|
|||||||
@@ -138,10 +138,11 @@ class SettingsSerializerService
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new StoreDTO(
|
return new StoreDTO(
|
||||||
$data['enable_store'] ?? true,
|
|
||||||
$data['feature_coupons'] ?? true,
|
$data['feature_coupons'] ?? true,
|
||||||
$data['feature_vouchers'] ?? true,
|
$data['feature_vouchers'] ?? true,
|
||||||
$data['show_category_products_button'] ?? true,
|
$data['show_category_products_button'] ?? true,
|
||||||
|
$data['product_interaction_mode'] ?? 'browser',
|
||||||
|
$data['manager_username'] ?? null,
|
||||||
$data['oc_default_currency'],
|
$data['oc_default_currency'],
|
||||||
$data['oc_config_tax'],
|
$data['oc_config_tax'],
|
||||||
$data['oc_store_id']
|
$data['oc_store_id']
|
||||||
@@ -270,9 +271,7 @@ class SettingsSerializerService
|
|||||||
|
|
||||||
private function validateStore(array $data): void
|
private function validateStore(array $data): void
|
||||||
{
|
{
|
||||||
if (isset($data['enable_store']) && ! is_bool($data['enable_store'])) {
|
// enable_store больше не валидируется, так как заменен на product_interaction_mode
|
||||||
throw new InvalidArgumentException('store.enable_store must be a boolean');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($data['feature_coupons']) && ! is_bool($data['feature_coupons'])) {
|
if (isset($data['feature_coupons']) && ! is_bool($data['feature_coupons'])) {
|
||||||
throw new InvalidArgumentException('store.feature_coupons must be a boolean');
|
throw new InvalidArgumentException('store.feature_coupons must be a boolean');
|
||||||
@@ -286,6 +285,25 @@ class SettingsSerializerService
|
|||||||
throw new InvalidArgumentException('store.show_category_products_button must be a boolean');
|
throw new InvalidArgumentException('store.show_category_products_button must be a boolean');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($data['product_interaction_mode']) && ! is_string($data['product_interaction_mode'])) {
|
||||||
|
throw new InvalidArgumentException('store.product_interaction_mode must be a string');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['product_interaction_mode']) && ! in_array($data['product_interaction_mode'], ['order', 'manager', 'browser'], true)) {
|
||||||
|
throw new InvalidArgumentException('store.product_interaction_mode must be one of: order, manager, browser');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['manager_username']) && $data['manager_username'] !== null) {
|
||||||
|
if (! is_string($data['manager_username'])) {
|
||||||
|
throw new InvalidArgumentException('store.manager_username must be a string or null');
|
||||||
|
}
|
||||||
|
// Проверяем, что это username (не числовой ID)
|
||||||
|
$managerUsername = trim($data['manager_username']);
|
||||||
|
if ($managerUsername !== '' && preg_match('/^-?\d+$/', $managerUsername)) {
|
||||||
|
throw new InvalidArgumentException('store.manager_username must be a username (e.g., @username), not a numeric ID');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (! isset($data['oc_default_currency'])) {
|
if (! isset($data['oc_default_currency'])) {
|
||||||
throw new InvalidArgumentException('store.oc_default_currency is required');
|
throw new InvalidArgumentException('store.oc_default_currency is required');
|
||||||
}
|
}
|
||||||
|
|||||||
0
module/oc_telegram_shop/upload/oc_telegram_shop/stubs/phpstan-bootstrap.php
Normal file → Executable file
0
module/oc_telegram_shop/upload/oc_telegram_shop/stubs/phpstan-bootstrap.php
Normal file → Executable file
Reference in New Issue
Block a user