This commit is contained in:
2026-07-06 17:06:03 +03:30
parent 0f775d20e8
commit c155d49070
4 changed files with 108 additions and 19 deletions
@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Http\Controllers\SuperController;
use App\Models\Fund;
use App\Models\Product;
use App\Services\AssetSummaryService;
use Illuminate\Http\Request;
use Rooyesh\Wallet\interfaces\TransactionInterface;
use Rooyesh\Wallet\interfaces\WalletInterface;
@@ -26,10 +27,13 @@ class ProductController extends SuperController
$query = Product::with($this->with);
parent::__construct($model, $this->with, $select, null,$query,$search);
}
public function pTable(Request $request)
public function pTable(Request $request, AssetSummaryService $assetSummaryService)
{
$user = auth('customer')->user();
$mainWalletProductIds = app(\App\Services\ConfigService::class)->mainWalletProductIds();
$inventoryByProduct = collect(
$assetSummaryService->buildForCustomer((int) $user->id, (int) $user->type)['inventory_cards'] ?? []
)->keyBy('product_id');
$products = Product::where(function ($query) use ($user, $mainWalletProductIds) {
$query->whereHas('wallet', function ($q) use ($user) {
@@ -70,7 +74,7 @@ class ProductController extends SuperController
}
// Prepare the response
$user['rows'] = $products->map(function ($product) use ($user, $representativeId, $mainWalletGold) {
$user['rows'] = $products->map(function ($product) use ($user, $representativeId, $mainWalletGold, $inventoryByProduct) {
$product->unit = self::productQuantityUnit([
'unit' => $product->unit ?? null,
'direct_price' => $product->direct_price ?? null,
@@ -81,12 +85,26 @@ class ProductController extends SuperController
$product->file->setAttribute('full_src', $product->file->src);
}
$card = $inventoryByProduct->get((int) $product->id);
$displayQty = $card !== null
? (float) ($card['quantity'] ?? 0)
: null;
if ($representativeId !== null && (int) $product->id === $representativeId) {
// کیف محصول فقط نقش هندل را دارد؛ موجودی واقعی از کیف اصلی است
$walletInterface = new WalletInterface($user->id, $user->type, null, $product->id);
$handleWallet = $walletInterface->wallet;
$handleWallet->gold = $mainWalletGold;
$handleWallet->gold = $displayQty ?? $mainWalletGold;
$product->setRelation('wallet', $handleWallet);
} elseif ($displayQty !== null) {
if ($product->wallet) {
$product->wallet->gold = $displayQty;
} else {
$walletInterface = new WalletInterface($user->id, $user->type, null, $product->id);
$handleWallet = $walletInterface->wallet;
$handleWallet->gold = $displayQty;
$product->setRelation('wallet', $handleWallet);
}
}
return $product;
@@ -319,6 +319,7 @@ class WalletController extends Controller
if (!$data['adress'] || $data['adress']->customer_id != $data['customer']->id) {
return response()->json(['message' => 'آدرس معتبر نیست یا متعلق به شما نمی‌باشد'], 403);
}
$assetSummaryService = app(AssetSummaryService::class);
foreach ($data['wallets'] as $key => $wallet) {
$data['wallets'][$key]['wallet'] = Wallet::with(["product"])->find($wallet['wallet_id']);
if (!$data['wallets'][$key]['wallet']) {
@@ -332,16 +333,15 @@ class WalletController extends Controller
return response()->json(['message' => 'محصولی در ولت مورد نظر یافت نشد.'], 422);
}
// محصول متصل به کیف اصلی (main_wallet_products): موجودی و تراکنش روی کیف اصلی است
$effectiveProductId = self::effectiveProductWalletId((int) $data['wallets'][$key]['wallet']['product_id']);
$availableGold = $data['wallets'][$key]['wallet']['gold'];
if ($effectiveProductId === null) {
$mainWallet = Wallet::where('owner', auth('customer')->id())
->whereNull('fund_id')
->whereNull('product_id')
->first();
$availableGold = (float) ($mainWallet->gold ?? 0);
}
$productId = (int) $data['wallets'][$key]['wallet']['product_id'];
$customerId = (int) $data['customer']['id'];
$ownerType = (int) auth('customer')->user()->type;
// موجودی و کیف مؤثر — همسان با assets-summary و صفحهٔ اصلی
$effectiveProductId = $assetSummaryService
->effectiveProductWalletIdForCustomer($customerId, $ownerType, $productId);
$availableGold = $assetSummaryService
->productDisplayQuantity($customerId, $ownerType, $productId);
if ($data['wallets'][$key]['value'] > $availableGold){
return response()->json([
+25 -6
View File
@@ -111,22 +111,36 @@ class File extends Model
{
$fromSettings = self::baseUrlFromSettings();
if ($fromSettings !== '') {
return $fromSettings;
return self::ensureAbsoluteUrl($fromSettings);
}
$domain = config('app.domain');
if (is_string($domain) && $domain !== '' && ! str_contains($domain, 'localhost')) {
return self::ensureServerPublicBase(rtrim($domain, '/'));
return self::ensureAbsoluteUrl(self::ensureServerPublicBase(rtrim($domain, '/')));
}
$appUrl = config('app.url');
if (is_string($appUrl) && $appUrl !== '' && ! str_contains($appUrl, 'localhost')) {
return self::ensureServerPublicBase(rtrim($appUrl, '/'));
return self::ensureAbsoluteUrl(self::ensureServerPublicBase(rtrim($appUrl, '/')));
}
return '';
}
private static function ensureAbsoluteUrl(string $base): string
{
$base = rtrim(trim($base), '/');
if ($base === '') {
return '';
}
if (! preg_match('/^https?:\/\//i', $base)) {
$base = 'https://'.ltrim($base, '/');
}
return $base;
}
private static function baseUrlFromSettings(): string
{
try {
@@ -137,7 +151,7 @@ class File extends Model
foreach (['server_url', 'bace_url'] as $key) {
$value = trim((string) ($rows[$key] ?? ''));
if ($value !== '' && preg_match('/^https?:\/\//i', $value)) {
if ($value !== '') {
return rtrim($value, '/');
}
}
@@ -148,8 +162,13 @@ class File extends Model
}
$siteUrl = trim((string) ($rows['app_url'] ?? ''));
if ($siteUrl !== '' && preg_match('/^https?:\/\//i', $siteUrl)) {
return rtrim($siteUrl, '/').'/server';
if ($siteUrl !== '') {
$siteUrl = rtrim($siteUrl, '/');
if (! preg_match('#/server$#i', $siteUrl)) {
$siteUrl .= '/server';
}
return $siteUrl;
}
} catch (\Throwable) {
}
+52
View File
@@ -260,6 +260,7 @@ class AssetSummaryService
'unit_weight' => $this->productUnitWeightLabel($catalogProduct),
'show_in_assets' => $this->shouldShowInAssets($catalogProduct),
'is_melted' => true,
'is_main_wallet' => true,
'file' => $this->filePayload($catalogProduct),
];
}
@@ -267,6 +268,57 @@ class AssetSummaryService
return $cards;
}
/**
* کارت موجودی یک محصول از assets-summary — منبع واحد با صفحهٔ اصلی و کیف پول.
*/
public function productInventoryCard(int $customerId, int $ownerType, int $productId): ?array
{
$summary = $this->buildForCustomer($customerId, $ownerType);
foreach ($summary['inventory_cards'] as $card) {
if ((int) ($card['product_id'] ?? 0) === $productId) {
return $card;
}
}
return null;
}
/** موجودی نمایشی محصول — همسان با inventory_cards */
public function productDisplayQuantity(int $customerId, int $ownerType, int $productId): float
{
$card = $this->productInventoryCard($customerId, $ownerType, $productId);
if ($card !== null) {
return (float) ($card['quantity'] ?? 0);
}
$wallet = Wallet::query()
->where('owner', $customerId)
->where('owner_type', $ownerType)
->where('product_id', $productId)
->whereNull('fund_id')
->first();
return (float) ($wallet->gold ?? 0);
}
/**
* کیف مؤثر برای برداشت/تحویل فیزیکی — null یعنی کیف اصلی.
*/
public function effectiveProductWalletIdForCustomer(int $customerId, int $ownerType, int $productId): ?int
{
if ($this->configService->isMainWalletProduct($productId)) {
return null;
}
$card = $this->productInventoryCard($customerId, $ownerType, $productId);
if ($card !== null && !empty($card['is_main_wallet'])) {
return null;
}
return $productId;
}
private function resolveMainWallet(Collection $userWallets): ?Wallet
{
return $userWallets->first(