added setting for user

This commit is contained in:
2026-08-16 22:00:23 +03:30
parent c3c3a60088
commit bd8284e4f8
11 changed files with 769 additions and 12 deletions
@@ -0,0 +1,264 @@
<?php
namespace App\Http\Controllers\Admin;
use App\DB\Te\TableEngine;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\CustomerSpecialPricePackage;
use App\Models\Product;
use App\Services\ConfigService;
use App\Services\CustomerSpecialPricingService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
class CustomerSpecialPricePackageController extends Controller
{
public function table(Request $request)
{
$this->assertFeatureEnabled();
$query = CustomerSpecialPricePackage::query()
->withCount(['customers', 'products'])
->orderByDesc('id');
TableEngine::create($query, $request->all())
->initSearch(['title'])
->select(['*'])
->export($query);
return $query;
}
public function show(int $id)
{
$this->assertFeatureEnabled();
$package = CustomerSpecialPricePackage::query()
->with([
'customers:id,name,mobile',
'products:id,title,group_type',
])
->findOrFail($id);
$categoryNames = [];
if (Schema::hasTable('customer_special_price_package_category')) {
$categoryNames = DB::table('customer_special_price_package_category')
->where('package_id', $package->id)
->pluck('category_name')
->all();
}
return response()->json([
'package' => $package,
'customer_ids' => $package->customers->pluck('id')->values(),
'product_ids' => $package->products->pluck('id')->values(),
'category_names' => array_values($categoryNames),
'customers' => $package->customers,
'products' => $package->products,
]);
}
public function store(Request $request)
{
$this->assertFeatureEnabled();
$data = $this->validatedPayload($request);
$warnings = app(CustomerSpecialPricingService::class)->overlapWarnings(
$data['customer_ids'],
$data['product_ids']
);
$package = DB::transaction(function () use ($data) {
$package = CustomerSpecialPricePackage::create([
'title' => $data['title'],
'profit_buy' => $data['profit_buy'],
'profit_sell' => $data['profit_sell'],
'show_product' => $data['show_product'],
'is_active' => $data['is_active'],
]);
$this->syncRelations($package, $data);
return $package->fresh();
});
return response()->json([
'message' => 'پکیج قیمت ویژه ثبت شد.',
'package' => $package,
'warnings' => $warnings,
]);
}
public function update(Request $request, int $id)
{
$this->assertFeatureEnabled();
$package = CustomerSpecialPricePackage::findOrFail($id);
$data = $this->validatedPayload($request);
$warnings = app(CustomerSpecialPricingService::class)->overlapWarnings(
$data['customer_ids'],
$data['product_ids'],
(int) $package->id
);
DB::transaction(function () use ($package, $data) {
$package->update([
'title' => $data['title'],
'profit_buy' => $data['profit_buy'],
'profit_sell' => $data['profit_sell'],
'show_product' => $data['show_product'],
'is_active' => $data['is_active'],
]);
$this->syncRelations($package, $data);
});
return response()->json([
'message' => 'پکیج قیمت ویژه به‌روز شد.',
'package' => $package->fresh(),
'warnings' => $warnings,
]);
}
public function destroy(int $id)
{
$this->assertFeatureEnabled();
$package = CustomerSpecialPricePackage::findOrFail($id);
DB::transaction(function () use ($package) {
$package->customers()->detach();
$package->products()->detach();
if (Schema::hasTable('customer_special_price_package_category')) {
DB::table('customer_special_price_package_category')->where('package_id', $package->id)->delete();
}
$package->delete();
});
return response()->json(['message' => 'پکیج حذف شد.']);
}
public function toggleActive(int $id)
{
$this->assertFeatureEnabled();
$package = CustomerSpecialPricePackage::findOrFail($id);
$package->is_active = ! $package->is_active;
$package->save();
return response()->json([
'message' => $package->is_active ? 'پکیج فعال شد.' : 'پکیج غیرفعال شد.',
'is_active' => $package->is_active,
]);
}
public function searchCustomers(Request $request)
{
$this->assertFeatureEnabled();
$q = trim((string) $request->get('q', ''));
$rows = Customer::query()
->where('type', 2)
->where(function ($query) {
$query->where('is_archive', false)->orWhereNull('is_archive');
})
->when($q !== '', function ($query) use ($q) {
$query->where(function ($inner) use ($q) {
$inner->where('name', 'like', '%' . $q . '%')
->orWhere('mobile', 'like', '%' . $q . '%')
->orWhere('id', $q);
});
})
->orderByDesc('id')
->limit(40)
->get(['id', 'name', 'mobile']);
return response()->json(['customers' => $rows]);
}
public function productsByCategories(Request $request)
{
$this->assertFeatureEnabled();
$names = array_values(array_filter(array_map('strval', (array) $request->input('category_names', []))));
if ($names === []) {
return response()->json(['products' => []]);
}
$products = Product::query()
->whereIn('group_type', $names)
->orderBy('title')
->get(['id', 'title', 'group_type', 'active']);
return response()->json(['products' => $products]);
}
private function validatedPayload(Request $request): array
{
$title = trim((string) $request->input('title', ''));
if ($title === '') {
throw new UnprocessableEntityHttpException('عنوان پکیج الزامی است.');
}
$customerIds = array_values(array_unique(array_filter(array_map('intval', (array) $request->input('customer_ids', [])))));
$productIds = array_values(array_unique(array_filter(array_map('intval', (array) $request->input('product_ids', [])))));
$categoryNames = array_values(array_unique(array_filter(array_map(
fn ($n) => trim((string) $n),
(array) $request->input('category_names', [])
))));
if ($customerIds === []) {
throw new UnprocessableEntityHttpException('حداقل یک کاربر انتخاب کنید.');
}
if ($categoryNames === []) {
throw new UnprocessableEntityHttpException('حداقل یک گروه دسته‌بندی انتخاب کنید.');
}
if ($productIds === []) {
throw new UnprocessableEntityHttpException('حداقل یک محصول انتخاب کنید.');
}
$profitBuy = (int) preg_replace('/[^\d]/', '', (string) $request->input('profit_buy', 0));
$profitSell = (int) preg_replace('/[^\d]/', '', (string) $request->input('profit_sell', 0));
return [
'title' => $title,
'customer_ids' => $customerIds,
'product_ids' => $productIds,
'category_names' => $categoryNames,
'profit_buy' => max(0, $profitBuy),
'profit_sell' => max(0, $profitSell),
'show_product' => filter_var($request->input('show_product', true), FILTER_VALIDATE_BOOLEAN),
'is_active' => filter_var($request->input('is_active', true), FILTER_VALIDATE_BOOLEAN),
];
}
private function syncRelations(CustomerSpecialPricePackage $package, array $data): void
{
$package->customers()->sync($data['customer_ids']);
$package->products()->sync($data['product_ids']);
if (Schema::hasTable('customer_special_price_package_category')) {
DB::table('customer_special_price_package_category')->where('package_id', $package->id)->delete();
$rows = [];
foreach ($data['category_names'] as $name) {
$rows[] = [
'package_id' => $package->id,
'category_name' => $name,
];
}
if ($rows !== []) {
DB::table('customer_special_price_package_category')->insert($rows);
}
}
}
private function assertFeatureEnabled(): void
{
if (! app(CustomerSpecialPricingService::class)->tablesReady()) {
throw new UnprocessableEntityHttpException('جدول پکیج قیمت ویژه وجود ندارد. مایگریشن را اجرا کنید.');
}
if (! app(ConfigService::class)->getBool(CustomerSpecialPricingService::SETTING_KEY, false)) {
throw new UnprocessableEntityHttpException('قابلیت قیمت ویژه مشتری غیرفعال است.');
}
}
}
@@ -572,6 +572,11 @@ class ProductController extends SuperController
$products->each(function (Product $product) use ($displayPriceService) {
$product->setAttribute('show', $displayPriceService->resolveCustomerShow($product));
});
$customerId = (int) (auth('customer')->id() ?? 0);
if ($customerId > 0) {
app(\App\Services\CustomerSpecialPricingService::class)->applyToProducts($products, $customerId);
}
}
/**
@@ -1451,10 +1451,42 @@ class SingleOrderController extends Controller
];
}
$customerId = (int) (auth('customer')->id() ?? 0);
if ($customerId <= 0) {
$customerId = (int) (request()->input('customer_id') ?? 0);
}
$specialSvc = app(\App\Services\CustomerSpecialPricingService::class);
$specialRule = $specialSvc->ruleForCustomerProduct($customerId, (int) ($product->id ?? 0));
if ($specialRule && ! ($specialRule['show_product'] ?? true)) {
throw new UnprocessableEntityHttpException('این محصول برای شما در دسترس نیست.');
}
$specialAmt = $specialSvc->profitForDirection($specialRule, $direction);
if ($specialAmt > 0) {
$amount += $specialAmt;
$keys[] = 'customer_special_pricing';
$lines[] = [
'kind' => 'special_pricing',
'key' => 'customer_special_pricing',
'label' => 'قیمت ویژه مشتری · ' . ($specialRule['title'] ?? '') . ' · ' . $side,
'is_percent' => false,
'raw' => $specialAmt,
'per_unit' => $specialAmt,
'detail' => number_format($specialAmt) . ' ریال / گرم',
'package_id' => $specialRule['package_id'] ?? null,
];
}
$hasApp = collect($lines)->contains(fn ($l) => ($l['kind'] ?? '') === 'app_spread');
$hasProduct = collect($lines)->contains(fn ($l) => ($l['kind'] ?? '') === 'product_spread');
$hasSpecial = collect($lines)->contains(fn ($l) => ($l['kind'] ?? '') === 'special_pricing');
if ($hasApp && $hasProduct) {
if ($hasSpecial && ($hasApp || $hasProduct)) {
$source = 'app+product+special';
$label = 'اختلاف پایه/محصول + قیمت ویژه مشتری = ' . number_format($amount) . ' ریال / گرم';
} elseif ($hasSpecial) {
$source = 'special';
$label = ($lines[array_key_last($lines)]['label'] ?? 'قیمت ویژه مشتری') . ' = ' . number_format($amount) . ' ریال';
} elseif ($hasApp && $hasProduct) {
$source = 'app+product';
$label = 'قیمت صفحه اصلی اپلیکیشن + تب اختلاف محصول = ' . number_format($amount) . ' ریال / گرم';
} elseif ($hasApp) {
@@ -54,6 +54,10 @@ class PriceStreamController extends Controller
$cache = app(LivePriceCacheService::class);
$last = $cache->get() ?? $cache->buildFromDatabase();
if (is_array($last) && $last !== []) {
if ($customerId > 0) {
$last = app(\App\Services\CustomerSpecialPricingService::class)
->applyToLastPayload($last, $customerId);
}
$send('last', app(ZaarboxPriceService::class)->attachGoldOunceToPayload($last));
}
@@ -62,6 +66,13 @@ class PriceStreamController extends Controller
$send('list', $list);
}
if ($customerId > 0) {
$send(
'special-pricing',
app(\App\Services\CustomerSpecialPricingService::class)->clientPayloadForCustomer($customerId)
);
}
$customerLastId = 0;
if ($customerId > 0) {
$assets = app(AssetSummaryService::class)->buildForCustomer(
+21 -9
View File
@@ -197,14 +197,14 @@ class ReportController extends Controller
if ($cached !== null) {
$cached['gold_ounce'] = $this->resolveGoldOuncePayload();
return $cached;
return $this->personalizeLastPriceForCustomer($cached);
}
// فقط وقتی کران اخیراً tick زده — وگرنه API زنده (جلوگیری از قفل قیمت)
if ($cache->hasRecentTick()) {
$dbBuilt = $cache->buildFromDatabase();
if (($dbBuilt['price'] ?? 0) > 0) {
return $this->appendUpPrices($dbBuilt, false);
return $this->personalizeLastPriceForCustomer($this->appendUpPrices($dbBuilt, false));
}
}
}
@@ -226,7 +226,7 @@ class ReportController extends Controller
);
}
return $this->appendUpPrices($storePayload, $forceRefresh);
return $this->personalizeLastPriceForCustomer($this->appendUpPrices($storePayload, $forceRefresh));
}
$closed = false;
@@ -246,7 +246,7 @@ class ReportController extends Controller
};
$zeroResponse = function () use ($forceRefresh) {
return $this->appendUpPrices([
return $this->personalizeLastPriceForCustomer($this->appendUpPrices([
'sell' => 0,
'buy' => 0,
'price_last' => 0,
@@ -256,7 +256,7 @@ class ReportController extends Controller
'zaarbox' => 0,
'closed' => true,
'time' => now(),
], $forceRefresh);
], $forceRefresh));
};
$priceProvider = $config->priceProvider();
@@ -440,7 +440,7 @@ class ReportController extends Controller
$priceSource = 'database_last';
} else {
// مظنه زنده نداریم و show_last_price خاموش است → نه «بسته»، بلکه «مظنه در دسترس نیست»
return $this->appendUpPrices([
return $this->personalizeLastPriceForCustomer($this->appendUpPrices([
'sell' => 0,
'buy' => 0,
'price_last' => 0,
@@ -451,7 +451,7 @@ class ReportController extends Controller
'closed' => false,
'status' => 'no_quote',
'time' => now(),
], $forceRefresh);
], $forceRefresh));
}
}
@@ -485,7 +485,7 @@ class ReportController extends Controller
);
}
return $this->appendUpPrices([
return $this->personalizeLastPriceForCustomer($this->appendUpPrices([
'sell' => $sell,
'buy' => $buy,
'price_last' => $priceLast,
@@ -496,7 +496,19 @@ class ReportController extends Controller
'zaarbox' => $zaarbox_price ?? 0,
'closed' => false,
'time' => now(),
], $forceRefresh);
], $forceRefresh));
}
/** اختلاف پکیج ویژه — فقط روی پاسخ مشتری؛ کش مشترک را آلوده نمی‌کند */
protected function personalizeLastPriceForCustomer(array $payload): array
{
$customerId = (int) (auth('customer')->id() ?? 0);
if ($customerId <= 0) {
return $payload;
}
return app(\App\Services\CustomerSpecialPricingService::class)
->applyToLastPayload($payload, $customerId);
}
protected function recordHalfHourPriceSnapshot(
@@ -0,0 +1,51 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class CustomerSpecialPricePackage extends Model
{
protected $table = 'customer_special_price_packages';
protected $fillable = [
'title',
'profit_buy',
'profit_sell',
'show_product',
'is_active',
];
protected $casts = [
'profit_buy' => 'integer',
'profit_sell' => 'integer',
'show_product' => 'boolean',
'is_active' => 'boolean',
];
public function customers(): BelongsToMany
{
return $this->belongsToMany(
Customer::class,
'customer_special_price_package_customer',
'package_id',
'customer_id'
)->withPivot([]);
}
public function products(): BelongsToMany
{
return $this->belongsToMany(
Product::class,
'customer_special_price_package_product',
'package_id',
'product_id'
)->withPivot([]);
}
public function scopeActive($query)
{
return $query->where('is_active', true);
}
}
@@ -0,0 +1,287 @@
<?php
namespace App\Services;
use App\Models\CustomerSpecialPricePackage;
use App\Models\Product;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* قیمت ویژه مشتری — افزون بر buy_fee/sell_fee و cash_fee محصول.
* تداخل: آخرین پکیج (updated_at، سپس id).
*/
class CustomerSpecialPricingService
{
public const SETTING_KEY = 'customer_special_pricing';
public function tablesReady(): bool
{
return Schema::hasTable('customer_special_price_packages')
&& Schema::hasTable('customer_special_price_package_customer')
&& Schema::hasTable('customer_special_price_package_product');
}
public function isEnabled(): bool
{
if (! $this->tablesReady()) {
return false;
}
return app(ConfigService::class)->getBool(self::SETTING_KEY, false);
}
/**
* @return array{package_id:int,title:string,profit_buy:int,profit_sell:int,show_product:bool}|null
*/
public function ruleForCustomerProduct(int $customerId, int $productId): ?array
{
if (! $this->isEnabled() || $customerId <= 0 || $productId <= 0) {
return null;
}
$row = CustomerSpecialPricePackage::query()
->active()
->whereHas('customers', fn ($q) => $q->where('customers.id', $customerId))
->whereHas('products', fn ($q) => $q->where('products.id', $productId))
->orderByDesc('updated_at')
->orderByDesc('id')
->first(['id', 'title', 'profit_buy', 'profit_sell', 'show_product', 'updated_at']);
if (! $row) {
return null;
}
return [
'package_id' => (int) $row->id,
'title' => (string) $row->title,
'profit_buy' => (int) ($row->profit_buy ?? 0),
'profit_sell' => (int) ($row->profit_sell ?? 0),
'show_product' => (bool) $row->show_product,
];
}
/**
* قوانین مؤثر برای مشتری — هر محصول یک قانون (آخرین پکیج).
*
* @return array<int, array{package_id:int,title:string,profit_buy:int,profit_sell:int,show_product:bool}>
*/
public function rulesMapForCustomer(int $customerId): array
{
if (! $this->isEnabled() || $customerId <= 0) {
return [];
}
$packages = CustomerSpecialPricePackage::query()
->active()
->whereHas('customers', fn ($q) => $q->where('customers.id', $customerId))
->with(['products:id'])
->orderByDesc('updated_at')
->orderByDesc('id')
->get(['id', 'title', 'profit_buy', 'profit_sell', 'show_product', 'updated_at']);
$map = [];
foreach ($packages as $package) {
foreach ($package->products as $product) {
$pid = (int) $product->id;
if (isset($map[$pid])) {
continue;
}
$map[$pid] = [
'package_id' => (int) $package->id,
'title' => (string) $package->title,
'profit_buy' => (int) ($package->profit_buy ?? 0),
'profit_sell' => (int) ($package->profit_sell ?? 0),
'show_product' => (bool) $package->show_product,
];
}
}
return $map;
}
public function profitForDirection(?array $rule, int $direction): int
{
if (! $rule) {
return 0;
}
return $direction === 2
? (int) ($rule['profit_sell'] ?? 0)
: (int) ($rule['profit_buy'] ?? 0);
}
/**
* اعمال روی payload تابلو last (buy/sell/price_last) برای محصول اصلی یا اولین قانون آبشده.
*/
public function applyToLastPayload(array $payload, int $customerId): array
{
if (! $this->isEnabled() || $customerId <= 0) {
return $payload;
}
$mainId = app(ConfigService::class)->mainProductId();
$rule = null;
if ($mainId) {
$rule = $this->ruleForCustomerProduct($customerId, (int) $mainId);
}
if (! $rule) {
$map = $this->rulesMapForCustomer($customerId);
$rule = $map ? reset($map) : null;
if (is_array($rule) && ! ($rule['show_product'] ?? true)) {
// فقط برای اختلاف قیمت؛ مخفی‌سازی روی لیست محصول است
}
}
if (! $rule) {
return $payload;
}
$buyExtra = (int) ($rule['profit_buy'] ?? 0);
$sellExtra = (int) ($rule['profit_sell'] ?? 0);
if (isset($payload['buy']) && (int) $payload['buy'] > 0 && $buyExtra > 0) {
$payload['buy'] = (int) $payload['buy'] + $buyExtra;
}
if (isset($payload['sell']) && (int) $payload['sell'] > 0 && $sellExtra > 0) {
$payload['sell'] = max(0, (int) $payload['sell'] - $sellExtra);
}
if (isset($payload['price_last']) && (int) $payload['price_last'] > 0 && $buyExtra > 0) {
$payload['price_last'] = (int) $payload['price_last'] + $buyExtra;
}
if (isset($payload['priceLast']) && (int) $payload['priceLast'] > 0 && $buyExtra > 0) {
$payload['priceLast'] = (int) $payload['priceLast'] + $buyExtra;
}
$payload['special_pricing'] = [
'package_id' => $rule['package_id'],
'title' => $rule['title'],
'profit_buy' => $buyExtra,
'profit_sell' => $sellExtra,
];
return $payload;
}
/**
* روی کالکشن محصول: ویژه را روی مدل می‌نشاند و در صورت نیاز show را false می‌کند.
*
* @param Collection<int, Product>|iterable<Product> $products
*/
public function applyToProducts($products, int $customerId): void
{
if (! $this->isEnabled() || $customerId <= 0) {
return;
}
$map = $this->rulesMapForCustomer($customerId);
if ($map === []) {
return;
}
$collection = $products instanceof Collection ? $products : collect($products);
foreach ($collection as $product) {
if (! $product instanceof Product) {
continue;
}
$pid = (int) ($product->id ?? 0);
$rule = $map[$pid] ?? null;
if (! $rule) {
continue;
}
$product->setAttribute('special_profit_buy', (int) $rule['profit_buy']);
$product->setAttribute('special_profit_sell', (int) $rule['profit_sell']);
$product->setAttribute('special_package_id', (int) $rule['package_id']);
$product->setAttribute('special_package_title', (string) $rule['title']);
if (! ($rule['show_product'] ?? true)) {
$product->setAttribute('show', false);
}
}
}
/**
* هشدار تداخل: مشتری+محصول در پکیج دیگری هم هست.
*
* @param list<int> $customerIds
* @param list<int> $productIds
* @return list<string>
*/
public function overlapWarnings(array $customerIds, array $productIds, ?int $excludePackageId = null): array
{
if (! $this->tablesReady() || $customerIds === [] || $productIds === []) {
return [];
}
$customerIds = array_values(array_unique(array_map('intval', $customerIds)));
$productIds = array_values(array_unique(array_map('intval', $productIds)));
$q = DB::table('customer_special_price_packages as p')
->join('customer_special_price_package_customer as pc', 'pc.package_id', '=', 'p.id')
->join('customer_special_price_package_product as pp', 'pp.package_id', '=', 'p.id')
->join('customers as c', 'c.id', '=', 'pc.customer_id')
->join('products as pr', 'pr.id', '=', 'pp.product_id')
->where('p.is_active', true)
->whereIn('pc.customer_id', $customerIds)
->whereIn('pp.product_id', $productIds)
->select([
'p.id as package_id',
'p.title as package_title',
'c.id as customer_id',
'c.name as customer_name',
'pr.id as product_id',
'pr.title as product_title',
]);
if ($excludePackageId) {
$q->where('p.id', '!=', $excludePackageId);
}
$rows = $q->limit(50)->get();
if ($rows->isEmpty()) {
return [];
}
$warnings = [];
foreach ($rows as $row) {
$warnings[] = sprintf(
'تداخل: مشتری «%s» (#%d) و محصول «%s» (#%d) در پکیج «%s» (#%d) هم هست — آخرین پکیج اعمال می‌شود.',
$row->customer_name ?: '-',
(int) $row->customer_id,
$row->product_title ?: '-',
(int) $row->product_id,
$row->package_title ?: '-',
(int) $row->package_id
);
}
return $warnings;
}
/** payload سبک برای SSE / me */
public function clientPayloadForCustomer(int $customerId): array
{
$map = $this->rulesMapForCustomer($customerId);
$products = [];
foreach ($map as $productId => $rule) {
$products[] = array_merge(['product_id' => (int) $productId], $rule);
}
$mainId = app(ConfigService::class)->mainProductId();
$board = null;
if ($mainId && isset($map[(int) $mainId])) {
$board = $map[(int) $mainId];
} elseif ($products !== []) {
$board = $products[0];
}
return [
'enabled' => $this->isEnabled(),
'products' => $products,
'board' => $board,
];
}
}
@@ -0,0 +1,60 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('customer_special_price_packages')) {
Schema::create('customer_special_price_packages', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->unsignedBigInteger('profit_buy')->default(0);
$table->unsignedBigInteger('profit_sell')->default(0);
$table->boolean('show_product')->default(true);
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
if (! Schema::hasTable('customer_special_price_package_customer')) {
Schema::create('customer_special_price_package_customer', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('package_id');
$table->unsignedBigInteger('customer_id');
$table->unique(['package_id', 'customer_id'], 'cspp_customer_unique');
$table->index('customer_id', 'cspp_customer_id_idx');
});
}
if (! Schema::hasTable('customer_special_price_package_category')) {
Schema::create('customer_special_price_package_category', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('package_id');
$table->string('category_name');
$table->unique(['package_id', 'category_name'], 'cspp_category_unique');
});
}
if (! Schema::hasTable('customer_special_price_package_product')) {
Schema::create('customer_special_price_package_product', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('package_id');
$table->unsignedBigInteger('product_id');
$table->unique(['package_id', 'product_id'], 'cspp_product_unique');
$table->index('product_id', 'cspp_product_id_idx');
});
}
}
public function down(): void
{
Schema::dropIfExists('customer_special_price_package_product');
Schema::dropIfExists('customer_special_price_package_category');
Schema::dropIfExists('customer_special_price_package_customer');
Schema::dropIfExists('customer_special_price_packages');
}
};
+14 -1
View File
@@ -554,6 +554,12 @@ SELECT 'group_sms', 'false', 'پیامک گروهی', 0, 1,
'switch'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'group_sms' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'customer_special_pricing', 'false', 'قیمت ویژه مشتری', 0, 1,
'روشن: زیرمنوی قیمت ویژه در مشتریان و اعمال سود خرید/فروش پکیج روی مظنه و سفارش برای کاربران انتخاب‌شده. خاموش: غیرفعال. فقط از دیپلوی قابل تغییر است.',
'switch'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'customer_special_pricing' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'sms_panel', 'true', 'پنل پیامک (کیف پول)', 0, 1, 'روشن: پنل پیامک و کیف پول billing فعال است. خاموش: پیامک‌ها مثل قبل ارسال می‌شوند بدون billing.', 'switch'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'sms_panel' LIMIT 1);
@@ -1159,7 +1165,7 @@ UPDATE `settings` SET `type` = 'switch' WHERE `key` IN (
'require_zero_main_gold_before_other_buy',
'profit_gold_api_enabled', 'payping_round_toman', 'payping_strict_failed_withdraw',
'has_page_earn_money', 'has_page_price_trigger', 'has_page_notification', 'has_page_gift_card',
'enable_customer_level_profit', 'charge_notif', 'group_sms', 'sms_panel', 'customer_direct_sms',
'enable_customer_level_profit', 'charge_notif', 'group_sms', 'customer_special_pricing', 'sms_panel', 'customer_direct_sms',
'enable_conditional_order', 'enable_product_funds',
'sms_notif_order_admin', 'sms_notif_order_customer',
'sms_notif_withdraw_admin', 'sms_notif_withdraw_otp', 'sms_notif_login_otp',
@@ -1774,6 +1780,12 @@ UPDATE `settings` SET `title_fa` = 'اعمال مبلغ دقیق در معامل
UPDATE `settings` SET `title_fa` = 'پاداش معرف‌شونده' WHERE `key` = 'gift_for_referral';
UPDATE `settings` SET `title_fa` = 'پاداش معرف' WHERE `key` = 'gift_for_referrer';
UPDATE `settings` SET `title_fa` = 'پیامک گروهی' WHERE `key` = 'group_sms';
UPDATE `settings` SET
`title_fa` = 'قیمت ویژه مشتری',
`type` = 'switch',
`can_edit` = 0,
`description` = 'روشن: زیرمنوی قیمت ویژه در مشتریان و اعمال سود خرید/فروش پکیج روی مظنه و سفارش برای کاربران انتخاب‌شده. خاموش: غیرفعال. فقط از دیپلوی قابل تغییر است.'
WHERE `key` = 'customer_special_pricing';
UPDATE `settings` SET `title_fa` = 'پنل پیامک (کیف پول)' WHERE `key` = 'sms_panel';
UPDATE `settings` SET
`title_fa` = 'چت پشتیبانی',
@@ -1931,6 +1943,7 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN (
'fee_deduct_from_gold',
'referral_bonus_amount',
'group_sms',
'customer_special_pricing',
'customer_direct_sms',
'enable_conditional_order',
'enable_product_funds',
@@ -497,6 +497,12 @@ SELECT 'group_sms', 'false', 'پیامک گروهی', 0, 1,
'switch'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'group_sms' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'customer_special_pricing', 'false', 'قیمت ویژه مشتری', 0, 1,
'روشن: زیرمنوی قیمت ویژه در مشتریان و اعمال سود خرید/فروش پکیج روی مظنه و سفارش برای کاربران انتخاب‌شده. خاموش: غیرفعال. فقط از دیپلوی قابل تغییر است.',
'switch'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'customer_special_pricing' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'sms_panel', 'true', 'پنل پیامک (کیف پول)', 0, 1, 'روشن: پنل پیامک و کیف پول billing فعال است. خاموش: پیامک‌ها مثل قبل ارسال می‌شوند بدون billing.', 'switch'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'sms_panel' LIMIT 1);
@@ -898,7 +904,7 @@ UPDATE `settings` SET `type` = 'switch' WHERE `key` IN (
'products_site_link_enabled',
'fee_deduct_from_gold',
'send_to_tahesab', 'profit_gold_api_enabled', 'payping_round_toman', 'payping_strict_failed_withdraw',
'has_page_earn_money', 'has_page_price_trigger', 'has_page_notification', 'charge_notif', 'group_sms', 'sms_panel', 'customer_direct_sms',
'has_page_earn_money', 'has_page_price_trigger', 'has_page_notification', 'charge_notif', 'group_sms', 'customer_special_pricing', 'sms_panel', 'customer_direct_sms',
'enable_conditional_order', 'enable_product_funds',
'sms_notif_order_admin', 'sms_notif_order_customer',
'sms_notif_withdraw_admin', 'sms_notif_withdraw_otp', 'sms_notif_login_otp',
@@ -1408,6 +1414,12 @@ UPDATE `settings` SET `description` = 'رمز مشترک بین لاراول و
UPDATE `settings` SET `title_fa` = 'پیامک پیام پشتیبانی به ادمین' WHERE `key` = 'sms_notif_support_admin';
UPDATE `settings` SET `title_fa` = 'رمز وب‌هوک چت' WHERE `key` = 'chat_webhook_secret';
UPDATE `settings` SET `description` = 'روشن: دکمه ارسال پیامک گروهی در پنل ادمین فعال است (ارسال ۱۰تایی). خاموش: ارسال پیامک گروهی غیرفعال است. فقط از تنظیمات پیشرفته.' WHERE `key` = 'group_sms';
UPDATE `settings` SET
`title_fa` = 'قیمت ویژه مشتری',
`type` = 'switch',
`can_edit` = 0,
`description` = 'روشن: زیرمنوی قیمت ویژه در مشتریان و اعمال سود خرید/فروش پکیج روی مظنه و سفارش برای کاربران انتخاب‌شده. خاموش: غیرفعال. فقط از دیپلوی قابل تغییر است.'
WHERE `key` = 'customer_special_pricing';
UPDATE `settings` SET `description` = 'روشن: پنل پیامک و کیف پول billing فعال است. خاموش: پیامک‌ها مثل قبل ارسال می‌شوند بدون billing.' WHERE `key` = 'sms_panel';
UPDATE `settings` SET `description` = 'محصولاتی که موجودی‌شان از کیف طلای اصلی خوانده می‌شود. چند محصول با کاما. پیش‌فرض: 13' WHERE `key` = 'main_wallet_products';
@@ -1457,6 +1469,7 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN (
'external_order_api_enabled',
'referral_bonus_amount',
'group_sms',
'customer_special_pricing',
'customer_direct_sms',
'enable_conditional_order',
'enable_product_funds',
+9
View File
@@ -66,6 +66,15 @@ Route::middleware('auth:user')->group(function (){
Route::put('conditional_orders/{id}', [\App\Http\Controllers\Admin\ConditionalOrderController::class, 'update']);
Route::put('conditional_orders/{id}/toggle', [\App\Http\Controllers\Admin\ConditionalOrderController::class, 'toggleActive']);
Route::delete('conditional_orders/{id}', [\App\Http\Controllers\Admin\ConditionalOrderController::class, 'destroy']);
//------------Customer special price packages
Route::post('customer_special_price_packages/table', [\App\Http\Controllers\Admin\CustomerSpecialPricePackageController::class, 'table']);
Route::get('customer_special_price_packages/search-customers', [\App\Http\Controllers\Admin\CustomerSpecialPricePackageController::class, 'searchCustomers']);
Route::post('customer_special_price_packages/products-by-categories', [\App\Http\Controllers\Admin\CustomerSpecialPricePackageController::class, 'productsByCategories']);
Route::get('customer_special_price_packages/{id}', [\App\Http\Controllers\Admin\CustomerSpecialPricePackageController::class, 'show']);
Route::post('customer_special_price_packages', [\App\Http\Controllers\Admin\CustomerSpecialPricePackageController::class, 'store']);
Route::put('customer_special_price_packages/{id}', [\App\Http\Controllers\Admin\CustomerSpecialPricePackageController::class, 'update']);
Route::put('customer_special_price_packages/{id}/toggle', [\App\Http\Controllers\Admin\CustomerSpecialPricePackageController::class, 'toggleActive']);
Route::delete('customer_special_price_packages/{id}', [\App\Http\Controllers\Admin\CustomerSpecialPricePackageController::class, 'destroy']);
//-----------product
Route::post('/product/table', [ProductController::class, 'tableProduct']);
Route::post('/product/archive/table', [ProductController::class, 'archiveTable']);