feat(treasury): separate receipts from income and fit finance on a phone

A receipt or payment only reduces the party balance, while an income or expense keeps both amounts. Finance screens stack into cards on a phone so the forms and lists stay readable.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-10 12:28:27 +03:30
co-authored by Cursor
parent 49459707cb
commit d12d7b91cb
16 changed files with 626 additions and 537 deletions
@@ -7,12 +7,15 @@ import {
Card as ACard,
Checkbox as ACheckbox,
Col as ACol,
Descriptions as ADescriptions,
DescriptionsItem as ADescriptionsItem,
Divider as ADivider,
Empty as AEmpty,
Flex as AFlex,
Form as AForm,
FormItem as AFormItem,
Input as AInput,
List as AList,
Row as ARow,
Select as ASelect,
Space as ASpace,
@@ -27,6 +30,7 @@ import type { FinancialAccountDto, FinancialReceivableDto } from '@mento/shared'
import MoneyInput from '@/components/MoneyInput.vue';
import JalaliDateInput from '@/components/JalaliDateInput.vue';
import { toman } from './treasury-format';
import { useTreasuryMobile } from './useTreasuryMobile';
import { formatJalaliNumeric } from '@/lib/jalali';
import { parseMoneyNumber } from '@/lib/money';
@@ -87,6 +91,9 @@ const installmentColumns: TableColumnsType<InstallmentRow> = [
{ title: '', key: 'actions' },
];
const isMobile = useTreasuryMobile();
const AListItem = AList.Item;
function apiMessage(e: unknown) {
if (!axios.isAxiosError(e)) return 'عملیات ناموفق بود.';
const msg = (e.response?.data as { message?: string | string[] })?.message;
@@ -365,7 +372,33 @@ onMounted(() => {
</div>
<a-typography-text strong>{{ toman(row.totalAmount) }}</a-typography-text>
</a-flex>
<a-list v-if="isMobile" :data-source="row.installments">
<template #renderItem="{ item }">
<a-list-item>
<a-card :title="`قسط ${item.position}`" style="width: 100%">
<a-descriptions :column="1" bordered>
<a-descriptions-item label="سررسید">
{{ formatJalaliNumeric(new Date(item.dueDate)) }}
</a-descriptions-item>
<a-descriptions-item label="مبلغ">{{ toman(item.amount) }}</a-descriptions-item>
<a-descriptions-item label="مانده">{{ toman(item.remaining) }}</a-descriptions-item>
<a-descriptions-item label="وضعیت">
{{ statusLabel[item.status] ?? item.status }}
</a-descriptions-item>
</a-descriptions>
<a-button
v-if="item.status !== 'settled'"
type="link"
@click="startCollect(row, item)"
>
دریافت
</a-button>
</a-card>
</a-list-item>
</template>
</a-list>
<a-table
v-else
:columns="installmentColumns"
:data-source="row.installments"
:pagination="false"