payfrit-api/api/orders/submitCash.php
John Mizerek 628172c31c Add TaskTypeID=0 to order task creation INSERTs
submitCash.php, submit.php, and webhook.php were creating kitchen
tasks without TaskTypeID, which is NOT NULL with no default. This
caused cash order submission to fail with a SQL error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 23:21:41 -07:00

135 lines
5.2 KiB
PHP

<?php
require_once __DIR__ . '/../helpers.php';
runAuth();
/**
* Submit Cash Order
* POST: { OrderID: int, CashAmount: float, Tip?: float }
*/
$data = readJsonBody();
$OrderID = (int) ($data['OrderID'] ?? 0);
$CashAmount = (float) ($data['CashAmount'] ?? 0);
$Tip = (float) ($data['Tip'] ?? 0);
if ($OrderID <= 0) {
apiAbort(['OK' => false, 'ERROR' => 'missing_orderid', 'MESSAGE' => 'OrderID is required.']);
}
if ($CashAmount <= 0) {
apiAbort(['OK' => false, 'ERROR' => 'missing_amount', 'MESSAGE' => 'CashAmount is required.']);
}
try {
$qOrder = queryOne("
SELECT o.ID, o.StatusID, o.UserID, o.BusinessID, o.ServicePointID, o.PaymentID,
o.DeliveryFee, o.OrderTypeID,
b.PayfritFee, b.TaxRate
FROM Orders o
INNER JOIN Businesses b ON b.ID = o.BusinessID
WHERE o.ID = ? LIMIT 1
", [$OrderID]);
if (!$qOrder) {
apiAbort(['OK' => false, 'ERROR' => 'not_found', 'MESSAGE' => 'Order not found.']);
}
if ((int) $qOrder['StatusID'] !== 0) {
apiAbort(['OK' => false, 'ERROR' => 'bad_state', 'MESSAGE' => 'Order is not in cart state.']);
}
// Calculate platform fee
$feeRate = (is_numeric($qOrder['PayfritFee']) && (float) $qOrder['PayfritFee'] > 0) ? (float) $qOrder['PayfritFee'] : 0;
$qSubtotal = queryOne(
"SELECT COALESCE(SUM(Price * Quantity), 0) AS Subtotal FROM OrderLineItems WHERE OrderID = ? AND IsDeleted = 0",
[$OrderID]
);
$subtotal = (float) ($qSubtotal['Subtotal'] ?? 0);
$platformFee = $subtotal * $feeRate;
// Calculate full order total
$taxRate = (is_numeric($qOrder['TaxRate']) && (float) $qOrder['TaxRate'] > 0) ? (float) $qOrder['TaxRate'] : 0;
$taxAmount = $subtotal * $taxRate;
$deliveryFee = ((int) $qOrder['OrderTypeID'] === 3) ? (float) $qOrder['DeliveryFee'] : 0;
$orderTotal = round(($subtotal + $taxAmount + $platformFee + $Tip + $deliveryFee) * 100) / 100;
// Auto-apply user balance
$balanceApplied = 0;
$cashNeeded = $orderTotal;
$userID = (int) $qOrder['UserID'];
if ($userID > 0) {
$qBalance = queryOne("SELECT Balance FROM Users WHERE ID = ?", [$userID]);
$userBalance = (float) ($qBalance['Balance'] ?? 0);
if ($userBalance > 0) {
$balanceToApply = round(min($userBalance, $orderTotal) * 100) / 100;
// Atomic deduct
$stmt = queryTimed(
"UPDATE Users SET Balance = Balance - ? WHERE ID = ? AND Balance >= ?",
[$balanceToApply, $userID, $balanceToApply]
);
// queryTimed returns PDOStatement for UPDATE; check rowCount
if ($stmt instanceof \PDOStatement && $stmt->rowCount() > 0) {
$balanceApplied = $balanceToApply;
$cashNeeded = max(0, $orderTotal - $balanceApplied);
}
}
}
// Create Payment record
$CashAmountCents = (int) round($cashNeeded * 100);
queryTimed(
"INSERT INTO Payments (PaymentPaidInCash, PaymentFromPayfritBalance, PaymentFromCreditCard, PaymentSentByUserID, PaymentReceivedByUserID, PaymentOrderID, PaymentPayfritsCut, PaymentAddedOn)
VALUES (?, ?, 0, ?, 0, ?, ?, NOW())",
[$cashNeeded, $balanceApplied, $userID, $OrderID, $platformFee]
);
$PaymentID = lastInsertId();
// Update order
$fullyPaidByBalance = ($cashNeeded <= 0);
$paymentStatus = $fullyPaidByBalance ? 'paid' : 'pending';
queryTimed("
UPDATE Orders
SET StatusID = 1, PaymentID = ?, PaymentStatus = ?, PlatformFee = ?,
TipAmount = ?, BalanceApplied = ?, SubmittedOn = NOW(), LastEditedOn = NOW(),
PaymentCompletedOn = CASE WHEN ? = 1 THEN NOW() ELSE PaymentCompletedOn END
WHERE ID = ?
", [$PaymentID, $paymentStatus, $platformFee, $Tip, $balanceApplied, $fullyPaidByBalance ? 1 : 0, $OrderID]);
// Create kitchen task for KDS display (same as webhook.php does for card payments)
$spName = '';
$spID = (int) ($qOrder['ServicePointID'] ?? 0);
if ($spID > 0) {
$qSP = queryOne("SELECT Name FROM ServicePoints WHERE ID = ?", [$spID]);
$spName = !empty(trim($qSP['Name'] ?? '')) ? $qSP['Name'] : 'Table';
} else {
$spName = 'Table';
}
$taskTitle = "Prepare Order #{$OrderID} for {$spName}";
queryTimed("
INSERT INTO Tasks (BusinessID, OrderID, ServicePointID, TaskTypeID, Title, CreatedOn, ClaimedByUserID)
VALUES (?, ?, ?, 0, ?, NOW(), 0)
", [$qOrder['BusinessID'], $OrderID, $spID > 0 ? $spID : null, $taskTitle]);
$response = [
'OK' => true,
'OrderID' => $OrderID,
'PaymentID' => (int) $PaymentID,
'CashAmountCents' => $CashAmountCents,
'MESSAGE' => 'Order submitted with cash payment.',
];
if ($balanceApplied > 0) {
$response['BalanceApplied'] = round($balanceApplied * 100) / 100;
$response['BalanceAppliedCents'] = (int) round($balanceApplied * 100);
$response['FullyPaidByBalance'] = ($cashNeeded <= 0);
}
jsonResponse($response);
} catch (Exception $e) {
jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => 'Failed to submit cash order', 'DETAIL' => $e->getMessage()]);
}