added setting for user
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user