Polish the manual invoice feature with 4 improvements: 1. Admin invoice form: numeric fields (unit_price, discount_amount) now display values with thousand-separators (e.g. 1,000,000 instead of 1000000) via formatStateUsing/dehydrateStateUsing for easier reading of large Rial amounts. The placeholder/subtotal/total calculations also strip commas before arithmetic. 2. Fix 'total amount payable = 0' bug in the form. The placeholder had a typo: 'foreach ( as )' overwrote the loop variable and made the inner calculation use the array instead of each item, so the sum was always 0. Now correctly iterates with . 3. Customer dashboard widget: add an 'Invoices pending payment' card showing the count, with a link to /my-account/?tab=invoices if > 0. Relabel the existing 'Pending payment' card to 'Pending payment (orders)' to clarify it's for orders, not invoices. 4. Customer profile financial status: include manual invoices in the accounting_balance and pending_payments_total. Show a breakdown list (orders vs invoices) under the balance card, plus two new stat cards (pending invoices count/total, paid invoices count/total) with a link to the invoices tab.
193 lines
11 KiB
PHP
193 lines
11 KiB
PHP
<?php
|
||
|
||
use App\Http\Controllers\Api\CustomerFinancialController;
|
||
use App\Http\Controllers\Api\CommitmentFormController;
|
||
use App\Http\Controllers\Api\MobileVerificationController;
|
||
use App\Http\Controllers\Api\Customer\CustomerOrderController;
|
||
use App\Http\Controllers\Api\DiscountCodeController;
|
||
use App\Http\Controllers\Api\MockGatewayController;
|
||
use App\Http\Controllers\Api\PaymentController;
|
||
use App\Http\Controllers\Api\PricingController;
|
||
use App\Http\Controllers\Api\StaffOrderController;
|
||
use App\Http\Controllers\Api\TrackController;
|
||
use App\Http\Controllers\Api\WalletController;
|
||
use App\Http\Middleware\ApiKeyMiddleware;
|
||
use App\Http\Middleware\StaffApiMiddleware;
|
||
use Illuminate\Support\Facades\Route;
|
||
use App\Http\Controllers\Api\AuthController;
|
||
use App\Http\Controllers\Api\BridgeAuthController;
|
||
use App\Http\Controllers\ShipmentPdfController;
|
||
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// Bridge Auth API (فقط برای پلاگین وردپرس - با API Key محافظت میشود)
|
||
// ══════════════════════════════════════════════════════════════
|
||
Route::post('/v1/bridge/login', [BridgeAuthController::class, 'login'])
|
||
->middleware('throttle:auth'); // Login — ۵/min per IP
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// API عمومی با API Key
|
||
// ══════════════════════════════════════════════════════════════
|
||
Route::middleware([ApiKeyMiddleware::class, 'throttle:public'])->prefix('v1')->group(function () {
|
||
Route::get('/track/{awb_no}', [TrackController::class, 'show']);
|
||
});
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// API عمومی (بدون نیاز به احراز هویت)
|
||
// ══════════════════════════════════════════════════════════════
|
||
Route::middleware(['throttle:public'])->prefix('v1')->group(function () {
|
||
Route::get('/countries', [CustomerOrderController::class, 'countries']);
|
||
});
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// Auth API (عمومی - برای دریافت توکن)
|
||
// ══════════════════════════════════════════════════════════════
|
||
Route::post('/v1/auth/login', [AuthController::class, 'login'])
|
||
->middleware('throttle:auth');
|
||
|
||
Route::middleware(['auth:sanctum'])->post('/v1/auth/logout', [AuthController::class, 'logout']);
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// API برای کاربران لاگینشده (با Sanctum / Bearer Token)
|
||
// ══════════════════════════════════════════════════════════════
|
||
Route::middleware(['auth:sanctum', 'throttle:customer'])->prefix('v1')->group(function () {
|
||
|
||
// ─── Wallet API (کاربر عادی) ───
|
||
Route::get('/wallet/balance', [WalletController::class, 'balance']);
|
||
Route::get('/wallet/transactions', [WalletController::class, 'transactions']);
|
||
|
||
// ─── Payment API ───
|
||
Route::post('/payment/redirect', [PaymentController::class, 'redirectToGateway'])
|
||
->middleware('throttle:wallet');
|
||
Route::get('/payment/check/{transaction}', [PaymentController::class, 'checkStatus'])
|
||
->middleware('throttle:wallet');
|
||
|
||
// ─── Admin Wallet API ───
|
||
Route::post('/wallet/admin-adjust', [WalletController::class, 'adminAdjust'])
|
||
->middleware('throttle:wallet');
|
||
Route::post('/wallet/{wallet}/freeze', [WalletController::class, 'freeze'])
|
||
->middleware('throttle:wallet');
|
||
Route::post('/wallet/{wallet}/unfreeze', [WalletController::class, 'unfreeze'])
|
||
->middleware('throttle:wallet');
|
||
Route::get('/wallet/{wallet}/activity-log', [WalletController::class, 'activityLog']);
|
||
|
||
// ─── Discount Codes (نیازمند احراز هویت) ───
|
||
Route::get('/discount-codes', [DiscountCodeController::class, 'index']);
|
||
Route::post('/discount-codes/validate', [DiscountCodeController::class, 'validate']);
|
||
|
||
// ─── Commitment Forms (نیازمند احراز هویت) ───
|
||
Route::get('/commitment-forms', [CommitmentFormController::class, 'index']);
|
||
Route::get('/commitment-forms/{direction}', [CommitmentFormController::class, 'byDirection']);
|
||
|
||
// ─── Customer Orders API (جدید) ───
|
||
Route::prefix('customer')->group(function () {
|
||
// پروفایل و آمار
|
||
Route::get('/profile', [CustomerOrderController::class, 'profile']);
|
||
|
||
// فاکتورهای دستی (صدور شده توسط مدیر)
|
||
Route::get('/invoices', [\App\Http\Controllers\Api\Customer\CustomerInvoiceController::class, 'index']);
|
||
Route::get('/invoices/{invoice}', [\App\Http\Controllers\Api\Customer\CustomerInvoiceController::class, 'show']);
|
||
Route::post('/invoices/{invoice}/pay', [\App\Http\Controllers\Api\Customer\CustomerInvoiceController::class, 'pay'])
|
||
->middleware('throttle:wallet');
|
||
|
||
// لیست کشورها (عمومی - بدون نیاز به احراز هویت)
|
||
// این endpoint در گروه auth:sanctum نیست
|
||
|
||
// سفارشات
|
||
Route::get('/orders', [CustomerOrderController::class, 'index']);
|
||
Route::post('/orders', [CustomerOrderController::class, 'store']);
|
||
Route::get('/orders/{shipment}', [CustomerOrderController::class, 'show']);
|
||
Route::post('/orders/{shipment}/cancel', [CustomerOrderController::class, 'cancel']);
|
||
Route::post('/orders/{shipment}/resubmit', [CustomerOrderController::class, 'resubmit']);
|
||
|
||
// دانلود PDFهای سفارش
|
||
Route::get('/orders/{shipment}/pdf/awb', [ShipmentPdfController::class, 'awb']);
|
||
Route::get('/orders/{shipment}/pdf/invoice', [ShipmentPdfController::class, 'invoice']);
|
||
Route::get('/orders/{shipment}/pdf/label', [ShipmentPdfController::class, 'label']);
|
||
Route::get('/orders/{shipment}/pdf/import-invoice', [ShipmentPdfController::class, 'importInvoice']);
|
||
|
||
// تعهدنامهها
|
||
Route::get('/orders/{shipment}/commitment-forms', [CommitmentFormController::class, 'shipmentForms']);
|
||
Route::post('/orders/{shipment}/commitment-forms/{form}/upload', [CommitmentFormController::class, 'uploadSigned']);
|
||
Route::get('/orders/{shipment}/commitment-forms/{form}/download', [CommitmentFormController::class, 'downloadSigned'])
|
||
->name('customer.commitment-forms.download');
|
||
// دانلود قالب خام (محافظتشده با auth به جای symlink عمومی)
|
||
Route::get('/orders/{shipment}/commitment-forms/{form}/template', [CommitmentFormController::class, 'downloadTemplate'])
|
||
->name('customer.commitment-forms.template');
|
||
// نوتیفیکیشنها
|
||
Route::get('/notifications', [CustomerOrderController::class, 'notifications']);
|
||
Route::post('/notifications/{notification}/read', [CustomerOrderController::class, 'markNotificationRead']);
|
||
|
||
// پرداخت سفارش
|
||
Route::post('/orders/{shipment}/pay-wallet', [CustomerOrderController::class, 'payFromWallet'])
|
||
->middleware('throttle:wallet');
|
||
Route::post('/orders/{shipment}/pay-gateway', [CustomerOrderController::class, 'payViaGateway'])
|
||
->middleware('throttle:wallet');
|
||
});
|
||
|
||
// ─── Staff Orders API (تأیید سفارشات) ───
|
||
Route::middleware([StaffApiMiddleware::class, 'throttle:staff'])
|
||
->prefix('staff')
|
||
->group(function () {
|
||
|
||
Route::get(
|
||
'/orders/pending-approval',
|
||
[StaffOrderController::class, 'pendingApproval']
|
||
);
|
||
|
||
Route::post(
|
||
'/orders/{shipment}/request-changes',
|
||
[StaffOrderController::class, 'requestChanges']
|
||
);
|
||
|
||
Route::post(
|
||
'/orders/{shipment}/approve',
|
||
[StaffOrderController::class, 'approve']
|
||
);
|
||
|
||
Route::post(
|
||
'/orders/{shipment}/reject',
|
||
[StaffOrderController::class, 'reject']
|
||
);
|
||
|
||
// ─── Customer Financial API ───
|
||
Route::get(
|
||
'/customers/search',
|
||
[CustomerFinancialController::class, 'search']
|
||
);
|
||
|
||
Route::get(
|
||
'/customers/{customer}/financial-status',
|
||
[CustomerFinancialController::class, 'financialStatus']
|
||
);
|
||
});
|
||
});
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// Mock Gateway Routes (عمومی - برای شبیهسازی درگاه)
|
||
// ══════════════════════════════════════════════════════════════
|
||
Route::prefix('v1/payment')->group(function () {
|
||
Route::get('/mock-gateway', [MockGatewayController::class, 'showGateway']);
|
||
Route::get('/mock-gateway/success', [MockGatewayController::class, 'simulateSuccess']);
|
||
Route::get('/mock-gateway/failure', [MockGatewayController::class, 'simulateFailure']);
|
||
});
|
||
|
||
// Callback از درگاه (بدون auth)
|
||
Route::any('/v1/payment/callback', [PaymentController::class, 'callback'])
|
||
->name('payment.callback')
|
||
->middleware('throttle:public');
|
||
|
||
// ══════════════════════════════════════════════════════════════
|
||
// APIهای عمومی (بدون auth) — فقط استعلام قیمت همگانی است
|
||
// ══════════════════════════════════════════════════════════════
|
||
Route::post('/v1/calculate', [PricingController::class, 'calculate'])
|
||
->middleware('throttle:public');
|
||
|
||
// API تأیید موبایل (عمومی)
|
||
Route::post('/v1/verify/send-code', [MobileVerificationController::class, 'sendCode'])
|
||
->middleware('throttle:sms'); // ۱/min per phone
|
||
Route::post('/v1/verify/check-code', [MobileVerificationController::class, 'checkCode'])
|
||
->middleware('throttle:auth'); // ۵/min per IP
|
||
|
||
// API ارسال پیامک (نیاز به auth دارد)
|
||
Route::middleware(['auth:sanctum'])->post('/v1/verify/send-sms', [MobileVerificationController::class, 'sendSms']); |