feat: port wallet charge/withdraw modal from tauri to patient admin page
Add manual wallet withdrawal (debit) endpoint mirroring the offline app's
balance guard, and rebuild the patient کیف پول tab around a single
charge/withdraw toggle modal (quick amounts, تومان→ریال conversion,
transaction filters).
Backend:
- POST /api/v1/patient/{uuid}/wallet/withdraw — creates a debit
WalletTransaction; 422 ERR_WALLET_INSUFFICIENT when amount exceeds balance.
- ErrorCodes: ERR_WALLET_INSUFFICIENT ('موجودی کیف پول کافی نیست').
- docs/api/patient.md updated.
Frontend:
- usePatientWallet hook (balance + charge/withdraw mutations).
- WalletTransactionModal (toggle, quick amounts, UI-only payment fields).
- WalletTab: charge button, همه/واریزی/برداشت filters.
Tests: backend withdraw success/insufficient/non-positive/ownership;
frontend modal + wallet tab interactions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -168,6 +168,46 @@ class PatientController extends BaseController
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manual wallet withdrawal (برداشت از کیف پول) — e.g. a refund or cash
|
||||
* hand-back at the desk. Creates a debit WalletTransaction for the record's
|
||||
* owner User. Rejected (422) when the amount exceeds the current balance,
|
||||
* mirroring the offline app's balance guard.
|
||||
*/
|
||||
#[Route('/api/v1/patient/{uuid}/wallet/withdraw', methods: ['POST'])]
|
||||
public function withdrawWallet(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId)) {
|
||||
return $this->error(ErrorCodes::ERR_PATIENT_NOT_FOUND, ErrorCodes::message(ErrorCodes::ERR_PATIENT_NOT_FOUND), 404);
|
||||
}
|
||||
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
$amount = (int) ($data['amount_rials'] ?? 0);
|
||||
if ($amount <= 0) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'مبلغ برداشت باید بزرگتر از صفر باشد', 422, 'amount_rials');
|
||||
}
|
||||
|
||||
$patient = $record->getUser();
|
||||
$current = $this->settlementRepo->getWalletBalance($patient);
|
||||
if ($amount > $current) {
|
||||
return $this->error(ErrorCodes::ERR_WALLET_INSUFFICIENT, ErrorCodes::message(ErrorCodes::ERR_WALLET_INSUFFICIENT), 422, 'amount_rials');
|
||||
}
|
||||
|
||||
$balance = $current - $amount;
|
||||
|
||||
$txn = new \App\Settlement\Entity\WalletTransaction($patient, $amount, 'debit', $balance);
|
||||
$description = trim((string) ($data['description'] ?? ''));
|
||||
$txn->setDescription($description !== '' ? $description : 'برداشت از کیف پول');
|
||||
$this->walletRepo->save($txn);
|
||||
|
||||
return $this->success([
|
||||
'transaction' => $txn->toArray(),
|
||||
'balance_rials' => $balance,
|
||||
], 201);
|
||||
}
|
||||
|
||||
// ── Call center (کال سنتر) ─────────────────────────────────────────────────
|
||||
|
||||
/** List the patient's call log (newest first, optional ?outcome=success|missed). */
|
||||
|
||||
Reference in New Issue
Block a user