fix
This commit is contained in:
parent
0bb22059d0
commit
78e4312f6c
@ -37,9 +37,7 @@ interface BankAccountRow {
|
||||
id: string
|
||||
userId: string
|
||||
ownerType: 'guest' | 'organizer'
|
||||
accountHolder: string
|
||||
iban: string
|
||||
bankName: string | null
|
||||
nationalCode: string | null
|
||||
verificationStatus: BankAccountVerificationStatus
|
||||
rejectionReason: string | null
|
||||
@ -129,7 +127,8 @@ const BankAccountsPage = () => {
|
||||
return
|
||||
}
|
||||
|
||||
showAlert(`حساب «${row.accountHolder}» تأیید شود؟`, () =>
|
||||
const userName = formatPersonName(row.user?.firstName, row.user?.lastName) || row.iban
|
||||
showAlert(`حساب «${userName}» تأیید شود؟`, () =>
|
||||
runAction(row.id, () => axiosInstance.patch(API_ROUTES.BANK_ACCOUNTS.ADMIN_APPROVE(row.id)), 'حساب بانکی تأیید شد')
|
||||
)
|
||||
}
|
||||
|
||||
@ -35,8 +35,6 @@ interface WithdrawalRequestUserSummary {
|
||||
|
||||
interface WithdrawalRequestBankAccountSummary {
|
||||
iban: string
|
||||
bankName: string | null
|
||||
accountHolder: string | null
|
||||
}
|
||||
|
||||
interface WithdrawalRequestRow {
|
||||
@ -213,16 +211,12 @@ const WithdrawalRequestsPage = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<span
|
||||
className="text-xs font-mono"
|
||||
dir="ltr"
|
||||
>
|
||||
{bankAccount.iban || '—'}
|
||||
</span>
|
||||
<span className="text-text-muted text-xs">{bankAccount.bankName ?? '—'}</span>
|
||||
<span className="text-text-muted text-xs">{bankAccount.accountHolder ?? '—'}</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
actions: (row) => {
|
||||
|
||||
3473
openapi.json
3473
openapi.json
File diff suppressed because it is too large
Load Diff
@ -10,8 +10,8 @@ export const wallet = {
|
||||
tabTransactions: 'تراکنشها',
|
||||
tabSettlements: 'تسویههای میزبانی',
|
||||
addAccount: 'افزودن حساب',
|
||||
editAccount: 'ویرایش حساب',
|
||||
noBankAccounts: 'هنوز حساب بانکی ثبت نکردهاید',
|
||||
defaultBadge: 'پیشفرض',
|
||||
noWithdrawals: 'درخواست برداشتی ثبت نشده است',
|
||||
noTransactions: 'تراکنشی ثبت نشده است',
|
||||
noSettlements: 'هنوز تسویهای برای شما ثبت نشده است',
|
||||
@ -19,23 +19,17 @@ export const wallet = {
|
||||
viewPaymentReceipt: 'مشاهده رسید پرداخت',
|
||||
|
||||
addBankAccountTitle: 'افزودن حساب بانکی',
|
||||
ownerTypeLabel: 'نوع حساب',
|
||||
nationalCodeHint: 'برای حساب مهمان، کد ملی لازم است. حساب میزبان از احراز هویت خوانده میشود.',
|
||||
hostIdentityRequired: 'برای ثبت حساب میزبانی، ابتدا کد ملی را در احراز هویت ارسال کنید.',
|
||||
rejectedHint: 'رد شده',
|
||||
onlyApprovedWithdrawal: 'فقط حساب تأییدشده قابل برداشت است',
|
||||
editBankAccountTitle: 'ویرایش حساب بانکی',
|
||||
nationalCodeHint: 'کد ملی برای استعلام شبا لازم است. پس از تأیید احراز هویت میزبان، از روی پرونده هویت خوانده میشود.',
|
||||
withdrawalNeedsBankAccount: 'ابتدا یک حساب بانکی ثبت کنید؛ پس از تأیید حساب میتوانید درخواست برداشت بدهید',
|
||||
withdrawalNeedsApprovedBankAccount: 'حساب بانکی شما هنوز تأیید نشده است؛ پس از تأیید ادمین میتوانید درخواست برداشت بدهید',
|
||||
submitAccount: 'ثبت حساب',
|
||||
accountHolder: 'نام صاحب حساب',
|
||||
accountHolderPlaceholder: 'نام و نام خانوادگی',
|
||||
saveAccount: 'ذخیره تغییرات',
|
||||
iban: 'شماره شبا',
|
||||
bankNameOptional: 'نام بانک (اختیاری)',
|
||||
bankNamePlaceholder: 'مثلاً ملت',
|
||||
|
||||
submitRequest: 'ثبت درخواست',
|
||||
bankAccount: 'حساب بانکی',
|
||||
bankAccountOption: '{holder} — {iban}',
|
||||
bankAccountOption: '{iban}',
|
||||
maxAmount: 'حداکثر: {amount}',
|
||||
amountToman: 'مبلغ (تومان)',
|
||||
|
||||
@ -45,6 +39,7 @@ export const wallet = {
|
||||
txDeposit: 'واریز',
|
||||
|
||||
bankAccountCreated: 'حساب بانکی ثبت شد',
|
||||
bankAccountUpdated: 'حساب بانکی بهروز شد و دوباره در صف بررسی قرار گرفت',
|
||||
bankAccountCreateFailed: 'ثبت حساب بانکی ناموفق بود',
|
||||
addBankAccountFirst: 'ابتدا یک حساب بانکی اضافه کنید',
|
||||
withdrawalCreated: 'درخواست برداشت ثبت شد',
|
||||
|
||||
@ -4,18 +4,33 @@ import { CreateBankAccountFormValidation, CreateWithdrawalRequestFormValidation
|
||||
|
||||
describe('financial form validation', () => {
|
||||
it('normalizes and validates Iranian IBAN values', () => {
|
||||
const result = CreateBankAccountFormValidation.parse({
|
||||
ownerType: 'organizer',
|
||||
accountHolder: 'علی رضایی',
|
||||
const result = CreateBankAccountFormValidation(false).parse({
|
||||
iban: 'ir12 3456 7890 1234 5678 9012 34',
|
||||
bankName: 'بانک',
|
||||
nationalCode: '',
|
||||
})
|
||||
|
||||
expect(result.iban).toBe('IR123456789012345678901234')
|
||||
})
|
||||
|
||||
it.each(['IR123', 'DE123456789012345678901234', 'IR12345678901234567890123A'])('rejects an invalid IBAN: %s', (iban) => {
|
||||
expect(CreateBankAccountFormValidation.safeParse({ ownerType: 'organizer', accountHolder: 'علی رضایی', iban }).success).toBe(false)
|
||||
expect(CreateBankAccountFormValidation(false).safeParse({ iban, nationalCode: '' }).success).toBe(false)
|
||||
})
|
||||
|
||||
it('normalizes Persian digits for IBAN and nationalCode', () => {
|
||||
const result = CreateBankAccountFormValidation(true).parse({
|
||||
iban: 'ir۱۲ ۳۴۵۶ ۷۸۹۰ ۱۲۳۴ ۵۶۷۸ ۹۰۱۲ ۳۴',
|
||||
nationalCode: '۰۰۱۲۳۴۵۶۷۸',
|
||||
})
|
||||
|
||||
expect(result.iban).toBe('IR123456789012345678901234')
|
||||
expect(result.nationalCode).toBe('0012345678')
|
||||
})
|
||||
|
||||
it('requires nationalCode when identity is not verified', () => {
|
||||
expect(CreateBankAccountFormValidation(true).safeParse({ iban: 'IR123456789012345678901234' }).success).toBe(false)
|
||||
expect(
|
||||
CreateBankAccountFormValidation(true).safeParse({ iban: 'IR123456789012345678901234', nationalCode: '0012345678' }).success
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('accepts only positive integer withdrawal amounts', () => {
|
||||
|
||||
@ -1,38 +1,37 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
import { convertPersianToEnglish } from '@/helpers'
|
||||
import { texts } from '@/texts'
|
||||
|
||||
const v = texts.validation.withdrawal
|
||||
const nationalId = texts.validation.nationalId
|
||||
|
||||
export const CreateBankAccountFormValidation = z
|
||||
const toEnglishDigits = (value: string) => convertPersianToEnglish(value).trim()
|
||||
|
||||
export const CreateBankAccountFormValidation = (requiresNationalCode: boolean) =>
|
||||
z
|
||||
.object({
|
||||
ownerType: z.enum(['guest', 'organizer']),
|
||||
accountHolder: z.string().trim().min(3, v.accountHolderRequired).max(200, v.accountHolderMax),
|
||||
iban: z
|
||||
.string()
|
||||
.trim()
|
||||
.transform((value) => value.toUpperCase().replace(/\s+/g, ''))
|
||||
.transform((value) => toEnglishDigits(value).toUpperCase().replace(/\s+/g, ''))
|
||||
.refine((value) => /^IR\d{24}$/.test(value), v.ibanInvalid),
|
||||
nationalCode: z.string().trim().optional().or(z.literal('')),
|
||||
bankName: z.string().trim().max(100, v.bankNameMax).optional().or(z.literal('')),
|
||||
nationalCode: z.string().trim().transform((value) => toEnglishDigits(value)),
|
||||
})
|
||||
.superRefine((value, ctx) => {
|
||||
if (value.ownerType !== 'guest') return
|
||||
if (!requiresNationalCode) return
|
||||
|
||||
const nationalCode = value.nationalCode ?? ''
|
||||
|
||||
if (!nationalCode) {
|
||||
if (!value.nationalCode) {
|
||||
ctx.addIssue({ code: 'custom', path: ['nationalCode'], message: nationalId.nationalCodeRequired })
|
||||
|
||||
return
|
||||
}
|
||||
if (!/^\d{10}$/.test(nationalCode)) {
|
||||
if (!/^\d{10}$/.test(value.nationalCode)) {
|
||||
ctx.addIssue({ code: 'custom', path: ['nationalCode'], message: nationalId.nationalCodeDigits })
|
||||
}
|
||||
})
|
||||
|
||||
export type CreateBankAccountFormValues = z.infer<typeof CreateBankAccountFormValidation>
|
||||
export type CreateBankAccountFormValues = z.infer<ReturnType<typeof CreateBankAccountFormValidation>>
|
||||
|
||||
export const CreateWithdrawalRequestFormValidation = z.object({
|
||||
bankAccountId: z.string().min(1, v.bankAccountRequired),
|
||||
@ -40,6 +39,7 @@ export const CreateWithdrawalRequestFormValidation = z.object({
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, v.amountRequired)
|
||||
.transform((value) => toEnglishDigits(value))
|
||||
.refine((value) => /^\d+$/.test(value), v.amountInteger)
|
||||
.refine((value) => Number(value) > 0, v.amountPositive),
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user