feat: add treasury audit, attachment download, and installment SMS

Let cheque clearance pick a cash account and export income, expense, and installment lists to Excel.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-18 19:59:24 +03:30
co-authored by Cursor
parent 8981a552fc
commit 96befd3867
11 changed files with 285 additions and 21 deletions
+47 -18
View File
@@ -27,6 +27,10 @@ const items = ref<ChequeDto[]>([]);
const status = ref('');
const q = ref('');
const savingId = ref('');
const clearOpen = ref(false);
const clearRow = ref<ChequeDto | null>(null);
const clearAccountId = ref('');
const clearAccounts = ref<Array<{ id: string; name: string }>>([]);
const tab = ref<ChequeDirection>('in');
const formOpen = ref(false);
@@ -95,9 +99,26 @@ async function load() {
}
}
async function confirmClear() {
if (!clearRow.value || !clearAccountId.value) return;
savingId.value = clearRow.value.id;
error.value = '';
try {
const { data } = await api.patch<ChequeDto>(`/cheques/${clearRow.value.id}`, {
status: 'cleared',
financialAccountId: clearAccountId.value,
});
items.value = items.value.map((item) => (item.id === data.id ? data : item));
clearOpen.value = false;
} catch {
error.value = 'وصول چک ناموفق بود.';
} finally {
savingId.value = '';
}
}
async function setStatus(row: ChequeDto, next: ChequeStatus) {
if (row.status === next) return;
let financialAccountId: string | undefined;
if (next === 'cleared' && row.status !== 'cleared') {
const accountsRes = await api
.get<Array<{ id: string; name: string; isActive: boolean }>>('/treasury/accounts')
@@ -114,30 +135,20 @@ async function setStatus(row: ChequeDto, next: ChequeStatus) {
}
return;
}
const accounts = accountsRes.data;
const active = accounts.filter((item) => item.isActive);
const active = accountsRes.data.filter((item) => item.isActive);
if (!active.length) {
error.value = 'برای وصول چک ابتدا یک حساب صندوق بسازید.';
return;
}
if (active.length === 1) {
financialAccountId = active[0]!.id;
} else {
const picked = window.prompt(`نام حساب صندوق را بنویسید: ${active.map((item) => item.name).join('، ')}`);
const match = active.find((item) => item.name.trim() === picked?.trim());
if (!match) {
error.value = 'حساب صندوق انتخاب نشد.';
return;
}
financialAccountId = match.id;
}
clearAccounts.value = active.map((item) => ({ id: item.id, name: item.name }));
clearAccountId.value = active[0]!.id;
clearRow.value = row;
clearOpen.value = true;
return;
}
savingId.value = row.id;
try {
const { data } = await api.patch<ChequeDto>(`/cheques/${row.id}`, {
status: next,
...(financialAccountId ? { financialAccountId } : {}),
});
const { data } = await api.patch<ChequeDto>(`/cheques/${row.id}`, { status: next });
items.value = items.value.map((item) => (item.id === row.id ? data : item));
} catch {
error.value = 'تغییر وضعیت چک ناموفق بود.';
@@ -326,6 +337,24 @@ onMounted(load);
</table>
</div>
<UiModal :open="clearOpen" title="حساب وصول چک" @close="clearOpen = false">
<p class="hint">چک {{ clearRow?.number }} در کدام حساب صندوق بنشیند؟</p>
<label class="field">
<span>حساب</span>
<select v-model="clearAccountId">
<option v-for="account in clearAccounts" :key="account.id" :value="account.id">
{{ account.name }}
</option>
</select>
</label>
<UiButton
:loading="savingId === clearRow?.id"
@click="confirmClear"
>
ثبت وصول
</UiButton>
</UiModal>
<UiModal
:open="formOpen"
:title="isIncomingForm ? 'دریافت چک' : 'پرداخت چک'"