From 7eb4650b80b2e31f7eba60ae01f64819cfe8aa51 Mon Sep 17 00:00:00 2001 From: adonis Date: Mon, 13 Jul 2026 13:31:44 +0330 Subject: [PATCH] fix bug in 22/4 --- .../Controllers/Admin/ProductController.php | 22 +++++++++ .../Controllers/Customer/WalletController.php | 37 +++++++++++++++ app/Models/Product.php | 46 +++++++++++++++++++ ...dd_physical_delivery_to_products_table.php | 40 ++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 database/migrations/2026_07_13_000001_add_physical_delivery_to_products_table.php diff --git a/app/Http/Controllers/Admin/ProductController.php b/app/Http/Controllers/Admin/ProductController.php index 4f26caa..8a6b641 100644 --- a/app/Http/Controllers/Admin/ProductController.php +++ b/app/Http/Controllers/Admin/ProductController.php @@ -681,6 +681,28 @@ class ProductController extends SuperController $data['override_trading_hours'] = !empty($data['override_trading_hours']); } + if (!Product::hasPhysicalDeliveryColumns()) { + unset($data['has_physical_delivery'], $data['min_physical_delivery_grams']); + } else { + $hasDelivery = array_key_exists('has_physical_delivery', $data) + ? filter_var($data['has_physical_delivery'], FILTER_VALIDATE_BOOLEAN) + : false; + $data['has_physical_delivery'] = $hasDelivery; + + if (!$hasDelivery) { + $data['min_physical_delivery_grams'] = null; + } else { + $rawMin = $data['min_physical_delivery_grams'] ?? null; + $min = $this->normalizeCashFeeValue($rawMin); + if ($min <= 0) { + throw new UnprocessableEntityHttpException( + 'برای فعال کردن تحویل فیزیکی باید حداقل گرم معتبر وارد شود' + ); + } + $data['min_physical_delivery_grams'] = $min; + } + } + $data['cash_fee_buy'] = $this->normalizeCashFeeValue($data['cash_fee_buy'] ?? 0); $data['cash_fee_sell'] = $this->normalizeCashFeeValue($data['cash_fee_sell'] ?? 0); diff --git a/app/Http/Controllers/Customer/WalletController.php b/app/Http/Controllers/Customer/WalletController.php index 8e129bd..0f25384 100644 --- a/app/Http/Controllers/Customer/WalletController.php +++ b/app/Http/Controllers/Customer/WalletController.php @@ -417,6 +417,16 @@ class WalletController extends Controller return response()->json($Withdraw); } + private function formatGramsNumber(float $value): string + { + $rounded = round($value, 3); + if (fmod($rounded, 1.0) === 0.0) { + return number_format($rounded, 0); + } + + return rtrim(rtrim(number_format($rounded, 3, '.', ''), '0'), '.'); + } + public function withdraw_physical(Request $request) { $data = $request->all(); @@ -456,6 +466,33 @@ class WalletController extends Controller $availableGold = $assetSummaryService ->productDisplayQuantity($customerId, $ownerType, $productId); + // گیت تحویل فیزیکی per-product — سوییچ محصول و حداقل گرم + $walletProduct = $data['wallets'][$key]['wallet']['product']; + if (Product::hasPhysicalDeliveryColumns() && !$walletProduct->physicalDeliveryEnabled()) { + return response()->json([ + 'message' => "امکان تحویل فیزیکی برای {$walletProduct['title']} وجود ندارد" + ], 422); + } + + $minDeliveryGrams = Product::hasPhysicalDeliveryColumns() + ? $walletProduct->minPhysicalDeliveryGrams() + : 0.0; + if ($minDeliveryGrams > 0) { + $card = $assetSummaryService->productInventoryCard($customerId, $ownerType, $productId); + $isCount = ($card['display_type'] ?? null) === 'count'; + $unitWeight = (float) ($walletProduct['amount'] ?? 0); + $availableGrams = $isCount ? $availableGold * $unitWeight : $availableGold; + + if ($availableGrams + 0.0001 < $minDeliveryGrams) { + $remaining = max(0, $minDeliveryGrams - $availableGrams); + return response()->json([ + 'message' => $this->formatGramsNumber($remaining) + . " گرم مانده تا رسیدن به حداقل تحویل فیزیکی " + . "{$walletProduct['title']}" + ], 422); + } + } + if ($data['wallets'][$key]['value'] > $availableGold){ return response()->json([ 'message' => "شما به اندازه کافی از {$data['wallets'][$key]['wallet']['product']['title']} ندارید" diff --git a/app/Models/Product.php b/app/Models/Product.php index 38b2f67..1eb4fdb 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -25,6 +25,7 @@ class Product extends Model private static ?bool $inventoryColumnExists = null; private static ?bool $overrideTradingHoursColumnExists = null; private static ?bool $cashFeePercentModeColumnsExist = null; + private static ?bool $physicalDeliveryColumnsExist = null; protected static function booted(): void { @@ -63,9 +64,32 @@ class Product extends Model $product->attributes['cash_fee_sell_is_percent'] ); } + if (!static::hasPhysicalDeliveryColumns()) { + unset( + $product->attributes['has_physical_delivery'], + $product->attributes['min_physical_delivery_grams'] + ); + } }); } + public static function hasPhysicalDeliveryColumns(): bool + { + if (static::$physicalDeliveryColumnsExist !== null) { + return static::$physicalDeliveryColumnsExist; + } + + try { + $table = (new static)->getTable(); + static::$physicalDeliveryColumnsExist = Schema::hasColumn($table, 'has_physical_delivery') + && Schema::hasColumn($table, 'min_physical_delivery_grams'); + } catch (\Throwable) { + static::$physicalDeliveryColumnsExist = false; + } + + return static::$physicalDeliveryColumnsExist; + } + public static function hasOverrideTradingHoursColumn(): bool { if (static::$overrideTradingHoursColumnExists !== null) { @@ -282,6 +306,24 @@ class Product extends Model } } + public function physicalDeliveryEnabled(): bool + { + if (static::hasPhysicalDeliveryColumns()) { + return (bool) ($this->has_physical_delivery ?? false); + } + + return (bool) ($this->attributes['has_physical_delivery'] ?? false); + } + + public function minPhysicalDeliveryGrams(): float + { + $value = static::hasPhysicalDeliveryColumns() + ? ($this->min_physical_delivery_grams ?? 0) + : ($this->attributes['min_physical_delivery_grams'] ?? 0); + + return max(0.0, (float) $value); + } + public function getPriceOffsetPercentAttribute($value): float { if (!static::hasPriceOffsetColumn()) { @@ -321,6 +363,8 @@ class Product extends Model 'order_limit_enabled', 'min_order_cash', 'max_order_cash', + 'has_physical_delivery', // روشن: این محصول امکان تحویل فیزیکی دارد + 'min_physical_delivery_grams', // حداقل موجودی (گرم) برای فعال شدن تحویل فیزیکی 'metal_id', //the material of metal (#gold or #silver) 'price_offset_percent', // admin only: +/- % vs automatic reference (baked into price on sync) 'price_profit', @@ -357,6 +401,8 @@ class Product extends Model 'order_limit_enabled' => 'boolean', 'min_order_cash' => 'int', 'max_order_cash' => 'int', + 'has_physical_delivery' => 'boolean', + 'min_physical_delivery_grams' => 'float', 'cash_fee_buy' => 'float', 'cash_fee_sell' => 'float', 'cash_fee_buy_is_percent' => 'boolean', diff --git a/database/migrations/2026_07_13_000001_add_physical_delivery_to_products_table.php b/database/migrations/2026_07_13_000001_add_physical_delivery_to_products_table.php new file mode 100644 index 0000000..1183db8 --- /dev/null +++ b/database/migrations/2026_07_13_000001_add_physical_delivery_to_products_table.php @@ -0,0 +1,40 @@ +boolean('has_physical_delivery')->default(false); + } + if (! Schema::hasColumn('products', 'min_physical_delivery_grams')) { + $table->decimal('min_physical_delivery_grams', 12, 3)->nullable(); + } + }); + } + + public function down(): void + { + if (! Schema::hasTable('products')) { + return; + } + + Schema::table('products', function (Blueprint $table) { + if (Schema::hasColumn('products', 'min_physical_delivery_grams')) { + $table->dropColumn('min_physical_delivery_grams'); + } + if (Schema::hasColumn('products', 'has_physical_delivery')) { + $table->dropColumn('has_physical_delivery'); + } + }); + } +};