Merge origin/Abshode_Khanegi and resolve WalletController conflict.

Keep unified charge API from remote while preserving main-wallet metal detection for deposits and transaction labels via BalanceReportService.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-24 01:22:56 +03:30
co-authored by Cursor
24 changed files with 1762 additions and 375 deletions
@@ -137,11 +137,7 @@ class WalletController extends Controller
} else {
$record->title = ($record->gold > 0 ? self::productBuyTitle($wallet->product->title) : self::productSellTitle($wallet->product->title));
}
$record->unit = self::productQuantityUnit([
'unit' => $wallet->product->unit ?? null,
'direct_price' => $wallet->product->direct_price ?? null,
'group_type' => $wallet->product->group_type ?? null,
]);
$record->unit = self::productQuantityUnit(self::productUnitPayload($wallet->product));
} else {
$record->title = $this->resolveMainWalletTransactionTitle($record);
}
@@ -406,17 +402,12 @@ class WalletController extends Controller
return;
}
$productId = $this->resolveAbshodeProductId($record);
if (!$productId) {
return;
}
$productTitle = $record->product->title
?? Product::query()->whereKey($productId)->value('title');
if (!$productTitle) {
$product = $this->resolveTransactionProduct($record);
if (!$product || trim((string) $product->title) === '') {
return;
}
$productTitle = trim((string) $product->title);
$direction = (int) ($record->order->direction ?? 0);
if ($direction === 1 || ($direction === 0 && (float) ($record->gold ?? 0) > 0)) {
$record->title = self::productBuyTitle($productTitle);
@@ -434,9 +425,14 @@ class WalletController extends Controller
return (int) $record->product_id;
}
$note = (string) ($record->note ?? '');
if ($note !== '' && preg_match('/(?:^|;)product_id:(\d+)/', $note, $matches)) {
return (int) $matches[1];
$noteProductId = $this->productIdFromTransactionNote($record->note ?? null);
if ($noteProductId) {
return $noteProductId;
}
$wallet = $this->resolveTransactionWallet($record);
if ($wallet && !empty($wallet->product_id)) {
return (int) $wallet->product_id;
}
if (empty($record->order_id)) {
@@ -449,7 +445,33 @@ class WalletController extends Controller
->where('product_id', '>', 0)
->value('product_id');
return $siblingProductId ? (int) $siblingProductId : null;
if ($siblingProductId) {
return (int) $siblingProductId;
}
$siblingNotes = Transaction::query()
->where('order_id', $record->order_id)
->whereNotNull('note')
->pluck('note');
foreach ($siblingNotes as $note) {
$productId = $this->productIdFromTransactionNote($note);
if ($productId) {
return $productId;
}
}
return null;
}
private function productIdFromTransactionNote(?string $note): ?int
{
$note = (string) ($note ?? '');
if ($note === '' || !preg_match('/(?:^|;)product_id:(\d+)/', $note, $matches)) {
return null;
}
return (int) $matches[1];
}
private function applyMetalTypeTransactionTitle($record): void
@@ -462,7 +484,7 @@ class WalletController extends Controller
return;
}
$wallet = $record->wallet ?? null;
$wallet = $this->resolveTransactionWallet($record);
if ($wallet && !empty($wallet->product)) {
return;
}
@@ -500,18 +522,12 @@ class WalletController extends Controller
return;
}
$productId = $this->resolveAbshodeProductId($record);
if (!$productId) {
return;
}
$productTitle = $record->product->title
?? $record->wallet->product->title
?? Product::query()->whereKey($productId)->value('title');
if (!$productTitle) {
$product = $this->resolveTransactionProduct($record);
if (!$product || trim((string) $product->title) === '') {
return;
}
$productTitle = trim((string) $product->title);
$expectedBuy = self::productBuyTitle($productTitle);
$expectedSell = self::productSellTitle($productTitle);
@@ -527,11 +543,28 @@ class WalletController extends Controller
if ($direction === 2 || ($direction === 0 && (float) ($record->gold ?? 0) < 0)) {
$record->title = $expectedSell;
return;
}
if (self::isGenericMetalTransactionTitle($record->title)) {
$record->title = $expectedBuy;
}
}
private function resolveMainWalletTransactionTitle($record): string
{
$product = $this->resolveTransactionProduct($record);
if ($product && trim((string) $product->title) !== '') {
$productTitle = trim((string) $product->title);
if ((int) $record->type === 1) {
return self::productBuyTitle($productTitle);
}
if ((int) $record->type === 2) {
return self::productSellTitle($productTitle);
}
}
if ((int) $record->type === 1) {
return self::mainMetalBuyTitle();
}
@@ -576,35 +609,86 @@ class WalletController extends Controller
}
if (!empty($record->wallet?->product)) {
$record->unit = self::productQuantityUnit([
'unit' => $record->wallet->product->unit ?? null,
'direct_price' => $record->wallet->product->direct_price ?? null,
'group_type' => $record->wallet->product->group_type ?? null,
]);
$this->applyProductTransactionUnit($record);
}
}
private function resolveTransactionWallet($record): ?Wallet
{
if ($record instanceof Transaction) {
$relation = $record->getRelationValue('wallet');
if ($relation instanceof Wallet) {
return $relation;
}
}
$walletId = (int) ($record->getAttributes()['wallet'] ?? 0);
if ($walletId <= 0) {
return null;
}
return Wallet::with('product')->find($walletId);
}
private function resolveTransactionProduct($record): ?Product
{
$productId = $this->resolveAbshodeProductId($record) ?? 0;
$wallet = $this->resolveTransactionWallet($record);
if ($productId <= 0) {
$partial = $wallet?->product ?? $record->product ?? null;
return $partial instanceof Product ? $partial : null;
}
$partial = $record->product ?? $wallet?->product ?? null;
if ($partial instanceof Product && (int) $partial->id === $productId && trim((string) $partial->title) !== '') {
return $partial;
}
return Product::query()
->select([
'id',
'title',
'group_type',
'unit',
'direct_price',
'amount',
'trade_by_quantity',
'metal_id',
])
->find($productId) ?? ($partial instanceof Product ? $partial : null);
}
private function applyProductTransactionUnit($record): void
{
$product = $record->wallet->product ?? $record->product ?? null;
$product = $this->resolveTransactionProduct($record);
if (!$product) {
if (!empty($record->gold) && empty($record->wallet?->product)) {
if (!empty($record->gold)) {
$record->unit = self::tradeWeightUnit();
}
return;
}
$record->unit = self::productQuantityUnit([
'unit' => $product->unit ?? null,
'direct_price' => $product->direct_price ?? null,
'group_type' => $product->group_type ?? null,
]);
$payload = self::productUnitPayload($product);
$onMainWallet = empty($record->wallet?->product_id);
[$displayGold, $unit] = self::resolveTransactionDisplayGoldAndUnit(
$payload,
(float) ($record->gold ?? 0),
$onMainWallet
);
$record->gold = $displayGold;
$record->unit = $unit;
}
private function isProductWalletTransaction($record): bool
{
return !empty($record->wallet?->product);
$wallet = $this->resolveTransactionWallet($record);
return !empty($wallet?->product);
}
/**