getById($quotation_id); if (!$q || (int)$q['status'] !== 2) { http_response_code(400); $answer['message'] = 'Quotation not found or not in Accepted status.'; exit(json_encode($answer)); } $qt_items = $q['items']; if (empty($qt_items)) { http_response_code(400); $answer['message'] = 'Quotation has no items.'; exit(json_encode($answer)); } if ((float)$q['total_remaining'] <= 0.000001) { http_response_code(400); $answer['message'] = 'Quotation is already fully converted.'; exit(json_encode($answer)); } // Index by item_id for validation $qt_by_id = []; foreach ($qt_items as $qi) { $qt_by_id[(int)$qi['item_id']] = $qi; } // Default to all remaining when caller sends no selection if (empty($convert_items)) { $convert_items = []; foreach ($qt_items as $qi) { $remaining = (float)$qi['remaining_qty']; if ($remaining > 0.000001) { $convert_items[] = ['item_id' => (int)$qi['item_id'], 'quantity' => $remaining]; } } } // Validate quantities and build order items $ord_items = []; $validated = []; foreach ($convert_items as $ci) { $item_id = (int)($ci['item_id'] ?? 0); $qty = (float)($ci['quantity'] ?? 0); if ($qty <= 0) continue; if (!isset($qt_by_id[$item_id])) { http_response_code(400); $answer['message'] = "Item #{$item_id} not found in this quotation."; exit(json_encode($answer)); } $qi = $qt_by_id[$item_id]; $remaining = (float)$qi['remaining_qty']; if ($qty - $remaining > 0.000001) { $name = $qi['product_name'] ?: $qi['product_sku']; http_response_code(400); $answer['message'] = "Cannot convert {$qty} for \"{$name}\": only {$remaining} remaining."; exit(json_encode($answer)); } $unit_price = (float)$qi['unit_price']; $total_price = round($qty * $unit_price, 4); $tax_amount = round($total_price * (float)($qi['tax_amount'] / max((float)$qi['total_price'], 0.000001)), 4); $ord_items[] = [ 'item_id' => $item_id - 1, 'product_sku' => $qi['product_sku'], 'product_name' => $qi['product_name'], 'warehouse_id' => 0, 'lot_number' => '', 'quantity' => $qty, 'unit_price' => $unit_price, 'total_price' => $total_price, 'tax_amount' => $tax_amount, 'tax_rate' => (float)($qi['tax_rate'] ?? 0), 'stock_out_id' => 0, ]; $validated[] = ['item_id' => $item_id, 'quantity' => $qty]; } if (empty($ord_items)) { http_response_code(400); $answer['message'] = 'No valid items to convert.'; exit(json_encode($answer)); } $order_data = [ 'source' => 'quotation', 'source_id' => $quotation_id, 'contact_id' => (int)$q['contact_id'], 'department_id'=> (int)($q['department_id'] ?? 0), 'order_date' => date('Y-m-d'), 'items' => $ord_items, 'discount' => 0, 'shipping_fee' => 0, 'shipping_tracking_number' => '', 'notes' => $q['notes'] ?? '', ]; try { $new_order_id = null; dbTransaction($pdo2, function($pdo) use ( $order_data, $company_id, $logging, $quotation_id, $validated, $qm, &$new_order_id ) { $om = new OrderManager($pdo, $company_id); $new_order_id = $om->saveOrder($order_data, $logging); $qm2 = new QuotationManager($pdo, $company_id); $qm2->incrementConvertedQty($quotation_id, $validated); }); $answer['success'] = 1; $answer['message'] = 'Order created successfully.'; $answer['order_id'] = $new_order_id; } catch (PDOException $e) { $answer['message'] = 'Database error, please try again.'; http_response_code(500); } catch (Exception $e) { $answer['message'] = $e->getMessage(); http_response_code(400); } exit(json_encode($answer));