2026-10-18 18:49:49 +03:30
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { computed, onMounted, ref } from 'vue';
|
2026-10-18 21:06:18 +03:30
|
|
|
|
import { RouterLink } from 'vue-router';
|
2026-10-18 18:49:49 +03:30
|
|
|
|
import axios from 'axios';
|
2026-10-18 21:06:18 +03:30
|
|
|
|
import {
|
|
|
|
|
|
Alert as AAlert,
|
|
|
|
|
|
Button as AButton,
|
|
|
|
|
|
Card as ACard,
|
|
|
|
|
|
Col as ACol,
|
|
|
|
|
|
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,
|
|
|
|
|
|
Statistic as AStatistic,
|
|
|
|
|
|
Tag as ATag,
|
|
|
|
|
|
Typography,
|
2026-10-21 21:47:00 +03:30
|
|
|
|
message,
|
2026-10-18 21:06:18 +03:30
|
|
|
|
} from 'ant-design-vue';
|
2026-10-18 18:49:49 +03:30
|
|
|
|
import { api } from '@/lib/api';
|
2026-11-04 10:02:31 +03:30
|
|
|
|
import type { FinancialAccountDto, FinancialAccountType, OrgUserDto } from '@mento/shared';
|
2026-10-21 21:47:00 +03:30
|
|
|
|
import MoneyInput from '@/components/MoneyInput.vue';
|
|
|
|
|
|
import JalaliDateInput from '@/components/JalaliDateInput.vue';
|
2026-10-18 18:49:49 +03:30
|
|
|
|
import { toman } from './treasury-format';
|
2026-10-21 21:47:00 +03:30
|
|
|
|
import { parseMoneyNumber } from '@/lib/money';
|
2026-10-18 18:49:49 +03:30
|
|
|
|
|
2026-10-21 21:47:00 +03:30
|
|
|
|
const { Text: ATypographyText } = Typography;
|
2026-10-18 21:06:18 +03:30
|
|
|
|
const AListItem = AList.Item;
|
|
|
|
|
|
const AListItemMeta = AList.Item.Meta;
|
|
|
|
|
|
const ASelectOption = ASelect.Option;
|
|
|
|
|
|
|
2026-10-18 18:49:49 +03:30
|
|
|
|
const accounts = ref<FinancialAccountDto[]>([]);
|
2026-11-04 10:02:31 +03:30
|
|
|
|
const users = ref<OrgUserDto[]>([]);
|
2026-10-18 18:49:49 +03:30
|
|
|
|
const error = ref('');
|
|
|
|
|
|
const ok = ref('');
|
|
|
|
|
|
const openForm = ref(false);
|
2026-11-04 10:02:31 +03:30
|
|
|
|
const editingId = ref<string | null>(null);
|
|
|
|
|
|
const responsibleKey = ref<string | undefined>(undefined);
|
2026-10-18 18:49:49 +03:30
|
|
|
|
const saving = ref(false);
|
|
|
|
|
|
const transferOpen = ref(false);
|
2026-11-04 10:02:31 +03:30
|
|
|
|
const emptyForm = () => ({
|
2026-10-18 18:49:49 +03:30
|
|
|
|
name: '',
|
|
|
|
|
|
type: 'cash' as FinancialAccountType,
|
|
|
|
|
|
bankName: '',
|
|
|
|
|
|
accountRef: '',
|
|
|
|
|
|
responsibleName: '',
|
|
|
|
|
|
openingBalance: '',
|
|
|
|
|
|
openingDate: '',
|
|
|
|
|
|
notes: '',
|
|
|
|
|
|
});
|
2026-11-04 10:02:31 +03:30
|
|
|
|
const form = ref(emptyForm());
|
2026-10-18 18:49:49 +03:30
|
|
|
|
const transfer = ref({
|
|
|
|
|
|
fromAccountId: '',
|
|
|
|
|
|
toAccountId: '',
|
|
|
|
|
|
amount: '',
|
|
|
|
|
|
memo: '',
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const active = computed(() => accounts.value.filter((row) => row.isActive));
|
|
|
|
|
|
const total = computed(() =>
|
|
|
|
|
|
active.value.reduce((sum, row) => sum + Number(row.balance), 0),
|
|
|
|
|
|
);
|
|
|
|
|
|
const periodIn = computed(() =>
|
|
|
|
|
|
active.value.reduce((sum, row) => sum + Number(row.periodIn), 0),
|
|
|
|
|
|
);
|
|
|
|
|
|
const periodOut = computed(() =>
|
|
|
|
|
|
active.value.reduce((sum, row) => sum + Number(row.periodOut), 0),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const typeLabel: Record<string, string> = {
|
|
|
|
|
|
bank: 'حساب بانکی',
|
|
|
|
|
|
cash: 'صندوق نقدی',
|
|
|
|
|
|
petty: 'تنخواه',
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function apiMessage(e: unknown) {
|
|
|
|
|
|
if (!axios.isAxiosError(e)) return 'عملیات ناموفق بود.';
|
|
|
|
|
|
const msg = (e.response?.data as { message?: string | string[] })?.message;
|
|
|
|
|
|
return Array.isArray(msg) ? msg.join('، ') : msg || 'عملیات ناموفق بود.';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-04 10:02:31 +03:30
|
|
|
|
const responsibleOptions = computed(() => {
|
|
|
|
|
|
const rows = users.value.map((user) => ({ id: user.id, fullName: user.fullName }));
|
|
|
|
|
|
const current = form.value.responsibleName.trim();
|
|
|
|
|
|
if (current && !rows.some((row) => row.fullName === current)) {
|
|
|
|
|
|
rows.unshift({ id: `name:${current}`, fullName: current });
|
|
|
|
|
|
}
|
|
|
|
|
|
return rows;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-10-18 18:49:49 +03:30
|
|
|
|
async function load() {
|
|
|
|
|
|
const { data } = await api.get<FinancialAccountDto[]>('/treasury/accounts');
|
|
|
|
|
|
accounts.value = data;
|
2026-11-04 10:02:31 +03:30
|
|
|
|
try {
|
|
|
|
|
|
const usersRes = await api.get<OrgUserDto[]>('/users');
|
|
|
|
|
|
users.value = usersRes.data;
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
users.value = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resetForm() {
|
|
|
|
|
|
editingId.value = null;
|
|
|
|
|
|
responsibleKey.value = undefined;
|
|
|
|
|
|
form.value = emptyForm();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function startEdit(item: FinancialAccountDto) {
|
|
|
|
|
|
editingId.value = item.id;
|
|
|
|
|
|
form.value = {
|
|
|
|
|
|
name: item.name,
|
|
|
|
|
|
type: item.type,
|
|
|
|
|
|
bankName: item.bankName ?? '',
|
|
|
|
|
|
accountRef: item.accountRef ?? '',
|
|
|
|
|
|
responsibleName: item.responsibleName ?? '',
|
|
|
|
|
|
openingBalance: item.openingBalance,
|
|
|
|
|
|
openingDate: item.openingDate.slice(0, 10),
|
|
|
|
|
|
notes: item.notes ?? '',
|
|
|
|
|
|
};
|
|
|
|
|
|
const match = users.value.find((user) => user.fullName === item.responsibleName);
|
|
|
|
|
|
responsibleKey.value = match?.id ?? (item.responsibleName ? `name:${item.responsibleName}` : undefined);
|
|
|
|
|
|
openForm.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function responsibleNameFromKey() {
|
|
|
|
|
|
const key = responsibleKey.value;
|
|
|
|
|
|
if (!key) return null;
|
|
|
|
|
|
if (key.startsWith('name:')) return key.slice(5);
|
|
|
|
|
|
return users.value.find((user) => user.id === key)?.fullName ?? null;
|
2026-10-18 18:49:49 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-21 21:47:00 +03:30
|
|
|
|
function onFinishFailed() {
|
|
|
|
|
|
message.warning('لطفاً فیلدهای الزامی را کامل کنید.');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-18 18:49:49 +03:30
|
|
|
|
async function createAccount() {
|
|
|
|
|
|
saving.value = true;
|
|
|
|
|
|
error.value = '';
|
|
|
|
|
|
ok.value = '';
|
|
|
|
|
|
try {
|
2026-10-21 21:47:00 +03:30
|
|
|
|
const openingBalance = parseMoneyNumber(String(form.value.openingBalance ?? ''), 0) ?? 0;
|
2026-11-04 10:02:31 +03:30
|
|
|
|
const body = {
|
2026-10-18 18:49:49 +03:30
|
|
|
|
...form.value,
|
2026-11-04 10:02:31 +03:30
|
|
|
|
responsibleName: responsibleNameFromKey(),
|
2026-10-21 21:47:00 +03:30
|
|
|
|
openingBalance,
|
2026-10-18 18:49:49 +03:30
|
|
|
|
openingDate: form.value.openingDate || undefined,
|
|
|
|
|
|
};
|
2026-11-04 10:02:31 +03:30
|
|
|
|
if (editingId.value) {
|
|
|
|
|
|
await api.patch(`/treasury/accounts/${editingId.value}`, body);
|
|
|
|
|
|
ok.value = 'حساب بهروز شد. موجودی از مانده افتتاحیه و گردش قطعی محاسبه میشود.';
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await api.post('/treasury/accounts', body);
|
|
|
|
|
|
ok.value = 'حساب ثبت شد. موجودی اولیه درآمد دوره نیست.';
|
|
|
|
|
|
}
|
|
|
|
|
|
openForm.value = false;
|
|
|
|
|
|
resetForm();
|
2026-10-18 18:49:49 +03:30
|
|
|
|
await load();
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = apiMessage(e);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
saving.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function sendTransfer() {
|
|
|
|
|
|
saving.value = true;
|
|
|
|
|
|
error.value = '';
|
|
|
|
|
|
ok.value = '';
|
|
|
|
|
|
try {
|
2026-10-21 21:47:00 +03:30
|
|
|
|
const amount = parseMoneyNumber(String(transfer.value.amount ?? ''), 0);
|
|
|
|
|
|
if (amount == null || amount <= 0) {
|
|
|
|
|
|
error.value = 'مبلغ انتقال معتبر نیست.';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-10-18 18:49:49 +03:30
|
|
|
|
await api.post('/treasury/transfers', {
|
2026-10-21 21:47:00 +03:30
|
|
|
|
fromAccountId: transfer.value.fromAccountId,
|
|
|
|
|
|
toAccountId: transfer.value.toAccountId,
|
|
|
|
|
|
amount,
|
|
|
|
|
|
memo: transfer.value.memo || undefined,
|
2026-10-18 18:49:49 +03:30
|
|
|
|
clientKey: crypto.randomUUID(),
|
|
|
|
|
|
});
|
|
|
|
|
|
transferOpen.value = false;
|
2026-10-21 21:47:00 +03:30
|
|
|
|
transfer.value = { fromAccountId: '', toAccountId: '', amount: '', memo: '' };
|
2026-10-18 18:49:49 +03:30
|
|
|
|
ok.value = 'انتقال ثبت شد و در درآمد یا هزینه نیامده است.';
|
|
|
|
|
|
await load();
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = apiMessage(e);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
saving.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
void load().catch(() => {
|
|
|
|
|
|
error.value = 'بارگذاری حسابها ناموفق بود.';
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<a-flex vertical :gap="16">
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<a-alert v-if="error" type="error" show-icon :message="error" />
|
|
|
|
|
|
<a-alert v-if="ok" type="success" show-icon :message="ok" />
|
|
|
|
|
|
|
|
|
|
|
|
<a-row :gutter="[16, 16]">
|
|
|
|
|
|
<a-col :xs="24" :sm="12" :md="6">
|
|
|
|
|
|
<a-card :bordered="false">
|
|
|
|
|
|
<a-statistic title="مجموع موجودی حسابها" :value="toman(total)" />
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :sm="12" :md="6">
|
|
|
|
|
|
<a-card :bordered="false">
|
|
|
|
|
|
<a-statistic
|
|
|
|
|
|
title="ورودی این فهرست"
|
|
|
|
|
|
:value="toman(periodIn)"
|
|
|
|
|
|
:value-style="{ color: 'var(--ant-color-success)' }"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :sm="12" :md="6">
|
|
|
|
|
|
<a-card :bordered="false">
|
|
|
|
|
|
<a-statistic
|
|
|
|
|
|
title="خروجی این فهرست"
|
|
|
|
|
|
:value="toman(periodOut)"
|
|
|
|
|
|
:value-style="{ color: 'var(--ant-color-error)' }"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :sm="12" :md="6">
|
|
|
|
|
|
<a-card :bordered="false">
|
|
|
|
|
|
<a-statistic title="حسابهای فعال" :value="`${active.length} حساب`" />
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
</a-row>
|
|
|
|
|
|
|
|
|
|
|
|
<a-card :bordered="false">
|
|
|
|
|
|
<a-space>
|
2026-11-04 10:02:31 +03:30
|
|
|
|
<a-button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
@click="
|
|
|
|
|
|
openForm = !openForm;
|
|
|
|
|
|
if (!openForm) resetForm();
|
|
|
|
|
|
"
|
|
|
|
|
|
>افزودن حساب جدید</a-button>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<a-button @click="transferOpen = !transferOpen">انتقال وجه</a-button>
|
|
|
|
|
|
</a-space>
|
|
|
|
|
|
|
|
|
|
|
|
<template v-if="openForm">
|
2026-11-04 10:02:31 +03:30
|
|
|
|
<a-divider orientation="right">{{ editingId ? 'ویرایش حساب' : 'حساب جدید' }}</a-divider>
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<a-form
|
|
|
|
|
|
layout="vertical"
|
|
|
|
|
|
:model="form"
|
|
|
|
|
|
@finish="createAccount"
|
|
|
|
|
|
@finishFailed="onFinishFailed"
|
|
|
|
|
|
>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<a-row :gutter="[16, 0]">
|
|
|
|
|
|
<a-col :xs="24" :md="12">
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<a-form-item
|
|
|
|
|
|
label="نام حساب"
|
|
|
|
|
|
name="name"
|
|
|
|
|
|
:rules="[{ required: true, message: 'نام حساب الزامی است' }]"
|
|
|
|
|
|
>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<a-input v-model:value="form.name" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<a-form-item label="نوع" name="type" :rules="[{ required: true }]">
|
2026-11-04 10:02:31 +03:30
|
|
|
|
<a-select
|
|
|
|
|
|
v-model:value="form.type"
|
|
|
|
|
|
show-search
|
|
|
|
|
|
option-filter-prop="label"
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-select-option value="cash" label="صندوق نقدی">صندوق نقدی</a-select-option>
|
|
|
|
|
|
<a-select-option value="bank" label="حساب بانکی">حساب بانکی</a-select-option>
|
|
|
|
|
|
<a-select-option value="petty" label="تنخواه">تنخواه</a-select-option>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
</a-select>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col v-if="form.type === 'bank'" :xs="24" :md="8">
|
|
|
|
|
|
<a-form-item label="نام بانک" name="bankName">
|
|
|
|
|
|
<a-input v-model:value="form.bankName" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
|
|
|
|
|
<a-form-item label="شماره یا شناسه" name="accountRef">
|
|
|
|
|
|
<a-input v-model:value="form.accountRef" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
2026-11-04 10:02:31 +03:30
|
|
|
|
<a-form-item label="مسئول">
|
|
|
|
|
|
<a-select
|
|
|
|
|
|
v-model:value="responsibleKey"
|
|
|
|
|
|
show-search
|
|
|
|
|
|
allow-clear
|
|
|
|
|
|
option-filter-prop="label"
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
placeholder="انتخاب کاربر"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-select-option
|
|
|
|
|
|
v-for="user in responsibleOptions"
|
|
|
|
|
|
:key="user.id"
|
|
|
|
|
|
:value="user.id"
|
|
|
|
|
|
:label="user.fullName"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ user.fullName }}
|
|
|
|
|
|
</a-select-option>
|
|
|
|
|
|
</a-select>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
|
|
|
|
|
<a-form-item label="موجودی اولیه" name="openingBalance">
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<MoneyInput v-model="form.openingBalance" :decimals="0" />
|
2026-10-18 21:06:18 +03:30
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
|
|
|
|
|
<a-form-item label="تاریخ شروع" name="openingDate">
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<JalaliDateInput v-model="form.openingDate" />
|
2026-10-18 21:06:18 +03:30
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="12">
|
|
|
|
|
|
<a-form-item label="توضیحات" name="notes">
|
|
|
|
|
|
<a-input v-model:value="form.notes" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
</a-row>
|
2026-11-04 10:02:31 +03:30
|
|
|
|
<a-button type="primary" html-type="submit" :loading="saving">
|
|
|
|
|
|
{{ editingId ? 'ذخیره تغییرات' : 'ثبت حساب' }}
|
|
|
|
|
|
</a-button>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
</a-form>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<template v-if="transferOpen">
|
|
|
|
|
|
<a-divider orientation="right">انتقال بین حسابها</a-divider>
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<a-form
|
|
|
|
|
|
layout="vertical"
|
|
|
|
|
|
:model="transfer"
|
|
|
|
|
|
@finish="sendTransfer"
|
|
|
|
|
|
@finishFailed="onFinishFailed"
|
|
|
|
|
|
>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<a-row :gutter="[16, 0]">
|
|
|
|
|
|
<a-col :xs="24" :md="12">
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<a-form-item
|
|
|
|
|
|
label="از حساب"
|
|
|
|
|
|
name="fromAccountId"
|
|
|
|
|
|
:rules="[{ required: true, message: 'حساب مبدأ الزامی است' }]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-select
|
|
|
|
|
|
v-model:value="transfer.fromAccountId"
|
2026-11-04 10:02:31 +03:30
|
|
|
|
show-search
|
|
|
|
|
|
option-filter-prop="label"
|
2026-10-21 21:47:00 +03:30
|
|
|
|
allow-clear
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
placeholder="انتخاب"
|
|
|
|
|
|
>
|
2026-11-04 10:02:31 +03:30
|
|
|
|
<a-select-option
|
|
|
|
|
|
v-for="account in active"
|
|
|
|
|
|
:key="account.id"
|
|
|
|
|
|
:value="account.id"
|
|
|
|
|
|
:label="account.name"
|
|
|
|
|
|
>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
{{ account.name }}
|
|
|
|
|
|
</a-select-option>
|
|
|
|
|
|
</a-select>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="12">
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<a-form-item
|
|
|
|
|
|
label="به حساب"
|
|
|
|
|
|
name="toAccountId"
|
|
|
|
|
|
:rules="[{ required: true, message: 'حساب مقصد الزامی است' }]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-select
|
|
|
|
|
|
v-model:value="transfer.toAccountId"
|
2026-11-04 10:02:31 +03:30
|
|
|
|
show-search
|
|
|
|
|
|
option-filter-prop="label"
|
2026-10-21 21:47:00 +03:30
|
|
|
|
allow-clear
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
placeholder="انتخاب"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-select-option
|
|
|
|
|
|
v-for="account in active"
|
|
|
|
|
|
:key="`to-${account.id}`"
|
|
|
|
|
|
:value="account.id"
|
2026-11-04 10:02:31 +03:30
|
|
|
|
:label="account.name"
|
2026-10-21 21:47:00 +03:30
|
|
|
|
>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
{{ account.name }}
|
|
|
|
|
|
</a-select-option>
|
|
|
|
|
|
</a-select>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
2026-10-21 21:47:00 +03:30
|
|
|
|
<a-form-item
|
|
|
|
|
|
label="مبلغ"
|
|
|
|
|
|
name="amount"
|
|
|
|
|
|
:rules="[{ required: true, message: 'مبلغ الزامی است' }]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<MoneyInput v-model="transfer.amount" :decimals="0" />
|
2026-10-18 21:06:18 +03:30
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="12">
|
|
|
|
|
|
<a-form-item label="شرح" name="memo">
|
|
|
|
|
|
<a-input v-model:value="transfer.memo" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
</a-row>
|
|
|
|
|
|
<a-typography-text type="secondary">
|
|
|
|
|
|
این انتقال درآمد یا هزینه نیست. اگر موجودی مبدأ کافی نباشد ثبت نمیشود.
|
|
|
|
|
|
</a-typography-text>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<a-button type="primary" html-type="submit" :loading="saving">تأیید انتقال</a-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-form>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<a-divider orientation="right">فهرست حسابها</a-divider>
|
|
|
|
|
|
<a-empty v-if="!accounts.length" description="حسابی ثبت نشده است." />
|
|
|
|
|
|
<a-list v-else item-layout="horizontal" :data-source="accounts">
|
|
|
|
|
|
<template #renderItem="{ item }">
|
|
|
|
|
|
<a-list-item>
|
|
|
|
|
|
<template #actions>
|
|
|
|
|
|
<a-typography-text strong>{{ toman(item.balance) }}</a-typography-text>
|
2026-11-04 10:02:31 +03:30
|
|
|
|
<a-button type="link" @click="startEdit(item)">ویرایش</a-button>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<RouterLink :to="`/finance/accounts/${item.id}`">جزئیات حساب</RouterLink>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<a-list-item-meta>
|
|
|
|
|
|
<template #title>
|
|
|
|
|
|
{{ item.name }}
|
|
|
|
|
|
<a-tag v-if="!item.isActive">غیرفعال</a-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #description>
|
2026-11-04 10:02:31 +03:30
|
|
|
|
{{ typeLabel[item.type] }}
|
|
|
|
|
|
<template v-if="item.responsibleName"> · {{ item.responsibleName }}</template>
|
|
|
|
|
|
· موجودی اولیه {{ toman(item.openingBalance) }}
|
2026-10-18 21:06:18 +03:30
|
|
|
|
</template>
|
|
|
|
|
|
</a-list-item-meta>
|
|
|
|
|
|
</a-list-item>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-list>
|
|
|
|
|
|
</a-card>
|
2026-10-21 21:47:00 +03:30
|
|
|
|
</a-flex>
|
2026-10-18 18:49:49 +03:30
|
|
|
|
</template>
|