payfrit-api/api/orders/setLineItem.php
John Mizerek 1f81d98c52 Initial PHP API migration from CFML
Complete port of all 163 API endpoints from Lucee/CFML to PHP 8.3.
Shared helpers in api/helpers.php (DB, auth, request/response, security).
PDO prepared statements throughout. Same JSON response shapes as CFML.
2026-03-14 14:26:59 -07:00

198 lines
7.6 KiB
PHP

<?php
require_once __DIR__ . '/../helpers.php';
require_once __DIR__ . '/_cartPayload.php';
runAuth();
/**
* Set Line Item (add/update/remove items in cart)
* POST: { OrderID, ItemID, IsSelected, Quantity?, Remark?, ParentOrderLineItemID?, OrderLineItemID?, ForceNew? }
*/
function nextId(string $table, string $idField): int {
$q = queryOne("SELECT IFNULL(MAX({$idField}),0) + 1 AS NextID FROM {$table}", []);
return (int) $q['NextID'];
}
function attachDefaultChildren(int $OrderID, int $ParentLineItemID, int $ParentItemID): void {
// Direct children
$qAllKids = queryTimed(
"SELECT ID, Price, IsCheckedByDefault FROM Items WHERE ParentItemID = ? AND IsActive = 1 ORDER BY SortOrder, ID",
[$ParentItemID]
);
// Template-linked children
$qTemplateKids = queryTimed(
"SELECT i.ID, i.Price, i.IsCheckedByDefault
FROM lt_ItemID_TemplateItemID tl
INNER JOIN Items i ON i.ParentItemID = tl.TemplateItemID
WHERE tl.ItemID = ? AND i.IsActive = 1
ORDER BY i.SortOrder, i.ID",
[$ParentItemID]
);
$allKids = array_merge($qAllKids, $qTemplateKids);
foreach ($allKids as $kid) {
if ((int) $kid['IsCheckedByDefault'] === 1) {
processDefaultChild($OrderID, $ParentLineItemID, (int) $kid['ID'], (float) $kid['Price']);
} else {
attachDefaultChildren($OrderID, $ParentLineItemID, (int) $kid['ID']);
}
}
}
function processDefaultChild(int $OrderID, int $ParentLineItemID, int $ItemID, float $Price): void {
$qExisting = queryOne(
"SELECT ID FROM OrderLineItems WHERE OrderID = ? AND ParentOrderLineItemID = ? AND ItemID = ? LIMIT 1",
[$OrderID, $ParentLineItemID, $ItemID]
);
if ($qExisting) {
queryTimed("UPDATE OrderLineItems SET IsDeleted = 0 WHERE ID = ?", [(int) $qExisting['ID']]);
attachDefaultChildren($OrderID, (int) $qExisting['ID'], $ItemID);
} else {
$NewLIID = nextId('OrderLineItems', 'ID');
queryTimed(
"INSERT INTO OrderLineItems (ID, ParentOrderLineItemID, OrderID, ItemID, StatusID, Price, Quantity, Remark, IsDeleted, AddedOn)
VALUES (?, ?, ?, ?, 0, ?, 1, NULL, 0, NOW())",
[$NewLIID, $ParentLineItemID, $OrderID, $ItemID, $Price]
);
attachDefaultChildren($OrderID, $NewLIID, $ItemID);
}
}
// --- Main logic ---
$data = readJsonBody();
$OrderID = (int) ($data['OrderID'] ?? 0);
$ParentLineItemID = (int) ($data['ParentOrderLineItemID'] ?? 0);
$OrderLineItemID = (int) ($data['OrderLineItemID'] ?? 0);
$OriginalItemID = $data['ItemID'] ?? 0;
$ItemID = (int) $OriginalItemID;
// Decode virtual IDs (format: menuItemID * 100000 + realItemID)
$WasDecoded = false;
if ($ItemID > 100000) {
$WasDecoded = true;
$ItemID = $ItemID % 100000;
}
$IsSelected = false;
if (isset($data['IsSelected'])) {
$v = $data['IsSelected'];
$IsSelected = ($v === true || $v === 1 || (is_string($v) && strtolower($v) === 'true'));
}
$Quantity = (int) ($data['Quantity'] ?? 0);
$Remark = (string) ($data['Remark'] ?? '');
$ForceNew = false;
if (isset($data['ForceNew'])) {
$v = $data['ForceNew'];
$ForceNew = ($v === true || $v === 1 || (is_string($v) && strtolower($v) === 'true'));
}
if ($OrderID <= 0 || $ItemID <= 0) {
apiAbort(['OK' => false, 'ERROR' => 'missing_params', 'MESSAGE' => 'OrderID and ItemID are required.']);
}
try {
// Load item
$qItem = queryOne("SELECT ID, Price, ParentItemID, IsActive FROM Items WHERE ID = ? LIMIT 1", [$ItemID]);
if (!$qItem || (int) $qItem['IsActive'] !== 1) {
apiAbort(['OK' => false, 'ERROR' => 'bad_item', 'MESSAGE' => "Item not found or inactive. Original={$OriginalItemID} Decoded={$ItemID} WasDecoded=" . ($WasDecoded ? 'true' : 'false')]);
}
// Root vs modifier rules
if ($ParentLineItemID === 0) {
if ($IsSelected && $Quantity <= 0) {
apiAbort(['OK' => false, 'ERROR' => 'bad_quantity', 'MESSAGE' => 'Root line items require Quantity > 0.']);
}
} else {
$Quantity = $IsSelected ? 1 : 0;
// Exclusive selection group handling
if ($IsSelected) {
$qParentLI = queryOne("SELECT ItemID FROM OrderLineItems WHERE ID = ? LIMIT 1", [$ParentLineItemID]);
if ($qParentLI) {
$qParentItem = queryOne("SELECT MaxNumSelectionReq FROM Items WHERE ID = ? LIMIT 1", [(int) $qParentLI['ItemID']]);
if ($qParentItem && (int) $qParentItem['MaxNumSelectionReq'] === 1) {
queryTimed(
"UPDATE OrderLineItems SET IsDeleted = 1 WHERE OrderID = ? AND ParentOrderLineItemID = ? AND ItemID != ? AND IsDeleted = 0",
[$OrderID, $ParentLineItemID, $ItemID]
);
}
}
}
}
// Find existing line item
if ($ForceNew) {
$qExisting = null;
} elseif ($OrderLineItemID > 0) {
$qExisting = queryOne(
"SELECT ID FROM OrderLineItems WHERE ID = ? AND OrderID = ? AND IsDeleted = 0 LIMIT 1",
[$OrderLineItemID, $OrderID]
);
} else {
$qExisting = queryOne(
"SELECT ID FROM OrderLineItems WHERE OrderID = ? AND ParentOrderLineItemID = ? AND ItemID = ? AND IsDeleted = 0 LIMIT 1",
[$OrderID, $ParentLineItemID, $ItemID]
);
}
if ($qExisting) {
// Update existing
if ($IsSelected) {
queryTimed(
"UPDATE OrderLineItems SET IsDeleted = 0, Quantity = ?, Price = ?, Remark = ?, StatusID = 0 WHERE ID = ?",
[$Quantity, (float) $qItem['Price'], trim($Remark) !== '' ? $Remark : null, (int) $qExisting['ID']]
);
attachDefaultChildren($OrderID, (int) $qExisting['ID'], $ItemID);
} else {
// Deselecting
if ($ParentLineItemID > 0) {
$qItemCheck = queryOne("SELECT IsCheckedByDefault FROM Items WHERE ID = ? LIMIT 1", [$ItemID]);
if ($qItemCheck && (int) $qItemCheck['IsCheckedByDefault'] === 1) {
// Default modifier: keep with Quantity=0
queryTimed("UPDATE OrderLineItems SET Quantity = 0 WHERE ID = ?", [(int) $qExisting['ID']]);
} else {
queryTimed("UPDATE OrderLineItems SET IsDeleted = 1 WHERE ID = ?", [(int) $qExisting['ID']]);
}
} else {
// Root item: always delete
queryTimed("UPDATE OrderLineItems SET IsDeleted = 1 WHERE ID = ?", [(int) $qExisting['ID']]);
}
}
} else {
// Insert new if selecting
if ($IsSelected) {
$NewLIID = nextId('OrderLineItems', 'ID');
queryTimed(
"INSERT INTO OrderLineItems (ID, ParentOrderLineItemID, OrderID, ItemID, StatusID, Price, Quantity, Remark, IsDeleted, AddedOn)
VALUES (?, ?, ?, ?, 0, ?, ?, ?, 0, NOW())",
[
$NewLIID,
$ParentLineItemID,
$OrderID,
$ItemID,
(float) $qItem['Price'],
($ParentLineItemID === 0 ? $Quantity : 1),
trim($Remark) !== '' ? $Remark : null,
]
);
attachDefaultChildren($OrderID, $NewLIID, $ItemID);
}
}
// Touch order last edited
queryTimed("UPDATE Orders SET LastEditedOn = NOW() WHERE ID = ?", [$OrderID]);
$payload = loadCartPayload($OrderID);
jsonResponse($payload);
} catch (Exception $e) {
jsonResponse([
'OK' => false,
'ERROR' => 'server_error',
'MESSAGE' => 'DB error setting line item: ' . $e->getMessage(),
]);
}