fix(tahesab): show missing docs and create local table if absent
Pending list was empty because a missing tahesab table hid unsynced orders and charges. Create the table when needed and keep customer on each row. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -45,6 +45,7 @@ class TahesabController extends Controller
|
|||||||
|
|
||||||
public function table(Request $request)
|
public function table(Request $request)
|
||||||
{
|
{
|
||||||
|
app(TahesabSyncService::class)->ensureLocalTable();
|
||||||
if (! Schema::hasTable('tahesab')) {
|
if (! Schema::hasTable('tahesab')) {
|
||||||
return response()->json([]);
|
return response()->json([]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Services;
|
|||||||
use App\Http\Controllers\Admin\TahesabController;
|
use App\Http\Controllers\Admin\TahesabController;
|
||||||
use App\Models\Customer;
|
use App\Models\Customer;
|
||||||
use App\Models\Tahesab;
|
use App\Models\Tahesab;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@@ -222,13 +223,12 @@ class TahesabSyncService
|
|||||||
*/
|
*/
|
||||||
public function pendingSummary(?int $customerId = null): array
|
public function pendingSummary(?int $customerId = null): array
|
||||||
{
|
{
|
||||||
if (! Schema::hasTable('tahesab')) {
|
$hasTable = $this->ensureLocalTable();
|
||||||
return $this->emptyPendingPayload();
|
$failedRows = $hasTable
|
||||||
}
|
? (int) $this->unsyncedRowsQuery()
|
||||||
|
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
||||||
$failedRows = (int) $this->unsyncedRowsQuery()
|
->count()
|
||||||
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
: 0;
|
||||||
->count();
|
|
||||||
$missingWithdraws = count($this->missingWithdrawIds(200, $customerId));
|
$missingWithdraws = count($this->missingWithdrawIds(200, $customerId));
|
||||||
$missingOrders = count($this->missingOrderIds(200, $customerId));
|
$missingOrders = count($this->missingOrderIds(200, $customerId));
|
||||||
|
|
||||||
@@ -248,16 +248,12 @@ class TahesabSyncService
|
|||||||
*/
|
*/
|
||||||
public function pendingTotals(?int $customerId = null): array
|
public function pendingTotals(?int $customerId = null): array
|
||||||
{
|
{
|
||||||
if (! Schema::hasTable('tahesab')) {
|
$hasTable = $this->ensureLocalTable();
|
||||||
$empty = $this->emptyPendingPayload();
|
$failedRows = $hasTable
|
||||||
unset($empty['doc_sync']);
|
? (int) $this->unsyncedRowsQuery()
|
||||||
|
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
||||||
return $empty;
|
->count()
|
||||||
}
|
: 0;
|
||||||
|
|
||||||
$failedRows = (int) $this->unsyncedRowsQuery()
|
|
||||||
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
|
||||||
->count();
|
|
||||||
$missingWithdraws = $this->countMissingWithdraws($customerId);
|
$missingWithdraws = $this->countMissingWithdraws($customerId);
|
||||||
$missingOrders = $this->countMissingOrders($customerId);
|
$missingOrders = $this->countMissingOrders($customerId);
|
||||||
|
|
||||||
@@ -275,42 +271,46 @@ class TahesabSyncService
|
|||||||
*/
|
*/
|
||||||
public function peekNextPending(array $skip = []): ?array
|
public function peekNextPending(array $skip = []): ?array
|
||||||
{
|
{
|
||||||
if (! Schema::hasTable('tahesab')) {
|
if ($this->ensureLocalTable()) {
|
||||||
return null;
|
$skipRow = array_values(array_filter($skip['row'] ?? []));
|
||||||
}
|
$row = $this->unsyncedRowsQuery()
|
||||||
|
->with('customer')
|
||||||
$skipRow = array_values(array_filter($skip['row'] ?? []));
|
->when($skipRow !== [], fn ($q) => $q->whereNotIn('id', $skipRow))
|
||||||
$row = $this->unsyncedRowsQuery()
|
->orderBy('id')
|
||||||
->when($skipRow !== [], fn ($q) => $q->whereNotIn('id', $skipRow))
|
->first();
|
||||||
->orderBy('id')
|
if ($row) {
|
||||||
->first();
|
$name = optional($row->customer)->name;
|
||||||
if ($row) {
|
return [
|
||||||
return [
|
'type' => 'row',
|
||||||
'type' => 'row',
|
'id' => (int) $row->id,
|
||||||
'id' => (int) $row->id,
|
'label' => trim('سند محلی #'.$row->id.($name ? ' — '.$name : '')),
|
||||||
'label' => 'سند محلی #'.$row->id,
|
];
|
||||||
];
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$wIds = $this->missingWithdrawIds(1, null, array_values(array_filter($skip['withdraw'] ?? [])));
|
$wIds = $this->missingWithdrawIds(1, null, array_values(array_filter($skip['withdraw'] ?? [])));
|
||||||
if ($wIds !== []) {
|
if ($wIds !== []) {
|
||||||
$id = (int) $wIds[0];
|
$id = (int) $wIds[0];
|
||||||
|
$w = \App\Models\Withdraw::query()->with('customer')->find($id);
|
||||||
|
$name = optional(optional($w)->customer)->name;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'type' => 'withdraw',
|
'type' => 'withdraw',
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
'label' => 'شارژ/برداشت #'.$id,
|
'label' => trim('شارژ/برداشت #'.$id.($name ? ' — '.$name : '')),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$oIds = $this->missingOrderIds(1, null, array_values(array_filter($skip['order'] ?? [])));
|
$oIds = $this->missingOrderIds(1, null, array_values(array_filter($skip['order'] ?? [])));
|
||||||
if ($oIds !== []) {
|
if ($oIds !== []) {
|
||||||
$id = (int) $oIds[0];
|
$id = (int) $oIds[0];
|
||||||
|
$order = \App\Models\Order::query()->with('customer')->find($id);
|
||||||
|
$name = optional(optional($order)->customer)->name;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'type' => 'order',
|
'type' => 'order',
|
||||||
'id' => $id,
|
'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
|
public function pendingDocs(?int $customerId = null, int $limit = 200): array
|
||||||
{
|
{
|
||||||
if (! Schema::hasTable('tahesab')) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$limit = max(1, min(500, $limit));
|
$limit = max(1, min(500, $limit));
|
||||||
$rows = [];
|
$rows = [];
|
||||||
|
|
||||||
$local = $this->unsyncedRowsQuery()
|
if ($this->ensureLocalTable()) {
|
||||||
->with('customer')
|
$local = $this->unsyncedRowsQuery()
|
||||||
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
->with('customer')
|
||||||
->orderByDesc('id')
|
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
||||||
->limit($limit)
|
->orderByDesc('id')
|
||||||
->get();
|
->limit($limit)
|
||||||
|
->get();
|
||||||
|
|
||||||
foreach ($local as $row) {
|
foreach ($local as $row) {
|
||||||
$arr = $row->toArray();
|
$arr = $row->toArray();
|
||||||
$arr['issue_kind'] = 'failed';
|
$arr['issue_kind'] = 'failed';
|
||||||
$arr['sync_target'] = ['type' => 'row', 'id' => (int) $row->id];
|
$arr['sync_target'] = ['type' => 'row', 'id' => (int) $row->id];
|
||||||
$rows[] = $arr;
|
$rows[] = $arr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$remaining = $limit - count($rows);
|
$remaining = $limit - count($rows);
|
||||||
@@ -575,11 +573,11 @@ class TahesabSyncService
|
|||||||
|
|
||||||
private function missingWithdrawQuery(?int $customerId = null, array $exclude = [])
|
private function missingWithdrawQuery(?int $customerId = null, array $exclude = [])
|
||||||
{
|
{
|
||||||
if (! Schema::hasTable('withdraws') || ! Schema::hasTable('tahesab')) {
|
if (! Schema::hasTable('withdraws')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return \App\Models\Withdraw::query()
|
$query = \App\Models\Withdraw::query()
|
||||||
->where('sts', 4)
|
->where('sts', 4)
|
||||||
->where(function ($q) {
|
->where(function ($q) {
|
||||||
$q->where('cash', '!=', 0)->orWhere('gold', '!=', 0);
|
$q->where('cash', '!=', 0)->orWhere('gold', '!=', 0);
|
||||||
@@ -589,10 +587,15 @@ class TahesabSyncService
|
|||||||
->orWhere('note', 'not like', 'fix:intval-gold-loss%');
|
->orWhere('note', 'not like', 'fix:intval-gold-loss%');
|
||||||
})
|
})
|
||||||
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
||||||
->when($exclude !== [], fn ($q) => $q->whereNotIn('id', $exclude))
|
->when($exclude !== [], fn ($q) => $q->whereNotIn('id', $exclude));
|
||||||
->whereNotExists(function ($sub) {
|
|
||||||
|
if (Schema::hasTable('tahesab')) {
|
||||||
|
$query->whereNotExists(function ($sub) {
|
||||||
$sub->from('tahesab')->selectRaw('1')->whereColumn('tahesab.withdraw_id', 'withdraws.id');
|
$sub->from('tahesab')->selectRaw('1')->whereColumn('tahesab.withdraw_id', 'withdraws.id');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return list<int> */
|
/** @return list<int> */
|
||||||
@@ -620,19 +623,24 @@ class TahesabSyncService
|
|||||||
|
|
||||||
private function missingOrderQuery(?int $customerId = null, array $exclude = [])
|
private function missingOrderQuery(?int $customerId = null, array $exclude = [])
|
||||||
{
|
{
|
||||||
if (! Schema::hasTable('orders') || ! Schema::hasTable('tahesab')) {
|
if (! Schema::hasTable('orders')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return \App\Models\Order::query()
|
$query = \App\Models\Order::query()
|
||||||
->where('sts', 2)
|
->where('sts', 2)
|
||||||
->where('payment_sts', 4)
|
->where('payment_sts', 4)
|
||||||
->whereIn('direction', [1, 2])
|
->whereIn('direction', [1, 2])
|
||||||
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
->when($customerId, fn ($q) => $q->where('customer_id', $customerId))
|
||||||
->when($exclude !== [], fn ($q) => $q->whereNotIn('id', $exclude))
|
->when($exclude !== [], fn ($q) => $q->whereNotIn('id', $exclude));
|
||||||
->whereNotExists(function ($sub) {
|
|
||||||
|
if (Schema::hasTable('tahesab')) {
|
||||||
|
$query->whereNotExists(function ($sub) {
|
||||||
$sub->from('tahesab')->selectRaw('1')->whereColumn('tahesab.order_id', 'orders.id');
|
$sub->from('tahesab')->selectRaw('1')->whereColumn('tahesab.order_id', 'orders.id');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function backfillWithdraw(TahesabController $controller, int $withdrawId): bool
|
private function backfillWithdraw(TahesabController $controller, int $withdrawId): bool
|
||||||
@@ -705,15 +713,37 @@ class TahesabSyncService
|
|||||||
return $result !== false;
|
return $result !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function emptyPendingPayload(): array
|
public function ensureLocalTable(): bool
|
||||||
{
|
{
|
||||||
return [
|
if (Schema::hasTable('tahesab')) {
|
||||||
'failed_rows' => 0,
|
return true;
|
||||||
'missing_withdraws' => 0,
|
}
|
||||||
'missing_orders' => 0,
|
|
||||||
'total' => 0,
|
try {
|
||||||
'doc_sync' => app(TahesabDocSyncService::class)->snapshot(),
|
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
|
private function hasSyncColumns(): bool
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('tahesab')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
// جدول ممکن است از قبل روی نصبهای قدیمی بوده باشد؛ drop نمیکنیم.
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user