* refactor/feat: i18n * chore: clean log * refactor: type * refactor: installment and product table state relation * refactor: handle split custom --------- Co-authored-by: Thanaphon Frappet <thanaphon@frappet.com>
161 lines
4.8 KiB
TypeScript
161 lines
4.8 KiB
TypeScript
import { QuotationFull } from 'src/stores/quotations/types';
|
|
import { precisionRound } from 'src/utils/arithmetic';
|
|
|
|
export type ProductTree = {
|
|
type?: string;
|
|
id: string;
|
|
title: string;
|
|
subtitle: string;
|
|
detail?: string;
|
|
remark?: string;
|
|
attributes?: Record<string, any>;
|
|
checked: boolean;
|
|
opened: boolean;
|
|
icon: string;
|
|
fg: string;
|
|
bg: string;
|
|
value?: {
|
|
vat: number;
|
|
pricePerUnit: number;
|
|
discount: number;
|
|
amount: number;
|
|
installmentNo?: number;
|
|
serviceId?: string;
|
|
service?: QuotationFull['productServiceList'][number]['service'];
|
|
workId?: string;
|
|
work?: QuotationFull['productServiceList'][number]['work'];
|
|
productId: string;
|
|
product: QuotationFull['productServiceList'][number]['product'];
|
|
};
|
|
children?: ProductTree;
|
|
}[];
|
|
|
|
export function quotationProductTree(
|
|
list: {
|
|
vat: number;
|
|
pricePerUnit: number;
|
|
discount: number;
|
|
amount: number;
|
|
installmentNo?: number;
|
|
service?: QuotationFull['productServiceList'][number]['service'];
|
|
work?: QuotationFull['productServiceList'][number]['work'];
|
|
product: QuotationFull['productServiceList'][number]['product'];
|
|
}[],
|
|
agentPrice?: boolean,
|
|
vat?: number,
|
|
): ProductTree {
|
|
const ret: ProductTree = [];
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
const current = list[i];
|
|
|
|
if (ret.find((v) => v.id === current.service?.id)) continue;
|
|
|
|
if (current.service && current.work) {
|
|
const service = current.service;
|
|
|
|
ret.push({
|
|
type: 'service',
|
|
id: service.id,
|
|
title: service.name,
|
|
subtitle: service.code,
|
|
attributes: service.attributes,
|
|
detail: service.detail,
|
|
opened: service.work.length > 0,
|
|
checked: true,
|
|
children: service.work.flatMap((work) => {
|
|
const mapper = (
|
|
relation: (typeof work)['productOnWork'][number],
|
|
): ProductTree[number] => {
|
|
const price = agentPrice
|
|
? relation.product.agentPrice
|
|
: relation.product.price;
|
|
const pricePerUnit = relation.product.vatIncluded
|
|
? precisionRound(price / (1 + (vat || 0.07)))
|
|
: price;
|
|
|
|
return {
|
|
type: 'product',
|
|
id: relation.product.id,
|
|
title: relation.product.name,
|
|
subtitle: relation.product.code,
|
|
detail: relation.product.detail,
|
|
remark: relation.product.remark,
|
|
opened: true,
|
|
checked: !!list.find(
|
|
(v) =>
|
|
v.service?.id === service.id &&
|
|
v.work?.id === work.id &&
|
|
v.product.id === relation.product.id,
|
|
),
|
|
value: {
|
|
vat: current.vat,
|
|
pricePerUnit: pricePerUnit,
|
|
discount: current.discount,
|
|
amount: current.amount,
|
|
serviceId: service.id,
|
|
service: service,
|
|
workId: work.id,
|
|
work: work,
|
|
productId: relation.product.id,
|
|
product: relation.product,
|
|
installmentNo: relation.installmentNo,
|
|
},
|
|
icon: 'mdi-shopping-outline',
|
|
bg: 'hsla(var(--teal-10-hsl)/0.1)',
|
|
fg: 'var(--teal-10)',
|
|
};
|
|
};
|
|
|
|
if (!!work.name) {
|
|
return {
|
|
type: 'work',
|
|
id: work.id,
|
|
title: work.name,
|
|
subtitle: ' ',
|
|
attributes: work.attributes,
|
|
opened: true,
|
|
checked: !!list.find(
|
|
(v) => v.service?.id === service.id && v.work?.id === work.id,
|
|
),
|
|
children: work.productOnWork.map(mapper),
|
|
icon: 'mdi-briefcase-outline',
|
|
bg: 'hsla(var(--violet-11-hsl)/0.1)',
|
|
fg: 'var(--violet-11)',
|
|
};
|
|
}
|
|
|
|
return work.productOnWork.map(mapper);
|
|
}),
|
|
icon: 'mdi-server-outline',
|
|
bg: 'hsla(var(--orange-5-hsl)/0.1)',
|
|
fg: 'var(--orange-5)',
|
|
});
|
|
} else {
|
|
ret.push({
|
|
type: 'product',
|
|
id: current.product.id,
|
|
title: current.product.name,
|
|
subtitle: current.product.code,
|
|
detail: current.product.detail,
|
|
remark: current.product.remark,
|
|
opened: true,
|
|
checked: true,
|
|
value: {
|
|
vat: current.vat,
|
|
pricePerUnit: current.pricePerUnit,
|
|
discount: current.discount,
|
|
amount: current.amount,
|
|
productId: current.product.id,
|
|
product: current.product,
|
|
installmentNo: current.installmentNo,
|
|
},
|
|
icon: 'mdi-shopping-outline',
|
|
bg: 'hsla(var(--teal-10-hsl)/0.1)',
|
|
fg: 'var(--teal-10)',
|
|
});
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|