feat: shayegan inbound charges workflow and copper kilogram unit enforcement

This commit is contained in:
2026-11-05 17:15:38 +03:30
parent e65b8053fd
commit 7dd5304638
13 changed files with 787 additions and 18 deletions
@@ -16,6 +16,7 @@ use App\Services\BalanceReportService;
use App\Services\ConfigService;
use App\Services\CustomerPresenceService;
use App\Services\ReferralGiftService;
use App\Services\ShayeganSyncService;
use App\Services\WalletBalanceService;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
@@ -198,6 +199,23 @@ class CustomerController extends SuperController
$updated = $data === [] ? false : $customer->update($data);
// همگام‌سازی با شایگان در صورت تغییر دستی کد حسابداری
if (array_key_exists('moshtari_code', $data) && app(ConfigService::class)->isShayeganAccounting()) {
$newCode = $data['moshtari_code'];
if ($newCode !== null && $newCode !== '') {
try {
$shayeganSync = app(ShayeganSyncService::class);
$shayeganSync->registerCustomer($customer, $newCode);
} catch (\Throwable $e) {
\Illuminate\Support\Facades\Log::warning('shayegan.manual_code_sync_failed', [
'customer_id' => $customer->id,
'code' => $newCode,
'error' => $e->getMessage(),
]);
}
}
}
return response()->json([
'g' => $goldUpdated,
'u' => $updated,
@@ -4,7 +4,10 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Shayegan;
use App\Models\ShayeganInboundCharge;
use App\Services\ConfigService;
use App\Services\ShayeganClient;
use App\Services\ShayeganInboundChargeService;
use App\Services\ShayeganSyncService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
@@ -88,7 +91,7 @@ class ShayeganController extends Controller
]);
}
$query = Shayegan::query()->orderByDesc('id');
$query = Shayegan::with('customer')->orderByDesc('id');
\App\DB\Te\TableEngine::create($query, $request)
->initSearch(['desc', 'type', 'shayegan_number', 'sync_status'])
->select(['*'])
@@ -99,7 +102,7 @@ class ShayeganController extends Controller
public function show($id)
{
$row = Shayegan::findOrFail($id);
$row = Shayegan::with('customer')->findOrFail($id);
return response()->json($row);
}
@@ -163,6 +166,132 @@ class ShayeganController extends Controller
return response()->json(array_merge(['ok' => false, 'message' => $row->sync_log, 'row' => $row], $exchange), 422);
}
/**
* وب‌هوک دریافت شارژ حساب مشتری از پنل حسابداری شایگان
*/
public function inboundWebhook(Request $request, ShayeganInboundChargeService $inboundService)
{
$secret = app(ConfigService::class)->getString('shayegan_webhook_secret', '');
$key = $request->header('X-Shayegan-Key') ?: $request->input('secret');
if ($secret !== '' && $key !== $secret) {
return response()->json([
'ok' => false,
'message' => 'کلید امنیتی وب‌هوک شایگان نامعتبر است (X-Shayegan-Key).',
], 403);
}
$res = $inboundService->registerCharge($request->all(), 'webhook');
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
}
/**
* دریافت لیست شارژهای در انتظار تایید برای نمایش اعلان در پنل ادمین
*/
public function pendingCharges()
{
if (! Schema::hasTable('shayegan_inbound_charges')) {
return response()->json(['ok' => true, 'charges' => []]);
}
$charges = ShayeganInboundCharge::with('customer')
->where('status', 'pending')
->orderByDesc('id')
->limit(20)
->get();
return response()->json([
'ok' => true,
'charges' => $charges,
]);
}
/**
* جدول شارژهای دریافتی از شایگان
*/
public function chargesTable(Request $request)
{
if (! Schema::hasTable('shayegan_inbound_charges')) {
return response()->json([
'rows' => [],
'query' => [
'limit' => (int) $request->input('limit', 20),
'page' => 1,
'total' => 0,
'filters' => $request->input('filters', []),
'search' => $request->input('search'),
],
]);
}
$query = ShayeganInboundCharge::with(['customer', 'approver'])->orderByDesc('id');
if ($request->filled('status')) {
$query->where('status', $request->input('status'));
}
if ($request->filled('search')) {
$s = trim((string) $request->input('search'));
$query->where(function ($q) use ($s) {
$q->where('shayegan_number', 'like', "%{$s}%")
->orWhere('customer_tafsil', 'like', "%{$s}%")
->orWhereHas('customer', function ($cq) use ($s) {
$cq->where('name', 'like', "%{$s}%")
->orWhere('mobile', 'like', "%{$s}%")
->orWhere('n_code', 'like', "%{$s}%");
});
});
}
$limit = max(1, (int) $request->input('limit', 20));
$page = max(1, (int) $request->input('page', 1));
$total = $query->count();
$rows = $query->skip(($page - 1) * $limit)->take($limit)->get();
return response()->json([
'rows' => $rows,
'query' => [
'page' => $page,
'limit' => $limit,
'total' => $total,
],
]);
}
/**
* تایید شارژ و افزایش کیف پول مشتری در بورس مس
*/
public function approveCharge(Request $request, $id, ShayeganInboundChargeService $inboundService)
{
$charge = ShayeganInboundCharge::with('customer')->findOrFail($id);
$res = $inboundService->approveCharge($charge, auth()->user());
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
}
/**
* رد درخواست شارژ
*/
public function rejectCharge(Request $request, $id, ShayeganInboundChargeService $inboundService)
{
$charge = ShayeganInboundCharge::findOrFail($id);
$reason = $request->input('reason');
$res = $inboundService->rejectCharge($charge, auth()->user(), $reason);
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
}
/**
* استعلام اسناد جدید از شایگان
*/
public function syncChargesFromShayegan(ShayeganInboundChargeService $inboundService)
{
$res = $inboundService->pollFromShayegan();
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
}
/**
* @param mixed $body
* @return mixed