GuDingZiChan/backend/src/utils/assetValue.ts

239 lines
6.7 KiB
TypeScript

import type { Prisma, PrismaClient } from '@prisma/client'
type EquipmentClient = PrismaClient | Prisma.TransactionClient
const NON_AVAILABLE_STATUSES = new Set(['维护中', '待报废', '已报废', '已损坏'])
const toSafeNumber = (value: unknown) => {
if (value === null || value === undefined || value === '') {
return 0
}
const numericValue = Number(value)
return Number.isNaN(numericValue) ? 0 : numericValue
}
const clampRemainingQuantity = (totalQuantity: number, remainingQuantity: number) => {
if (remainingQuantity < 0) return 0
if (remainingQuantity > totalQuantity) return totalQuantity
return remainingQuantity
}
export const calculateEquipmentValues = ({
unitPrice,
totalQuantity,
remainingQuantity,
status
}: {
unitPrice: number
totalQuantity: number
remainingQuantity: number
status?: string | null
}) => {
const normalizedUnitPrice = toSafeNumber(unitPrice)
const normalizedTotalQuantity = Math.max(0, Math.floor(toSafeNumber(totalQuantity)))
const normalizedRemainingQuantity = clampRemainingQuantity(
normalizedTotalQuantity,
Math.floor(toSafeNumber(remainingQuantity))
)
const totalValue = normalizedUnitPrice * normalizedTotalQuantity
if (status && NON_AVAILABLE_STATUSES.has(status)) {
return {
total_value: totalValue,
borrowed_value: 0,
available_value: 0
}
}
const borrowedQuantity = Math.max(normalizedTotalQuantity - normalizedRemainingQuantity, 0)
return {
total_value: totalValue,
borrowed_value: normalizedUnitPrice * borrowedQuantity,
available_value: normalizedUnitPrice * normalizedRemainingQuantity
}
}
export const sumBundleChildrenValue = (
children: Array<{
unit_price?: number | string | null
quantity?: number | string | null
}>
) => {
return children.reduce((sum, child) => {
const quantity = Math.max(1, Math.floor(toSafeNumber(child.quantity) || 1))
return sum + toSafeNumber(child.unit_price) * quantity
}, 0)
}
export const deriveParentBaseUnitPrice = (bundleTotalPrice: unknown, childrenTotalValue: number) => {
const normalizedBundleTotalPrice = toSafeNumber(bundleTotalPrice)
const normalizedChildrenTotalValue = toSafeNumber(childrenTotalValue)
if (normalizedChildrenTotalValue > normalizedBundleTotalPrice) {
throw new Error('整机总价不能小于子资产总价,请先调整价格后再保存')
}
return normalizedBundleTotalPrice - normalizedChildrenTotalValue
}
export const calculateChildrenTotalValueFromRecords = (
children: Array<{ unit_price?: unknown }>
) => {
return children.reduce((sum, child) => sum + toSafeNumber(child.unit_price), 0)
}
export const refreshParentTotalValueWithChildren = async (client: EquipmentClient, parentId: number) => {
const parent = await client.equipment.findUnique({
where: { id: parentId },
include: {
children: {
select: {
unit_price: true
}
}
}
})
if (!parent) {
return
}
const totalValueWithChildren =
toSafeNumber(parent.unit_price) + calculateChildrenTotalValueFromRecords(parent.children)
await client.equipment.update({
where: { id: parentId },
data: {
total_value_with_children: totalValueWithChildren
}
})
}
export const syncMountedChildrenState = async (
client: EquipmentClient,
parentId: number,
targetStatus: '可用' | '已借出' | '维护中' | '待报废' | '已报废'
) => {
const children = await client.equipment.findMany({
where: { parent_id: parentId },
select: {
id: true,
unit_price: true,
total_quantity: true
}
})
for (const child of children) {
const remainingQuantity = targetStatus === '已借出' ? 0 : Number(child.total_quantity)
const values = calculateEquipmentValues({
unitPrice: Number(child.unit_price),
totalQuantity: Number(child.total_quantity),
remainingQuantity,
status: targetStatus
})
await client.equipment.update({
where: { id: child.id },
data: {
status: targetStatus,
remaining_quantity: remainingQuantity,
total_value: values.total_value,
borrowed_value: values.borrowed_value,
available_value: values.available_value
}
})
}
}
export const getDisplayEquipmentValue = (equipment: {
unit_price?: unknown
total_value_with_children?: unknown
}) => {
const totalValueWithChildren = toSafeNumber(equipment.total_value_with_children)
if (totalValueWithChildren > 0) {
return totalValueWithChildren
}
return toSafeNumber(equipment.unit_price)
}
export const getEquipmentDisplayTotalValue = (equipment: {
total_value?: unknown
total_value_with_children?: unknown
}) => {
const totalValueWithChildren = toSafeNumber(equipment.total_value_with_children)
if (totalValueWithChildren > 0) {
return totalValueWithChildren
}
return toSafeNumber(equipment.total_value)
}
export const getEquipmentDisplayItemValue = (equipment: {
unit_price?: unknown
total_quantity?: unknown
total_value?: unknown
total_value_with_children?: unknown
}) => {
const displayTotalValue = getEquipmentDisplayTotalValue(equipment)
const totalQuantity = Math.max(1, Math.floor(toSafeNumber(equipment.total_quantity) || 1))
if (displayTotalValue <= 0) {
return toSafeNumber(equipment.unit_price)
}
return displayTotalValue / totalQuantity
}
export const getEquipmentDisplayBorrowedValue = (equipment: {
borrowed_value?: unknown
total_value_with_children?: unknown
status?: unknown
}) => {
const totalValueWithChildren = toSafeNumber(equipment.total_value_with_children)
if (totalValueWithChildren > 0) {
return equipment.status === '已借出' ? totalValueWithChildren : 0
}
return toSafeNumber(equipment.borrowed_value)
}
export const getEquipmentDisplayAvailableValue = (equipment: {
available_value?: unknown
total_value_with_children?: unknown
status?: unknown
}) => {
const totalValueWithChildren = toSafeNumber(equipment.total_value_with_children)
if (totalValueWithChildren > 0) {
return equipment.status === '可用' ? totalValueWithChildren : 0
}
return toSafeNumber(equipment.available_value)
}
export const getEquipmentMountedChildrenValue = (equipment: {
total_value?: unknown
total_value_with_children?: unknown
}) => {
const displayTotalValue = getEquipmentDisplayTotalValue(equipment)
const ownTotalValue = toSafeNumber(equipment.total_value)
return Math.max(displayTotalValue - ownTotalValue, 0)
}
export const sumSnapshotChildrenValue = (snapshot?: string | null) => {
if (!snapshot) {
return 0
}
try {
const children = JSON.parse(snapshot) as Array<{ unit_price?: unknown }>
return calculateChildrenTotalValueFromRecords(children)
} catch {
return 0
}
}