169 lines
6.9 KiB
Vue
169 lines
6.9 KiB
Vue
<!--
|
||||
|
|
Ported from legacy henza_pwa/src/views/user/src/components/transactions.vue
|
|||
|
|
("اسناد" — financial documents). Replaces the English AccountView scaffold
|
|||
|
|
that had no legacy counterpart.
|
|||
|
|
|
|||
|
|
Data source: legacy listed `Cash` rows from POST /get-my-findoc. The new
|
|||
|
|
system's equivalent is LedgerEntry, already exposed by
|
|||
|
|
GET /customers/:id/ledger-entries, so the list is real data.
|
|||
|
|
|
|||
|
|
Legacy's colour/icon logic is preserved exactly: `hasFactor(item)` was
|
|||
|
|
`item.type != 9` (type 9 = سفارش, the debt-creating order entry) → green
|
|||
|
|
#F0FDF4/#22C55E + add-square for everything else, red #FEF2F2/#F44336 +
|
|||
|
|
minus-square for orders. That maps 1:1 onto our CREDIT/DEBIT direction,
|
|||
|
|
since an ORDER_STOCK entry is exactly the DEBIT case.
|
|||
|
|
|
|||
|
|
DROPPED: the "دریافت فایل" per-document download — legacy's getFile() hit the
|
|||
|
|
old Laravel host; there is no document-file endpoint in the new API.
|
|||
|
|
-->
|
|||
|
|
<template>
|
|||
|
|
<div class="fixed top-0 z-10 w-full">
|
|||
|
|
<AppHeader>
|
|||
|
|
<div class="flex justify-between items-center">
|
|||
|
|
<Icon icon="vuesax-linear:arrow-right-1" class="cursor-pointer text-white" size="25" @click="router.push({ name: 'userPage::hasSubRoute' })" />
|
|||
|
|
<h3 class="text-white bold mb-0">اسناد</h3>
|
|||
|
|
<div></div>
|
|||
|
|
</div>
|
|||
|
|
</AppHeader>
|
|||
|
|
</div>
|
|||
|
|
<div class="h-24 bg-[#F9FAFB]"></div>
|
|||
|
|
|
|||
|
|
<container class="overflow-y-auto bg-[#F9FAFB]">
|
|||
|
|
<Spinner :spinning="loading" tip="درحال دریافت اطلاعات" cls="no-scrollbar overflow-auto">
|
|||
|
|
<div
|
|||
|
|
v-if="entries.length"
|
|||
|
|
v-for="item in entries"
|
|||
|
|
:key="item.id"
|
|||
|
|
@click="openEntry(item)"
|
|||
|
|
class="rounded-[10px] border-[#E8EAEE] py-[16px] px-[14px] w-full mb-[16px] grid grid-cols-8 items-center cursor-pointer bg-white"
|
|||
|
|
>
|
|||
|
|
<div class="col-span-1 items-end h-full">
|
|||
|
|
<Icon
|
|||
|
|
:icon="getIcon(item)"
|
|||
|
|
:style="{ background: getColor(item), color: getColor2(item) }"
|
|||
|
|
class="rounded-full p-[10px] ml-2 h-[40px] w-[40px]"
|
|||
|
|
size="20"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="col-span-7 pr-1 text-primary mt-2">
|
|||
|
|
<div class="flex justify-between items-center">
|
|||
|
|
<div class="font-bold mb-2">{{ item.note || '- -' }}</div>
|
|||
|
|
<div
|
|||
|
|
class="rounded-[5px] px-[20px] py-[4px] w-fit text-[16px] mb-2 font-semibold"
|
|||
|
|
:style="{ background: getColor(item), color: getColor2(item) }"
|
|||
|
|
>
|
|||
|
|
{{ SOURCE_LABELS[item.sourceType] ?? item.sourceType }}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="flex justify-between items-center">
|
|||
|
|
<div class="text-[14px] font-semibold mb-2">{{ formatAmount(item) }}</div>
|
|||
|
|
<div class="h-10"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-2 text-[14px] text-grey font-semibold">{{ jdate(item.createdAt) }}</div>
|
|||
|
|
<div class="flex justify-between pt-1">
|
|||
|
|
<div class="text-[12px] font-bold">{{ item.confirmed ? 'تایید شده' : 'در انتظار تایید' }}</div>
|
|||
|
|
<Icon icon="vuesax-linear:arrow-left-1" class="cursor-pointer text-primary" size="20" />
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<EmptyState v-else :image="transactionEmpty" />
|
|||
|
|
</Spinner>
|
|||
|
|
|
|||
|
|
<Drawer
|
|||
|
|
v-model:visible="showPopUp"
|
|||
|
|
placement="bottom"
|
|||
|
|
v-if="activeEntry"
|
|||
|
|
:closable="false"
|
|||
|
|
height="450px"
|
|||
|
|
:class="['henza-rounded-drawer rtl', 'bg-white', 'text-primary']"
|
|||
|
|
:bodyStyle="{ display: 'flex', flexDirection: 'column' }"
|
|||
|
|
>
|
|||
|
|
<div class="w-full h-full flex flex-col">
|
|||
|
|
<div class="pb-[10px] border-b border-solid border-[#E5E7EB] flex text-[16px] font-bold">جزئیات سند</div>
|
|||
|
|
<div class="flex-1 py-[10px] overflow-auto no-scrollbar">
|
|||
|
|
<div class="flex justify-between mb-4">
|
|||
|
|
<div class="text-grey">نوع سند :</div>
|
|||
|
|
<div class="font-semibold">{{ SOURCE_LABELS[activeEntry.sourceType] ?? activeEntry.sourceType }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="flex justify-between mb-4">
|
|||
|
|
<div class="text-grey">مبلغ :</div>
|
|||
|
|
<div class="font-semibold">{{ formatAmount(activeEntry) }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="flex justify-between mb-4">
|
|||
|
|
<div class="text-grey">وضعیت :</div>
|
|||
|
|
<div class="font-semibold">{{ activeEntry.confirmed ? 'تایید شده' : 'در انتظار تایید' }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="flex justify-between mb-4">
|
|||
|
|
<div class="text-grey">تاریخ :</div>
|
|||
|
|
<div class="font-semibold">{{ jdate(activeEntry.createdAt) }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="flex justify-between mb-4">
|
|||
|
|
<div class="text-grey">توضیحات :</div>
|
|||
|
|
<div class="font-semibold">{{ activeEntry.note || '- -' }}</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</Drawer>
|
|||
|
|
</container>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { onMounted, ref } from 'vue'
|
|||
|
|
import { useRouter } from 'vue-router'
|
|||
|
|
import { Drawer } from 'ant-design-vue'
|
|||
|
|
import Icon from '../../components/icon/Icon.vue'
|
|||
|
|
import AppHeader from '../../components/AppHeader.vue'
|
|||
|
|
import Spinner from '../../components/Spinner.vue'
|
|||
|
|
import EmptyState from '../../components/EmptyState.vue'
|
|||
|
|
import transactionEmpty from '../../assets/img/empty/transactionEmpty.svg'
|
|||
|
|
import { jdate, price } from '../../utils/filters'
|
|||
|
|
import { useSessionStore } from '../../stores/session'
|
|||
|
|
import { api, type LedgerEntrySummary } from '../../api/client'
|
|||
|
|
|
|||
|
|
const SOURCE_LABELS: Record<string, string> = {
|
|||
|
|
ORDER_STOCK: 'سفارش',
|
|||
|
|
ORDER_PRE_ORDER: 'سفارش ساخت',
|
|||
|
|
REPAYMENT_MELTED_GOLD: 'طلای آبشده',
|
|||
|
|
REPAYMENT_TRANSFER: 'حواله',
|
|||
|
|
REPAYMENT_CHEQUE: 'چک',
|
|||
|
|
REPAYMENT_CASH: 'پولی',
|
|||
|
|
WALLET_BONUS: 'پاداش',
|
|||
|
|
ADJUSTMENT: 'اصلاحیه',
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const router = useRouter()
|
|||
|
|
const session = useSessionStore()
|
|||
|
|
|
|||
|
|
const loading = ref(false)
|
|||
|
|
const entries = ref<LedgerEntrySummary[]>([])
|
|||
|
|
const showPopUp = ref(false)
|
|||
|
|
const activeEntry = ref<LedgerEntrySummary | null>(null)
|
|||
|
|
|
|||
|
|
// legacy hasFactor(item) === item.type != 9 (9 = سفارش/order). Our DEBIT
|
|||
|
|
// entries are exactly that case.
|
|||
|
|
const isCredit = (item: LedgerEntrySummary) => item.direction === 'CREDIT'
|
|||
|
|
const getIcon = (item: LedgerEntrySummary) => (isCredit(item) ? 'vuesax-linear:add-square' : 'vuesax-linear:minus-square')
|
|||
|
|
const getColor = (item: LedgerEntrySummary) => (isCredit(item) ? '#F0FDF4' : '#FEF2F2')
|
|||
|
|
const getColor2 = (item: LedgerEntrySummary) => (isCredit(item) ? '#22C55E' : '#F44336')
|
|||
|
|
|
|||
|
|
const formatAmount = (item: LedgerEntrySummary) =>
|
|||
|
|
item.currency === 'GOLD_GRAM' ? `${item.amount} گرم` : `${price(item.amount)} ریال`
|
|||
|
|
|
|||
|
|
const openEntry = (item: LedgerEntrySummary) => {
|
|||
|
|
activeEntry.value = item
|
|||
|
|
showPopUp.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
onMounted(async () => {
|
|||
|
|
if (!session.customerId) return
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
entries.value = await api.getLedgerEntries(session.customerId)
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
</script>
|