Swap steps 5/6 (license before deposit), charge balance guard, refund cancels booking

This commit is contained in:
2026-05-25 18:49:35 +00:00
parent 3e18d71378
commit af5712f761
+111 -86
View File
@@ -198,7 +198,7 @@ if ($isAjax) {
if (!$pid) { echo json_encode(['error'=>'No payment on file']); exit; } if (!$pid) { echo json_encode(['error'=>'No payment on file']); exit; }
$resp = squareApi('POST', "/payments/{$pid}/cancel"); $resp = squareApi('POST', "/payments/{$pid}/cancel");
if (($resp['payment']['status'] ?? '') === 'CANCELED') { if (($resp['payment']['status'] ?? '') === 'CANCELED') {
db()->prepare("UPDATE bookings SET square_payment_status='CANCELED' WHERE id=?")->execute([$id]); db()->prepare("UPDATE bookings SET square_payment_status='CANCELED', status='cancelled' WHERE id=?")->execute([$id]);
echo json_encode(['ok'=>true,'status'=>'CANCELED']); echo json_encode(['ok'=>true,'status'=>'CANCELED']);
} else { } else {
echo json_encode(['error' => $resp['errors'][0]['detail'] ?? 'Void failed']); echo json_encode(['error' => $resp['errors'][0]['detail'] ?? 'Void failed']);
@@ -221,7 +221,7 @@ if ($isAjax) {
'reason' => 'Security deposit refund — booking returned in good condition', 'reason' => 'Security deposit refund — booking returned in good condition',
]); ]);
if (!empty($resp['refund']['id'])) { if (!empty($resp['refund']['id'])) {
db()->prepare("UPDATE bookings SET square_payment_status='REFUNDED', square_refund_id=?, deposit_paid=0 WHERE id=?") db()->prepare("UPDATE bookings SET square_payment_status='REFUNDED', square_refund_id=?, deposit_paid=0, status='cancelled' WHERE id=?")
->execute([$resp['refund']['id'], $id]); ->execute([$resp['refund']['id'], $id]);
echo json_encode(['ok'=>true,'status'=>'REFUNDED']); echo json_encode(['ok'=>true,'status'=>'REFUNDED']);
} else { } else {
@@ -686,12 +686,12 @@ textarea.notes-ta:focus{border-color:#f97316}
</select> </select>
</td> </td>
<td> <td>
<div class="dots" title="Confirmed / Waiver / Insurance / Deposit / License / Helmet / Safety / Ops / Returned"> <div class="dots" title="Confirmed / Waiver / Insurance / License / Deposit / Helmet / Safety / Ops / Returned">
<div class="dot <?= $dotClass($stepConfirmed) ?>" title="Confirmed"></div> <div class="dot <?= $dotClass($stepConfirmed) ?>" title="Confirmed"></div>
<div class="dot <?= $dotClass($stepWaiver) ?>" title="Waiver Signed"></div> <div class="dot <?= $dotClass($stepWaiver) ?>" title="Waiver Signed"></div>
<div class="dot <?= $dotClass($stepInsurance) ?>" title="Insurance" id="dot-<?= $bid ?>-insurance_verified"></div> <div class="dot <?= $dotClass($stepInsurance) ?>" title="Insurance" id="dot-<?= $bid ?>-insurance_verified"></div>
<div class="dot <?= $dotClass($stepDeposit) ?>" title="Deposit" id="dot-<?= $bid ?>-deposit_received"></div>
<div class="dot <?= $dotClass($stepLicense) ?>" title="License" id="dot-<?= $bid ?>-license_verified"></div> <div class="dot <?= $dotClass($stepLicense) ?>" title="License" id="dot-<?= $bid ?>-license_verified"></div>
<div class="dot <?= $dotClass($stepDeposit) ?>" title="Deposit" id="dot-<?= $bid ?>-deposit_received"></div>
<div class="dot <?= $dotClass($stepHelmet) ?>" title="Helmet" id="dot-<?= $bid ?>-helmet_provided"></div> <div class="dot <?= $dotClass($stepHelmet) ?>" title="Helmet" id="dot-<?= $bid ?>-helmet_provided"></div>
<div class="dot <?= $dotClass($stepSafety) ?>" title="Safety Course" id="dot-<?= $bid ?>-safety_course"></div> <div class="dot <?= $dotClass($stepSafety) ?>" title="Safety Course" id="dot-<?= $bid ?>-safety_course"></div>
<div class="dot <?= $dotClass($stepOps) ?>" title="Ops Course" id="dot-<?= $bid ?>-operational_course"></div> <div class="dot <?= $dotClass($stepOps) ?>" title="Ops Course" id="dot-<?= $bid ?>-operational_course"></div>
@@ -812,7 +812,29 @@ textarea.notes-ta:focus{border-color:#f97316}
</div> </div>
</div> </div>
<!-- Step 5: Deposit (Square-aware) --> <!-- Step 5: License -->
<div class="flow-step">
<div class="flow-icon <?= $stepLicense?'done':($cancelled?'skip':'pending') ?>" id="icon-<?= $bid ?>-license_verified">
<?= $stepLicense?'✓':($cancelled?'—':'5') ?>
</div>
<div class="flow-body">
<span class="flow-label">Driver's License Verified</span>
<span class="flow-meta" id="meta-<?= $bid ?>-license_verified">
<?= $stepLicense?'License verified':($cancelled?'N/A':'Verify at pickup') ?>
</span>
<?php if (!$cancelled): ?>
<div class="flow-action">
<button class="flow-toggle <?= $stepLicense?'active':'' ?>"
id="btn-<?= $bid ?>-license_verified"
onclick="toggleReq(<?= $bid ?>,'license_verified',this)">
<?= $stepLicense?'✓ License Verified':'Mark License Verified' ?>
</button>
</div>
<?php endif; ?>
</div>
</div>
<!-- Step 6: Deposit (Square-aware) -->
<?php <?php
$sqStatus = $b['square_payment_status'] ?? ''; $sqStatus = $b['square_payment_status'] ?? '';
$sqId = $b['square_payment_id'] ?? ''; $sqId = $b['square_payment_id'] ?? '';
@@ -820,10 +842,11 @@ textarea.notes-ta:focus{border-color:#f97316}
$cardLast4 = $b['card_last4'] ?? ''; $cardLast4 = $b['card_last4'] ?? '';
$cardBrand = $b['card_brand'] ?? ''; $cardBrand = $b['card_brand'] ?? '';
$depositIcon = 'pending'; $depositIcon = 'pending';
$depositLabel = '5'; $depositLabel = '6';
if ($cancelled) { $depositIcon='skip'; $depositLabel='—'; } if ($cancelled) { $depositIcon='skip'; $depositLabel='—'; }
elseif ($stepDeposit || elseif ($stepDeposit ||
in_array($sqStatus,['COMPLETED','BAL_COMPLETED'])) { $depositIcon='done'; $depositLabel='✓'; } in_array($sqStatus,['COMPLETED','BAL_COMPLETED'])) { $depositIcon='done'; $depositLabel='✓'; }
$priorStepsDone = $stepConfirmed && $stepWaiver && $stepInsurance && $stepLicense;
?> ?>
<div class="flow-step"> <div class="flow-step">
<div class="flow-icon <?= $depositIcon ?>" id="icon-<?= $bid ?>-deposit_received"> <div class="flow-icon <?= $depositIcon ?>" id="icon-<?= $bid ?>-deposit_received">
@@ -853,14 +876,18 @@ textarea.notes-ta:focus{border-color:#f97316}
<div class="flow-action" id="deposit-actions-<?= $bid ?>"> <div class="flow-action" id="deposit-actions-<?= $bid ?>">
<?php if ($sqStatus === 'APPROVED' || $sqStatus === 'PENDING'): ?> <?php if ($sqStatus === 'APPROVED' || $sqStatus === 'PENDING'): ?>
<button class="flow-toggle" onclick="squareAction(<?= $bid ?>,'square_capture',this)" style="margin-right:4px">Capture $<?= number_format(DEPOSIT_AMOUNT,0) ?></button> <button class="flow-toggle" onclick="squareAction(<?= $bid ?>,'square_capture',this)" style="margin-right:4px">Capture $<?= number_format(DEPOSIT_AMOUNT,0) ?></button>
<button class="flow-toggle" onclick="squareAction(<?= $bid ?>,'square_void',this)" style="border-color:#dc2626;color:#dc2626">Void Hold</button> <button class="flow-toggle" onclick="squareAction(<?= $bid ?>,'square_void',this)" style="border-color:#dc2626;color:#dc2626">Void &amp; Cancel Booking</button>
<?php if ($sqCardId): ?> <?php if ($sqCardId && $priorStepsDone): ?>
<button class="flow-toggle" onclick="chargeBalance(<?= $bid ?>,this)" style="margin-left:4px;border-color:#16a34a;color:#16a34a">Charge Balance $<?= number_format($b['amount']-DEPOSIT_AMOUNT,2) ?></button> <button class="flow-toggle" onclick="chargeBalance(<?= $bid ?>,this)" style="margin-left:4px;border-color:#16a34a;color:#16a34a">Charge Balance $<?= number_format($b['amount']-DEPOSIT_AMOUNT,2) ?></button>
<?php elseif ($sqCardId): ?>
<button class="flow-toggle" disabled style="margin-left:4px;border-color:#9ca3af;color:#9ca3af;opacity:.55;cursor:not-allowed" title="Complete steps 15 before charging balance">Charge Balance $<?= number_format($b['amount']-DEPOSIT_AMOUNT,2) ?></button>
<?php endif; ?> <?php endif; ?>
<?php elseif ($sqStatus === 'COMPLETED'): ?> <?php elseif ($sqStatus === 'COMPLETED'): ?>
<button class="flow-toggle" onclick="squareAction(<?= $bid ?>,'square_refund',this)" style="border-color:#2563eb;color:#2563eb">Refund $<?= number_format((float)($b['deposit_paid']??DEPOSIT_AMOUNT),2) ?></button> <button class="flow-toggle" onclick="squareAction(<?= $bid ?>,'square_refund',this)" style="border-color:#2563eb;color:#2563eb">Refund &amp; Cancel Booking</button>
<?php if ($sqCardId): ?> <?php if ($sqCardId && $priorStepsDone): ?>
<button class="flow-toggle" onclick="chargeBalance(<?= $bid ?>,this)" style="margin-left:4px;border-color:#16a34a;color:#16a34a">Charge Balance $<?= number_format($b['amount']-DEPOSIT_AMOUNT,2) ?></button> <button class="flow-toggle" onclick="chargeBalance(<?= $bid ?>,this)" style="margin-left:4px;border-color:#16a34a;color:#16a34a">Charge Balance $<?= number_format($b['amount']-DEPOSIT_AMOUNT,2) ?></button>
<?php elseif ($sqCardId): ?>
<button class="flow-toggle" disabled style="margin-left:4px;border-color:#9ca3af;color:#9ca3af;opacity:.55;cursor:not-allowed" title="Complete steps 15 before charging balance">Charge Balance $<?= number_format($b['amount']-DEPOSIT_AMOUNT,2) ?></button>
<?php endif; ?> <?php endif; ?>
<?php elseif (!$sqId || $sqStatus === 'DECLINED'): ?> <?php elseif (!$sqId || $sqStatus === 'DECLINED'): ?>
<button class="flow-toggle <?= $stepDeposit?'active':'' ?>" <button class="flow-toggle <?= $stepDeposit?'active':'' ?>"
@@ -874,28 +901,6 @@ textarea.notes-ta:focus{border-color:#f97316}
</div> </div>
</div> </div>
<!-- Step 6: License -->
<div class="flow-step">
<div class="flow-icon <?= $stepLicense?'done':($cancelled?'skip':'pending') ?>" id="icon-<?= $bid ?>-license_verified">
<?= $stepLicense?'✓':($cancelled?'—':'6') ?>
</div>
<div class="flow-body">
<span class="flow-label">Driver's License Verified</span>
<span class="flow-meta" id="meta-<?= $bid ?>-license_verified">
<?= $stepLicense?'License verified':($cancelled?'N/A':'Verify at pickup') ?>
</span>
<?php if (!$cancelled): ?>
<div class="flow-action">
<button class="flow-toggle <?= $stepLicense?'active':'' ?>"
id="btn-<?= $bid ?>-license_verified"
onclick="toggleReq(<?= $bid ?>,'license_verified',this)">
<?= $stepLicense?'✓ License Verified':'Mark License Verified' ?>
</button>
</div>
<?php endif; ?>
</div>
</div>
<?php if (!$cancelled): ?> <?php if (!$cancelled): ?>
<!-- ── Pre-departure checklist (admin only, after deposit confirmed) ── --> <!-- ── Pre-departure checklist (admin only, after deposit confirmed) ── -->
<div style="margin:.6rem 0 .2rem;padding:.4rem .5rem;background:#fff7ed;border-radius:6px;font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:#92400e"> <div style="margin:.6rem 0 .2rem;padding:.4rem .5rem;background:#fff7ed;border-radius:6px;font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:#92400e">
@@ -1010,14 +1015,14 @@ textarea.notes-ta:focus{border-color:#f97316}
<input type="checkbox" value="insurance" <?= !$stepInsurance?'checked':'' ?>> <input type="checkbox" value="insurance" <?= !$stepInsurance?'checked':'' ?>>
Bring proof of insurance Bring proof of insurance
</label> </label>
<label>
<input type="checkbox" value="deposit" <?= !$stepDeposit?'checked':'' ?>>
Security deposit info
</label>
<label> <label>
<input type="checkbox" value="license" <?= !$stepLicense?'checked':'' ?>> <input type="checkbox" value="license" <?= !$stepLicense?'checked':'' ?>>
Bring driver's license Bring driver's license
</label> </label>
<label>
<input type="checkbox" value="deposit" <?= !$stepDeposit?'checked':'' ?>>
Security deposit info
</label>
</div> </div>
<button class="send-btn" id="remind-btn-<?= $bid ?>" onclick="sendReminder(<?= $bid ?>)"> <button class="send-btn" id="remind-btn-<?= $bid ?>" onclick="sendReminder(<?= $bid ?>)">
Send Reminder Email Send Reminder Email
@@ -1292,9 +1297,29 @@ textarea.notes-ta:focus{border-color:#f97316}
</div> </div>
</div> </div>
<div class="flow-step">
<div class="flow-icon <?= $stepLicense?'done':($cancelled?'skip':'pending') ?>" id="cv-icon-<?= $bid ?>-license_verified">
<?= $stepLicense?'✓':($cancelled?'—':'5') ?>
</div>
<div class="flow-body">
<span class="flow-label">Driver's License Verified</span>
<span class="flow-meta" id="cv-meta-<?= $bid ?>-license_verified">
<?= $stepLicense?'Verified — on file':($cancelled?'N/A':'Verify at pickup') ?>
</span>
<?php if (!$cancelled): ?>
<div class="flow-action">
<button class="flow-toggle <?= $stepLicense?'active':'' ?>" id="cv-btn-<?= $bid ?>-license_verified"
onclick="cvToggleReq(<?= $bid ?>,'license_verified',this)">
<?= $stepLicense?'✓ License Verified':'Mark License Verified' ?>
</button>
</div>
<?php endif; ?>
</div>
</div>
<?php <?php
$cvDepIcon = $cancelled ? 'skip' : (($stepDeposit||$sqStatus==='COMPLETED') ? 'done' : 'pending'); $cvDepIcon = $cancelled ? 'skip' : (($stepDeposit||$sqStatus==='COMPLETED') ? 'done' : 'pending');
$cvDepLabel = $cancelled ? '—' : (($stepDeposit||$sqStatus==='COMPLETED') ? '✓' : '5'); $cvDepLabel = $cancelled ? '—' : (($stepDeposit||$sqStatus==='COMPLETED') ? '✓' : '6');
?> ?>
<div class="flow-step"> <div class="flow-step">
<div class="flow-icon <?= $cvDepIcon ?>" id="cv-icon-<?= $bid ?>-deposit_received"> <div class="flow-icon <?= $cvDepIcon ?>" id="cv-icon-<?= $bid ?>-deposit_received">
@@ -1316,9 +1341,9 @@ textarea.notes-ta:focus{border-color:#f97316}
<div class="flow-action" id="cv-dep-actions-<?= $bid ?>"> <div class="flow-action" id="cv-dep-actions-<?= $bid ?>">
<?php if ($sqStatus==='APPROVED'||$sqStatus==='PENDING'): ?> <?php if ($sqStatus==='APPROVED'||$sqStatus==='PENDING'): ?>
<button class="flow-toggle" onclick="cvSquareAction(<?= $bid ?>,'square_capture',this)" style="margin-right:4px">Capture $<?= number_format(DEPOSIT_AMOUNT,0) ?></button> <button class="flow-toggle" onclick="cvSquareAction(<?= $bid ?>,'square_capture',this)" style="margin-right:4px">Capture $<?= number_format(DEPOSIT_AMOUNT,0) ?></button>
<button class="flow-toggle" onclick="cvSquareAction(<?= $bid ?>,'square_void',this)" style="border-color:#dc2626;color:#dc2626">Void Hold</button> <button class="flow-toggle" onclick="cvSquareAction(<?= $bid ?>,'square_void',this)" style="border-color:#dc2626;color:#dc2626">Void &amp; Cancel Booking</button>
<?php elseif ($sqStatus==='COMPLETED'): ?> <?php elseif ($sqStatus==='COMPLETED'): ?>
<button class="flow-toggle" onclick="cvSquareAction(<?= $bid ?>,'square_refund',this)" style="border-color:#2563eb;color:#2563eb">Refund $<?= number_format((float)($cb['deposit_paid']??DEPOSIT_AMOUNT),2) ?></button> <button class="flow-toggle" onclick="cvSquareAction(<?= $bid ?>,'square_refund',this)" style="border-color:#2563eb;color:#2563eb">Refund &amp; Cancel Booking</button>
<?php elseif (!$sqId): ?> <?php elseif (!$sqId): ?>
<button class="flow-toggle <?= $stepDeposit?'active':'' ?>" id="cv-btn-<?= $bid ?>-deposit_received" <button class="flow-toggle <?= $stepDeposit?'active':'' ?>" id="cv-btn-<?= $bid ?>-deposit_received"
onclick="cvToggleReq(<?= $bid ?>,'deposit_received',this)"> onclick="cvToggleReq(<?= $bid ?>,'deposit_received',this)">
@@ -1330,26 +1355,6 @@ textarea.notes-ta:focus{border-color:#f97316}
</div> </div>
</div> </div>
<div class="flow-step">
<div class="flow-icon <?= $stepLicense?'done':($cancelled?'skip':'pending') ?>" id="cv-icon-<?= $bid ?>-license_verified">
<?= $stepLicense?'✓':($cancelled?'—':'6') ?>
</div>
<div class="flow-body">
<span class="flow-label">Driver's License Verified</span>
<span class="flow-meta" id="cv-meta-<?= $bid ?>-license_verified">
<?= $stepLicense?'Verified — on file':($cancelled?'N/A':'Verify at pickup') ?>
</span>
<?php if (!$cancelled): ?>
<div class="flow-action">
<button class="flow-toggle <?= $stepLicense?'active':'' ?>" id="cv-btn-<?= $bid ?>-license_verified"
onclick="cvToggleReq(<?= $bid ?>,'license_verified',this)">
<?= $stepLicense?'✓ License Verified':'Mark License Verified' ?>
</button>
</div>
<?php endif; ?>
</div>
</div>
<?php if (!$cancelled): ?> <?php if (!$cancelled): ?>
<div style="margin:.4rem 0 .1rem;padding:.3rem .5rem;background:#fff7ed;border-radius:5px;font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:#92400e">Pre-Departure</div> <div style="margin:.4rem 0 .1rem;padding:.3rem .5rem;background:#fff7ed;border-radius:5px;font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:#92400e">Pre-Departure</div>
@@ -1532,7 +1537,7 @@ function toggleReq(id, field, btn) {
const icon = document.getElementById('icon-'+id+'-'+field); const icon = document.getElementById('icon-'+id+'-'+field);
if (icon) { if (icon) {
icon.className = 'flow-icon ' + (on ? 'done' : 'pending'); icon.className = 'flow-icon ' + (on ? 'done' : 'pending');
const stepNums = {insurance_verified:'4', deposit_received:'5', license_verified:'6', helmet_provided:'7', safety_course:'8', operational_course:'9'}; const stepNums = {insurance_verified:'4', license_verified:'5', deposit_received:'6', helmet_provided:'7', safety_course:'8', operational_course:'9'};
icon.textContent = on ? '✓' : (stepNums[field] || '?'); icon.textContent = on ? '✓' : (stepNums[field] || '?');
} }
// Update meta text // Update meta text
@@ -1624,8 +1629,8 @@ function squareAction(id, action, btn) {
const [orig, working, done] = labels[action]; const [orig, working, done] = labels[action];
const confirmMsg = { const confirmMsg = {
square_capture: 'Charge the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit hold to this card?', square_capture: 'Charge the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit hold to this card?',
square_void: 'Void the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit hold? The customer will NOT be charged.', square_void: 'Void the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit hold and CANCEL this booking? The customer will NOT be charged.',
square_refund: 'Refund the deposit to this card?', square_refund: 'Refund the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit and CANCEL this booking? This cannot be undone.',
}[action]; }[action];
if (!confirm(confirmMsg)) return; if (!confirm(confirmMsg)) return;
btn.disabled = true; btn.disabled = true;
@@ -1650,19 +1655,31 @@ function squareAction(id, action, btn) {
if (dot) dot.className='dot dot-done'; if (dot) dot.className='dot dot-done';
// Replace action area with refund button // Replace action area with refund button
const area = document.getElementById('deposit-actions-'+id); const area = document.getElementById('deposit-actions-'+id);
if (area) area.innerHTML = '<button class="flow-toggle" onclick="squareAction('+id+',\'square_refund\',this)" style="border-color:#2563eb;color:#2563eb">Refund Deposit</button>'; if (area) area.innerHTML = '<button class="flow-toggle" onclick="squareAction('+id+',\'square_refund\',this)" style="border-color:#2563eb;color:#2563eb">Refund &amp; Cancel Booking</button>';
} else if (d.status === 'CANCELED') { } else if (d.status === 'CANCELED' || d.status === 'REFUNDED') {
if (meta) meta.textContent = 'Hold voided — no charge'; const t = d.status === 'CANCELED' ? 'Hold voided — booking cancelled' : 'Refunded — booking cancelled';
if (meta) meta.textContent = t;
if (icon) { icon.className='flow-icon skip'; icon.textContent='—'; } if (icon) { icon.className='flow-icon skip'; icon.textContent='—'; }
if (dot) dot.className='dot dot-skip'; if (dot) dot.className='dot dot-skip';
const area = document.getElementById('deposit-actions-'+id); const area = document.getElementById('deposit-actions-'+id);
if (area) area.innerHTML = ''; if (area) area.innerHTML = '';
} else if (d.status === 'REFUNDED') { // Update booking status selects and row data-status
if (meta) meta.textContent = 'Refunded — deposit returned'; document.querySelectorAll('select.status-sel[data-id="'+id+'"]').forEach(sel => {
if (icon) { icon.className='flow-icon pending'; icon.textContent='↩'; } sel.value = 'cancelled';
if (dot) dot.className='dot dot-skip'; });
const area = document.getElementById('deposit-actions-'+id); document.querySelectorAll('tr.booking-row, tr.detail-row').forEach(row => {
if (area) area.innerHTML = ''; const s = row.querySelector('select[data-id="'+id+'"]');
if (s) row.dataset.status = 'cancelled';
});
// Update confirmed step icon
['icon','cv-icon'].forEach(p => {
const el = document.getElementById(p+'-'+id+'-confirmed');
if (el) { el.className='flow-icon skip'; el.textContent='—'; }
});
['meta','cv-meta'].forEach(p => {
const el = document.getElementById(p+'-'+id+'-confirmed');
if (el) el.textContent = 'Cancelled';
});
} }
} else { } else {
btn.textContent = orig; btn.textContent = orig;
@@ -1830,8 +1847,8 @@ function cvSquareAction(bid, action, btn) {
const [orig, working] = labels[action]; const [orig, working] = labels[action];
const confirmMsg = { const confirmMsg = {
square_capture: 'Charge the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit hold to this card?', square_capture: 'Charge the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit hold to this card?',
square_void: 'Void the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit hold? The customer will NOT be charged.', square_void: 'Void the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit hold and CANCEL this booking? The customer will NOT be charged.',
square_refund: 'Refund the deposit to this card?', square_refund: 'Refund the $<?= number_format(DEPOSIT_AMOUNT,2) ?> deposit and CANCEL this booking? This cannot be undone.',
}[action]; }[action];
if (!confirm(confirmMsg)) return; if (!confirm(confirmMsg)) return;
btn.disabled = true; btn.disabled = true;
@@ -1853,13 +1870,13 @@ function cvSquareAction(bid, action, btn) {
const t = 'Captured — deposit charged'; const t = 'Captured — deposit charged';
if (cvMeta) cvMeta.textContent = t; if (cvMeta) cvMeta.textContent = t;
if (cvIcon) { cvIcon.className='flow-icon done'; cvIcon.textContent='✓'; } if (cvIcon) { cvIcon.className='flow-icon done'; cvIcon.textContent='✓'; }
if (cvArea) cvArea.innerHTML = '<button class="flow-toggle" onclick="cvSquareAction('+bid+',\'square_refund\',this)" style="border-color:#2563eb;color:#2563eb">Refund Deposit</button>'; if (cvArea) cvArea.innerHTML = '<button class="flow-toggle" onclick="cvSquareAction('+bid+',\'square_refund\',this)" style="border-color:#2563eb;color:#2563eb">Refund &amp; Cancel Booking</button>';
if (meta) meta.textContent = t; if (meta) meta.textContent = t;
if (icon) { icon.className='flow-icon done'; icon.textContent='✓'; } if (icon) { icon.className='flow-icon done'; icon.textContent='✓'; }
if (dot) dot.className='dot dot-done'; if (dot) dot.className='dot dot-done';
if (area) area.innerHTML = '<button class="flow-toggle" onclick="squareAction('+bid+',\'square_refund\',this)" style="border-color:#2563eb;color:#2563eb">Refund Deposit</button>'; if (area) area.innerHTML = '<button class="flow-toggle" onclick="squareAction('+bid+',\'square_refund\',this)" style="border-color:#2563eb;color:#2563eb">Refund &amp; Cancel Booking</button>';
} else if (d.status === 'CANCELED') { } else if (d.status === 'CANCELED' || d.status === 'REFUNDED') {
const t = 'Hold voided — no charge'; const t = d.status === 'CANCELED' ? 'Hold voided — booking cancelled' : 'Refunded — booking cancelled';
if (cvMeta) cvMeta.textContent = t; if (cvMeta) cvMeta.textContent = t;
if (cvIcon) { cvIcon.className='flow-icon skip'; cvIcon.textContent='—'; } if (cvIcon) { cvIcon.className='flow-icon skip'; cvIcon.textContent='—'; }
if (cvArea) cvArea.innerHTML = ''; if (cvArea) cvArea.innerHTML = '';
@@ -1867,15 +1884,23 @@ function cvSquareAction(bid, action, btn) {
if (icon) { icon.className='flow-icon skip'; icon.textContent='—'; } if (icon) { icon.className='flow-icon skip'; icon.textContent='—'; }
if (dot) dot.className='dot dot-skip'; if (dot) dot.className='dot dot-skip';
if (area) area.innerHTML = ''; if (area) area.innerHTML = '';
} else if (d.status === 'REFUNDED') { // Update booking status selects and row data-status
const t = 'Refunded — deposit returned'; document.querySelectorAll('select.status-sel[data-id="'+bid+'"]').forEach(sel => {
if (cvMeta) cvMeta.textContent = t; sel.value = 'cancelled';
if (cvIcon) { cvIcon.className='flow-icon pending'; cvIcon.textContent='↩'; } });
if (cvArea) cvArea.innerHTML = ''; document.querySelectorAll('tr.booking-row, tr.detail-row').forEach(row => {
if (meta) meta.textContent = t; const s = row.querySelector('select[data-id="'+bid+'"]');
if (icon) { icon.className='flow-icon pending'; icon.textContent='↩'; } if (s) row.dataset.status = 'cancelled';
if (dot) dot.className='dot dot-skip'; });
if (area) area.innerHTML = ''; // Update confirmed step icon
['icon','cv-icon'].forEach(p => {
const el = document.getElementById(p+'-'+bid+'-confirmed');
if (el) { el.className='flow-icon skip'; el.textContent='—'; }
});
['meta','cv-meta'].forEach(p => {
const el = document.getElementById(p+'-'+bid+'-confirmed');
if (el) el.textContent = 'Cancelled';
});
} }
} else { } else {
btn.textContent = orig; btn.textContent = orig;