fix(tahesab): register unlinked customers before sending documents

File cache cannot use Cache::lock, so docs for customers without a code always failed. Register first, put the customer name on the sanad, and surface the real error.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-03 09:25:18 +03:30
co-authored by Cursor
parent bb52e4c35a
commit ff5430eceb
2 changed files with 126 additions and 71 deletions
@@ -1004,21 +1004,22 @@ class TahesabController extends Controller
$isColleagueLeg = ! empty($data['colleague_leg']) && $accountOverride !== '';
if (! $isColleagueLeg) {
if ($customer && ($customer->moshtari_code === null)) {
$this->createCustomerInTahesab($customer->id);
$customer = Customer::where('id', $data['customer_id'])->first();
$ensured = $this->ensureCustomerHasTahesabCode($customer);
if (! ($ensured['ok'] ?? false)) {
return $ensured['message'] ?? 'مشتری کد ته‌حساب ندارد';
}
$customer = $ensured['customer'];
}
if (! $customer) {
return false;
return 'مشتری سند پیدا نشد';
}
$hesabCode = $isColleagueLeg
? (int) $accountOverride
: (int) ($customer->moshtari_code ?? 0);
if ($hesabCode <= 0) {
return false;
return 'مشتری کد ته‌حساب ندارد';
}
$kind = $mapper->resolveKind($product);
@@ -1056,6 +1057,7 @@ class TahesabController extends Controller
if ($sharh === '') {
$sharh = trim($title !== '' ? "{$action} {$title}" : "{$action} {$kindFa}");
}
$sharh = $this->sharhWithCustomer($sharh, $customer);
$orderId = is_object($data['order'] ?? null)
? ($data['order']->id ?? null)
@@ -1138,14 +1140,17 @@ class TahesabController extends Controller
// throw new UnprocessableEntityHttpException("Auth needed");
// }
$customer = Customer::where('id', $data['customer_id'])->first();
if ($customer && ($customer->moshtari_code === null)) {
$this->createCustomerInTahesab($customer->id);
$customer = Customer::where('id', $data['customer_id'])->first();
$ensured = $this->ensureCustomerHasTahesabCode($customer);
if (! ($ensured['ok'] ?? false)) {
return $ensured['message'] ?? 'مشتری کد ته‌حساب ندارد';
}
$customer = $ensured['customer'];
$data['handler'] = 'wallet';
$isDeposit = (int) ($data['direction'] ?? 0) === 0;
$sharh = self::accountingWalletDesc($isDeposit, $data['note'] ?? null);
$sharh = $this->sharhWithCustomer(
self::accountingWalletDesc($isDeposit, $data['note'] ?? null),
$customer
);
$tahesab = $existing ?? Tahesab::create([
"customer_id" => $customer->id,
"desc" => $sharh,
@@ -1327,17 +1332,17 @@ class TahesabController extends Controller
}
$customer = Customer::where('id', $data['customer_id'])->first();
if (!$customer) {
return false;
}
if ($customer->moshtari_code === null) {
$this->createCustomerInTahesab($customer->id);
$customer = Customer::where('id', $data['customer_id'])->first();
$ensured = $this->ensureCustomerHasTahesabCode($customer);
if (! ($ensured['ok'] ?? false)) {
return $ensured['message'] ?? 'مشتری کد ته‌حساب ندارد';
}
$customer = $ensured['customer'];
$amount = abs((float) $data['value']);
$desc = !empty($data['desc']) ? (string) $data['desc'] : 'پاداش';
$desc = $this->sharhWithCustomer(
! empty($data['desc']) ? (string) $data['desc'] : 'پاداش',
$customer
);
$data['handler'] = 'goldReward';
$tahesab = $existing ?? Tahesab::create([
@@ -1432,11 +1437,11 @@ class TahesabController extends Controller
}
$customer = Customer::where('id', $data['customer_id'])->first();
if ($customer && ($customer->moshtari_code === null)) {
$this->createCustomerInTahesab($customer->id);
$customer = Customer::where('id', $data['customer_id'])->first();
$ensured = $this->ensureCustomerHasTahesabCode($customer);
if (! ($ensured['ok'] ?? false)) {
return $ensured['message'] ?? 'مشتری کد ته‌حساب ندارد';
}
$customer = $ensured['customer'];
$kind = $mapper->resolveKind($product);
$rawValue = (float) $data['value'];
@@ -1447,9 +1452,12 @@ class TahesabController extends Controller
$isDeposit = (int) ($data['direction'] ?? 1) === 1;
$isVorood = $isDeposit ? 1 : 0;
$sharh = self::accountingWalletDesc(
$isDeposit,
!empty($data['note']) ? (string) $data['note'] : (!empty($data['desc']) ? (string) $data['desc'] : null)
$sharh = $this->sharhWithCustomer(
self::accountingWalletDesc(
$isDeposit,
! empty($data['note']) ? (string) $data['note'] : (! empty($data['desc']) ? (string) $data['desc'] : null)
),
$customer
);
$data['note'] = $sharh;
$data['handler'] = 'walletGold';
@@ -1643,18 +1651,49 @@ class TahesabController extends Controller
];
}
private function ensureCustomerHasTahesabCode(?Customer $customer): array
{
if (! $customer) {
return ['ok' => false, 'message' => 'مشتری سند پیدا نشد'];
}
if ((int) ($customer->moshtari_code ?? 0) > 0) {
return ['ok' => true, 'customer' => $customer];
}
$reg = $this->createCustomerInTahesab($customer->id);
$customer->refresh();
if ((int) ($customer->moshtari_code ?? 0) > 0) {
return ['ok' => true, 'customer' => $customer];
}
$message = is_string($reg) && $reg !== ''
? $reg
: 'ثبت مشتری در ته‌حساب ناموفق بود';
return ['ok' => false, 'message' => $message];
}
private function sharhWithCustomer(string $sharh, Customer $customer): string
{
$name = trim((string) ($customer->name ?? ''));
if ($name === '' || str_contains($sharh, $name)) {
return $sharh;
}
return trim($sharh.' — '.$name);
}
public function createCustomerInTahesab($customer_id)
{
$customer = Customer::findOrFail($customer_id);
$response = null;
if ($customer->moshtari_code == null) {
$lock = Cache::lock('tahesab:register-customer:'.$customer->id, 30);
if (! $lock->get()) {
if ((int) ($customer->moshtari_code ?? 0) <= 0) {
$lockKey = 'tahesab:register-customer:'.$customer->id;
if (! Cache::add($lockKey, 1, 30)) {
$customer->refresh();
if ($customer->moshtari_code != null) {
if ((int) ($customer->moshtari_code ?? 0) > 0) {
return $customer->moshtari_code;
}
@@ -1664,7 +1703,7 @@ class TahesabController extends Controller
try {
$customer->refresh();
if ($customer->moshtari_code != null) {
if ((int) ($customer->moshtari_code ?? 0) > 0) {
return $customer->moshtari_code;
}
@@ -1697,9 +1736,11 @@ class TahesabController extends Controller
return $e->getMessage();
}
return 'tahesab api error!!!';
return is_array($response)
? ($this->sync()->buildLog($response, 'ثبت مشتری در ته‌حساب ناموفق بود') ?: 'ثبت مشتری در ته‌حساب ناموفق بود')
: 'ثبت مشتری در ته‌حساب ناموفق بود';
} finally {
$lock->release();
Cache::forget($lockKey);
}
}
+52 -38
View File
@@ -436,14 +436,10 @@ class TahesabSyncService
return $this->retry($row);
}
if ($type === 'withdraw') {
$ok = $this->backfillWithdraw($controller, $id);
return ['ok' => $ok, 'message' => $ok ? 'سند شارژ/برداشت ثبت شد' : 'ارسال شارژ/برداشت ناموفق بود'];
return $this->backfillWithdraw($controller, $id);
}
if ($type === 'order') {
$ok = $this->backfillOrder($controller, $id);
return ['ok' => $ok, 'message' => $ok ? 'سند سفارش ثبت شد' : 'ارسال سفارش ناموفق بود'];
return $this->backfillOrder($controller, $id);
}
return ['ok' => false, 'message' => 'نوع نامعتبر است'];
@@ -506,12 +502,12 @@ class TahesabSyncService
foreach ($this->missingWithdrawIds(max(0, $limit - $used), $customerId) as $withdrawId) {
$used++;
try {
$ok = $this->backfillWithdraw($controller, (int) $withdrawId);
if ($ok) {
$sent = $this->backfillWithdraw($controller, (int) $withdrawId);
if ($sent['ok'] ?? false) {
$backfilledWithdraws++;
} else {
$failed++;
$errors[] = 'شارژ/برداشت #'.$withdrawId.': ارسال به ته‌حساب ناموفق';
$errors[] = 'شارژ/برداشت #'.$withdrawId.': '.($sent['message'] ?? 'ارسال به ته‌حساب ناموفق');
}
} catch (\Throwable $e) {
$failed++;
@@ -523,12 +519,12 @@ class TahesabSyncService
foreach ($this->missingOrderIds(max(0, $limit - $used), $customerId) as $orderId) {
$used++;
try {
$ok = $this->backfillOrder($controller, (int) $orderId);
if ($ok) {
$sent = $this->backfillOrder($controller, (int) $orderId);
if ($sent['ok'] ?? false) {
$backfilledOrders++;
} else {
$failed++;
$errors[] = 'سفارش #'.$orderId.': ارسال به ته‌حساب ناموفق';
$errors[] = 'سفارش #'.$orderId.': '.($sent['message'] ?? 'ارسال به ته‌حساب ناموفق');
}
} catch (\Throwable $e) {
$failed++;
@@ -643,48 +639,64 @@ class TahesabSyncService
return $query;
}
private function backfillWithdraw(TahesabController $controller, int $withdrawId): bool
private function outcomeFromController($result, string $okMessage, string $failMessage): array
{
if (is_array($result)) {
return ['ok' => true, 'message' => $okMessage];
}
if (is_string($result) && $result !== '') {
return ['ok' => false, 'message' => $result];
}
return ['ok' => false, 'message' => $failMessage];
}
private function backfillWithdraw(TahesabController $controller, int $withdrawId): array
{
$withdraw = \App\Models\Withdraw::query()->find($withdrawId);
if (! $withdraw || ! $withdraw->customer_id) {
return false;
return ['ok' => false, 'message' => 'شارژ بدون مشتری است'];
}
$cash = (float) ($withdraw->cash ?? 0);
$gold = (float) ($withdraw->gold ?? 0);
if (abs($cash) > 1e-9) {
$result = $controller->wallet([
'customer_id' => (int) $withdraw->customer_id,
'value' => abs($cash),
'direction' => $cash >= 0 ? 0 : 1,
'withdraw_id' => $withdrawId,
'note' => $withdraw->note,
]);
return $result !== false;
return $this->outcomeFromController(
$controller->wallet([
'customer_id' => (int) $withdraw->customer_id,
'value' => abs($cash),
'direction' => $cash >= 0 ? 0 : 1,
'withdraw_id' => $withdrawId,
'note' => $withdraw->note,
]),
'سند شارژ/برداشت ثبت شد',
'ارسال شارژ/برداشت ناموفق بود'
);
}
if (abs($gold) > 1e-9) {
$result = $controller->walletGold([
'customer_id' => (int) $withdraw->customer_id,
'value' => abs($gold),
'direction' => $gold >= 0 ? 1 : 0,
'withdraw_id' => $withdrawId,
'note' => $withdraw->note,
]);
return $result !== false;
return $this->outcomeFromController(
$controller->walletGold([
'customer_id' => (int) $withdraw->customer_id,
'value' => abs($gold),
'direction' => $gold >= 0 ? 1 : 0,
'withdraw_id' => $withdrawId,
'note' => $withdraw->note,
]),
'سند شارژ/برداشت ثبت شد',
'ارسال شارژ/برداشت ناموفق بود'
);
}
return false;
return ['ok' => false, 'message' => 'مبلغ شارژ صفر است'];
}
private function backfillOrder(TahesabController $controller, int $orderId): bool
private function backfillOrder(TahesabController $controller, int $orderId): array
{
$order = \App\Models\Order::query()->with('product')->find($orderId);
if (! $order || ! $order->customer_id) {
return false;
return ['ok' => false, 'message' => 'سفارش بدون مشتری است'];
}
$product = $order->product;
@@ -708,9 +720,11 @@ class TahesabSyncService
'order' => ['id' => $orderId, 'product_id' => $order->product_id],
];
$result = $controller->storeTrade($payload);
return $result !== false;
return $this->outcomeFromController(
$controller->storeTrade($payload),
'سند سفارش ثبت شد',
'ارسال سفارش ناموفق بود'
);
}
public function ensureLocalTable(): bool