jws-frontend/src/pages/05_quotation/utils.ts
puriphatt 1b4c06b182
fix/refactor: quotation installment (#121)
* 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>
2024-12-06 11:01:52 +07:00

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;
}