import { computed, type Ref } from 'vue' import type { Order } from '../api/orders' import type { CounterFormState } from './useOrderActions' import { readSnapshot, roundMoney, roundQuantity, readNumber } from './useOrderSnapshot' export interface OwnerSimulation { role: 'owner' totalIncomeYuan: number rentIncomeYuan: number damageCompensationYuan: number lossCompensationYuan: number shortfallYuan: number isDepositSufficient: boolean } export interface RenterSimulation { role: 'renter' actualCostYuan: number buyerRentYuan: number damageDeductYuan: number totalRefundYuan: number rentRefundYuan: number depositRefundYuan: number overshootYuan: number shortfallYuan: number isDepositSufficient: boolean } export type CheckoutSimulation = OwnerSimulation | RenterSimulation | null export function useCheckoutSimulator(options: { order: Ref isOwner: Ref isRenter: Ref form: Ref }) { return computed(() => { const o = options.order.value if (!o) return null const consumedM = roundQuantity(Math.max(Number(options.form.value.coin_consumed_m || 0), 0)) const consumableYuan = roundMoney( Math.max(Number(options.form.value.consumableAmountYuan || 0), 0) ) const depositDeductYuan = roundMoney( Math.max(Number(options.form.value.depositDeductAmountYuan || 0), 0) ) const snapshot = readSnapshot(o) const assetSummary = snapshot?.asset_summary as Record | undefined const priceBreakdown = ((assetSummary?.price_breakdown || snapshot?.price_breakdown) as | Record | undefined) || {} const totalCoinM = roundQuantity(readNumber(snapshot?.haf_coin_amount) / 1000000) const buyerRatio = readNumber(priceBreakdown['buyer_ratio']) const sellerRatio = readNumber(priceBreakdown['seller_ratio']) const buyerCoinBaseYuan = readNumber(priceBreakdown['buyer_coin_base_price']) const sellerCoinBaseYuan = readNumber(priceBreakdown['seller_coin_base_price']) const prepaidConsumableYuan = readNumber(priceBreakdown['consumable_price']) const coinUseRatio = totalCoinM > 0 ? roundQuantity(consumedM / totalCoinM) : 1 const isCoinOvershoot = totalCoinM > 0 && consumedM > totalCoinM const depositYuan = Number(o.deposit_amount_cent || 0) / 100 if (options.isOwner.value) { // ────────── 号主视角试算 ────────── let ownerCoinYuan = sellerRatio > 0 && (totalCoinM > 0 || consumedM > 0) ? roundMoney((consumedM * 100) / sellerRatio) : roundMoney(sellerCoinBaseYuan * coinUseRatio) if (!isCoinOvershoot) { ownerCoinYuan = Math.min( ownerCoinYuan, sellerCoinBaseYuan > 0 ? sellerCoinBaseYuan : (o.owner_rent_amount_cent || 0) / 100 ) } // 消耗品/后置损耗计算 let ownerConsumableYuan: number if (prepaidConsumableYuan > 0) { const ownerRentBaseYuan = (o.owner_rent_amount_cent || 0) / 100 const prepaidOwnerConsumableYuan = Math.max( roundMoney(ownerRentBaseYuan - sellerCoinBaseYuan), 0 ) if (consumableYuan <= prepaidConsumableYuan) { ownerConsumableYuan = roundMoney( prepaidOwnerConsumableYuan * (consumableYuan / prepaidConsumableYuan) ) } else { // 超出预收部分全额归号主 const excessYuan = roundMoney(consumableYuan - prepaidConsumableYuan) ownerConsumableYuan = roundMoney(prepaidOwnerConsumableYuan + excessYuan) } } else { // 无预收消耗品:后置申报损耗 100% 归号主 ownerConsumableYuan = consumableYuan } const ownerRentIncomeYuan = roundMoney(ownerCoinYuan + ownerConsumableYuan) // 押金赔付覆盖与差额 const damageCoveredYuan = Math.min(depositDeductYuan, depositYuan) const shortfallYuan = Math.max(roundMoney(depositDeductYuan - depositYuan), 0) const totalIncomeYuan = roundMoney(ownerRentIncomeYuan + damageCoveredYuan) return { role: 'owner', totalIncomeYuan, rentIncomeYuan: ownerRentIncomeYuan, damageCompensationYuan: damageCoveredYuan, lossCompensationYuan: ownerConsumableYuan, shortfallYuan, isDepositSufficient: shortfallYuan <= 0, } } if (options.isRenter.value) { // ────────── 租客视角试算 ────────── let buyerCoinYuan = buyerRatio > 0 && (totalCoinM > 0 || consumedM > 0) ? roundMoney((consumedM * 100) / buyerRatio) : roundMoney(buyerCoinBaseYuan * coinUseRatio) if (!isCoinOvershoot) { buyerCoinYuan = Math.min( buyerCoinYuan, buyerCoinBaseYuan > 0 ? buyerCoinBaseYuan : (o.rent_amount_cent || 0) / 100 ) } const actualBuyerRentYuan = roundMoney(buyerCoinYuan + consumableYuan) const prepaidRentYuan = (o.rent_amount_cent || 0) / 100 const rentOvershootYuan = Math.max(roundMoney(actualBuyerRentYuan - prepaidRentYuan), 0) const rentRefundYuan = Math.max(roundMoney(prepaidRentYuan - actualBuyerRentYuan), 0) const damageAppliedYuan = Math.min(depositDeductYuan, depositYuan) const depositLeftAfterDamage = Math.max(roundMoney(depositYuan - damageAppliedYuan), 0) const overshootCoveredYuan = Math.min(rentOvershootYuan, depositLeftAfterDamage) const depositRefundYuan = Math.max( roundMoney(depositLeftAfterDamage - overshootCoveredYuan), 0 ) const shortfallYuan = roundMoney( Math.max(roundMoney(depositDeductYuan - damageAppliedYuan), 0) + Math.max(roundMoney(rentOvershootYuan - overshootCoveredYuan), 0) ) const totalRefundYuan = roundMoney(rentRefundYuan + depositRefundYuan) const actualCostYuan = roundMoney(actualBuyerRentYuan + damageAppliedYuan) return { role: 'renter', actualCostYuan, buyerRentYuan: actualBuyerRentYuan, damageDeductYuan: depositDeductYuan, totalRefundYuan, rentRefundYuan, depositRefundYuan, overshootYuan: rentOvershootYuan, shortfallYuan, isDepositSufficient: shortfallYuan <= 0, } } return null }) }