Apply Tahesab account balances to wallets with admin overwrite control.
Add inbound mande sync on customer import plus per-customer and bulk wallet-sync endpoints so previously linked accounts can catch up. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -11,6 +11,8 @@ use Illuminate\Support\Facades\Cache;
|
||||
use App\Models\Tahesab;
|
||||
use App\Models\Customer;
|
||||
use App\Services\AccountingProductDocMapper;
|
||||
use App\Services\ConfigService;
|
||||
use App\Services\TahesabInboundWalletService;
|
||||
use App\Services\TahesabSyncService;
|
||||
use Morilog\Jalali\Jalalian;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
@@ -237,6 +239,8 @@ class TahesabController extends Controller
|
||||
$success = 0;
|
||||
$failed = 0;
|
||||
$errors = [];
|
||||
/** @var list<Customer> $importedForWallet */
|
||||
$importedForWallet = [];
|
||||
|
||||
foreach ($validated['customers'] as $item) {
|
||||
$mobile = Customer::normalizeMobile($item['tel'] ?? '') ?? '';
|
||||
@@ -282,6 +286,7 @@ class TahesabController extends Controller
|
||||
if ($changed) {
|
||||
$existing->save();
|
||||
}
|
||||
$importedForWallet[] = $existing;
|
||||
$success++;
|
||||
continue;
|
||||
}
|
||||
@@ -300,6 +305,7 @@ class TahesabController extends Controller
|
||||
$byMobile->password = $mobile;
|
||||
}
|
||||
$byMobile->save();
|
||||
$importedForWallet[] = $byMobile;
|
||||
$success++;
|
||||
continue;
|
||||
}
|
||||
@@ -340,6 +346,7 @@ class TahesabController extends Controller
|
||||
$customer->password = $mobile;
|
||||
}
|
||||
$customer->save();
|
||||
$importedForWallet[] = $customer;
|
||||
$success++;
|
||||
} catch (\Throwable $e) {
|
||||
$failed++;
|
||||
@@ -353,6 +360,18 @@ class TahesabController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// مانده طلا/ریال تهحساب را روی کیف مشتریان واردشده بنشان
|
||||
if ($importedForWallet !== []) {
|
||||
try {
|
||||
app(TahesabInboundWalletService::class)->syncAfterImport($importedForWallet);
|
||||
} catch (\Throwable $e) {
|
||||
\Log::warning('Tahesab import wallet sync batch failed', [
|
||||
'count' => count($importedForWallet),
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'ok' => true,
|
||||
'success' => $success,
|
||||
@@ -452,6 +471,161 @@ class TahesabController extends Controller
|
||||
return response()->json($result, ($result['ok'] ?? false) ? 200 : 422);
|
||||
}
|
||||
|
||||
/**
|
||||
* همگامسازی مانده یک مشتری با تهحساب.
|
||||
* body: overwrite=1 | keep=1 | ask_if_conflict=1 (پیشفرض ask برای جلوگیری از overwrite ناخواسته)
|
||||
*/
|
||||
public function syncCustomerWallet(Request $request, $customerId)
|
||||
{
|
||||
if (! app(ConfigService::class)->isTahesabAccounting()) {
|
||||
return response()->json([
|
||||
'ok' => false,
|
||||
'message' => 'حسابداری تهحساب فعال نیست',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$customer = Customer::findOrFail($customerId);
|
||||
if ((int) ($customer->moshtari_code ?? 0) <= 0) {
|
||||
return response()->json([
|
||||
'ok' => false,
|
||||
'message' => 'این مشتری کد تهحساب ندارد',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$overwrite = $request->boolean('overwrite');
|
||||
$keep = $request->boolean('keep');
|
||||
// اگر صراحتاً overwrite/keep نباشد → از ادمین بپرس وقتی تداخل هست
|
||||
$askIfConflict = $request->boolean('ask_if_conflict', ! $overwrite && ! $keep);
|
||||
|
||||
$result = app(TahesabInboundWalletService::class)->syncWalletFromTahesab(
|
||||
$customer,
|
||||
null,
|
||||
[
|
||||
'overwrite' => $overwrite,
|
||||
'keep' => $keep,
|
||||
'ask_if_conflict' => $askIfConflict,
|
||||
]
|
||||
);
|
||||
|
||||
$needsDecision = (bool) ($result['needs_decision'] ?? false);
|
||||
$applied = (bool) ($result['applied'] ?? false);
|
||||
$skipped = (bool) ($result['skipped'] ?? false);
|
||||
$same = ! $applied && ! $needsDecision && ! $skipped
|
||||
&& str_contains((string) ($result['message'] ?? ''), 'یکسان');
|
||||
|
||||
return response()->json([
|
||||
'ok' => $applied || $skipped || $same,
|
||||
'needs_decision' => $needsDecision,
|
||||
'message' => $result['message'] ?? '',
|
||||
'result' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* همگامسازی مانده همه مشتریان دارای کد تهحساب — تکهتکه.
|
||||
* body: overwrite=1 برای اعمال اجباری اختلافها؛ بدون آن فقط کیف خالی یا یکسان پردازش میشود و تداخلها در needs_decision میآیند.
|
||||
*/
|
||||
public function syncAllWallets(Request $request)
|
||||
{
|
||||
if (! app(ConfigService::class)->isTahesabAccounting()) {
|
||||
return response()->json([
|
||||
'ok' => false,
|
||||
'message' => 'حسابداری تهحساب فعال نیست',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$limit = max(1, min(50, (int) $request->input('limit', 20)));
|
||||
$offset = max(0, (int) $request->input('offset', 0));
|
||||
$overwrite = $request->boolean('overwrite');
|
||||
|
||||
$base = Customer::query()
|
||||
->whereNull('deleted_at')
|
||||
->whereNotNull('moshtari_code')
|
||||
->where('moshtari_code', '!=', '')
|
||||
->where('moshtari_code', '!=', '0');
|
||||
|
||||
$total = (clone $base)->count();
|
||||
$customers = (clone $base)
|
||||
->orderBy('id')
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->get(['id', 'name', 'mobile', 'moshtari_code', 'type']);
|
||||
|
||||
$walletService = app(TahesabInboundWalletService::class);
|
||||
$codes = $customers->pluck('moshtari_code')->all();
|
||||
$mandes = $walletService->fetchMandeByCodes($codes);
|
||||
|
||||
$success = 0;
|
||||
$changed = 0;
|
||||
$needsDecision = 0;
|
||||
$failed = 0;
|
||||
$errors = [];
|
||||
$decisions = [];
|
||||
|
||||
foreach ($customers as $customer) {
|
||||
try {
|
||||
$code = (string) ((int) $customer->moshtari_code);
|
||||
$result = $walletService->syncWalletFromTahesab(
|
||||
$customer,
|
||||
$mandes[$code] ?? null,
|
||||
[
|
||||
'overwrite' => $overwrite,
|
||||
'ask_if_conflict' => ! $overwrite,
|
||||
]
|
||||
);
|
||||
|
||||
if ($result['needs_decision'] ?? false) {
|
||||
$needsDecision++;
|
||||
$decisions[] = [
|
||||
'id' => $customer->id,
|
||||
'name' => $customer->name,
|
||||
'mobile' => $customer->mobile,
|
||||
'app_gold' => $result['app_gold'] ?? null,
|
||||
'app_cash' => $result['app_cash'] ?? null,
|
||||
'tahesab_gold' => $result['tahesab_gold'] ?? null,
|
||||
'tahesab_cash' => $result['tahesab_cash'] ?? null,
|
||||
'message' => $result['message'] ?? '',
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$success++;
|
||||
if ($result['applied'] ?? false) {
|
||||
$changed++;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$failed++;
|
||||
$errors[] = [
|
||||
'id' => $customer->id,
|
||||
'name' => $customer->name,
|
||||
'mobile' => $customer->mobile,
|
||||
'error' => $e->getMessage() !== '' ? $e->getMessage() : 'خطای ناشناخته',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$processed = $customers->count();
|
||||
$nextOffset = $offset + $processed;
|
||||
|
||||
return response()->json([
|
||||
'ok' => true,
|
||||
'message' => $total === 0
|
||||
? 'مشتری دارای کد تهحساب یافت نشد'
|
||||
: "دستهٔ {$offset} تا {$nextOffset} از {$total}",
|
||||
'total' => $total,
|
||||
'offset' => $offset,
|
||||
'processed' => $processed,
|
||||
'next_offset' => $nextOffset,
|
||||
'done' => $nextOffset >= $total,
|
||||
'success' => $success,
|
||||
'changed' => $changed,
|
||||
'needs_decision' => $needsDecision,
|
||||
'failed' => $failed,
|
||||
'errors' => $errors,
|
||||
'decisions' => $decisions,
|
||||
]);
|
||||
}
|
||||
|
||||
private function tahesabApiUrl(): string
|
||||
{
|
||||
$host = trim((string) $this->Config('tahesab_host'));
|
||||
|
||||
@@ -184,6 +184,9 @@ class WalletController extends Controller
|
||||
if ((int) ($record->type ?? 0) === 22) {
|
||||
$record->title = 'تعدیل حسابداری پرگار';
|
||||
$record->unit = 'گرم';
|
||||
} elseif ((int) ($record->type ?? 0) === 23) {
|
||||
$record->title = 'تعدیل حسابداری تهحساب';
|
||||
$record->unit = 'گرم';
|
||||
} elseif ((int) ($record->type ?? 0) === 12) {
|
||||
$record->title = 'برداشت وجه نقد';
|
||||
$record->unit = 'ریال';
|
||||
@@ -883,6 +886,10 @@ class WalletController extends Controller
|
||||
return 'تعدیل حسابداری پرگار';
|
||||
}
|
||||
|
||||
if ((int) $record->type === 23) {
|
||||
return 'تعدیل حسابداری تهحساب';
|
||||
}
|
||||
|
||||
return $record->type_str;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Customer;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||||
use Rooyesh\Wallet\interfaces\WalletInterface;
|
||||
|
||||
/**
|
||||
* ماندهٔ طلا/ریال تهحساب → کیف اپ.
|
||||
*
|
||||
* منبع: GetMandeHesabByCode
|
||||
* MandeyeKolBeTALA → طلای کیف اصلی
|
||||
* MandeyeKolBePool → موجودی ریالی
|
||||
*
|
||||
* سیاست تصمیم ادمین:
|
||||
* overwrite=true → اختلاف را اعمال کن
|
||||
* keep=true → موجودی اپ را دست نزن
|
||||
* ask_if_conflict → اگر اپ مانده دارد و با تهحساب فرق دارد، بدون نوشتن برگردان
|
||||
*/
|
||||
class TahesabInboundWalletService
|
||||
{
|
||||
public const TX_TYPE_TAHESAB = 23;
|
||||
|
||||
private const BATCH_SIZE = 40;
|
||||
|
||||
private const EPSILON_GOLD = 0.0005;
|
||||
|
||||
private const EPSILON_CASH = 1.0;
|
||||
|
||||
public function __construct(
|
||||
private readonly ConfigService $config,
|
||||
private readonly WalletBroadcastService $walletBroadcast,
|
||||
private readonly WalletBalanceService $walletBalance,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<int|string> $codes
|
||||
* @return array<string, array{gold: float, cash: float, raw: array}>
|
||||
*/
|
||||
public function fetchMandeByCodes(array $codes): array
|
||||
{
|
||||
$normalized = [];
|
||||
foreach ($codes as $code) {
|
||||
$c = (int) $code;
|
||||
if ($c > 0) {
|
||||
$normalized[(string) $c] = $c;
|
||||
}
|
||||
}
|
||||
if ($normalized === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach (array_chunk(array_values($normalized), self::BATCH_SIZE) as $chunk) {
|
||||
$batch = $this->requestMandeChunk($chunk);
|
||||
foreach ($batch as $code => $row) {
|
||||
$out[(string) $code] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{gold?: float, cash?: float}|null $mande
|
||||
* @param array{overwrite?: bool, keep?: bool, ask_if_conflict?: bool} $options
|
||||
* @return array{
|
||||
* applied: bool,
|
||||
* needs_decision?: bool,
|
||||
* skipped?: bool,
|
||||
* gold_delta?: float,
|
||||
* rial_delta?: float,
|
||||
* app_gold?: float,
|
||||
* app_cash?: float,
|
||||
* tahesab_gold?: float,
|
||||
* tahesab_cash?: float,
|
||||
* message: string
|
||||
* }
|
||||
*/
|
||||
public function syncWalletFromTahesab(Customer $customer, ?array $mande = null, array $options = []): array
|
||||
{
|
||||
$overwrite = (bool) ($options['overwrite'] ?? false);
|
||||
$keep = (bool) ($options['keep'] ?? false);
|
||||
$askIfConflict = (bool) ($options['ask_if_conflict'] ?? false);
|
||||
|
||||
$code = (int) ($customer->moshtari_code ?? 0);
|
||||
if ($code <= 0) {
|
||||
return ['applied' => false, 'message' => 'کد تهحساب ندارد'];
|
||||
}
|
||||
|
||||
if ($mande === null) {
|
||||
$all = $this->fetchMandeByCodes([$code]);
|
||||
$mande = $all[(string) $code] ?? null;
|
||||
}
|
||||
|
||||
if ($mande === null) {
|
||||
return ['applied' => false, 'message' => 'مانده از تهحساب دریافت نشد'];
|
||||
}
|
||||
|
||||
$goldInv = (float) ($mande['gold'] ?? 0);
|
||||
$rialInv = (float) ($mande['cash'] ?? 0);
|
||||
|
||||
$ownerType = (int) ($customer->type ?? 2);
|
||||
$walletInterface = new WalletInterface((int) $customer->id, $ownerType);
|
||||
$wallet = $walletInterface->wallet;
|
||||
|
||||
$mainGold = $this->walletBalance
|
||||
->accountingVisibleMainWalletGold((int) $customer->id, $ownerType);
|
||||
$mainCash = (float) ($wallet->cash ?? 0);
|
||||
|
||||
$goldDiff = $goldInv - $mainGold;
|
||||
$rialDiff = $rialInv - $mainCash;
|
||||
|
||||
$goldSame = abs($goldDiff) < self::EPSILON_GOLD;
|
||||
$rialSame = abs($rialDiff) < self::EPSILON_CASH;
|
||||
|
||||
$comparison = [
|
||||
'app_gold' => $mainGold,
|
||||
'app_cash' => $mainCash,
|
||||
'tahesab_gold' => $goldInv,
|
||||
'tahesab_cash' => $rialInv,
|
||||
'gold_delta' => $goldDiff,
|
||||
'rial_delta' => $rialDiff,
|
||||
];
|
||||
|
||||
if ($goldSame && $rialSame) {
|
||||
return array_merge($comparison, [
|
||||
'applied' => false,
|
||||
'message' => 'مانده با تهحساب یکسان است',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($keep) {
|
||||
return array_merge($comparison, [
|
||||
'applied' => false,
|
||||
'skipped' => true,
|
||||
'message' => 'موجودی اپ حفظ شد — مانده تهحساب اعمال نشد',
|
||||
]);
|
||||
}
|
||||
|
||||
$appHasBalance = abs($mainGold) >= self::EPSILON_GOLD
|
||||
|| abs($mainCash) >= self::EPSILON_CASH;
|
||||
|
||||
// اگر اپ مانده دارد و با تهحساب فرق دارد و ادمین هنوز تصمیم نگرفته
|
||||
if ($askIfConflict && ! $overwrite && $appHasBalance) {
|
||||
return array_merge($comparison, [
|
||||
'applied' => false,
|
||||
'needs_decision' => true,
|
||||
'message' => 'موجودی اپ با تهحساب فرق دارد — تصمیم ادمین لازم است',
|
||||
]);
|
||||
}
|
||||
|
||||
$input = [
|
||||
'title' => 'تطبیق ماندهٔ تهحساب',
|
||||
'note' => 'tahesab-mande:'.$code,
|
||||
'expire_at' => now(),
|
||||
'active' => true,
|
||||
];
|
||||
if (! $goldSame) {
|
||||
$input['gold'] = abs($goldDiff);
|
||||
}
|
||||
if (! $rialSame) {
|
||||
$input['cash'] = (int) round(abs($rialDiff));
|
||||
}
|
||||
|
||||
new TransactionInterface(
|
||||
$walletInterface,
|
||||
$input,
|
||||
self::TX_TYPE_TAHESAB,
|
||||
$goldDiff > 0,
|
||||
$rialDiff > 0,
|
||||
0,
|
||||
[],
|
||||
true
|
||||
);
|
||||
|
||||
$this->walletBroadcast->broadcastForCustomer(
|
||||
(int) $customer->id,
|
||||
$ownerType
|
||||
);
|
||||
|
||||
Log::info('Tahesab mande applied to wallet', [
|
||||
'customer_id' => (int) $customer->id,
|
||||
'moshtari_code' => $code,
|
||||
'gold_target' => $goldInv,
|
||||
'cash_target' => $rialInv,
|
||||
'gold_delta' => $goldDiff,
|
||||
'rial_delta' => $rialDiff,
|
||||
'overwrite' => $overwrite,
|
||||
]);
|
||||
|
||||
return array_merge($comparison, [
|
||||
'applied' => true,
|
||||
'message' => 'مانده با تهحساب همتراز شد',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* بعد از import: اگر کیف خالی باشد مانده را بنشان؛ اگر مانده دارد صبر کن تا ادمین تصمیم بگیرد.
|
||||
*
|
||||
* @param list<Customer> $customers
|
||||
*/
|
||||
public function syncAfterImport(array $customers): void
|
||||
{
|
||||
$customers = array_values(array_filter($customers, fn ($c) => $c instanceof Customer));
|
||||
if ($customers === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$codes = [];
|
||||
foreach ($customers as $customer) {
|
||||
$code = (int) ($customer->moshtari_code ?? 0);
|
||||
if ($code > 0) {
|
||||
$codes[] = $code;
|
||||
}
|
||||
}
|
||||
|
||||
$mandes = $this->fetchMandeByCodes($codes);
|
||||
|
||||
foreach ($customers as $customer) {
|
||||
try {
|
||||
$code = (string) ((int) ($customer->moshtari_code ?? 0));
|
||||
$this->syncWalletFromTahesab(
|
||||
$customer,
|
||||
$mandes[$code] ?? null,
|
||||
['ask_if_conflict' => true]
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Tahesab wallet sync after import failed', [
|
||||
'customer_id' => $customer->id,
|
||||
'moshtari_code' => $customer->moshtari_code,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<int> $codes
|
||||
* @return array<string, array{gold: float, cash: float, raw: array}>
|
||||
*/
|
||||
private function requestMandeChunk(array $codes): array
|
||||
{
|
||||
$url = $this->apiUrl();
|
||||
if ($url === '' || $codes === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::withoutVerifying()->timeout(45)->withHeaders([
|
||||
'DBName' => $this->config->getString('tahesab_db', ''),
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Bearer '.$this->config->getString('tahesab_token', ''),
|
||||
])->post($url, [
|
||||
'GetMandeHesabByCode' => array_map('strval', $codes),
|
||||
])->json();
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Tahesab GetMandeHesabByCode failed', [
|
||||
'codes' => $codes,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
if (! is_array($response)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $response['MandeHesab'] ?? null;
|
||||
if (! is_array($rows)) {
|
||||
if ($this->looksLikeMandeRow($response) && count($codes) === 1) {
|
||||
$rows = [array_merge($response, ['Code' => (string) $codes[0]])];
|
||||
} else {
|
||||
$rows = array_is_list($response) ? $response : array_values($response);
|
||||
}
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach ($rows as $row) {
|
||||
if (! is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$code = (string) ($row['Code'] ?? $row['code'] ?? '');
|
||||
if ($code === '' || $code === '0') {
|
||||
continue;
|
||||
}
|
||||
$parsed = $this->parseMandeRow($row);
|
||||
if ($parsed !== null) {
|
||||
$out[$code] = $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
if ($out === [] && count($codes) === 1 && $this->looksLikeMandeRow($response)) {
|
||||
$parsed = $this->parseMandeRow($response);
|
||||
if ($parsed !== null) {
|
||||
$out[(string) $codes[0]] = $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @return array{gold: float, cash: float, raw: array}|null
|
||||
*/
|
||||
private function parseMandeRow(array $row): ?array
|
||||
{
|
||||
$gold = $this->pickNumber($row, [
|
||||
'MandeyeKolBeTALA',
|
||||
'MandeyeKolBeTala',
|
||||
'MandeyeVazni',
|
||||
'MandeMenhayeSekeBeTala',
|
||||
'GoldInv',
|
||||
]);
|
||||
$cash = $this->pickNumber($row, [
|
||||
'MandeyeKolBePool',
|
||||
'MandeyeMali',
|
||||
'MandeMenhayeSekeBePool',
|
||||
'RialInv',
|
||||
]);
|
||||
|
||||
$hasAny = false;
|
||||
foreach ([
|
||||
'MandeyeKolBeTALA', 'MandeyeKolBeTala', 'MandeyeVazni', 'MandeyeKolBePool', 'MandeyeMali',
|
||||
] as $key) {
|
||||
if (array_key_exists($key, $row)) {
|
||||
$hasAny = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! $hasAny) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'gold' => $gold,
|
||||
'cash' => $cash,
|
||||
'raw' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @param list<string> $keys
|
||||
*/
|
||||
private function pickNumber(array $row, array $keys): float
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (! array_key_exists($key, $row) || $row[$key] === null || $row[$key] === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->parseNumber($row[$key]);
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
private function parseNumber(mixed $value): float
|
||||
{
|
||||
if (is_int($value) || is_float($value)) {
|
||||
return (float) $value;
|
||||
}
|
||||
$s = str_replace([',', ' ', '٫', '/'], ['', '', '.', '.'], trim((string) $value));
|
||||
|
||||
return is_numeric($s) ? (float) $s : 0.0;
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $row */
|
||||
private function looksLikeMandeRow(array $row): bool
|
||||
{
|
||||
foreach (['MandeyeKolBeTALA', 'MandeyeKolBeTala', 'MandeyeVazni', 'MandeyeKolBePool', 'MandeyeMali'] as $key) {
|
||||
if (array_key_exists($key, $row)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function apiUrl(): string
|
||||
{
|
||||
$host = trim($this->config->getString('tahesab_host', ''));
|
||||
$port = trim($this->config->getString('tahesab_port', ''));
|
||||
|
||||
if ($host !== '' && $port !== '') {
|
||||
$host = rtrim(preg_replace('#^https?://#i', '', $host), '/');
|
||||
|
||||
return 'httpS://'.$host.':'.$port.'/';
|
||||
}
|
||||
|
||||
$legacy = trim((string) env('TAHESAB', ''));
|
||||
|
||||
return $legacy !== '' ? $legacy : '';
|
||||
}
|
||||
}
|
||||
@@ -251,9 +251,11 @@ Route::middleware('auth:user')->group(function (){
|
||||
Route::post('tahesab/customer-sync/panel-to-tahesab', [TahesabController::class, 'syncPanelToTahesab']);
|
||||
Route::post('tahesab/customer-sync/import-from-tahesab', [TahesabController::class, 'importFromTahesab']);
|
||||
Route::post('tahesab/customer-sync/rollback-import', [TahesabController::class, 'rollbackImport']);
|
||||
Route::post('tahesab/wallet-sync/all', [TahesabController::class, 'syncAllWallets']);
|
||||
Route::get('tahesab/{id}', [TahesabController::class, 'show']);
|
||||
Route::post('tahesab/{id}/retry', [TahesabController::class, 'retrySanad']);
|
||||
Route::post('customer/{id}/tahesab/register', [TahesabController::class, 'registerCustomer']);
|
||||
Route::post('customer/{id}/tahesab/sync-wallet', [TahesabController::class, 'syncCustomerWallet']);
|
||||
|
||||
Route::get('zarin/health', [ZarinController::class, 'healthCheck']);
|
||||
Route::post('wallet/zarin', [ZarinController::class, 'wallet']);
|
||||
|
||||
Reference in New Issue
Block a user