khanegi setting and prices records

This commit is contained in:
2026-06-13 17:35:40 +03:30
parent c70cda0b6b
commit 91a82c06e4
15 changed files with 509 additions and 73 deletions
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace App\Console\Commands;
use App\Services\LivePriceTickService;
use Illuminate\Console\Command;
class PriceTickCommand extends Command
{
protected $signature = 'prices:tick';
protected $description = 'Fetch live prices, sync products, cache last/list, log snapshot (every 10s)';
public function handle(LivePriceTickService $tickService): int
{
$tickService->tick();
return self::SUCCESS;
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Console\Commands;
use App\Services\PriceSnapshotService;
use Illuminate\Console\Command;
class PrunePriceSnapshotsCommand extends Command
{
protected $signature = 'prices:prune';
protected $description = 'Downsample and prune price history (daily at midnight)';
public function handle(PriceSnapshotService $snapshots): int
{
$stats = $snapshots->pruneHistory();
$this->info(json_encode($stats, JSON_UNESCAPED_UNICODE));
return self::SUCCESS;
}
}
+4 -5
View File
@@ -2,19 +2,18 @@
namespace App\Console\Commands;
use App\Services\ProductPriceSyncService;
use App\Services\LivePriceTickService;
use Illuminate\Console\Command;
class SyncProductPrices extends Command
{
protected $signature = 'product:sync-prices';
protected $description = 'Fetch zaarbox price board + sync product DB (scheduler: every 30s)';
protected $description = 'Alias for prices:tick (backward compatible cron)';
public function handle(ProductPriceSyncService $syncService): int
public function handle(LivePriceTickService $tickService): int
{
// کران: GET https://price.zaarbox.ir/api/free (یا zaarbox_api_url) → کش + UPDATE محصولات
$syncService->syncAll(true);
$tickService->tick();
return self::SUCCESS;
}
+13 -19
View File
@@ -2,44 +2,38 @@
namespace App\Console;
use App\Console\Commands\Price;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected $commands = [
Price::class,
Commands\Price::class,
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// همگام‌سازی قیمت محصولات متغیر (reading_method) — هر ۳۰ ثانیه
// تیک قیمت — هر ۱۰ ثانیه (۶ بار در دقیقه)
$schedule->call(function () {
for ($i = 0; $i < 2; $i++) {
\Illuminate\Support\Facades\Artisan::call('product:sync-prices');
if ($i < 1) {
sleep(30);
for ($i = 0; $i < 6; $i++) {
\Illuminate\Support\Facades\Artisan::call('prices:tick');
if ($i < 5) {
sleep(10);
}
}
})->everyMinute()->name('sync_product_prices_30s')->withoutOverlapping();
})->everyMinute()->name('prices_tick_10s')->withoutOverlapping();
$schedule->command('prices:refresh-zaarbox')
->everyThirtyMinutes()
->name('refresh_zaarbox_prices')
->withoutOverlapping();
// پاک‌سازی و downsample تاریخچه — ساعت ۱۲ شب
$schedule->command('prices:prune')
->dailyAt('00:00')
->name('prune_price_snapshots');
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Admin;
use App\DB\Te\TableEngine;
use App\Http\Controllers\Controller;
use App\Models\PriceSyncLog;
use App\Services\ProductPriceSyncService;
use App\Services\LivePriceTickService;
use Illuminate\Http\Request;
class PriceSyncLogController extends Controller
@@ -29,14 +29,14 @@ class PriceSyncLogController extends Controller
return response()->json($log);
}
public function runSync(ProductPriceSyncService $syncService)
public function runSync(LivePriceTickService $tickService)
{
$syncService->syncStorePrices(true);
$tickService->tick();
$log = PriceSyncLog::with('items')->orderByDesc('id')->first();
return response()->json([
'message' => $log ? 'قیمت‌ها به‌روزرسانی شد' : 'تغییری در قیمت محصولات نبود',
'message' => $log ? 'قیمت‌ها به‌روزرسانی شد' : 'همگام‌سازی انجام شد',
'data' => $log,
]);
}
@@ -175,15 +175,12 @@ class ProductController extends SuperController
return response()->json($products);
}
// آبشده خانگی — ادمین: sync از کش؛ کاربر: enrich بدون UPDATE
if (! str_contains(request()->path(), '/customer/')) {
app(ProductPriceSyncService::class)->syncAll(false);
}
// آبشده خانگی — کران قیمت را آپدیت می‌کند؛ list فقط از DB می‌خواند
$products = Product::with(['file'])->where('active', 1)->get();
if (str_contains(request()->path(), '/customer/')) {
$displayPriceService = app(ProductDisplayPriceService::class);
$displayPriceService->warmLivePriceCache();
$displayPriceService->enrichForApi($products);
$products->each(function (Product $product) use ($displayPriceService) {
if (! $displayPriceService->hasCustomerDisplayPrice($product)) {
+31 -21
View File
@@ -21,6 +21,7 @@ use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Morilog\Jalali\Jalalian;
use App\Services\ConfigService;
use App\Services\LivePriceCacheService;
use App\Services\ProductDisplayPriceService;
@@ -127,15 +128,37 @@ class ReportController extends Controller
$payload['last_historical'] = app(ProductDisplayPriceService::class)->resolveHistoricalBasePrice();
app(ProductDisplayPriceService::class)->cacheLivePricePayload($payload);
if (request()->boolean('force_refresh')) {
return app(LivePriceCacheService::class)->put($payload);
}
return $payload;
}
public function lastPrice()
public function lastPrice(Request $request)
{
$config = app(ConfigService::class);
$cache = app(LivePriceCacheService::class);
if (! $request->boolean('force_refresh')) {
$cached = $cache->get();
if ($cached !== null) {
return $cached;
}
$dbBuilt = $cache->buildFromDatabase();
if (($dbBuilt['price'] ?? 0) > 0) {
return $dbBuilt;
}
}
if ($config->isStore()) {
return $this->storeLastPrice($config);
return $this->appendUpPrices(array_merge([
'closed' => false,
'time' => now(),
'main_product' => 0,
'zaarbox' => 0,
], $this->storeLastPrice($config)));
}
$closed = false;
@@ -361,24 +384,14 @@ class ReportController extends Controller
}
if ($priceValue <= 0) {
if (!$show_last_price) {
return $this->appendUpPrices([
'sell' => 0,
'buy' => 0,
'price_last' => 0,
'price' => 0,
'main_product' => $pr_price ?? 0,
'montzar' => $montzar_price ?? 0,
'zaarbox' => $zaarbox_price ?? 0,
'closed' => true,
'time' => now(),
]);
}
$lastPrice = Price::latest()->first();
$storedPrice = $lastPrice ? (float) $lastPrice->price : 0.0;
$useFallback = $request->boolean('force_refresh') || $show_last_price;
if ($storedPrice <= 0) {
if ($storedPrice > 30_000_000 && $useFallback) {
$priceValue = $storedPrice;
$priceSource = 'database_last';
} else {
return $this->appendUpPrices([
'sell' => 0,
'buy' => 0,
@@ -391,9 +404,6 @@ class ReportController extends Controller
'time' => now(),
]);
}
$priceValue = $storedPrice;
$priceSource = 'database_last';
}
/*
@@ -408,7 +418,7 @@ class ReportController extends Controller
if (
! $closed &&
(! $lastPrice || (float) $lastPrice->price != (float) $priceToStore) &&
$priceToStore > 1000000
$priceToStore > 30_000_000
) {
$price = new Price();
$price->price = $priceToStore;
+89
View File
@@ -0,0 +1,89 @@
<?php
namespace App\Services;
use App\Models\Price;
use Illuminate\Support\Facades\Cache;
class LivePriceCacheService
{
public const FULL_RESPONSE_KEY = 'live_price_full_response';
public const TTL_SECONDS = 15;
public function get(): ?array
{
$cached = Cache::get(self::FULL_RESPONSE_KEY);
if (! is_array($cached) || ! isset($cached['body'], $cached['stored_at'])) {
return null;
}
if (now()->diffInSeconds($cached['stored_at']) > self::TTL_SECONDS) {
return null;
}
return $cached['body'];
}
public function put(array $body): array
{
Cache::put(self::FULL_RESPONSE_KEY, [
'body' => $body,
'stored_at' => now(),
], now()->addSeconds(self::TTL_SECONDS + 5));
app(ProductDisplayPriceService::class)->cacheLivePricePayload($body);
return $body;
}
/** پاسخ سریع بدون API خارجی — از آخرین رکورد prices + تنظیمات */
public function buildFromDatabase(): array
{
$config = app(ConfigService::class);
$display = app(ProductDisplayPriceService::class);
$last = Price::orderByDesc('id')->first();
$base = $last ? (int) $last->price : 0;
if ($base <= 0) {
$payload = [
'sell' => 0,
'buy' => 0,
'price_last' => 0,
'price' => 0,
'main_product' => 0,
'montzar' => 0,
'zaarbox' => 0,
'closed' => true,
'time' => now(),
];
} else {
$diffBuy = (int) $config->displayBuySpreadRial();
$diffSell = (int) $config->displaySellSpreadRial();
$buy = (int) ceil($base + $diffBuy);
$sell = max(0, (int) ceil($base - $diffSell));
$payload = [
'sell' => $sell,
'buy' => $buy,
'price_last' => $buy,
'price' => $base,
'main_product' => 0,
'montzar' => 0,
'zaarbox' => 0,
'closed' => false,
'time' => now(),
];
}
$payload['up'] = $config->resolvedDailyBaseExtremes()['up'] > 0
? [
'sell' => max(0, (int) ceil($config->resolvedDailyBaseExtremes()['down'] - $config->displaySellSpreadRial())),
'buy' => (int) ceil($config->resolvedDailyBaseExtremes()['up'] + $config->displayBuySpreadRial()),
]
: ['sell' => $payload['sell'] ?? 0, 'buy' => $payload['buy'] ?? 0];
$payload['last_historical'] = $display->resolveHistoricalBasePrice();
return $payload;
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace App\Services;
use App\Http\Controllers\Site\ReportController;
use App\Models\Product;
use Illuminate\Http\Request;
class LivePriceTickService
{
public function tick(): void
{
$config = app(ConfigService::class);
$sync = app(ProductPriceSyncService::class);
if ($config->isStore()) {
$sync->syncStorePrices(true);
$source = 'zaarbox';
$sourceUrl = app(ZaarboxPriceService::class)->apiUrl();
} else {
$sync->syncAll(true);
$source = $config->priceProvider() ?: 'legacy';
$sourceUrl = $source === 'zaarbox'
? app(ZaarboxPriceService::class)->apiUrl()
: null;
}
$liquidGold = (int) $config->getInt('montzar_live_price', 0);
if ($liquidGold <= 0) {
$liquidGold = app(ProductDisplayPriceService::class)->resolveHistoricalBasePrice();
}
$activeProducts = Product::query()
->where('active', 1)
->whereNotNull('reading_method')
->count();
app(PriceSyncLogService::class)->recordTick($source, $sourceUrl, $liquidGold, $activeProducts);
$request = Request::create('/api/price/last', 'GET', ['force_refresh' => '1']);
$response = app(ReportController::class)->lastPrice($request);
$payload = json_decode($response->getContent(), true);
if (is_array($payload)) {
app(PriceSnapshotService::class)->recordTick($payload);
}
}
}
+173
View File
@@ -0,0 +1,173 @@
<?php
namespace App\Services;
use App\Models\Price;
use App\Models\PriceCandle;
use Carbon\Carbon;
class PriceSnapshotService
{
/** هر تیک کران — ذخیره در prices برای نمودار (هرگز صفر ثبت نمی‌شود) */
public function recordTick(array $payload): void
{
$priceValue = (int) ($payload['price'] ?? 0);
if ($priceValue <= 30_000_000) {
$last = Price::orderByDesc('id')->first();
if (! $last || (int) $last->price <= 30_000_000) {
return;
}
$priceValue = (int) $last->price;
}
Price::create([
'price' => $priceValue,
'date_at' => Carbon::now()->toDateString(),
'time_at' => Carbon::now()->toTimeString(),
'user_id' => null,
]);
$this->touchCandle($priceValue);
}
private function touchCandle(int $priceValue): void
{
$candle = PriceCandle::whereDate('date_at', date('Y-m-d'))->first();
if ($candle) {
$candle->end = $priceValue;
if ($priceValue < $candle->low) {
$candle->low = $priceValue;
} elseif ($priceValue > $candle->high) {
$candle->high = $priceValue;
}
$candle->save();
return;
}
PriceCandle::create([
'start' => $priceValue,
'end' => $priceValue,
'high' => $priceValue,
'low' => $priceValue,
'date_at' => date('Y-m-d'),
]);
}
/**
* پاک‌سازی شبانه:
* - قبل از ۳۱ روز: حذف
* - ۷ تا ۳۱ روز: هر ۱۲ ساعت یک رکورد
* - ۲۴ ساعت تا ۷ روز: هر ۳۰ دقیقه یک رکورد
* - ۲۴ ساعت اخیر: نگه‌داری همه (هر ۱۰ ثانیه)
*/
public function pruneHistory(): array
{
$deletedOld = Price::query()
->where('created_at', '<', now()->subDays(31))
->delete();
$deleted7to31 = $this->downsampleRange(now()->subDays(31), now()->subDays(7), 12 * 3600);
$deleted1to7 = $this->downsampleRange(now()->subDays(7), now()->subDay(), 30 * 60);
return [
'deleted_older_than_31d' => $deletedOld,
'downsampled_7_to_31d' => $deleted7to31,
'downsampled_1_to_7d' => $deleted1to7,
];
}
private function downsampleRange(Carbon $from, Carbon $to, int $bucketSeconds): int
{
$rows = Price::query()
->where('created_at', '>=', $from)
->where('created_at', '<', $to)
->orderBy('created_at')
->get(['id', 'created_at']);
if ($rows->isEmpty()) {
return 0;
}
$keepIds = [];
$lastBucket = null;
foreach ($rows as $row) {
$ts = $row->created_at?->timestamp ?? 0;
$bucket = (int) floor($ts / $bucketSeconds);
if ($bucket !== $lastBucket) {
$keepIds[] = $row->id;
$lastBucket = $bucket;
} else {
$keepIds[count($keepIds) - 1] = $row->id;
}
}
return Price::query()
->where('created_at', '>=', $from)
->where('created_at', '<', $to)
->whereNotIn('id', $keepIds)
->delete();
}
/** @return array{DAILY: array<int, array{timestamp: int, value: int}>, WEEKLY: array, MONTHLY: array} */
public function chartFromDatabase(): array
{
$daily = Price::query()
->where('created_at', '>=', now()->subDay())
->orderBy('created_at')
->get(['price', 'created_at']);
$dailyPoints = $daily->map(function (Price $row) {
$ts = ($row->created_at ?? now())->timestamp * 1000;
return ['timestamp' => $ts, 'value' => (int) $row->price];
})->values()->all();
$weekRows = Price::query()
->where('created_at', '>=', now()->subDays(7))
->orderBy('created_at')
->get(['id', 'price', 'created_at']);
$weeklyPoints = $this->bucketRows($weekRows, 30 * 60);
$monthRows = Price::query()
->where('created_at', '>=', now()->subDays(31))
->orderBy('created_at')
->get(['id', 'price', 'created_at']);
$monthlyPoints = $this->bucketRows($monthRows, 12 * 3600);
return [
'DAILY' => $dailyPoints,
'WEEKLY' => $weeklyPoints,
'MONTHLY' => $monthlyPoints,
];
}
/** @param \Illuminate\Support\Collection<int, Price> $rows */
private function bucketRows($rows, int $bucketSeconds): array
{
$points = [];
$lastBucket = null;
foreach ($rows as $row) {
$ts = $row->created_at?->timestamp ?? 0;
$bucket = (int) floor($ts / $bucketSeconds);
$point = [
'timestamp' => $ts * 1000,
'value' => (int) $row->price,
];
if ($bucket !== $lastBucket) {
$points[] = $point;
$lastBucket = $bucket;
} else {
$points[count($points) - 1] = $point;
}
}
return $points;
}
}
+16
View File
@@ -56,4 +56,20 @@ class PriceSyncLogService
return $log->load('items');
}
/** هر تیک کران — حتی بدون تغییر قیمت محصول */
public function recordTick(string $source, ?string $sourceUrl, int $liquidGoldPrice, int $productsSynced): ?PriceSyncLog
{
if (! $this->isAvailable()) {
return null;
}
return PriceSyncLog::create([
'source' => $source,
'source_url' => $sourceUrl,
'liquid_gold_price' => $liquidGoldPrice,
'products_updated' => $productsSynced,
'created_at' => now(),
]);
}
}
+13 -1
View File
@@ -18,7 +18,7 @@ class ProductDisplayPriceService
{
public const LIVE_PRICE_CACHE_KEY = 'live_price_api_payload';
public const LIVE_PRICE_CACHE_SECONDS = 45;
public const LIVE_PRICE_CACHE_SECONDS = 15;
public function usesAppPrice(Product $product): bool
{
@@ -262,6 +262,18 @@ class ProductDisplayPriceService
/** @param array<int, array<string, mixed>> $feed */
private function applyReferenceFeedPrice(Product $product, array $feed, ZaarboxPriceService $zaarbox): void
{
if (Product::hasStoredPriceColumns()) {
$stored = (int) ($product->price ?? 0) + (int) ($product->price_profit ?? 0);
if ($stored > 0) {
$product->setAttribute('price', (int) ($product->price ?? 0));
if (Schema::hasColumn($product->getTable(), 'price_profit')) {
$product->setAttribute('price_profit', (int) ($product->price_profit ?? 0));
}
return;
}
}
$row = $zaarbox->findFeedRow($feed, (string) $product->reading_method);
if ($row === null) {
$useLast = $this->productUsesLastPriceIfZero($product);
+8 -1
View File
@@ -20,7 +20,7 @@ class ZaarboxPriceService
private const CACHE_TTL_MINUTES = 35;
/** TTL کش فید — کران هر ۳۰ث refresh می‌کند؛ list فقط از همین می‌خواند */
private const PRICE_LIST_CACHE_SECONDS = 45;
private const PRICE_LIST_CACHE_SECONDS = 15;
private const DEFAULT_HISTORY_ID = 'ca3800a0-1f57-3528-b963-178f9168e04f';
@@ -90,6 +90,13 @@ class ZaarboxPriceService
*/
public function getAllPrices(): array
{
if ($this->config->getBool('chart_use_local_data', false)) {
$local = app(PriceSnapshotService::class)->chartFromDatabase();
if (! empty($local['DAILY'])) {
return $local;
}
}
$cached = Cache::get($this->cacheKey());
if (is_array($cached) && isset($cached['prices'])) {
+2
View File
@@ -17,6 +17,8 @@ return [
'zaarbox_price_key' => '',
/** UUID نمودار Zaarbox — خالی = پیش‌فرض ca3800a0-... در ZaarboxPriceService */
'chart_id' => '',
/** false = نمودار از Zaarbox (chart_id) | true = داده ذخیره‌شده خود سایت */
'chart_use_local_data' => false,
'register_jibit_verify' => false,
'register_referral_sms_to_referrer' => false,
'register_referral_bonus_message' => false,
+63 -15
View File
@@ -89,8 +89,13 @@ WHERE r.`key` = 'referral_id'
-- =============================================================================
-- قیمت zaarbox (اگر از قبل دارید value دست نخورده)
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
SELECT 'chart_id', '', 'UUID نمودار Zaarbox', 1, 1, 'خالی = ca3800a0-1f57-3528-b963-178f9168e04f'
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'chart_use_local_data', 'false', 'نمودار از داده خود سایت', 1, 1,
'خاموش = Zaarbox (chart_id) | روشن = تاریخچه ذخیره‌شده prices', 'switch'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'chart_use_local_data' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'chart_id', '', 'UUID نمودار Zaarbox', 1, 1, 'خالی = ca3800a0-1f57-3528-b963-178f9168e04f', 'text'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'chart_id' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
@@ -451,26 +456,26 @@ UPDATE `settings` SET `type` = 'longtext' WHERE `key` IN ('text_rule', 'text_abo
-- select (+ گزینه‌ها در description)
UPDATE `settings` SET `type` = 'select', `description` = 'منبع فعال قیمت|montzar,zaarbox,main_product' WHERE `key` = 'price_provider';
UPDATE `settings` SET `type` = 'select', `description` = 'required = اجباری؛ خالی = اختیاری|required,', `can_edit` = 1 WHERE `key` = 'referral_id';
UPDATE `settings` SET `type` = 'switch', `title_fa` = 'کد معرف اجباری', `description` = 'روشن = اجباری در ثبت‌نام | خاموش = اختیاری', `can_edit` = 1 WHERE `key` = 'referral_id';
UPDATE `settings` SET `type` = 'select', `description` = 'sepal,' WHERE `key` = 'payping_gateway_name';
UPDATE `settings` SET `type` = 'select', `description` = 'bale,telegram,whatsapp,rubika,eitaa,soroush' WHERE `key` = 'support_messenger';
-- switch
UPDATE `settings` SET `type` = 'switch' WHERE `key` IN (
'chart_use_local_data',
'jibit_verification', 'register_jibit_verify', 'open_register', 'auto_active_customer',
'close_price', 'montzar_price', 'main_product_price', 'show_last_price',
'close_price', 'show_last_price',
'send_to_tahesab', 'profit_gold_api_enabled', 'payping_round_toman', 'payping_strict_failed_withdraw',
'has_page_earn_money', 'has_page_fund', 'has_page_permission', 'has_page_price_trigger',
'has_page_physical_gold', 'has_chat', 'charge_notif', 'group_sms', 'display_montzar',
'has_page_earn_money', 'charge_notif', 'group_sms',
'montzar_closed', 'montzar_open_buy', 'montzar_open_sell',
'product_update_price_on_list', 'product_auto_close_open_on_list', 'payping_fin_sync_enabled',
'fee_free', 'external_order_api_enabled', 'is_store'
'payping_fin_sync_enabled', 'external_order_api_enabled', 'is_store',
'register_referral_sms_to_referrer', 'register_referral_bonus_message', 'price_from_nasiryan'
);
-- number
UPDATE `settings` SET `type` = 'number' WHERE `key` IN (
'fee_single', 'buy_fee', 'sell_fee', 'fee_cash', 'fee_service_charge_percentage',
'min_withdraw', 'min_first_buy', 'minimum_sell', 'referral_count_for_gift',
'fee_single', 'buy_fee', 'sell_fee', 'fee_cash', 'fee_service_charge_percentage', 'fee_free',
'min_withdraw', 'min_first_buy', 'minimum_sell',
'montzar_product_id', 'balance_report_exclude_customer_id', 'gift_for_referrer',
'gift_for_referral', 'up_price', 'down_price', 'payping_fin_customer_id'
);
@@ -580,10 +585,16 @@ WHERE NOT EXISTS (SELECT 1 FROM `const` WHERE `key` = 'liman' LIMIT 1);
-- پاکسازی کلیدهای legacy / تست
-- ---------------------------------------------------------------------------
DELETE FROM `settings` WHERE `key` IN (
'test', 'test2', 'test22', 'test3', 'test33', 'longtest', 'longtest2',
'test', 'test2', 'test22', 'test3', 'test33', 'longtest', 'longtest2', 'test*',
'is_requaire_referral', 'send_to_goldir', 'goldir_price', 'liman_token',
'melted_product_id', 'display_montzar', 'tabanGohar_price', 'etehadie_api_url',
'price_method', 'price_primary_source'
'price_method', 'price_primary_source', 'main_product_price', 'montzar_price',
'has_page_fund', 'has_page_permission', 'has_page_price_trigger', 'has_page_physical_gold',
'has_chat', 'product_update_price_on_list', 'product_auto_close_open_on_list',
'referral_count_for_gift', 'store_status',
'name_en', 'bace_url', 'server_url', 'key', 'color_primary', 'color_secondary',
'logo_register', 'logo_login', 'title_pwa', 'title_fav', 'text_rule', 'text_about',
'payping_gateway_item'
);
DELETE FROM `const` WHERE `key` = 'log' OR `key` LIKE 'goldir%';
@@ -601,11 +612,48 @@ WHERE `key` = 'price_provider';
UPDATE `settings` SET `value` = 'montzar'
WHERE `key` = 'price_provider' AND LOWER(TRIM(`value`)) IN ('taban_gohar', 'etehadie');
UPDATE `settings` SET `description` = 'required = اجباری|required,', `title_fa` = 'کد معرف', `type` = 'select'
UPDATE `settings` SET
`type` = 'switch',
`title_fa` = 'کد معرف اجباری',
`description` = 'روشن = اجباری در ثبت‌نام | خاموش = اختیاری',
`can_edit` = 1,
`is_active` = 1
WHERE `key` = 'referral_id';
UPDATE `settings` SET `description` = 'جایگزین: price_provider=main_product' WHERE `key` = 'main_product_price';
UPDATE `settings` SET `description` = 'true یا price_provider=montzar' WHERE `key` = 'montzar_price';
UPDATE `settings` SET `value` = 'required'
WHERE `key` = 'referral_id'
AND LOWER(TRIM(COALESCE(`value`, ''))) IN ('required', 'true', '1', 'yes');
UPDATE `settings` SET `value` = ''
WHERE `key` = 'referral_id'
AND LOWER(TRIM(COALESCE(`value`, ''))) NOT IN ('required');
UPDATE `settings` SET `type` = 'switch', `value` = 'false', `is_active` = 1, `can_edit` = 1
WHERE `key` IN ('register_referral_sms_to_referrer', 'register_referral_bonus_message')
AND (`type` IS NULL OR TRIM(`type`) = '' OR `type` = 'text')
AND TRIM(COALESCE(`value`, '')) = '';
UPDATE `settings` SET `type` = 'switch', `is_active` = 1
WHERE `key` IN ('register_referral_sms_to_referrer', 'register_referral_bonus_message');
UPDATE `settings` SET `value` = 'false'
WHERE `key` IN ('register_referral_sms_to_referrer', 'register_referral_bonus_message')
AND TRIM(COALESCE(`value`, '')) = '';
UPDATE `settings` SET
`type` = 'switch',
`title_fa` = 'نمودار از داده خود سایت',
`description` = 'خاموش = Zaarbox (chart_id) | روشن = تاریخچه prices',
`is_active` = 1,
`can_edit` = 1
WHERE `key` = 'chart_use_local_data';
UPDATE `settings` SET `type` = 'number', `title_fa` = 'تعداد سفارش اول بدون کارمزد', `description` = 'عدد — ۰ یعنی غیرفعال'
WHERE `key` = 'fee_free';
-- همه تنظیمات قابل ویرایش همیشه فعال
UPDATE `settings` SET `is_active` = 1 WHERE `can_edit` = 1 AND (`is_active` IS NULL OR `is_active` = 0);
UPDATE `settings` SET `description` = 'تعداد سفارش اول بدون کارمزد' WHERE `key` = 'fee_free';
UPDATE `settings` SET `description` = 'id یا reading_method محصول آبشده' WHERE `key` = 'main_product';