fix some handling 2

This commit is contained in:
2026-07-08 11:25:19 +03:30
parent 130fadc193
commit 5781d5bf53
3 changed files with 279 additions and 0 deletions
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin;
use App\DB\Te\TableEngine;
use App\Http\Controllers\Controller;
use App\Models\Address;
use App\Models\Customer;
use App\Models\Order;
use App\Models\Price;
@@ -21,6 +22,7 @@ use App\Http\Controllers\Admin\TahesabController;
use App\Models\Setting;
use App\Models\User;
use App\Models\TransactionsAdmin;
use App\Services\AssetSummaryService;
use App\Services\BalanceReportService;
use App\Services\ConfigService;
use App\Services\WalletBalanceService;
@@ -623,6 +625,266 @@ class WalletController extends Controller
return response()->json($withdraw);
}
public function indexPhysicalWithdraw(Request $request)
{
$records = Withdraw::with(['customer', 'Address', 'Transaction.product']);
Withdraw::scopePhysicalWithdrawals($records);
TableEngine::create($records, $request->all())
->initSearch(['customer.name', 'customer.mobile'])
->select([
'*', Withdraw::$SELECT_STS_STR, Withdraw::$SELECT_DAJ
])->export($records);
return response()->json($records);
}
public function physicalWithdrawOptions($customerId, AssetSummaryService $assetSummaryService)
{
$customer = Customer::findOrFail($customerId);
$summary = $assetSummaryService->buildForCustomer((int) $customer->id, (int) $customer->type);
$inventoryByProduct = collect($summary['inventory_cards'] ?? [])->keyBy('product_id');
$configService = app(ConfigService::class);
$mainWalletProductIds = $configService->mainWalletProductIds();
$products = Product::where(function ($query) use ($customer, $mainWalletProductIds) {
$query->whereHas('wallet', function ($q) use ($customer) {
$q->where('owner', $customer->id);
});
if ($mainWalletProductIds !== []) {
$query->orWhereIn('id', $mainWalletProductIds);
}
})->with(['wallet' => function ($query) use ($customer) {
$query->where('owner', $customer->id);
}])->get();
$representativeId = null;
if ($mainWalletProductIds !== []) {
foreach ($mainWalletProductIds as $pid) {
if ($products->firstWhere('id', $pid)) {
$representativeId = (int) $pid;
break;
}
}
$products = $products->filter(function ($product) use ($mainWalletProductIds, $representativeId) {
if (in_array((int) $product->id, array_map('intval', $mainWalletProductIds), true)) {
return (int) $product->id === $representativeId;
}
return true;
})->values();
}
$productRows = $products->map(function ($product) use ($customer, $assetSummaryService, $inventoryByProduct, $representativeId) {
$card = $inventoryByProduct->get((int) $product->id);
$displayQty = $card !== null ? (float) ($card['quantity'] ?? 0) : 0.0;
$walletInterface = new WalletInterface(
(int) $customer->id,
(int) $customer->type,
null,
$assetSummaryService->effectiveProductWalletIdForCustomer(
(int) $customer->id,
(int) $customer->type,
(int) $product->id
)
);
$wallet = $walletInterface->wallet;
if (!$wallet) {
return null;
}
return [
'product_id' => (int) $product->id,
'title' => (string) $product->title,
'wallet_id' => (int) $wallet->id,
'available' => $displayQty,
'unit' => self::productQuantityUnit([
'unit' => $product->unit ?? null,
'direct_price' => $product->direct_price ?? null,
'group_type' => $product->group_type ?? null,
]),
'is_main_wallet' => $representativeId !== null && (int) $product->id === $representativeId,
];
})->filter(function ($row) {
return $row !== null && ($row['available'] ?? 0) > 0 && $row['title'] !== 'آبشده نقدی';
})->values();
$addresses = Address::query()
->where('customer_id', $customer->id)
->orderByDesc('default_address')
->orderByDesc('id')
->get(['id', 'province', 'city', 'address', 'phone', 'zip', 'default_address']);
return response()->json([
'products' => $productRows,
'addresses' => $addresses,
]);
}
public function createPhysicalWithdraw(Request $request, AssetSummaryService $assetSummaryService)
{
$data = $request->validate([
'customer_id' => 'required|integer|exists:customers,id',
'address_id' => 'required|integer|exists:addresses,id',
'wallets' => 'required|array|min:1',
'wallets.*.wallet_id' => 'required|integer|exists:wallets,id',
'wallets.*.value' => 'required|numeric|min:0.0001',
]);
$customer = Customer::findOrFail($data['customer_id']);
$address = Address::find($data['address_id']);
if (!$address || (int) $address->customer_id !== (int) $customer->id) {
return response()->json(['message' => 'آدرس معتبر نیست یا متعلق به این مشتری نمی‌باشد'], 403);
}
$withdraw = DB::transaction(function () use ($data, $customer, $address, $assetSummaryService) {
$withdraw = new Withdraw();
$withdraw->note = 'برداشت فیزیکی';
$withdraw->address_id = $address->id;
$withdraw->customer_id = $customer->id;
$withdraw->user_id = auth('user')->id();
$withdraw->sts = 50;
$withdraw->save();
foreach ($data['wallets'] as $walletRow) {
$wallet = Wallet::with('product')->findOrFail($walletRow['wallet_id']);
if ((int) $wallet->owner !== (int) $customer->id) {
throw new UnprocessableEntityHttpException('کیف پول انتخاب‌شده متعلق به این مشتری نیست');
}
if (!$wallet->product) {
throw new UnprocessableEntityHttpException('محصولی در کیف پول مورد نظر یافت نشد');
}
$productId = (int) $wallet->product_id;
$value = (float) $walletRow['value'];
$effectiveProductId = $assetSummaryService->effectiveProductWalletIdForCustomer(
(int) $customer->id,
(int) $customer->type,
$productId
);
$availableGold = $assetSummaryService->productDisplayQuantity(
(int) $customer->id,
(int) $customer->type,
$productId
);
if ($value > $availableGold) {
throw new UnprocessableEntityHttpException(
"مشتری به اندازه کافی از {$wallet->product->title} ندارد"
);
}
$walletInterface = new WalletInterface(
(int) $customer->id,
(int) $customer->type,
null,
$effectiveProductId
);
new TransactionInterface($walletInterface, [
'gold' => $value,
'number' => (float) $wallet->product->amount * $value,
'product_id' => $wallet->product->id,
'expire_at' => now(),
'active' => true,
'title' => " برداشت {$wallet->product->title}",
'note' => " برداشت محصول {$wallet->product->title} شماره سند {$withdraw->id}",
'withdraw_id' => $withdraw->id,
], 6, false);
}
return $withdraw;
});
$this->broadcastCustomerWalletAssets($customer);
return response()->json(['id' => $withdraw->id]);
}
public function adjustPhysicalWithdraw(Request $request, $id, AssetSummaryService $assetSummaryService)
{
$payload = $request->validate([
'transaction_id' => 'required|integer|exists:transactions,id',
'direction' => 'required|in:increase,decrease',
]);
$withdraw = Withdraw::with(['Transaction.product'])->findOrFail($id);
if (!Withdraw::isPhysicalWithdrawNote($withdraw->note)) {
return response()->json(['message' => 'این سند برداشت فیزیکی نیست'], 422);
}
if ((int) $withdraw->sts !== 50) {
return response()->json(['message' => 'تغییر مقدار فقط در وضعیت «در انتظار ارسال» مجاز است'], 422);
}
$transaction = Transaction::with('product')->findOrFail($payload['transaction_id']);
if ((int) $transaction->withdraw_id !== (int) $withdraw->id) {
return response()->json(['message' => 'تراکنش به این سند تعلق ندارد'], 422);
}
$product = $transaction->product;
if (!$product) {
return response()->json(['message' => 'محصول تراکنش یافت نشد'], 422);
}
$step = 1.0;
$currentUnits = abs((float) $transaction->gold);
$direction = $payload['direction'];
if ($direction === 'decrease' && $currentUnits <= $step) {
return response()->json(['message' => 'حداقل مقدار قابل برداشت یک واحد است'], 422);
}
$customer = Customer::findOrFail($withdraw->customer_id);
$effectiveProductId = $assetSummaryService->effectiveProductWalletIdForCustomer(
(int) $customer->id,
(int) $customer->type,
(int) $product->id
);
$walletInterface = new WalletInterface(
(int) $customer->id,
(int) $customer->type,
null,
$effectiveProductId
);
$wallet = $walletInterface->wallet;
if (!$wallet) {
return response()->json(['message' => 'کیف پول مشتری یافت نشد'], 422);
}
if ($direction === 'increase') {
$available = $assetSummaryService->productDisplayQuantity(
(int) $customer->id,
(int) $customer->type,
(int) $product->id
);
if ($available < $step) {
return response()->json(['message' => "موجودی {$product->title} کافی نیست"], 422);
}
$wallet->gold -= $step;
$transaction->gold = -($currentUnits + $step);
$transaction->number = (float) $transaction->number + ((float) $product->amount * $step);
} else {
$wallet->gold += $step;
$transaction->gold = -($currentUnits - $step);
$transaction->number = max(0, (float) $transaction->number - ((float) $product->amount * $step));
}
$transaction->balance_gold = $wallet->gold;
$wallet->save();
$transaction->save();
$walletInterface->set_hash();
$this->broadcastCustomerWalletAssets($customer);
return response()->json([
'message' => 'مقدار برداشت به‌روزرسانی شد',
'transaction' => $transaction->fresh('product'),
]);
}
public function completeWithdraw(Request $request)
{
$request->validate([
+13
View File
@@ -34,6 +34,7 @@ class Withdraw extends Model
50 => 'در انتظار ارسال',
51 => 'ارسال شده',
52 => 'لغو شده',
53 => 'در انتظار تعیین مجدد قیمت',
];
public function customer()
@@ -56,6 +57,18 @@ class Withdraw extends Model
return $this->hasOne('App\Models\BankAccount', 'id', 'bank_account_id');
}
/** برداشت فیزیکی — درخواست تحویل محصول */
public static function scopePhysicalWithdrawals($query)
{
return $query->where('note', 'like', '%برداشت فیزیکی%')
->whereBetween('sts', [49, 70]);
}
public static function isPhysicalWithdrawNote(?string $note): bool
{
return $note !== null && str_contains($note, 'برداشت فیزیکی');
}
public static function applyFlowFilter($query, ?string $flowType = 'all')
{
if (!$flowType || $flowType === 'all') {
+4
View File
@@ -122,6 +122,10 @@ Route::middleware('auth:user')->group(function (){
Route::put('/wallet/withdraw/{id}',[WalletController::class,'updateWithdraw']);
Route::get('/wallet/withdraw/{id}',[WalletController::class,'getWithdraw']);
Route::post('/wallet/withdraw/complete',[WalletController::class,'completeWithdraw']);
Route::post('/wallet/withdraw_physical/table',[WalletController::class,'indexPhysicalWithdraw']);
Route::get('/wallet/withdraw_physical/options/{customerId}',[WalletController::class,'physicalWithdrawOptions']);
Route::post('/wallet/withdraw_physical',[WalletController::class,'createPhysicalWithdraw']);
Route::post('/wallet/withdraw_physical/{id}/adjust',[WalletController::class,'adjustPhysicalWithdraw']);
//---------------order
Route::post('order/table', [OrderController::class, 'table']);