fix: correct pricing for instant server customization
Major changes: - Track original drives to avoid double-charging for included components - For instant customization, original drives have 0 cost (included in base price) - When user changes drives, only charge the price difference - Separate storage pricing from other component pricing This ensures the €1520 instant server keeps its base price when customizing, and only adds charges for actual upgrades beyond the included configuration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4306,15 +4306,23 @@ var wpcf7 = {
|
|||||||
selectedValId = nonNoneOptions.length > 0 ? nonNoneOptions[0].id : opt.values[0].id;
|
selectedValId = nonNoneOptions.length > 0 ? nonNoneOptions[0].id : opt.values[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply selection and price adjustment
|
// For storage options, we don't adjust prices as they're handled by pooled grid
|
||||||
if (selectedValId) {
|
if (opt.type === 'storage') {
|
||||||
preselected[opt.id] = selectedValId;
|
// Don't adjust storage prices - let pooled grid handle it
|
||||||
const selectedVal = opt.values.find(v => v.id === selectedValId);
|
if (selectedValId) {
|
||||||
if (selectedVal && selectedVal.price > 0) {
|
preselected[opt.id] = selectedValId;
|
||||||
const offset = selectedVal.price;
|
}
|
||||||
opt.values.forEach(v => {
|
} else {
|
||||||
v.price = parseFloat((v.price - offset).toFixed(2));
|
// For non-storage options, apply price adjustment
|
||||||
});
|
if (selectedValId) {
|
||||||
|
preselected[opt.id] = selectedValId;
|
||||||
|
const selectedVal = opt.values.find(v => v.id === selectedValId);
|
||||||
|
if (selectedVal && selectedVal.price > 0) {
|
||||||
|
const offset = selectedVal.price;
|
||||||
|
opt.values.forEach(v => {
|
||||||
|
v.price = parseFloat((v.price - offset).toFixed(2));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -4838,6 +4846,9 @@ var wpcf7 = {
|
|||||||
|
|
||||||
// For instant customization, pre-fill with the server's storage configuration
|
// For instant customization, pre-fill with the server's storage configuration
|
||||||
if (isInstant && storageRequirements.length > 0) {
|
if (isInstant && storageRequirements.length > 0) {
|
||||||
|
// Track original drives to avoid double-charging
|
||||||
|
window.originalDrives = [];
|
||||||
|
|
||||||
// First, reset all slots to None
|
// First, reset all slots to None
|
||||||
initializedSlots.forEach(slot => {
|
initializedSlots.forEach(slot => {
|
||||||
const noneOption = slot.values.find(v =>
|
const noneOption = slot.values.find(v =>
|
||||||
@@ -4908,8 +4919,17 @@ var wpcf7 = {
|
|||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
emptySlot.currentVal = match;
|
emptySlot.currentVal = match;
|
||||||
configState[emptySlot.label] = match.price;
|
// For instant customization, original drives have no additional cost
|
||||||
|
configState[emptySlot.label] = 0; // Price is 0 because it's included in base price
|
||||||
configIds[emptySlot.id] = match.id;
|
configIds[emptySlot.id] = match.id;
|
||||||
|
|
||||||
|
// Track this as an original drive
|
||||||
|
window.originalDrives.push({
|
||||||
|
slotId: emptySlot.id,
|
||||||
|
driveId: match.id,
|
||||||
|
text: match.text,
|
||||||
|
price: match.price
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Could not find matching drive for requirement: ${req.spec}`);
|
console.warn(`Could not find matching drive for requirement: ${req.spec}`);
|
||||||
}
|
}
|
||||||
@@ -4975,38 +4995,70 @@ var wpcf7 = {
|
|||||||
if (!pool) return;
|
if (!pool) return;
|
||||||
|
|
||||||
if (change > 0) {
|
if (change > 0) {
|
||||||
// Find first slot that is "None" or default (assuming index 0 is None/Default)
|
// Find first slot that is "None" or default
|
||||||
// We need to identify what is "Empty". We assume the first value in options list is "Empty" or "None".
|
|
||||||
const targetSlot = pool.slots.find(s => s.currentVal === s.values[0]);
|
const targetSlot = pool.slots.find(s => s.currentVal === s.values[0]);
|
||||||
|
|
||||||
if (targetSlot) {
|
if (targetSlot) {
|
||||||
// Find the matching value object in this slot's values
|
// Find the matching value object in this slot's values
|
||||||
const newVal = targetSlot.values.find(v => v.text === valText);
|
const newVal = targetSlot.values.find(v => v.text === valText);
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
targetSlot.currentVal = newVal;
|
targetSlot.currentVal = newVal;
|
||||||
|
|
||||||
// Update Global Config State
|
// For instant customization, calculate price difference
|
||||||
configState[targetSlot.label] = newVal.price;
|
if (isInstant && window.originalDrives) {
|
||||||
|
// Check if this slot had an original drive
|
||||||
|
const originalDrive = window.originalDrives.find(d => d.slotId === targetSlot.id);
|
||||||
|
if (originalDrive) {
|
||||||
|
// Calculate price difference
|
||||||
|
const priceDiff = newVal.price - originalDrive.price;
|
||||||
|
configState[targetSlot.label] = priceDiff;
|
||||||
|
|
||||||
|
// Update original drives tracking
|
||||||
|
originalDrive.driveId = newVal.id;
|
||||||
|
originalDrive.text = newVal.text;
|
||||||
|
originalDrive.price = newVal.price;
|
||||||
|
} else {
|
||||||
|
// This is a new drive, charge full price
|
||||||
|
configState[targetSlot.label] = newVal.price;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Custom server or no tracking, charge full price
|
||||||
|
configState[targetSlot.label] = newVal.price;
|
||||||
|
}
|
||||||
|
|
||||||
configIds[targetSlot.id] = newVal.id;
|
configIds[targetSlot.id] = newVal.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Decrement: Find last slot that matches this value
|
// Decrement: Find last slot that matches this value
|
||||||
// We search array in reverse to remove from end
|
|
||||||
for (let i = pool.slots.length - 1; i >= 0; i--) {
|
for (let i = pool.slots.length - 1; i >= 0; i--) {
|
||||||
if (pool.slots[i].currentVal.text === valText) {
|
if (pool.slots[i].currentVal.text === valText) {
|
||||||
// Reset to default (index 0)
|
// Reset to default
|
||||||
const defaultVal = pool.slots[i].values[0];
|
const defaultVal = pool.slots[i].values[0];
|
||||||
pool.slots[i].currentVal = defaultVal;
|
pool.slots[i].currentVal = defaultVal;
|
||||||
|
|
||||||
// Update Global Config State
|
// For instant customization, refund the price if it was an upgrade
|
||||||
configState[pool.slots[i].label] = defaultVal.price;
|
if (isInstant && window.originalDrives) {
|
||||||
|
const originalDrive = window.originalDrives.find(d => d.slotId === pool.slots[i].id);
|
||||||
|
if (originalDrive && pool.slots[i].currentVal.id !== originalDrive.driveId) {
|
||||||
|
// We're removing an upgrade, refund the difference
|
||||||
|
const currentVal = pool.slots[i].values.find(v => v.id === configIds[pool.slots[i].id]);
|
||||||
|
if (currentVal) {
|
||||||
|
configState[pool.slots[i].label] = -currentVal.price;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configState[pool.slots[i].label] = defaultVal.price;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configState[pool.slots[i].label] = defaultVal.price;
|
||||||
|
}
|
||||||
|
|
||||||
configIds[pool.slots[i].id] = defaultVal.id;
|
configIds[pool.slots[i].id] = defaultVal.id;
|
||||||
break; // Only remove one
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePooledVisuals(poolKey, isInstant);
|
updatePooledVisuals(poolKey, isInstant);
|
||||||
updateSummary();
|
updateSummary();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user