feat(shop): add dynamic linked fees to modules
This commit is contained in:
@@ -333,6 +333,8 @@ export function OrderWizard({
|
||||
return { monthlyTotal: monthly, oneTimeTotal: oneTime }
|
||||
}, [selections, products, categories, moduleQuantities])
|
||||
|
||||
|
||||
|
||||
// Ermittlung der Update-Faktoren basierend auf dem Lizenzdatum (nur für Kauf/einmalig relevant)
|
||||
const updatePriceModifier = useMemo(() => {
|
||||
if (selectedBillingInterval !== 'one_time' || !lastLicenseDate) return { factor: 1 }
|
||||
@@ -386,6 +388,96 @@ export function OrderWizard({
|
||||
})
|
||||
}, [visibleCategories, selections, products, selectedBillingInterval])
|
||||
|
||||
const finalItemsToShow = basketItems.length > 0
|
||||
? basketItems
|
||||
: (allCategoriesFilled && productValidationErrors.length === 0
|
||||
? [{ deviceName: deviceName || 'Kasse 1', selections, moduleQuantities, billingInterval: selectedBillingInterval }]
|
||||
: [])
|
||||
|
||||
// Gesamtsummen für alle Items in der Bestellung (finalItemsToShow)
|
||||
const { overallMonthlyTotal, overallOneTimeTotal, linkedFeeProducts } = useMemo(() => {
|
||||
let monthly = 0
|
||||
let oneTime = 0
|
||||
const feeProductIds = new Set<string>()
|
||||
|
||||
finalItemsToShow.forEach(item => {
|
||||
categories.forEach(cat => {
|
||||
const sel = item.selections[cat.id]
|
||||
if (!sel) return
|
||||
|
||||
const selectedProds: Product[] = []
|
||||
if (cat.allow_multiselect && sel.productIds && sel.productIds.length > 0) {
|
||||
sel.productIds.forEach((pId: string) => {
|
||||
const p = products.find(prod => prod.id === pId)
|
||||
if (p) selectedProds.push(p)
|
||||
})
|
||||
} else if (sel.productId) {
|
||||
const p = products.find(prod => prod.id === sel.productId)
|
||||
if (p) selectedProds.push(p)
|
||||
}
|
||||
|
||||
const freeLimit = cat.allow_multiselect ? cat.free_items_limit : 0
|
||||
const sortedProds = [...selectedProds].sort((a, b) => a.base_price - b.base_price)
|
||||
|
||||
sortedProds.forEach((prod, idx) => {
|
||||
const isFree = idx < freeLimit
|
||||
const basePrice = isFree ? 0 : prod.base_price
|
||||
|
||||
if (prod.billing_interval === 'monthly') {
|
||||
monthly += basePrice
|
||||
} else {
|
||||
oneTime += basePrice
|
||||
}
|
||||
|
||||
// Module
|
||||
sel.moduleIds?.forEach((mId: string) => {
|
||||
const mod = prod.modules?.find(m => m.id === mId)
|
||||
if (mod) {
|
||||
const qty = item.moduleQuantities?.[mId] || 1
|
||||
if (prod.billing_interval === 'monthly') {
|
||||
monthly += mod.price * qty
|
||||
} else {
|
||||
oneTime += mod.price * qty
|
||||
}
|
||||
|
||||
if (mod.linked_fee_product_id) {
|
||||
feeProductIds.add(mod.linked_fee_product_id)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const feeProducts: Product[] = []
|
||||
feeProductIds.forEach(id => {
|
||||
const p = products.find(prod => prod.id === id)
|
||||
if (p) {
|
||||
feeProducts.push(p)
|
||||
if (p.billing_interval === 'monthly') {
|
||||
monthly += p.base_price
|
||||
} else {
|
||||
oneTime += p.base_price
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
overallMonthlyTotal: monthly,
|
||||
overallOneTimeTotal: oneTime,
|
||||
linkedFeeProducts: feeProducts
|
||||
}
|
||||
}, [finalItemsToShow, products, categories])
|
||||
|
||||
// Gesamte Endbeträge ermitteln
|
||||
const overallOneTimeNet = overallOneTimeTotal * discountFactor
|
||||
const overallOneTimeTax = overallOneTimeNet * 0.19
|
||||
const overallOneTimeGross = overallOneTimeNet + overallOneTimeTax
|
||||
|
||||
const overallMonthlyNet = overallMonthlyTotal
|
||||
const overallMonthlyTax = overallMonthlyNet * 0.19
|
||||
const overallMonthlyGross = overallMonthlyNet + overallMonthlyTax
|
||||
|
||||
const isNextStepDisabled = !allCategoriesFilled || productValidationErrors.length > 0
|
||||
|
||||
// Helper: toggle module for a category's selected product
|
||||
@@ -587,11 +679,7 @@ export function OrderWizard({
|
||||
}
|
||||
}
|
||||
|
||||
const finalItemsToShow = basketItems.length > 0
|
||||
? basketItems
|
||||
: (allCategoriesFilled && productValidationErrors.length === 0
|
||||
? [{ deviceName: deviceName || 'Kasse 1', selections, moduleQuantities, billingInterval: selectedBillingInterval }]
|
||||
: [])
|
||||
|
||||
|
||||
function selectProduct(catId: string, productId: string) {
|
||||
setSelections(prev => {
|
||||
@@ -817,14 +905,14 @@ export function OrderWizard({
|
||||
finalItemsToShow={finalItemsToShow}
|
||||
visibleCategories={visibleCategories}
|
||||
products={products}
|
||||
oneTimeTotal={oneTimeTotal}
|
||||
monthlyTotal={monthlyTotal}
|
||||
oneTimeNet={oneTimeNet}
|
||||
oneTimeTax={oneTimeTax}
|
||||
oneTimeGross={oneTimeGross}
|
||||
monthlyNet={monthlyNet}
|
||||
monthlyTax={monthlyTax}
|
||||
monthlyGross={monthlyGross}
|
||||
oneTimeTotal={overallOneTimeTotal}
|
||||
monthlyTotal={overallMonthlyTotal}
|
||||
oneTimeNet={overallOneTimeNet}
|
||||
oneTimeTax={overallOneTimeTax}
|
||||
oneTimeGross={overallOneTimeGross}
|
||||
monthlyNet={overallMonthlyNet}
|
||||
monthlyTax={overallMonthlyTax}
|
||||
monthlyGross={overallMonthlyGross}
|
||||
updatePriceModifier={updatePriceModifier}
|
||||
selectedEndCustomer={selectedEndCustomer}
|
||||
customerData={customerData}
|
||||
@@ -833,6 +921,7 @@ export function OrderWizard({
|
||||
isSubmitting={isSubmitting}
|
||||
initialOrder={initialOrder}
|
||||
prevStep={prevStep}
|
||||
linkedFeeProducts={linkedFeeProducts}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user