753 lines
29 KiB
PHP
753 lines
29 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Pargar;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Http\Middleware\PargarAuth;
|
||
use App\Models\Cart;
|
||
use App\Models\Customer;
|
||
use App\Models\Order;
|
||
use App\Models\PargarSyncLog;
|
||
use App\Services\ConfigService;
|
||
use App\Services\PargarInboundWalletService;
|
||
use App\Services\PargarSyncService;
|
||
use App\Services\WalletBalanceService;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Schema;
|
||
use Morilog\Jalali\Jalalian;
|
||
use Rooyesh\Wallet\interfaces\WalletInterface;
|
||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||
|
||
class PargarInboundController extends Controller
|
||
{
|
||
protected function sync(): PargarSyncService
|
||
{
|
||
return app(PargarSyncService::class);
|
||
}
|
||
|
||
public function login(Request $request)
|
||
{
|
||
if (! app(ConfigService::class)->isPargarAccounting()) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'data' => null,
|
||
'message' => 'Pargar integration is disabled',
|
||
], 503);
|
||
}
|
||
|
||
[$username, $password] = $this->pargarLoginCredentials($request);
|
||
|
||
$expectedUser = $this->normalizeCredential(trim((string) $this->Config('pargar_username')));
|
||
$expectedPass = $this->normalizeCredential(trim((string) $this->Config('pargar_password')));
|
||
|
||
if ($username !== $expectedUser || $password !== $expectedPass || $expectedUser === '') {
|
||
return response()->json([
|
||
'success' => false,
|
||
'data' => null,
|
||
'message' => 'Invalid credentials!',
|
||
]);
|
||
}
|
||
|
||
$token = PargarAuth::issueToken($username);
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'data' => ['token' => $token],
|
||
'message' => 'You are logged in successfully.',
|
||
]);
|
||
}
|
||
|
||
public function upsertCustomers(Request $request)
|
||
{
|
||
$payload = $this->pargarPayload($request);
|
||
$customers = $payload['Customers'] ?? [];
|
||
if (! is_array($customers) || $customers === []) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'message' => 'Customers list is required',
|
||
'data' => null,
|
||
], 422);
|
||
}
|
||
|
||
$messages = [];
|
||
|
||
foreach ($customers as $row) {
|
||
if (! is_array($row)) {
|
||
continue;
|
||
}
|
||
|
||
$accountId = trim((string) ($row['AccountId'] ?? ''));
|
||
$fullName = trim((string) ($row['FullName'] ?? ''));
|
||
$mobile = Customer::normalizeMobile((string) ($row['CellPhone'] ?? ''));
|
||
|
||
if ($accountId === '' || $mobile === null) {
|
||
$messages[] = 'ردیف نامعتبر: AccountId یا CellPhone خالی است';
|
||
continue;
|
||
}
|
||
|
||
$byAccount = Customer::whereNull('deleted_at')
|
||
->where('pargar_account_id', $accountId)
|
||
->first();
|
||
|
||
if ($byAccount && Customer::normalizeMobile($byAccount->mobile) !== $mobile) {
|
||
$messages[] = "کد حساب {$accountId} قبلاً برای مشتری دیگری ثبت شده است";
|
||
continue;
|
||
}
|
||
|
||
$customer = Customer::findActiveByMobile($mobile);
|
||
|
||
if ($customer) {
|
||
$currentAccount = (string) ($customer->pargar_account_id ?? '');
|
||
|
||
if ($currentAccount === $accountId) {
|
||
$messages[] = 'کد حساب مشتری وجود دارد';
|
||
continue;
|
||
}
|
||
|
||
if ($currentAccount !== '' && $currentAccount !== $accountId) {
|
||
$messages[] = "شماره {$mobile} قبلاً به حساب پرگار دیگری متصل است";
|
||
continue;
|
||
}
|
||
|
||
$customer->pargar_account_id = $accountId;
|
||
$this->applyPargarCustomerName($customer, $fullName);
|
||
$customer->save();
|
||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||
$messages[] = "کد حسابداری مشتری {$fullName} ویرایش شد";
|
||
continue;
|
||
}
|
||
|
||
if ($byAccount) {
|
||
if (Customer::isMobileTaken($mobile, (int) $byAccount->id)) {
|
||
$messages[] = "شماره {$mobile} تکراری است";
|
||
continue;
|
||
}
|
||
|
||
if ($fullName !== '') {
|
||
$this->applyPargarCustomerName($byAccount, $fullName);
|
||
}
|
||
$byAccount->mobile = $mobile;
|
||
if (empty($byAccount->password)) {
|
||
$byAccount->password = $mobile;
|
||
}
|
||
$byAccount->save();
|
||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $byAccount->id);
|
||
$messages[] = "مشتری با کد حسابداری {$accountId} بهروزرسانی شد";
|
||
continue;
|
||
}
|
||
|
||
try {
|
||
$customer = new Customer();
|
||
$this->applyPargarCustomerName($customer, $fullName !== '' ? $fullName : 'مشتری');
|
||
$customer->mobile = $mobile;
|
||
$customer->pargar_account_id = $accountId;
|
||
$customer->active = true;
|
||
$customer->type = 2;
|
||
$customer->password = $mobile;
|
||
$customer->referral_id = $this->uniqueReferralId();
|
||
$customer->save();
|
||
|
||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||
$messages[] = "مشتری با کد حسابداری {$accountId} ایجاد شد";
|
||
} catch (\Throwable $e) {
|
||
$messages[] = "خطا برای {$mobile}: " . (
|
||
$e instanceof UnprocessableEntityHttpException
|
||
? $e->getMessage()
|
||
: 'ثبت مشتری ناموفق بود'
|
||
);
|
||
}
|
||
}
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => implode(' | ', $messages),
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
public function listOrders(Request $request)
|
||
{
|
||
$documents = collect();
|
||
|
||
$orders = Order::query()
|
||
->where('sts', 2)
|
||
->whereNull('pargar_confirmed_at')
|
||
->limit(100)
|
||
->get();
|
||
|
||
foreach ($orders as $order) {
|
||
$payload = $this->orderExportPayload($order);
|
||
if ($payload === null) {
|
||
continue;
|
||
}
|
||
$documents->push($payload);
|
||
}
|
||
|
||
$walletLogs = PargarSyncLog::query()
|
||
->whereIn('type', [PargarSyncService::TYPE_WALLET_CASH, PargarSyncService::TYPE_WALLET_GOLD])
|
||
->where('sync_status', PargarSyncService::STATUS_PENDING)
|
||
->orderBy('id')
|
||
->limit(100)
|
||
->get();
|
||
|
||
foreach ($walletLogs as $log) {
|
||
$payload = $this->walletExportPayload($log);
|
||
if ($payload !== null) {
|
||
$documents->push($payload);
|
||
}
|
||
}
|
||
|
||
return response()->json([
|
||
'state' => 'true',
|
||
'msg' => '',
|
||
'data' => [
|
||
'documents' => $documents->values(),
|
||
'duplicated' => 'false',
|
||
'result' => 'OK',
|
||
],
|
||
]);
|
||
}
|
||
|
||
public function confirmOrders(Request $request)
|
||
{
|
||
$payload = $this->pargarPayload($request);
|
||
$orderIds = $payload['OrderIds'] ?? [];
|
||
if (! is_array($orderIds)) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'message' => 'OrderIds must be an array',
|
||
'data' => null,
|
||
], 422);
|
||
}
|
||
|
||
$result = $this->sync()->confirmOrders($orderIds);
|
||
|
||
// سند در پرگار ثبت شد → ماندهٔ معطلمانده را همگام کن
|
||
foreach ($result['customer_ids'] ?? [] as $customerId) {
|
||
$this->inboundWallet()->queueWalletSyncAfterResponse((int) $customerId);
|
||
}
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'تاییدیه اسناد با موفقیت دریافت شد',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
protected function inboundWallet(): PargarInboundWalletService
|
||
{
|
||
return app(PargarInboundWalletService::class);
|
||
}
|
||
|
||
public function storeDocuments(Request $request)
|
||
{
|
||
$data = $this->pargarPayload($request);
|
||
$accountId = trim((string) ($data['accountId'] ?? ''));
|
||
$summary = $this->sync()->summarizeInboundDocument($data);
|
||
|
||
$customer = $accountId !== ''
|
||
? Customer::where('pargar_account_id', $accountId)->first()
|
||
: null;
|
||
|
||
// فقط لاگ — replay تاریخچه روی کیف خاموش است؛ بعد از ذخیره، مانده بهصورت خودکار همتراز میشود
|
||
PargarSyncLog::create([
|
||
'customer_id' => $customer?->id,
|
||
'amount' => $summary['amount'],
|
||
'desc' => $customer
|
||
? ($summary['desc'] !== '' ? $summary['desc'] : 'دریافت اسناد از پرگار')
|
||
: 'دریافت اسناد از پرگار — مشتری یافت نشد',
|
||
'type' => PargarSyncService::TYPE_DOCUMENT,
|
||
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
||
'pargar_ref' => $accountId !== '' ? $accountId : null,
|
||
'data' => json_encode(array_merge($data, [
|
||
'_summary' => $summary,
|
||
'wallet_applied' => false,
|
||
'code_build' => 'docs-then-mande-auto',
|
||
]), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
|
||
]);
|
||
|
||
\Illuminate\Support\Facades\Log::info('Pargar documents stored; queue auto mande sync', [
|
||
'account_id' => $accountId,
|
||
'customer_id' => $customer?->id,
|
||
'tx_count' => is_array($data['transactions'] ?? null) ? count($data['transactions']) : 0,
|
||
]);
|
||
|
||
// بهمحض ورود اسناد → ack + اعمال آخرین مانده (با تأخیر کوتاه تا webhook مانده قفل را بگیرد)
|
||
if ($customer) {
|
||
$this->inboundWallet()->queueWalletSyncAfterResponse((int) $customer->id, delaySeconds: 2);
|
||
}
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'اسناد ثبت شد؛ همگامسازی مانده در صف است',
|
||
'data' => [
|
||
'wallet_applied' => false,
|
||
'auto_mande_queued' => (bool) $customer,
|
||
'build' => 'docs-then-mande-auto',
|
||
],
|
||
]);
|
||
}
|
||
|
||
public function storeMande(Request $request)
|
||
{
|
||
$data = $this->pargarPayload($request);
|
||
$accountId = trim((string) ($data['AccountId'] ?? ''));
|
||
|
||
$customer = Customer::where('pargar_account_id', $accountId)->first();
|
||
|
||
$goldInv = isset($data['GoldInv']) ? (float) str_replace('/', '.', (string) $data['GoldInv']) : 0;
|
||
$rialInv = isset($data['RialInv']) ? (float) str_replace('/', '.', (string) $data['RialInv']) : 0;
|
||
|
||
if (! $customer) {
|
||
PargarSyncLog::create([
|
||
'customer_id' => null,
|
||
'desc' => 'مانده پرگار — مشتری هنوز در پنل نیست',
|
||
'type' => PargarSyncService::TYPE_MANDE,
|
||
'amount' => $goldInv,
|
||
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
||
'pargar_ref' => $accountId,
|
||
'data' => json_encode(array_merge($data, [
|
||
'orphan_mande' => true,
|
||
'wallet_adjusted' => false,
|
||
]), JSON_UNESCAPED_UNICODE),
|
||
]);
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'مانده ذخیره شد؛ پس از ثبت مشتری اعمال میشود',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
$ownerType = (int) ($customer->type ?? 2);
|
||
$walletInterface = new WalletInterface($customer->id, $ownerType);
|
||
$wallet = $walletInterface->wallet;
|
||
|
||
// GoldInv پرگار با جمع آزاد+قلک محصولات کیف اصلی مقایسه میشود (انتقال قلک سند نمیزند)
|
||
$appGold = app(WalletBalanceService::class)
|
||
->accountingVisibleMainWalletGold((int) $customer->id, $ownerType);
|
||
$appCash = (float) ($wallet->cash ?? 0);
|
||
$goldDiff = $goldInv - $appGold;
|
||
$rialDiff = $rialInv - $appCash;
|
||
$hasDiff = abs($goldDiff) >= 0.01 || abs($rialDiff) >= 1;
|
||
|
||
$pendingOutbound = $this->inboundWallet()->hasPendingOutboundToPargar($customer);
|
||
|
||
$desc = $pendingOutbound
|
||
? 'مانده دریافت شد — منتظر ثبت سند پنل در پرگار'
|
||
: ($hasDiff
|
||
? 'اختلاف مانده پرگار با اپ — در حال اعمال روی کیف'
|
||
: 'دریافت مانده از پرگار — بدون اختلاف');
|
||
|
||
PargarSyncLog::create([
|
||
'customer_id' => $customer->id,
|
||
'desc' => $desc,
|
||
'type' => PargarSyncService::TYPE_MANDE,
|
||
'amount' => $goldInv,
|
||
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
||
'pargar_ref' => $accountId,
|
||
'data' => json_encode(array_merge($data, [
|
||
'app_gold' => $appGold,
|
||
'app_cash' => $appCash,
|
||
'gold_diff' => $goldDiff,
|
||
'rial_diff' => $rialDiff,
|
||
'wallet_adjusted' => false,
|
||
'awaiting_outbound_confirm' => $pendingOutbound,
|
||
]), JSON_UNESCAPED_UNICODE),
|
||
]);
|
||
|
||
// بدون سند pending → همینجا اعمال؛ اگر قفل/خطا بود صف با تأخیر تکرار میکند
|
||
// (اسناد و مانده اغلب پشتسرهم میآیند و قفل همزمان شایع است)
|
||
$appliedNow = false;
|
||
if ($pendingOutbound) {
|
||
$this->inboundWallet()->queueWalletSyncAfterResponse((int) $customer->id, delaySeconds: 2);
|
||
} else {
|
||
try {
|
||
$syncResult = $this->inboundWallet()->syncWalletFromPargar($customer, forceMande: false, auto: true);
|
||
$appliedNow = (bool) ($syncResult['mande']['applied'] ?? false);
|
||
} catch (\Throwable $e) {
|
||
report($e);
|
||
}
|
||
|
||
if (! $appliedNow) {
|
||
$this->inboundWallet()->queueWalletSyncAfterResponse((int) $customer->id, delaySeconds: 3);
|
||
}
|
||
}
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => $pendingOutbound
|
||
? 'مانده ذخیره شد؛ بعد از ثبت سند در پرگار خودکار سینک میشود'
|
||
: ($appliedNow
|
||
? 'مانده دریافت و روی کیف اپ اعمال شد'
|
||
: 'مانده دریافت شد؛ همگامسازی خودکار در صف است'),
|
||
'data' => [
|
||
'awaiting_outbound_confirm' => $pendingOutbound,
|
||
'wallet_applied' => $appliedNow,
|
||
'auto_sync_queued' => ! $appliedNow,
|
||
],
|
||
]);
|
||
}
|
||
|
||
public function listNewCustomers(Request $request)
|
||
{
|
||
$customers = Customer::query()
|
||
->whereNull('deleted_at')
|
||
->where(function ($q) {
|
||
$q->whereNull('pargar_account_id')
|
||
->orWhere('pargar_account_id', '')
|
||
->orWhere('pargar_account_id', '0')
|
||
->orWhere('pargar_account_id', '-1');
|
||
})
|
||
->limit(100)
|
||
->get(['id', 'fname', 'lname', 'name', 'mobile']);
|
||
|
||
if ($customers->isEmpty()) {
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'مشتری جدید وجود ندارد',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
$rows = $customers->map(function (Customer $customer) {
|
||
$fname = trim((string) ($customer->fname ?? ''));
|
||
$lname = trim((string) ($customer->lname ?? ''));
|
||
|
||
if ($fname === '' && $lname === '') {
|
||
$nameParts = preg_split('/\s+/', trim((string) ($customer->name ?? '')), 2) ?: ['', ''];
|
||
$fname = trim((string) ($nameParts[0] ?? ''));
|
||
$lname = trim((string) ($nameParts[1] ?? ''));
|
||
}
|
||
|
||
return [
|
||
'id' => (string) $customer->id,
|
||
'name' => $fname,
|
||
'family' => $lname,
|
||
'mobile' => (string) $customer->mobile,
|
||
];
|
||
});
|
||
|
||
return response()->json([
|
||
'state' => 'true',
|
||
'msg' => '',
|
||
'customer' => $rows,
|
||
]);
|
||
}
|
||
|
||
public function confirmCustomers(Request $request)
|
||
{
|
||
$payload = $this->pargarPayload($request);
|
||
$rows = $payload['customers'] ?? [];
|
||
if (! is_array($rows)) {
|
||
return response()->json([
|
||
'success' => false,
|
||
'message' => 'customers must be an array',
|
||
'data' => null,
|
||
], 422);
|
||
}
|
||
|
||
foreach ($rows as $row) {
|
||
if (! is_array($row)) {
|
||
continue;
|
||
}
|
||
|
||
$customerId = $row['customer_id'] ?? $row['id_customer'] ?? null;
|
||
$accountId = $row['accounting_id'] ?? null;
|
||
if ($customerId === null || $accountId === null) {
|
||
continue;
|
||
}
|
||
|
||
$normalizedAccountId = trim((string) $accountId);
|
||
if ($normalizedAccountId === '' || in_array($normalizedAccountId, ['0', '-1'], true)) {
|
||
continue;
|
||
}
|
||
|
||
$customer = Customer::find($customerId);
|
||
if ($customer) {
|
||
$customer->pargar_account_id = $normalizedAccountId;
|
||
$customer->save();
|
||
|
||
$log = PargarSyncLog::query()
|
||
->where('customer_id', $customer->id)
|
||
->where('type', 'customer')
|
||
->where('sync_status', PargarSyncService::STATUS_PENDING)
|
||
->latest('id')
|
||
->first();
|
||
|
||
if ($log) {
|
||
$this->sync()->markSuccess($log, $normalizedAccountId);
|
||
}
|
||
|
||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||
}
|
||
}
|
||
|
||
return response()->json([
|
||
'success' => true,
|
||
'message' => 'تاییدیه ثبت مشتری با موفقیت دریافت شد',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* @return array{0: string, 1: string}
|
||
*/
|
||
private function pargarLoginCredentials(Request $request): array
|
||
{
|
||
$payload = $this->pargarPayload($request);
|
||
|
||
return [
|
||
$this->normalizeCredential((string) ($payload['username'] ?? '')),
|
||
$this->normalizeCredential((string) ($payload['password'] ?? '')),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* @return array<string, mixed>
|
||
*/
|
||
private function pargarPayload(Request $request): array
|
||
{
|
||
$payload = $request->all();
|
||
if ($payload !== []) {
|
||
return $payload;
|
||
}
|
||
|
||
$decoded = json_decode((string) $request->getContent(), true);
|
||
|
||
return is_array($decoded) ? $decoded : [];
|
||
}
|
||
|
||
private function normalizeCredential(string $value): string
|
||
{
|
||
$persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
|
||
$arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
|
||
$latin = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||
|
||
return trim(str_replace(array_merge($persian, $arabic), array_merge($latin, $latin), $value));
|
||
}
|
||
|
||
private function applyPargarCustomerName(Customer $customer, string $fullName): void
|
||
{
|
||
$fullName = trim($fullName);
|
||
if ($fullName === '') {
|
||
return;
|
||
}
|
||
|
||
[$fname, $lname] = $this->splitPargarFullName($fullName);
|
||
$customer->fname = $fname;
|
||
$customer->lname = $lname;
|
||
// mutator مدل Customer مقدار name را از fname + lname میسازد
|
||
$customer->name = trim("$fname $lname");
|
||
}
|
||
|
||
/**
|
||
* @return array{0: string, 1: string}
|
||
*/
|
||
private function splitPargarFullName(string $fullName): array
|
||
{
|
||
$parts = preg_split('/\s+/u', trim($fullName), 2) ?: ['', ''];
|
||
|
||
return [
|
||
trim((string) ($parts[0] ?? '')),
|
||
trim((string) ($parts[1] ?? '')),
|
||
];
|
||
}
|
||
|
||
private function uniqueReferralId(): string
|
||
{
|
||
do {
|
||
$referral = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'), 0, 4);
|
||
} while (Customer::where('referral_id', $referral)->exists());
|
||
|
||
return $referral;
|
||
}
|
||
|
||
private function pargarItemName(Order $order): string
|
||
{
|
||
return $this->isCartOrder($order) ? 'سبد خرید' : 'آبشده';
|
||
}
|
||
|
||
private function isCartOrder(Order $order): bool
|
||
{
|
||
if (Schema::hasTable('cart') && Schema::hasColumn('cart', 'order_id')) {
|
||
return Cart::where('order_id', $order->id)->exists();
|
||
}
|
||
|
||
return PargarSyncLog::where('order_id', $order->id)
|
||
->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 = abs((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>0 → سند تعدادی (وزن را نادیده میگیرد)
|
||
// آبشده/وزنی مثل آذین: Count=0 و Weight=گرم
|
||
$count = 0;
|
||
$weight = $amount;
|
||
if ($isCountBased) {
|
||
$count = $number > 0 ? max(1, (int) round($number)) : 1;
|
||
$weight = $amount;
|
||
}
|
||
|
||
$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);
|
||
// فقط محصولات main_wallet_products روی GoldInv آبشده اثر دارند.
|
||
// group_type=آبشده بهتنهایی کافی نیست (مثلاً آبشده سرمایهگذاری / قلک).
|
||
if ($tradedProductId > 0) {
|
||
$tradesMainGold = app(ConfigService::class)->isMainWalletProduct($tradedProductId);
|
||
} else {
|
||
$tradesMainGold = $product && self::isMeltedTradeGroup($product->group_type ?? null);
|
||
}
|
||
|
||
// اثر مانده از روش پرداخت؛ وزن طلا فقط وقتی کالای معامله به کیف اصلی مربوط است
|
||
if ($paymentKind === PargarSyncService::PAYMENT_CASH) {
|
||
if ($tradesMainGold) {
|
||
// خرید/فروش آبشده کیفاصلی با نقدی: وزن + مبلغ — حتماً تعدادی نباشد
|
||
$itemId = $sync->pargarItemIdGold();
|
||
$count = 0;
|
||
$weight = $amount;
|
||
} else {
|
||
// محصول غیر اصلی با پرداخت نقدی: فقط ریال (بدون وزن طلای پرگار)
|
||
$rialItemId = $sync->pargarItemIdRial();
|
||
$itemId = $rialItemId > 0 ? $rialItemId : $sync->pargarItemIdGold();
|
||
$weight = 0;
|
||
$count = 0;
|
||
}
|
||
} elseif ($paymentKind === PargarSyncService::PAYMENT_MAIN_GOLD) {
|
||
$itemId = $sync->pargarItemIdGold();
|
||
$itemName = $sync->mainWalletGoldItemName();
|
||
$paymentGold = abs((float) ($payload['payment_gold'] ?? $amount));
|
||
$weight = $paymentGold;
|
||
$count = 0;
|
||
$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 = -1
|
||
// واریز ریالی → باید ریال بستانکار شود → OrderType = 1
|
||
$orderType = $kind === 'gold'
|
||
? ($isDeposit ? -1 : 1)
|
||
: ($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' => 0,
|
||
'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' => 0,
|
||
'Weight' => 0,
|
||
'Price' => abs($signed),
|
||
'Carat' => 750,
|
||
'Description' => $isDeposit ? 'واریز ریالی' : 'برداشت ریالی',
|
||
];
|
||
}
|
||
}
|