This commit is contained in:
parent
2a2613d72c
commit
c750ed5fee
4 changed files with 276 additions and 1 deletions
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
// NOTE: Library
|
||||
import { computed, onMounted, reactive, watch } from 'vue';
|
||||
import { ref, computed, onMounted, reactive, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getRole } from 'src/services/keycloak';
|
||||
|
||||
|
|
@ -19,28 +19,58 @@ import {
|
|||
colReport,
|
||||
colReportProduct,
|
||||
colReportSale,
|
||||
colProfit,
|
||||
colProfitByMoth,
|
||||
colProfitByYear,
|
||||
} from './constants';
|
||||
import useFlowStore from 'src/stores/flow';
|
||||
import { useReportStore } from 'src/stores/report';
|
||||
import BadgeComponent from 'src/components/BadgeComponent.vue';
|
||||
import Expansion from 'src/components/14_report/Expansion.vue';
|
||||
import { SaveButton } from 'src/components/button';
|
||||
import { ReportProfit } from 'src/stores/report/types';
|
||||
|
||||
// NOTE: Variable
|
||||
const navigatorStore = useNavigator();
|
||||
const reportStore = useReportStore();
|
||||
const flow = useFlowStore();
|
||||
|
||||
const dataReportProfitByYears = ref<ReportProfit['dataset']>([]);
|
||||
const pastYears = ref<number>(5);
|
||||
const optionsPastYears = ref<number[]>([5, 10, 20, 30, 50, 80]);
|
||||
|
||||
const {
|
||||
dataReportQuotation,
|
||||
dataReportInvoice,
|
||||
dataReportReceipt,
|
||||
dataReportSale,
|
||||
dataReportProduct,
|
||||
dataReportProfit,
|
||||
} = storeToRefs(reportStore);
|
||||
|
||||
const userRoles = computed(() => getRole() || []);
|
||||
|
||||
const combinedProfitYear = computed(() => {
|
||||
return (
|
||||
dataReportProfitByYears.value?.reduce<ReportProfit['dataset']>(
|
||||
(acc, val) => {
|
||||
let find = acc.find((item) => item.year === val.year);
|
||||
|
||||
if (find) {
|
||||
find.expenses += val.expenses;
|
||||
find.netProfit += val.netProfit;
|
||||
find.income += val.income;
|
||||
} else {
|
||||
acc.push({ ...val });
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
[],
|
||||
) ?? []
|
||||
);
|
||||
});
|
||||
|
||||
async function fetchReportQuotation() {
|
||||
dataReportQuotation.value = (await reportStore.getReportQuotation()) || [];
|
||||
}
|
||||
|
|
@ -58,6 +88,16 @@ async function fetchReportProduct() {
|
|||
dataReportProduct.value = (await reportStore.getReportProduct()) || [];
|
||||
}
|
||||
|
||||
async function fetchReportProfit() {
|
||||
dataReportProfit.value = (await reportStore.getReportProfit()) || undefined;
|
||||
dataReportProfitByYears.value = dataReportProfit.value?.dataset || [];
|
||||
}
|
||||
|
||||
async function fetchReportProfitByYears(years: number) {
|
||||
const res = (await reportStore.getReportProfit({ years })) || undefined;
|
||||
dataReportProfitByYears.value = res?.dataset || [];
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
navigatorStore.current.title = 'report.title';
|
||||
navigatorStore.current.path = [{ text: '' }];
|
||||
|
|
@ -102,6 +142,10 @@ async function fetchReportTab() {
|
|||
await fetchReportSale();
|
||||
break;
|
||||
}
|
||||
case ViewMode.Profit: {
|
||||
await fetchReportProfit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +156,10 @@ onMounted(async () => {
|
|||
watch([() => pageState.currentTab], async () => {
|
||||
await fetchReportTab();
|
||||
});
|
||||
|
||||
watch([() => pastYears.value], async () => {
|
||||
await fetchReportProfitByYears(pastYears.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -495,6 +543,99 @@ watch([() => pageState.currentTab], async () => {
|
|||
</Expansion>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="pageState.currentTab === ViewMode.Profit">
|
||||
<div class="q-gutter-y-md">
|
||||
<Expansion default-opened>
|
||||
<template #header>
|
||||
<div class="flex full-width items-center">
|
||||
{{ $t('report.profit.byMonth') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #main>
|
||||
<TableReport
|
||||
:row="dataReportProfit?.dataset"
|
||||
:columns="colProfitByMoth"
|
||||
>
|
||||
<template #title="{ item }">
|
||||
{{ $t(`month.${item.row.month}`) }}
|
||||
</template>
|
||||
</TableReport>
|
||||
</template>
|
||||
</Expansion>
|
||||
|
||||
<Expansion default-opened>
|
||||
<template #header>
|
||||
<div class="flex full-width items-center">
|
||||
{{ $t('report.profit.byYear') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #main>
|
||||
<div class="q-gutter-y-md">
|
||||
<q-select
|
||||
style="max-width: 150px"
|
||||
outlined
|
||||
use-input
|
||||
fill-input
|
||||
emit-value
|
||||
map-options
|
||||
hide-selected
|
||||
hide-bottom-space
|
||||
input-debounce="0"
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
dense
|
||||
v-model="pastYears"
|
||||
:options="
|
||||
optionsPastYears.map((v) => ({
|
||||
value: v,
|
||||
label: `${v} ${$t('general.year')}`,
|
||||
}))
|
||||
"
|
||||
label="เลือกปีย้อนหลัง"
|
||||
></q-select>
|
||||
|
||||
<TableReport
|
||||
:row="combinedProfitYear"
|
||||
:columns="colProfitByYear"
|
||||
></TableReport>
|
||||
</div>
|
||||
</template>
|
||||
</Expansion>
|
||||
|
||||
<Expansion default-opened>
|
||||
<template #header>
|
||||
<div class="flex full-width items-center">
|
||||
{{ $t('report.view.Profit') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #main>
|
||||
<TableReport
|
||||
:row="[
|
||||
{
|
||||
title: $t('report.profit.netProfit'),
|
||||
amount: dataReportProfit?.netProfit || 0,
|
||||
},
|
||||
{
|
||||
title: $t('report.profit.expenses'),
|
||||
amount: dataReportProfit?.expenses || 0,
|
||||
},
|
||||
{
|
||||
title: $t('report.profit.income'),
|
||||
amount: dataReportProfit?.income || 0,
|
||||
},
|
||||
]"
|
||||
:columns="colProfit"
|
||||
hide-header
|
||||
hide-bottom
|
||||
></TableReport>
|
||||
</template>
|
||||
</Expansion>
|
||||
</div>
|
||||
</template>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export enum ViewMode {
|
|||
Receipt = 'receipt',
|
||||
Product = 'product',
|
||||
Sale = 'sale',
|
||||
Profit = 'profit',
|
||||
}
|
||||
|
||||
type ColumnsSale = {
|
||||
|
|
@ -28,6 +29,25 @@ type ColumnsBySale = ColumnsSale & {
|
|||
gender?: string;
|
||||
};
|
||||
|
||||
type ColumnsProfix = {
|
||||
title: string;
|
||||
amount: number;
|
||||
};
|
||||
|
||||
type ColumnsProfixByMonth = {
|
||||
month: string;
|
||||
netProfit: number;
|
||||
expenses: number;
|
||||
income: number;
|
||||
};
|
||||
|
||||
type ColumnsProfixByYear = Omit<ColumnsProfixByMonth, 'moth'> & {
|
||||
year: string;
|
||||
netProfit: number;
|
||||
expenses: number;
|
||||
income: number;
|
||||
};
|
||||
|
||||
export const colReportQuotation = [
|
||||
{
|
||||
name: 'code',
|
||||
|
|
@ -169,9 +189,81 @@ export const colReportBySale = [
|
|||
},
|
||||
] as const satisfies QTableProps['columns'];
|
||||
|
||||
export const colProfit = [
|
||||
{
|
||||
name: 'title',
|
||||
align: 'left',
|
||||
label: '',
|
||||
field: (data: ColumnsProfix) => data.title,
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
align: 'left',
|
||||
label: '',
|
||||
field: (data: ColumnsProfix) => formatNumberDecimal(data.amount, 2),
|
||||
},
|
||||
] as const satisfies QTableProps['columns'];
|
||||
|
||||
export const colProfitByMoth = [
|
||||
{
|
||||
name: '#title',
|
||||
align: 'left',
|
||||
label: 'report.profit.month',
|
||||
field: '',
|
||||
},
|
||||
{
|
||||
name: 'netProfit',
|
||||
align: 'left',
|
||||
label: 'report.profit.netProfit',
|
||||
field: (data: ColumnsProfixByMonth) =>
|
||||
formatNumberDecimal(data.netProfit, 2),
|
||||
},
|
||||
{
|
||||
name: 'expenses',
|
||||
align: 'left',
|
||||
label: 'report.profit.expenses',
|
||||
field: (data: ColumnsProfixByMonth) =>
|
||||
formatNumberDecimal(data.expenses, 2),
|
||||
},
|
||||
{
|
||||
name: 'income',
|
||||
align: 'left',
|
||||
label: 'report.profit.income',
|
||||
field: (data: ColumnsProfixByMonth) => formatNumberDecimal(data.income, 2),
|
||||
},
|
||||
] as const satisfies QTableProps['columns'];
|
||||
|
||||
export const colProfitByYear = [
|
||||
{
|
||||
name: 'title',
|
||||
align: 'left',
|
||||
label: 'report.profit.year',
|
||||
field: (data: ColumnsProfixByYear) => data.year,
|
||||
},
|
||||
{
|
||||
name: 'netProfit',
|
||||
align: 'left',
|
||||
label: 'report.profit.netProfit',
|
||||
field: (data: ColumnsProfixByYear) =>
|
||||
formatNumberDecimal(data.netProfit, 2),
|
||||
},
|
||||
{
|
||||
name: 'expenses',
|
||||
align: 'left',
|
||||
label: 'report.profit.expenses',
|
||||
field: (data: ColumnsProfixByYear) => formatNumberDecimal(data.expenses, 2),
|
||||
},
|
||||
{
|
||||
name: 'income',
|
||||
align: 'left',
|
||||
label: 'report.profit.income',
|
||||
field: (data: ColumnsProfixByYear) => formatNumberDecimal(data.income, 2),
|
||||
},
|
||||
] as const satisfies QTableProps['columns'];
|
||||
export const pageTabs = [
|
||||
{ label: 'Document', value: ViewMode.Document, by: ['user'] },
|
||||
{ label: 'Invoice', value: ViewMode.Invoice, by: ['user'] },
|
||||
{ label: 'Product', value: ViewMode.Product, by: ['user'] },
|
||||
{ label: 'Sale', value: ViewMode.Sale, by: ['admin'] },
|
||||
{ label: 'Profit', value: ViewMode.Profit, by: ['admin'] },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
ReportProduct,
|
||||
ReportQuotation,
|
||||
ReportSale,
|
||||
ReportProfit,
|
||||
} from './types';
|
||||
import { baseUrl } from '../utils';
|
||||
import { getToken } from 'src/services/keycloak';
|
||||
|
|
@ -120,6 +121,31 @@ export async function getReportPayment(params?: {
|
|||
return null;
|
||||
}
|
||||
|
||||
export async function getReportProfit(params?: {
|
||||
years?: number;
|
||||
startDate?: string | Date;
|
||||
endDate?: string | Date;
|
||||
}) {
|
||||
let opts = params || {};
|
||||
|
||||
if (params?.years) {
|
||||
const currentYear = new Date().getFullYear();
|
||||
opts.startDate = new Date(currentYear - params.years, 0, 1); // ✅ 1 ม.ค. ของปีที่ต้องการ
|
||||
opts.endDate = new Date(currentYear, 11, 31); // ✅ 31 ธ.ค. ของปีปัจจุบัน
|
||||
}
|
||||
|
||||
const res = await api.get<ReportProfit>(`/${ENDPOINT}/profit`, {
|
||||
params: {
|
||||
startDate: opts.startDate,
|
||||
endDate: opts.endDate,
|
||||
},
|
||||
});
|
||||
if (res.status < 400) {
|
||||
return res.data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const useReportStore = defineStore('report-store', () => {
|
||||
const dataReportQuotation = ref<ReportQuotation[]>([]);
|
||||
const dataReportInvoice = ref<Report[]>([]);
|
||||
|
|
@ -127,6 +153,7 @@ export const useReportStore = defineStore('report-store', () => {
|
|||
const dataReportSale = ref<ReportSale>();
|
||||
const dataReportProduct = ref<ReportProduct[]>([]);
|
||||
const dataReportPayment = ref<ReportPayment[]>([]);
|
||||
const dataReportProfit = ref<ReportProfit>();
|
||||
|
||||
return {
|
||||
dataReportQuotation,
|
||||
|
|
@ -135,6 +162,7 @@ export const useReportStore = defineStore('report-store', () => {
|
|||
dataReportSale,
|
||||
dataReportProduct,
|
||||
dataReportPayment,
|
||||
dataReportProfit,
|
||||
|
||||
downloadReportQuotation,
|
||||
getReportQuotation,
|
||||
|
|
@ -147,5 +175,6 @@ export const useReportStore = defineStore('report-store', () => {
|
|||
downloadReportProduct,
|
||||
getReportProduct,
|
||||
getReportPayment,
|
||||
getReportProfit,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -50,3 +50,16 @@ export type ReportSale = {
|
|||
bySale: (User & { _count: number })[];
|
||||
byProductGroup: (Omit<ProductGroup, '_count'> & { _count: number })[];
|
||||
};
|
||||
|
||||
export type ReportProfit = {
|
||||
dataset: {
|
||||
netProfit: number;
|
||||
expenses: number;
|
||||
income: number;
|
||||
year: number;
|
||||
month: number;
|
||||
}[];
|
||||
netProfit: number;
|
||||
expenses: number;
|
||||
income: number;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue