- Updated SubscriptionPeriod interface to include tax-related fields: tax_percent, tax_rials, and payable_rials. - Modified payment API documentation to reflect changes in tax handling for subscriptions and SMS wallet charges. - Adjusted PaymentController to calculate payment amounts based on subscription period details instead of client input. - Enhanced PaymentManager to handle net amounts for SMS wallet charges, ensuring tax is not credited to the wallet. - Created PaymentTaxCalculator and SubscriptionTaxCalculator services to manage tax calculations consistently across payment types. - Added tests for tax calculations in both subscription and SMS wallet contexts, ensuring correct behavior with and without tax enabled. - Updated frontend components to display tax information appropriately during payment processes.
84 lines
3.7 KiB
TypeScript
84 lines
3.7 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { screen, fireEvent } from '@testing-library/react';
|
|
import { renderWithProviders } from '../test/utils';
|
|
import { formatRial, tomanToRial } from '../lib/utils';
|
|
|
|
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }));
|
|
vi.mock('../lib/api', () => ({
|
|
api: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), put: vi.fn(), delete: vi.fn() },
|
|
ApiError: class extends Error {},
|
|
}));
|
|
|
|
import { api } from '../lib/api';
|
|
import { useAuthStore } from '../stores/authStore';
|
|
import SmsWalletPage from './SmsWalletPage';
|
|
|
|
const get = api.get as ReturnType<typeof vi.fn>;
|
|
|
|
/**
|
|
* مبلغِ واردشده اعتبارِ کیف پول است — خالص. مالیات رویش اضافه میشود، پس عددِ
|
|
* دکمهٔ پرداخت باید بزرگتر از مبلغ واردشده باشد وگرنه کاربر سرِ درگاه غافلگیر
|
|
* میشود.
|
|
*/
|
|
function mockApi(taxPercent: number) {
|
|
get.mockImplementation((url: string) => {
|
|
if (url.includes('/sms/wallet/balance')) {
|
|
return Promise.resolve({ success: true, data: { balance_rials: 0, sms_price_rials: 5000 } });
|
|
}
|
|
if (url.includes('/sms/wallet/logs')) {
|
|
return Promise.resolve({ success: true, data: [], meta: { totalRecords: 0, totalPages: 0, currentPage: 1 } });
|
|
}
|
|
if (url.includes('/sms/settings')) return Promise.resolve({ success: true, data: null });
|
|
// FeatureGate صفحه را پشت قابلیت پنل پیامک نگه میدارد.
|
|
if (url.includes('/subscription/my')) {
|
|
return Promise.resolve({
|
|
success: true,
|
|
data: { subscription: null, used_trial: false, effective_plan: { features: { sms_panel: true } } },
|
|
});
|
|
}
|
|
if (url.includes('/payment/config')) {
|
|
return Promise.resolve({
|
|
success: true,
|
|
data: { test_mode: false, appointment_fee_rials: 0, tax_percent: taxPercent, gateways: [{ name: 'mellat', label: 'بانک ملت' }] },
|
|
});
|
|
}
|
|
return Promise.resolve({ success: true, data: null });
|
|
});
|
|
}
|
|
|
|
async function openChargeModalWith(amountToman: string) {
|
|
renderWithProviders(<SmsWalletPage />, { route: '/admin/sms-wallet' });
|
|
fireEvent.click(await screen.findByText('شارژ کیف پول'));
|
|
|
|
const input = await screen.findByLabelText('مبلغ شارژ (تومان)');
|
|
fireEvent.change(input, { target: { value: amountToman } });
|
|
}
|
|
|
|
describe('SmsWalletPage — tax', () => {
|
|
beforeEach(() => {
|
|
get.mockReset();
|
|
// useSubscription فقط برای این نقشها کوئری میزند؛ بدونش FeatureGate صفحه را میبندد.
|
|
useAuthStore.setState({ primaryRole: 'doctor', context: null } as any);
|
|
});
|
|
|
|
it('adds tax on top of the credit and shows the split', async () => {
|
|
mockApi(10);
|
|
await openChargeModalWith('100000');
|
|
|
|
const net = tomanToRial(100000);
|
|
expect(await screen.findByText('اعتباری که به کیف پول اضافه میشود')).toBeInTheDocument();
|
|
expect(screen.getByText('مالیات بر ارزش افزوده ۱۰٪')).toBeInTheDocument();
|
|
expect(screen.getByText(formatRial(net * 0.1))).toBeInTheDocument();
|
|
expect(screen.getAllByText(formatRial(net * 1.1)).length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('shows no tax row and charges the plain amount when tax is off', async () => {
|
|
mockApi(0);
|
|
await openChargeModalWith('100000');
|
|
|
|
expect(await screen.findByText(/پرداخت .* از طریق بانک ملت/)).toBeInTheDocument();
|
|
expect(screen.queryByText('اعتباری که به کیف پول اضافه میشود')).not.toBeInTheDocument();
|
|
expect(screen.getByText(new RegExp(formatRial(tomanToRial(100000))))).toBeInTheDocument();
|
|
});
|
|
});
|