feat(tahesab): register remaining panel customers after document sync

After pending docs finish, the same pump writes customers without a code into Tahesab. Inbound import is server-side; the admin only polls status.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-03 10:09:54 +03:30
co-authored by Cursor
parent ff5430eceb
commit c802c67e0d
3 changed files with 399 additions and 30 deletions
@@ -100,6 +100,8 @@ class TahesabController extends Controller
public function customerSyncStatus(Request $request): \Illuminate\Http\JsonResponse
{
app(TahesabDocSyncService::class)->tickIfStale();
// مشتریان پنل
$panelTotal = Customer::whereNull('deleted_at')->count();
$panelRegistered = Customer::whereNull('deleted_at')->whereNotNull('moshtari_code')->count();
@@ -326,6 +328,46 @@ class TahesabController extends Controller
return ['by_code' => $byCode, 'by_mobile' => $byMobile];
}
/**
* مشتریان ته‌حساب که در پنل نیستند و شماره دارند — برای ورود تکی‌تکی سمت سرور.
*
* @return list<array{code:int,name:string,tel:string}>
*/
public function collectImportableTahesabCustomers(): array
{
$tahesabCustomers = $this->fetchAllTahesabCustomers();
$maps = $this->panelAccountingMaps();
$out = [];
foreach ($tahesabCustomers as $item) {
if (! is_array($item)) {
continue;
}
$code = $this->extractTahesabCode($item);
$codeKey = $this->accountingCodeKey($code);
$phone = $this->extractTahesabPhone($item);
if (Customer::normalizeMobile($phone) === null) {
continue;
}
if ($codeKey !== null && isset($maps['by_code'][$codeKey])) {
continue;
}
$mobileKey = Customer::normalizeMobile($phone);
if ($mobileKey && isset($maps['by_mobile'][$mobileKey])) {
continue;
}
if ($codeKey === null) {
continue;
}
$out[] = [
'code' => (int) $code,
'name' => trim((string) ($item['Name'] ?? $item['name'] ?? '')),
'tel' => $phone,
];
}
return $out;
}
/** لینک کد ته‌حساب روی مشتری پنل وقتی شماره یکسان است */
public function linkMatchingPhones(): \Illuminate\Http\JsonResponse
{
@@ -701,6 +743,20 @@ class TahesabController extends Controller
return response()->json($result);
}
public function startCustomerOutbound()
{
$result = app(TahesabDocSyncService::class)->startCustomerOutbound();
return response()->json($result, ($result['ok'] ?? false) ? 200 : 422);
}
public function startCustomerInbound()
{
$result = app(TahesabDocSyncService::class)->startCustomerInbound();
return response()->json($result, ($result['ok'] ?? false) ? 200 : 422);
}
public function pendingDocs(Request $request)
{
$customerId = $request->filled('customer_id') ? (int) $request->input('customer_id') : null;