feat(tahesab): net sales by factor, group, and commission
Report weight after returns for the selected period, with parent customer groups and separate silver and investment commission. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { theme } from 'ant-design-vue';
|
||||
import type { TableColumnsType } from 'ant-design-vue';
|
||||
import type {
|
||||
SalesMovementStatsDto,
|
||||
SalesNamedMovementDto,
|
||||
TahesabSalesOverviewDto,
|
||||
} from '@mento/shared';
|
||||
import { toFaDigits } from '@/lib/jalali';
|
||||
import { formatTahesabBedBes } from '@/lib/tahesab';
|
||||
|
||||
const props = defineProps<{
|
||||
overview: TahesabSalesOverviewDto | null;
|
||||
}>();
|
||||
|
||||
const { token } = theme.useToken();
|
||||
|
||||
function formatNum(n: number | null | undefined) {
|
||||
if (n == null || Number.isNaN(n)) return '—';
|
||||
return toFaDigits(new Intl.NumberFormat('fa-IR', { maximumFractionDigits: 3 }).format(n));
|
||||
}
|
||||
|
||||
function formatMali(n: number | null | undefined) {
|
||||
if (n == null) return '—';
|
||||
return formatTahesabBedBes(n);
|
||||
}
|
||||
|
||||
function percentText(stats: SalesMovementStatsDto, empty = 'درصدی ثبت نشده') {
|
||||
const p = stats.percent;
|
||||
if (!p || p.avg == null) return empty;
|
||||
return `اول ${formatNum(p.first)} · آخر ${formatNum(p.last)} · میانگین ${formatNum(p.avg)} · کم ${formatNum(p.min)} · بیش ${formatNum(p.max)}`;
|
||||
}
|
||||
|
||||
const factors = computed(() =>
|
||||
(props.overview?.factors ?? []).filter((f) => f.key !== 'other' || f.sale.grossWeight > 0 || f.purchase.grossWeight > 0),
|
||||
);
|
||||
|
||||
const karColumns: TableColumnsType<SalesNamedMovementDto> = [
|
||||
{ title: 'کارساخته', dataIndex: 'name', key: 'name', ellipsis: true },
|
||||
{
|
||||
title: 'خالص فروش',
|
||||
key: 'saleNet',
|
||||
customRender: ({ record }) => formatNum(record.sale.netWeight),
|
||||
},
|
||||
{
|
||||
title: 'مرجوعی فروش',
|
||||
key: 'saleRet',
|
||||
customRender: ({ record }) => formatNum(record.sale.returnWeight),
|
||||
},
|
||||
{
|
||||
title: 'درصد خروج',
|
||||
key: 'salePct',
|
||||
customRender: ({ record }) => percentText(record.sale),
|
||||
},
|
||||
{
|
||||
title: 'خالص خرید',
|
||||
key: 'buyNet',
|
||||
customRender: ({ record }) => formatNum(record.purchase.netWeight),
|
||||
},
|
||||
{
|
||||
title: 'مرجوعی خرید',
|
||||
key: 'buyRet',
|
||||
customRender: ({ record }) => formatNum(record.purchase.returnWeight),
|
||||
},
|
||||
{
|
||||
title: 'درصد ورود',
|
||||
key: 'buyPct',
|
||||
customRender: ({ record }) => percentText(record.purchase),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-row v-if="overview" :gutter="[16, 16]">
|
||||
<a-col v-if="!overview.percentReady" :span="24">
|
||||
<a-alert
|
||||
type="info"
|
||||
show-icon
|
||||
message="درصد ورود و خروج بعد از یک بهروزرسانی تازه از تهحساب پر میشود. وزن و مرجوعی با سندهای فعلی حساب شدهاند."
|
||||
/>
|
||||
</a-col>
|
||||
|
||||
<a-col v-for="factor in factors" :key="factor.key" :xs="24" :sm="12" :xl="6">
|
||||
<a-card :bordered="false" :title="factor.label">
|
||||
<a-row :gutter="[12, 8]">
|
||||
<a-col :span="12">
|
||||
<a-statistic title="خالص فروش" :value="formatNum(factor.sale.netWeight)" />
|
||||
<a-typography-text type="secondary">
|
||||
مرجوعی {{ formatNum(factor.sale.returnWeight) }}
|
||||
<template v-if="factor.key === 'bullion'">
|
||||
· تعداد {{ formatNum(factor.sale.netCount) }}
|
||||
</template>
|
||||
</a-typography-text>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-statistic title="خالص خرید" :value="formatNum(factor.purchase.netWeight)" />
|
||||
<a-typography-text type="secondary">
|
||||
مرجوعی {{ formatNum(factor.purchase.returnWeight) }}
|
||||
</a-typography-text>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-typography-text type="secondary">مبلغ فروش {{ formatMali(factor.sale.netMali) }}</a-typography-text>
|
||||
</a-col>
|
||||
<a-col v-if="percentText(factor.sale, '') || percentText(factor.purchase, '')" :span="24">
|
||||
<div v-if="percentText(factor.sale, '')">خروج: {{ percentText(factor.sale, '') }}</div>
|
||||
<div v-if="percentText(factor.purchase, '')">ورود: {{ percentText(factor.purchase, '') }}</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="24">
|
||||
<a-card :bordered="false" title="کارساخته، خالص بعد از مرجوعی">
|
||||
<a-table
|
||||
:columns="karColumns"
|
||||
:data-source="overview.karsakhte"
|
||||
row-key="name"
|
||||
:pagination="{ pageSize: 8, showSizeChanger: false }"
|
||||
:scroll="{ x: 980 }"
|
||||
>
|
||||
<template #emptyText>
|
||||
<a-empty description="در این بازه سند کارساخته نیست." />
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col :xs="24" :lg="12">
|
||||
<a-card :bordered="false" title="گروه اصلی و فرعی">
|
||||
<a-empty v-if="!overview.groups.length" description="گروهی در این بازه فروش ندارد." />
|
||||
<div v-for="group in overview.groups" :key="group.id">
|
||||
<a-flex justify="space-between" :style="{ marginTop: token.marginSM }">
|
||||
<a-typography-text strong>{{ group.label }}</a-typography-text>
|
||||
<a-typography-text>
|
||||
خالص {{ formatNum(group.netSaleWeight) }} · مرجوعی {{ formatNum(group.saleReturnWeight) }}
|
||||
</a-typography-text>
|
||||
</a-flex>
|
||||
<a-list v-if="group.children.length" :data-source="group.children">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :title="item.label">
|
||||
<template #description>
|
||||
خالص {{ formatNum(item.netSaleWeight) }} · مبلغ {{ formatMali(item.netSaleMali) }} · مرجوعی
|
||||
{{ formatNum(item.saleReturnWeight) }}
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col :xs="24" :lg="12">
|
||||
<a-card :bordered="false" title="پورسانت کارشناس">
|
||||
<a-typography-paragraph type="secondary">
|
||||
کارساختهٔ نقره و ساچمه (اتیکت جواهر و فروش اینترنتی) جدا حساب میشوند.
|
||||
</a-typography-paragraph>
|
||||
<a-list
|
||||
:data-source="overview.experts"
|
||||
item-layout="vertical"
|
||||
:locale="{ emptyText: 'کارشناسی برای این بازه نیست.' }"
|
||||
>
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :title="item.name" :description="`درصد ${formatNum(item.percent)}`" />
|
||||
<div>
|
||||
کارساخته نقره: وزن {{ formatNum(item.silverWeight) }} · پورسانت
|
||||
{{ formatMali(item.silverCommission) }}
|
||||
</div>
|
||||
<div>
|
||||
سرمایهگذاری: وزن {{ formatNum(item.investWeight) }} · پورسانت
|
||||
{{ formatMali(item.investCommission) }}
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="24">
|
||||
<a-card :bordered="false" title="فروش اینترنتی (مونتیفای)">
|
||||
<a-row :gutter="[16, 16]">
|
||||
<a-col :xs="24" :sm="8">
|
||||
<a-statistic title="سفارش" :value="formatNum(overview.internet.orderCount)" />
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="8">
|
||||
<a-statistic title="خالص نقره" :value="formatNum(overview.internet.netSilver)" />
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="8">
|
||||
<a-statistic title="خالص طلا" :value="formatNum(overview.internet.netGold)" />
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-typography-text type="secondary">
|
||||
مبلغ {{ formatMali(overview.internet.netMali) }} · لغوشده
|
||||
{{ formatNum(overview.internet.returnCount) }} سفارش، نقره
|
||||
{{ formatNum(overview.internet.returnSilver) }} ·
|
||||
{{ formatNum(overview.internet.linkedSkipped) }} سفارش چون در تهحساب سند دارد اینجا دوباره نیامده
|
||||
</a-typography-text>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col v-if="overview.otherNos.length" :span="24">
|
||||
<a-card :bordered="false" title="سندهایی که در چهار بخش جا نگرفتند">
|
||||
<a-space wrap>
|
||||
<a-tag v-for="name in overview.otherNos" :key="name">{{ name }}</a-tag>
|
||||
</a-space>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
Reference in New Issue
Block a user