ifnex/04_Laravel/app/Http/Controllers/Api/Customer/CustomerInvoiceController.php
Kazem Alghasi 7fe368bc46 feat(invoices): thousand-separator formatting + fix total calc + integrate invoices into customer portal
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.
2026-10-29 23:59:19 +03:30

119 lines
3.7 KiB
PHP

<?php
namespace App\Http\Controllers\Api\Customer;
use App\Http\Controllers\Controller;
use App\Models\ManualInvoice;
use App\Services\ManualInvoiceService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* CustomerInvoiceController — API فاکتورهای دستی برای مشتری (در پورتال وردپرس)
*
* مسیرها:
* GET /api/v1/customer/invoices — لیست فاکتورها
* GET /api/v1/customer/invoices/{id} — جزئیات فاکتور
* POST /api/v1/customer/invoices/{id}/pay — پرداخت از کیف پول
*/
class CustomerInvoiceController extends Controller
{
public function __construct(
protected ManualInvoiceService $invoiceService,
) {}
/**
* GET /api/v1/customer/invoices
* لیست فاکتورهای مشتری با امکان فیلتر بر اساس وضعیت.
*/
public function index(Request $request): JsonResponse
{
$validated = $request->validate([
'status' => ['nullable', 'string', 'in:pending_payment,paid,cancelled,expired'],
'per_page' => ['nullable', 'integer', 'min:10', 'max:100'],
]);
$user = Auth::user();
$invoices = $this->invoiceService->getCustomerInvoices($user, $validated);
return response()->json([
'success' => true,
'data' => [
'invoices' => $invoices->items(),
'pagination' => [
'current_page' => $invoices->currentPage(),
'last_page' => $invoices->lastPage(),
'per_page' => $invoices->perPage(),
'total' => $invoices->total(),
],
],
]);
}
/**
* GET /api/v1/customer/invoices/{id}
* جزئیات یک فاکتور.
*/
public function show(int $id): JsonResponse
{
$user = Auth::user();
$invoice = ManualInvoice::where('id', $id)
->where('user_id', $user->id)
->with(['issuedBy:id,name'])
->first();
if (! $invoice) {
return response()->json([
'success' => false,
'message' => 'فاکتور یافت نشد.',
], 404);
}
return response()->json([
'success' => true,
'data' => $this->invoiceService->toArray($invoice),
]);
}
/**
* POST /api/v1/customer/invoices/{id}/pay
* پرداخت فاکتور از کیف پول مشتری.
*/
public function pay(int $id): JsonResponse
{
$user = Auth::user();
$invoice = ManualInvoice::where('id', $id)
->where('user_id', $user->id)
->first();
if (! $invoice) {
return response()->json([
'success' => false,
'message' => 'فاکتور یافت نشد.',
], 404);
}
try {
$paid = $this->invoiceService->payFromWallet($invoice, $user->id);
return response()->json([
'success' => true,
'message' => 'فاکتور با موفقیت پرداخت شد.',
'data' => $this->invoiceService->toArray($paid),
]);
} catch (\RuntimeException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
} catch (\Throwable $e) {
report($e);
return response()->json([
'success' => false,
'message' => 'خطای داخلی سرور هنگام پرداخت فاکتور.',
], 500);
}
}
}