fix pargar bug

This commit is contained in:
2026-07-20 15:52:42 +03:30
parent 160d6bd923
commit a809a843b8
7 changed files with 586 additions and 62 deletions
@@ -25,6 +25,7 @@ use App\Models\TransactionsAdmin;
use App\Services\AssetSummaryService;
use App\Services\BalanceReportService;
use App\Services\ConfigService;
use App\Services\PargarSyncService;
use App\Services\WalletBalanceService;
use App\Services\WalletBroadcastService;
use Hekmatinasser\Verta\Verta;
@@ -83,6 +84,20 @@ class WalletController extends Controller
'active' => true
], 3, false, true, $gold_block);
try {
app(PargarSyncService::class)->queueWalletDocument(
$customer,
'cash',
(float) $data['value'],
[
'withdraw_id' => $Withdraw->id,
'source' => 'admin_charge2',
]
);
} catch (\Throwable $e) {
report($e);
}
$message = 'کیف پول شما به مبلغ ' . self::formatSmsRialAmount($data['value']) . " ریال در تاریخ " . verta()->format('Y/n/j H:i:s') . " شارژ شد " . "\n"
. $this->Config('name_fa');
$this->Send($customer->mobile, $message);
@@ -140,6 +155,21 @@ class WalletController extends Controller
}
}
try {
app(PargarSyncService::class)->queueWalletDocument(
$customer,
'cash',
(float) $data['value'],
[
'withdraw_id' => $Withdraw->id,
'note' => $data['note'] ?? null,
'source' => 'admin_cash',
]
);
} catch (\Throwable $e) {
report($e);
}
//send sms to users and customer
$status = $data['value'] > 0 ?" شارژ شد ": " کسر شد ";
if (self::isSmsNotifEnabled('charge_notif')) {
@@ -212,6 +242,23 @@ class WalletController extends Controller
report($e);
}
}
try {
app(PargarSyncService::class)->queueWalletDocument(
$customer,
'gold',
(float) $data['value'],
[
'withdraw_id' => $Withdraw->id,
'note' => $data['note'] ?? null,
'product_id' => $representative?->id,
'source' => 'admin_gold',
]
);
} catch (\Throwable $e) {
report($e);
}
//send sms to users and customer
$status = $data['value'] > 0 ?" شارژ شد ": " کسر شد ";
$weightUnit = self::tradeWeightUnit();
@@ -361,6 +408,23 @@ class WalletController extends Controller
}
}
try {
// فقط محصول متصل به کیف اصلی → سند طلای پرگار
app(PargarSyncService::class)->queueWalletDocument(
$customer,
'gold',
(float) $data['value'],
[
'withdraw_id' => $Withdraw->id,
'note' => $data['note'] ?? null,
'product_id' => $productId,
'source' => 'admin_product',
]
);
} catch (\Throwable $e) {
report($e);
}
$status = $deposit ? ' شارژ شد ' : ' کسر شد ';
$formatted = self::formatSmsGoldWeight($data['value']);
@@ -386,7 +386,10 @@ class CartController extends Controller
if ($config->isPargarAccounting()) {
try {
app(PargarSyncService::class)->recordTrade($order, true);
app(PargarSyncService::class)->recordTrade($order, true, [
'payment_kind' => PargarSyncService::PAYMENT_CASH,
'product_id' => null,
]);
} catch (\Throwable $e) {
report($e);
}
@@ -8,13 +8,11 @@ use App\Models\Cart;
use App\Models\Customer;
use App\Models\Order;
use App\Models\PargarSyncLog;
use App\Models\Product;
use App\Services\ConfigService;
use App\Services\PargarSyncService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Morilog\Jalali\Jalalian;
use Rooyesh\Wallet\interfaces\TransactionInterface;
use Rooyesh\Wallet\interfaces\WalletInterface;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
@@ -164,44 +162,41 @@ class PargarInboundController extends Controller
public function listOrders(Request $request)
{
$documents = collect();
$orders = Order::query()
->where('sts', 2)
->whereNull('pargar_confirmed_at')
->limit(100)
->get();
$documents = $orders->map(function (Order $order) {
$customer = Customer::find((int) ($order->customer_id ?? 0));
foreach ($orders as $order) {
$payload = $this->orderExportPayload($order);
if ($payload === null) {
continue;
}
$documents->push($payload);
}
// direction اپ: 1=خرید مشتری، 2=فروش مشتری
// OrderType پرگار از دید فروشگاه است: خرید مشتری = فروش فروشگاه (-1)، فروش مشتری = خرید فروشگاه (1)
$isCustomerBuy = (int) ($order->direction ?? 1) === 1;
$customerDirection = (int) ($order->direction ?? 0);
$walletLogs = PargarSyncLog::query()
->whereIn('type', [PargarSyncService::TYPE_WALLET_CASH, PargarSyncService::TYPE_WALLET_GOLD])
->where('sync_status', PargarSyncService::STATUS_PENDING)
->orderBy('id')
->limit(100)
->get();
return [
'OrderId' => (string) $order->id,
'OrderType' => $isCustomerBuy ? -1 : 1,
'PriceType' => 1,
'CustomerAccountId' => (int) ($customer->pargar_account_id ?? 0),
'ItemId' => 11,
'ItemName' => $this->pargarItemName($order),
'OrderDate' => $order->pay_at
? Jalalian::fromDateTime($order->pay_at)->format('Y/m/d-H:i:s')
: Jalalian::fromDateTime($order->created_at)->format('Y/m/d-H:i:s'),
'Fee' => (float) ($order->price ?? 0),
'Count' => 1,
'Weight' => (float) ($order->amount ?? 0),
'Price' => (float) ($order->total_price ?? 0),
'Carat' => 750,
'Description' => self::accountingShopTradeDesc($customerDirection, $this->isCartOrder($order)),
];
})->values();
foreach ($walletLogs as $log) {
$payload = $this->walletExportPayload($log);
if ($payload !== null) {
$documents->push($payload);
}
}
return response()->json([
'state' => 'true',
'msg' => '',
'data' => [
'documents' => $documents,
'documents' => $documents->values(),
'duplicated' => 'false',
'result' => 'OK',
],
@@ -242,7 +237,7 @@ class PargarInboundController extends Controller
PargarSyncLog::create([
'customer_id' => $customer?->id,
'desc' => 'دریافت اسناد از پرگار',
'type' => 'document',
'type' => PargarSyncService::TYPE_DOCUMENT,
'sync_status' => PargarSyncService::STATUS_SUCCESS,
'pargar_ref' => $accountId,
'data' => json_encode($data, JSON_UNESCAPED_UNICODE),
@@ -272,33 +267,35 @@ class PargarInboundController extends Controller
$goldInv = isset($data['GoldInv']) ? (float) str_replace('/', '.', (string) $data['GoldInv']) : 0;
$rialInv = isset($data['RialInv']) ? (float) str_replace('/', '.', (string) $data['RialInv']) : 0;
// خواندن و نوشتن روی همان کیف اصلی مشتری — نه first() تصادفی
// فقط مقایسه برای لاگ — اپ source of truth است؛ کیف را از مانده پرگار عوض نکن
$ownerType = (int) ($customer->type ?? 2);
$walletInterface = new WalletInterface($customer->id, $ownerType);
$wallet = $walletInterface->wallet;
$goldDiff = $goldInv - (float) ($wallet->gold ?? 0);
$rialDiff = $rialInv - (float) ($wallet->cash ?? 0);
$appGold = (float) ($wallet->gold ?? 0);
$appCash = (float) ($wallet->cash ?? 0);
$goldDiff = $goldInv - $appGold;
$rialDiff = $rialInv - $appCash;
$hasDiff = abs($goldDiff) >= 0.01 || abs($rialDiff) >= 1;
if (abs($goldDiff) >= 0.01 || abs($rialDiff) >= 1) {
new TransactionInterface($walletInterface, [
'cash' => $rialDiff,
'gold' => $goldDiff,
'title' => 'تعدیل حسابداری پرگار',
'expire_at' => now(),
'active' => true,
'start_at' => now(),
], 22, $goldDiff > 0, $rialDiff > 0);
}
$desc = $hasDiff
? 'اختلاف مانده پرگار با اپ — کیف اپ دست نخورده'
: 'دریافت مانده از پرگار — بدون اختلاف';
PargarSyncLog::create([
'customer_id' => $customer->id,
'desc' => 'دریافت مانده از پرگار',
'type' => 'mande',
'desc' => $desc,
'type' => PargarSyncService::TYPE_MANDE,
'amount' => $goldInv,
'sync_status' => PargarSyncService::STATUS_SUCCESS,
'pargar_ref' => $accountId,
'data' => json_encode($data, JSON_UNESCAPED_UNICODE),
'data' => json_encode(array_merge($data, [
'app_gold' => $appGold,
'app_cash' => $appCash,
'gold_diff' => $goldDiff,
'rial_diff' => $rialDiff,
'wallet_adjusted' => false,
]), JSON_UNESCAPED_UNICODE),
]);
return response()->json([
@@ -495,4 +492,162 @@ class PargarInboundController extends Controller
->whereIn('desc', ['سبد خرید', 'فروش سبد به مشتری'])
->exists();
}
/**
* @return array<string, mixed>|null
*/
private function orderExportPayload(Order $order): ?array
{
$sync = $this->sync();
$log = PargarSyncLog::where('order_id', $order->id)->latest('id')->first();
$payload = $log ? (json_decode((string) $log->data, true) ?: []) : [];
$paymentKind = (string) ($payload['payment_kind'] ?? PargarSyncService::PAYMENT_CASH);
if ($paymentKind === PargarSyncService::PAYMENT_OTHER_PRODUCT) {
return null;
}
if ($log && is_string($log->pargar_ref) && str_starts_with($log->pargar_ref, 'skip:')) {
return null;
}
$customer = Customer::find((int) ($order->customer_id ?? 0));
$isCustomerBuy = (int) ($order->direction ?? 1) === 1;
$customerDirection = (int) ($order->direction ?? 0);
$fromCart = (bool) ($payload['from_cart'] ?? $this->isCartOrder($order));
$product = $sync->resolveOrderProduct($order, $payload);
$number = (float) ($payload['number'] ?? 0);
$amount = (float) ($order->amount ?? 0);
$totalPrice = (float) ($order->total_price ?? $order->payment_amount ?? 0);
$unitPrice = (float) ($order->price ?? 0);
$isCountBased = $product
? self::isCountBasedProduct(self::productUnitPayload($product))
: false;
$count = 1;
$weight = $amount;
if ($isCountBased && $number > 0) {
$count = max(1, (int) round($number));
$weight = $amount;
} elseif ($number > 0 && abs($number - $amount) > 0.0001) {
$count = max(1, (int) round($number));
}
$itemId = $sync->pargarItemIdGold();
$itemName = $this->pargarItemName($order);
if ($product && trim((string) $product->title) !== '') {
$itemName = trim((string) $product->title);
}
$tradedProductId = (int) ($payload['product_id'] ?? $order->product_id ?? 0);
$tradesMainGold = $tradedProductId <= 0
|| app(ConfigService::class)->isMainWalletProduct($tradedProductId);
// اثر مانده از روش پرداخت؛ وزن طلا فقط وقتی کالای معامله یا پرداخت به کیف اصلی مربوط است
if ($paymentKind === PargarSyncService::PAYMENT_CASH) {
if ($tradesMainGold) {
// خرید/فروش آبشده کیف‌اصلی با نقدی: وزن + مبلغ
$itemId = $sync->pargarItemIdGold();
} else {
// محصول غیر اصلی با پرداخت نقدی: فقط ریال
$rialItemId = $sync->pargarItemIdRial();
$itemId = $rialItemId > 0 ? $rialItemId : $sync->pargarItemIdGold();
$weight = 0;
$count = 1;
}
} elseif ($paymentKind === PargarSyncService::PAYMENT_MAIN_GOLD) {
$itemId = $sync->pargarItemIdGold();
$itemName = $sync->mainWalletGoldItemName();
$paymentGold = (float) ($payload['payment_gold'] ?? $amount);
$weight = abs($paymentGold);
$totalPrice = 0;
}
return [
'OrderId' => (string) $order->id,
'OrderType' => $isCustomerBuy ? -1 : 1,
'PriceType' => 1,
'CustomerAccountId' => (int) ($customer->pargar_account_id ?? 0),
'ItemId' => $itemId,
'ItemName' => $itemName,
'OrderDate' => $order->pay_at
? Jalalian::fromDateTime($order->pay_at)->format('Y/m/d-H:i:s')
: Jalalian::fromDateTime($order->created_at)->format('Y/m/d-H:i:s'),
'Fee' => $unitPrice,
'Count' => $count,
'Weight' => $weight,
'Price' => $totalPrice,
'Carat' => 750,
'Description' => self::accountingShopTradeDesc($customerDirection, $fromCart),
];
}
/**
* @return array<string, mixed>|null
*/
private function walletExportPayload(PargarSyncLog $log): ?array
{
$sync = $this->sync();
$payload = json_decode((string) $log->data, true) ?: [];
$kind = (string) ($payload['kind'] ?? ($log->type === PargarSyncService::TYPE_WALLET_GOLD ? 'gold' : 'cash'));
$signed = (float) ($payload['signed_amount'] ?? $log->amount ?? 0);
if (abs($signed) < ($kind === 'cash' ? 1 : 0.01)) {
$signed = (float) ($log->amount ?? 0);
// اگر علامت نبود، از desc حدس بزن — پیش‌فرض واریز مثبت
if (! isset($payload['signed_amount']) && is_string($log->desc) && str_contains($log->desc, 'برداشت')) {
$signed = -abs($signed);
} elseif (! isset($payload['signed_amount'])) {
$signed = abs($signed);
}
}
$customer = Customer::find((int) ($log->customer_id ?? 0));
$withdrawId = (int) ($log->withdraw_id ?? 0);
$orderId = $withdrawId > 0 ? 'W-' . $withdrawId : 'WLOG-' . $log->id;
$isDeposit = $signed >= 0;
// OrderType از دید فروشگاه: واریز به مشتری = فروشگاه بدهکار؟ قرارداد قبلی خرید مشتری = -1
// واریز ریالی مشتری ≈ افزایش بدهی فروشگاه به مشتری از نظر حساب → مشابه خرید نیست؛
// برای واریز: OrderType=1 (ورود وجه به حساب مشتری در پرگار بسته به نرم‌افزار)
// هم‌راستا با آذین: اسناد کیف را با OrderType بر اساس علامت می‌فرستیم
$orderType = $isDeposit ? 1 : -1;
if ($kind === 'gold') {
return [
'OrderId' => $orderId,
'OrderType' => $orderType,
'PriceType' => 1,
'CustomerAccountId' => (int) ($customer->pargar_account_id ?? 0),
'ItemId' => $sync->pargarItemIdGold(),
'ItemName' => $sync->mainWalletGoldItemName(),
'OrderDate' => Jalalian::fromDateTime($log->date ?? $log->created_at)->format('Y/m/d-H:i:s'),
'Fee' => 0,
'Count' => 1,
'Weight' => abs($signed),
'Price' => 0,
'Carat' => 750,
'Description' => $isDeposit ? 'شارژ طلای کیف اصلی' : 'برداشت طلای کیف اصلی',
];
}
$rialItemId = $sync->pargarItemIdRial();
return [
'OrderId' => $orderId,
'OrderType' => $orderType,
'PriceType' => 1,
'CustomerAccountId' => (int) ($customer->pargar_account_id ?? 0),
'ItemId' => $rialItemId > 0 ? $rialItemId : $sync->pargarItemIdGold(),
'ItemName' => $isDeposit ? 'واریز ریالی' : 'برداشت ریالی',
'OrderDate' => Jalalian::fromDateTime($log->date ?? $log->created_at)->format('Y/m/d-H:i:s'),
'Fee' => 0,
'Count' => 1,
'Weight' => 0,
'Price' => abs($signed),
'Carat' => 750,
'Description' => $isDeposit ? 'واریز ریالی' : 'برداشت ریالی',
];
}
}
@@ -208,7 +208,30 @@ class SingleOrderController extends Controller
if ($config->isPargarAccounting()) {
try {
app(\App\Services\PargarSyncService::class)->recordTrade($data['order'] ?? null);
$paymentProductId = (int) ($data['payment_product_id'] ?? 0);
$paymentKind = app(\App\Services\PargarSyncService::class)
->inferPaymentKindFromContext([
'payment_product_id' => $paymentProductId,
]);
// خرید/فروش عادی از کیف ریالی (بدون payment_product_id) = نقدی
if ($paymentProductId <= 0) {
$paymentKind = \App\Services\PargarSyncService::PAYMENT_CASH;
}
$paymentGold = null;
if ($paymentKind === \App\Services\PargarSyncService::PAYMENT_MAIN_GOLD) {
$paymentGold = (float) ($data['total_amount'] ?? $data['amount'] ?? 0);
}
app(\App\Services\PargarSyncService::class)->recordTrade(
$data['order'] ?? null,
false,
[
'payment_kind' => $paymentKind,
'payment_product_id' => $paymentProductId > 0 ? $paymentProductId : null,
'product_id' => (int) ($data['product_id'] ?? 0) ?: null,
'number' => isset($data['number']) ? (float) $data['number'] : null,
'payment_gold' => $paymentGold,
]
);
} catch (\Throwable $e) {
report($e);
}
@@ -182,6 +182,21 @@ class paypingController extends Controller
'active' => true,
], 3, false, true);
try {
app(\App\Services\PargarSyncService::class)->queueWalletDocument(
$User,
'cash',
(float) ($GTransaction['amount'] * 10),
[
'withdraw_id' => $Withdraw->id,
'note' => 'واریز درگاه',
'source' => 'gateway',
]
);
} catch (\Throwable $e) {
report($e);
}
$message = 'کاربر ' . $Withdraw->customer->name . ' با شماره موبایل ' . $Withdraw->customer->mobile
. ' حساب خود را به مقدار ' . number_format($Withdraw->cash) . ' ریال شارژ کرد. با تشکر، ' . $this->Config('name_fa');
@@ -238,6 +253,21 @@ class paypingController extends Controller
'active' => true,
], 3, false, true);
try {
app(\App\Services\PargarSyncService::class)->queueWalletDocument(
$User,
'cash',
(float) ($decodedResponse['data']['amount'] * 10),
[
'withdraw_id' => (int) $Withdraw->id,
'note' => 'واریز درگاه',
'source' => 'gateway',
]
);
} catch (\Throwable $e) {
report($e);
}
if ($this->cfg()->getBool('payping_fin_sync_enabled', (bool) config('payment.payping_fin_sync_enabled', false))) {
$finUrl = $this->cfg()->getString('payping_fin_url', 'https://limangold.ir/kernel/admin/fin');
$finToken = $this->cfg()->getString('payping_fin_token', '');