diff --git a/apps/api/src/integrations/tahesab/documents.service.ts b/apps/api/src/integrations/tahesab/documents.service.ts index cb1be53..f721a34 100644 --- a/apps/api/src/integrations/tahesab/documents.service.ts +++ b/apps/api/src/integrations/tahesab/documents.service.ts @@ -16,8 +16,17 @@ function asAsnadList(raw: unknown): Array> { if (!raw) return []; if (Array.isArray(raw)) return raw as Array>; if (typeof raw === 'object') { - return Object.values(raw as Record).filter( - (v) => v && typeof v === 'object', + const obj = raw as Record; + for (const key of ['DoListAsnad', 'Asnad', 'asnad', 'Items', 'items', 'Data']) { + const nested = obj[key]; + if (Array.isArray(nested)) { + return nested.filter((v) => v && typeof v === 'object') as Array< + Record + >; + } + } + return Object.values(obj).filter( + (v) => v && typeof v === 'object' && !Array.isArray(v), ) as Array>; } return []; @@ -68,8 +77,8 @@ export class TahesabDocumentsService { ); const link = await this.requireLink(organizationId, personId, resolvedId); const { api } = await this.connections.getApi(organizationId, resolvedId); - // Count_Last = -1 برای مانده ردیف‌به‌ردیف مثل همکار - const raw = await api.doListAsnad(-1, link.moshtariCode, from, to); + // Count_Last = سقف تعداد سند برگشتی (۰ در نمونه گاهی خالی برمی‌گرداند) + const raw = await api.doListAsnad(500, link.moshtariCode, from, to); const items = asAsnadList(raw); return { connectionId: resolvedId, diff --git a/apps/api/src/integrations/tahesab/tahesab-methods.ts b/apps/api/src/integrations/tahesab/tahesab-methods.ts index 5562019..232d61b 100644 --- a/apps/api/src/integrations/tahesab/tahesab-methods.ts +++ b/apps/api/src/integrations/tahesab/tahesab-methods.ts @@ -222,11 +222,14 @@ export class TahesabApi { fromShamsi: string, toShamsi: string, ) { + // OpenAPI: Count_Last, Moshtari_Code, AzTarikhShamsi, TaTarikhShamsi, FilterNoSanad, Mande_Jens_Felez return this.call('DoListAsnad', 'DoListAsnad', [ countLast, moshtariCode, fromShamsi, toShamsi, + '', + 0, ]); } diff --git a/apps/web/src/components/person-assets/PersonAssetsPanel.vue b/apps/web/src/components/person-assets/PersonAssetsPanel.vue index 8efc08c..959c5ac 100644 --- a/apps/web/src/components/person-assets/PersonAssetsPanel.vue +++ b/apps/web/src/components/person-assets/PersonAssetsPanel.vue @@ -3,7 +3,13 @@ import { computed, onMounted, ref, watch } from 'vue'; import axios from 'axios'; import { api } from '@/lib/api'; import { formatMoney } from '@/lib/money'; +import { + dateToJalali, + formatLocalDateInput, + parseLocalInput, +} from '@/lib/jalali'; import MoneyInput from '@/components/MoneyInput.vue'; +import JalaliDateInput from '@/components/JalaliDateInput.vue'; import type { PersonAccountingLinkDto, PersonAssetsDto, @@ -16,6 +22,10 @@ const props = defineProps<{ personId: string; }>(); +const emit = defineEmits<{ + linkChanged: [moshtariCode: number | null]; +}>(); + const loading = ref(false); const error = ref(''); const ok = ref(''); @@ -26,6 +36,7 @@ const assets = ref(null); const activeTab = ref<'مانده حساب' | 'جزئیات حساب' | 'ثبت سند'>('مانده حساب'); const asnadLoading = ref(false); const asnadItems = ref[]>([]); +/** مدل JalaliDateInput = تاریخ محلی میلادی YYYY-MM-DD */ const fromDate = ref(''); const toDate = ref(''); const manualCode = ref(''); @@ -120,18 +131,28 @@ function formatMandehAmount(amount: string | number, unit: string) { return abs.toLocaleString('fa-IR', { maximumFractionDigits: 3 }); } +/** تبدیل تاریخ محلی میلادی به شمسی YYYY-MM-DD برای API ته‌حساب */ +function toShamsiYmd(localIso: string): string | null { + const d = parseLocalInput(localIso); + if (!d) return null; + const { jy, jm, jd } = dateToJalali(d); + return `${jy}-${String(jm).padStart(2, '0')}-${String(jd).padStart(2, '0')}`; +} + function defaultJalaliRange() { - // تقریبی: کاربر می‌تواند عوض کند؛ پیش‌فرض بازه یک‌ماهه متنی const now = new Date(); - // بدون وابستگی سنگین: مقادیر نمونه قابل ویرایش - toDate.value = `${now.getFullYear() - 621}-01-01`; - // بهتر: از API تاریخ نخواهیم؛ فیلدها را خالی‌تر با placeholder بگذاریم - const y = 1405; - fromDate.value = `${y}-01-01`; - toDate.value = `${y}-12-29`; - sanad.value.year = y; - sanad.value.month = 1; - sanad.value.day = 1; + const from = new Date(now); + from.setMonth(from.getMonth() - 6); + fromDate.value = formatLocalDateInput(from); + toDate.value = formatLocalDateInput(now); + const j = dateToJalali(now); + sanad.value.year = j.jy; + sanad.value.month = j.jm; + sanad.value.day = j.jd; +} + +function emitLinkCode() { + emit('linkChanged', currentLink.value?.moshtariCode ?? null); } async function loadConnectionsAndLinks() { @@ -155,6 +176,12 @@ async function loadConnectionsAndLinks() { ) { connectionId.value = def?.id ?? ''; } + if (!currentLink.value && links.value.length) { + const preferred = + links.value.find((l) => l.isDefaultConnection) ?? links.value[0]; + if (preferred) connectionId.value = preferred.connectionId; + } + emitLinkCode(); } catch (e) { error.value = axios.isAxiosError(e) ? String( @@ -162,6 +189,7 @@ async function loadConnectionsAndLinks() { 'بارگذاری اتصال ته‌حساب ناموفق بود.', ) : 'بارگذاری اتصال ته‌حساب ناموفق بود.'; + emit('linkChanged', null); } finally { loading.value = false; } @@ -195,8 +223,10 @@ async function refreshAssets() { async function loadAsnad() { if (!connectionId.value || !currentLink.value) return; - if (!fromDate.value || !toDate.value) { - error.value = 'تاریخ از و تا را وارد کنید (شمسی YYYY-MM-DD).'; + const from = toShamsiYmd(fromDate.value); + const to = toShamsiYmd(toDate.value); + if (!from || !to) { + error.value = 'تاریخ از و تا را از تقویم انتخاب کنید.'; return; } asnadLoading.value = true; @@ -207,12 +237,16 @@ async function loadAsnad() { { params: { connectionId: connectionId.value, - from: fromDate.value, - to: toDate.value, + from, + to, }, + timeout: 60_000, }, ); asnadItems.value = data.items ?? []; + ok.value = asnadItems.value.length + ? `${asnadItems.value.length} سند نمایش داده شد.` + : 'در این بازه سندی یافت نشد.'; } catch (e) { error.value = axios.isAxiosError(e) ? String( @@ -315,6 +349,7 @@ async function unlink() { `/integrations/tahesab/people/${props.personId}/links/${connectionId.value}`, ); assets.value = null; + asnadItems.value = []; await loadConnectionsAndLinks(); } @@ -355,10 +390,17 @@ async function pushSanad() { watch(connectionId, async () => { matchCandidates.value = []; asnadItems.value = []; + emitLinkCode(); if (currentLink.value) await refreshAssets(); else assets.value = null; }); +watch(activeTab, (t) => { + if (t === 'جزئیات حساب' && currentLink.value && !asnadItems.value.length) { + void loadAsnad(); + } +}); + onMounted(async () => { defaultJalaliRange(); await loadConnectionsAndLinks(); @@ -374,8 +416,8 @@ onMounted(async () => {