diff --git a/app/Http/Controllers/Admin/TahesabController.php b/app/Http/Controllers/Admin/TahesabController.php index 24c20a8..caadd3c 100644 --- a/app/Http/Controllers/Admin/TahesabController.php +++ b/app/Http/Controllers/Admin/TahesabController.php @@ -45,6 +45,7 @@ class TahesabController extends Controller public function table(Request $request) { + app(TahesabSyncService::class)->ensureLocalTable(); if (! Schema::hasTable('tahesab')) { return response()->json([]); } diff --git a/app/Services/TahesabSyncService.php b/app/Services/TahesabSyncService.php index 2bf055d..d77e5cf 100644 --- a/app/Services/TahesabSyncService.php +++ b/app/Services/TahesabSyncService.php @@ -5,6 +5,7 @@ namespace App\Services; use App\Http\Controllers\Admin\TahesabController; use App\Models\Customer; use App\Models\Tahesab; +use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; @@ -222,13 +223,12 @@ class TahesabSyncService */ public function pendingSummary(?int $customerId = null): array { - if (! Schema::hasTable('tahesab')) { - return $this->emptyPendingPayload(); - } - - $failedRows = (int) $this->unsyncedRowsQuery() - ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) - ->count(); + $hasTable = $this->ensureLocalTable(); + $failedRows = $hasTable + ? (int) $this->unsyncedRowsQuery() + ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) + ->count() + : 0; $missingWithdraws = count($this->missingWithdrawIds(200, $customerId)); $missingOrders = count($this->missingOrderIds(200, $customerId)); @@ -248,16 +248,12 @@ class TahesabSyncService */ public function pendingTotals(?int $customerId = null): array { - if (! Schema::hasTable('tahesab')) { - $empty = $this->emptyPendingPayload(); - unset($empty['doc_sync']); - - return $empty; - } - - $failedRows = (int) $this->unsyncedRowsQuery() - ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) - ->count(); + $hasTable = $this->ensureLocalTable(); + $failedRows = $hasTable + ? (int) $this->unsyncedRowsQuery() + ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) + ->count() + : 0; $missingWithdraws = $this->countMissingWithdraws($customerId); $missingOrders = $this->countMissingOrders($customerId); @@ -275,42 +271,46 @@ class TahesabSyncService */ public function peekNextPending(array $skip = []): ?array { - if (! Schema::hasTable('tahesab')) { - return null; - } - - $skipRow = array_values(array_filter($skip['row'] ?? [])); - $row = $this->unsyncedRowsQuery() - ->when($skipRow !== [], fn ($q) => $q->whereNotIn('id', $skipRow)) - ->orderBy('id') - ->first(); - if ($row) { - return [ - 'type' => 'row', - 'id' => (int) $row->id, - 'label' => 'سند محلی #'.$row->id, - ]; + if ($this->ensureLocalTable()) { + $skipRow = array_values(array_filter($skip['row'] ?? [])); + $row = $this->unsyncedRowsQuery() + ->with('customer') + ->when($skipRow !== [], fn ($q) => $q->whereNotIn('id', $skipRow)) + ->orderBy('id') + ->first(); + if ($row) { + $name = optional($row->customer)->name; + return [ + 'type' => 'row', + 'id' => (int) $row->id, + 'label' => trim('سند محلی #'.$row->id.($name ? ' — '.$name : '')), + ]; + } } $wIds = $this->missingWithdrawIds(1, null, array_values(array_filter($skip['withdraw'] ?? []))); if ($wIds !== []) { $id = (int) $wIds[0]; + $w = \App\Models\Withdraw::query()->with('customer')->find($id); + $name = optional(optional($w)->customer)->name; return [ 'type' => 'withdraw', 'id' => $id, - 'label' => 'شارژ/برداشت #'.$id, + 'label' => trim('شارژ/برداشت #'.$id.($name ? ' — '.$name : '')), ]; } $oIds = $this->missingOrderIds(1, null, array_values(array_filter($skip['order'] ?? []))); if ($oIds !== []) { $id = (int) $oIds[0]; + $order = \App\Models\Order::query()->with('customer')->find($id); + $name = optional(optional($order)->customer)->name; return [ 'type' => 'order', 'id' => $id, - 'label' => 'سفارش #'.$id, + 'label' => trim('سفارش #'.$id.($name ? ' — '.$name : '')), ]; } @@ -324,25 +324,23 @@ class TahesabSyncService */ public function pendingDocs(?int $customerId = null, int $limit = 200): array { - if (! Schema::hasTable('tahesab')) { - return []; - } - $limit = max(1, min(500, $limit)); $rows = []; - $local = $this->unsyncedRowsQuery() - ->with('customer') - ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) - ->orderByDesc('id') - ->limit($limit) - ->get(); + if ($this->ensureLocalTable()) { + $local = $this->unsyncedRowsQuery() + ->with('customer') + ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) + ->orderByDesc('id') + ->limit($limit) + ->get(); - foreach ($local as $row) { - $arr = $row->toArray(); - $arr['issue_kind'] = 'failed'; - $arr['sync_target'] = ['type' => 'row', 'id' => (int) $row->id]; - $rows[] = $arr; + foreach ($local as $row) { + $arr = $row->toArray(); + $arr['issue_kind'] = 'failed'; + $arr['sync_target'] = ['type' => 'row', 'id' => (int) $row->id]; + $rows[] = $arr; + } } $remaining = $limit - count($rows); @@ -575,11 +573,11 @@ class TahesabSyncService private function missingWithdrawQuery(?int $customerId = null, array $exclude = []) { - if (! Schema::hasTable('withdraws') || ! Schema::hasTable('tahesab')) { + if (! Schema::hasTable('withdraws')) { return null; } - return \App\Models\Withdraw::query() + $query = \App\Models\Withdraw::query() ->where('sts', 4) ->where(function ($q) { $q->where('cash', '!=', 0)->orWhere('gold', '!=', 0); @@ -589,10 +587,15 @@ class TahesabSyncService ->orWhere('note', 'not like', 'fix:intval-gold-loss%'); }) ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) - ->when($exclude !== [], fn ($q) => $q->whereNotIn('id', $exclude)) - ->whereNotExists(function ($sub) { + ->when($exclude !== [], fn ($q) => $q->whereNotIn('id', $exclude)); + + if (Schema::hasTable('tahesab')) { + $query->whereNotExists(function ($sub) { $sub->from('tahesab')->selectRaw('1')->whereColumn('tahesab.withdraw_id', 'withdraws.id'); }); + } + + return $query; } /** @return list */ @@ -620,19 +623,24 @@ class TahesabSyncService private function missingOrderQuery(?int $customerId = null, array $exclude = []) { - if (! Schema::hasTable('orders') || ! Schema::hasTable('tahesab')) { + if (! Schema::hasTable('orders')) { return null; } - return \App\Models\Order::query() + $query = \App\Models\Order::query() ->where('sts', 2) ->where('payment_sts', 4) ->whereIn('direction', [1, 2]) ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) - ->when($exclude !== [], fn ($q) => $q->whereNotIn('id', $exclude)) - ->whereNotExists(function ($sub) { + ->when($exclude !== [], fn ($q) => $q->whereNotIn('id', $exclude)); + + if (Schema::hasTable('tahesab')) { + $query->whereNotExists(function ($sub) { $sub->from('tahesab')->selectRaw('1')->whereColumn('tahesab.order_id', 'orders.id'); }); + } + + return $query; } private function backfillWithdraw(TahesabController $controller, int $withdrawId): bool @@ -705,15 +713,37 @@ class TahesabSyncService return $result !== false; } - private function emptyPendingPayload(): array + public function ensureLocalTable(): bool { - return [ - 'failed_rows' => 0, - 'missing_withdraws' => 0, - 'missing_orders' => 0, - 'total' => 0, - 'doc_sync' => app(TahesabDocSyncService::class)->snapshot(), - ]; + if (Schema::hasTable('tahesab')) { + return true; + } + + try { + Schema::create('tahesab', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('customer_id')->nullable()->index(); + $table->unsignedBigInteger('order_id')->nullable()->index(); + $table->unsignedBigInteger('withdraw_id')->nullable()->index(); + $table->decimal('amount', 18, 4)->nullable(); + $table->string('desc')->nullable(); + $table->string('type', 32)->nullable(); + $table->timestamp('date')->nullable(); + $table->string('tahesab_id')->nullable()->index(); + $table->string('sh_factor')->nullable(); + $table->string('factor_number')->nullable(); + $table->string('tah_id')->nullable(); + $table->longText('data')->nullable(); + $table->longText('res')->nullable(); + $table->string('sync_status', 20)->default('pending'); + $table->text('sync_log')->nullable(); + $table->timestamps(); + }); + } catch (\Throwable $e) { + Log::warning('Could not create tahesab table', ['error' => $e->getMessage()]); + } + + return Schema::hasTable('tahesab'); } private function hasSyncColumns(): bool diff --git a/database/migrations/2026_09_02_000003_create_tahesab_table.php b/database/migrations/2026_09_02_000003_create_tahesab_table.php new file mode 100644 index 0000000..715b012 --- /dev/null +++ b/database/migrations/2026_09_02_000003_create_tahesab_table.php @@ -0,0 +1,40 @@ +id(); + $table->unsignedBigInteger('customer_id')->nullable()->index(); + $table->unsignedBigInteger('order_id')->nullable()->index(); + $table->unsignedBigInteger('withdraw_id')->nullable()->index(); + $table->decimal('amount', 18, 4)->nullable(); + $table->string('desc')->nullable(); + $table->string('type', 32)->nullable(); + $table->timestamp('date')->nullable(); + $table->string('tahesab_id')->nullable()->index(); + $table->string('sh_factor')->nullable(); + $table->string('factor_number')->nullable(); + $table->string('tah_id')->nullable(); + $table->longText('data')->nullable(); + $table->longText('res')->nullable(); + $table->string('sync_status', 20)->default('pending'); + $table->text('sync_log')->nullable(); + $table->timestamps(); + }); + } + + public function down(): void + { + // جدول ممکن است از قبل روی نصب‌های قدیمی بوده باشد؛ drop نمی‌کنیم. + } +};