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