Files
boursecu_server/app/Http/Controllers/Admin/CustomerSpecialPricePackageController.php
T
mohmdsdgandCursor cae41e72b8 fix(search): match Arabic ye/kaf with Persian letters in admin search
Customer names stored with ي/ك were missed when typing ی/ک; fold both sides of LIKE.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 21:38:09 +03:30

288 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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 App\Support\PersianText;
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->whereRaw(
PersianText::sqlFoldExpr('lower(`name`)').' like ?',
[PersianText::likePattern(strtolower($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 = $this->parseSignedRial($request->input('profit_buy', 0));
$profitSell = $this->parseSignedRial($request->input('profit_sell', 0));
return [
'title' => $title,
'customer_ids' => $customerIds,
'product_ids' => $productIds,
'category_names' => $categoryNames,
'profit_buy' => $profitBuy,
'profit_sell' => $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 parseSignedRial(mixed $value): int
{
$raw = trim((string) $value);
$raw = str_replace(["\u{066C}", ',', '٬', ' ', 'ریال'], '', $raw);
$raw = strtr($raw, [
'۰' => '0', '۱' => '1', '۲' => '2', '۳' => '3', '۴' => '4',
'۵' => '5', '۶' => '6', '۷' => '7', '۸' => '8', '۹' => '9',
'٠' => '0', '١' => '1', '٢' => '2', '٣' => '3', '٤' => '4',
'٥' => '5', '٦' => '6', '٧' => '7', '٨' => '8', '٩' => '9',
'−' => '-', '–' => '-', '—' => '-',
]);
$negative = str_starts_with($raw, '-');
$digits = preg_replace('/\D/', '', $raw) ?? '0';
$amount = (int) ($digits === '' ? '0' : $digits);
return $negative ? -$amount : $amount;
}
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('قابلیت قیمت ویژه مشتری غیرفعال است.');
}
}
}