Files
2026-07-24 22:40:49 +02:00

478 lines
16 KiB
PHP

<?php
// buy.php - Add ISIN, quantity (nrbght), and depot to a specific stock in stocks.json
$jsonFile = 'stocks.json';
function loadStocks() {
global $jsonFile;
if (!file_exists($jsonFile)) {
return [];
}
$content = file_get_contents($jsonFile);
return json_decode($content, true) ?: [];
}
function saveStocks($stocks) {
global $jsonFile;
file_put_contents($jsonFile, json_encode($stocks, JSON_PRETTY_PRINT));
}
$symbol = isset($_GET['go']) ? strtoupper(trim($_GET['go'])) : '';
$message = '';
$messageType = '';
if (empty($symbol)) {
$message = 'No stock symbol specified. Please use ?go=SYMBOL';
$messageType = 'error';
}
$stocks = loadStocks();
$stockIndex = null;
foreach ($stocks as $index => $stock) {
if ($stock['stock'] === $symbol) {
$stockIndex = $index;
break;
}
}
if ($stockIndex === null && !empty($symbol)) {
$message = "Stock '$symbol' not found in your portfolio.";
$messageType = 'error';
}
$currentIsin = '';
$currentNrbght = '';
$currentDepot = 'scalablecapital'; // Default value
$currentPricetarget = '';
if ($stockIndex !== null) {
$currentIsin = $stocks[$stockIndex]['isin'] ?? '';
$currentNrbght = $stocks[$stockIndex]['nrbght'] ?? '';
$currentDepot = $stocks[$stockIndex]['depot'] ?? 'scalablecapital';
$currentPricetarget = $stocks[$stockIndex]['pricetarget'] ?? '';
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $stockIndex !== null) {
$isin = isset($_POST['isin']) ? strtoupper(trim($_POST['isin'])) : '';
$nrbght = isset($_POST['nrbght']) ? trim($_POST['nrbght']) : '';
$depot = isset($_POST['depot']) ? trim($_POST['depot']) : 'scalablecapital';
$pricetarget = isset($_POST['pricetarget']) ? trim($_POST['pricetarget']) : '';
$errors = [];
if (empty($isin)) {
$errors[] = 'ISIN code is required.';
} elseif (!preg_match('/^[A-Z]{2}[A-Z0-9]{9}[0-9]$/', $isin)) {
$errors[] = 'Invalid ISIN format. ISIN must be 12 characters: 2 letters, 9 alphanumeric, 1 check digit.';
}
if (empty($nrbght)) {
$errors[] = 'Number bought is required.';
} elseif (!is_numeric($nrbght) || $nrbght <= 0 || floor($nrbght) != $nrbght) {
$errors[] = 'Number bought must be a positive whole number.';
}
if (!in_array($depot, ['scalablecapital', 'ingdiba', 'comdirect'])) {
$errors[] = 'Invalid depot selection.';
}
// pricetarget is optional - only validate if the user actually filled it in
if ($pricetarget !== '' && !is_numeric($pricetarget)) {
$errors[] = 'Price target must be a number.';
}
if (empty($errors)) {
$stocks[$stockIndex]['isin'] = $isin;
$stocks[$stockIndex]['nrbght'] = (int)$nrbght;
$stocks[$stockIndex]['depot'] = $depot;
// Only add/update "pricetarget" in the JSON if the user filled it in.
// If left empty, behaviour stays exactly as it is now (field untouched).
if ($pricetarget !== '') {
$stocks[$stockIndex]['pricetarget'] = (float)$pricetarget;
}
saveStocks($stocks);
$message = "Successfully added ISIN: $isin, Quantity: $nrbght, and Depot: $depot for $symbol.";
if ($pricetarget !== '') {
$message .= " Price target: $pricetarget.";
}
$messageType = 'success';
$currentIsin = $isin;
$currentNrbght = $nrbght;
$currentDepot = $depot;
if ($pricetarget !== '') {
$currentPricetarget = $pricetarget;
}
} else {
$message = implode('<br>', $errors);
$messageType = 'error';
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Buy Stock - Add ISIN, Quantity & Depot</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
}
.card {
background: white;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.15);
overflow: hidden;
}
.card-header {
background: linear-gradient(135deg, #1e2a3e 0%, #0f1a2a 100%);
color: white;
padding: 30px;
text-align: center;
}
.card-header h1 {
font-size: 28px;
margin-bottom: 8px;
}
.card-header .symbol-badge {
display: inline-block;
background: rgba(255,255,255,0.2);
padding: 6px 16px;
border-radius: 40px;
font-family: monospace;
font-size: 20px;
font-weight: bold;
margin-top: 10px;
}
.card-body {
padding: 30px;
}
.alert {
padding: 15px 20px;
border-radius: 12px;
margin-bottom: 25px;
font-size: 14px;
}
.alert-success {
background: #d4edda;
color: #155724;
border-left: 4px solid #28a745;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border-left: 4px solid #dc3545;
}
.alert-info {
background: #d1ecf1;
color: #0c5460;
border-left: 4px solid #17a2b8;
}
.form-group {
margin-bottom: 25px;
}
label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #1e2a3e;
font-size: 14px;
}
label .required {
color: #dc3545;
margin-left: 4px;
}
label .optional {
color: #6c757d;
margin-left: 4px;
font-weight: 400;
font-style: italic;
}
input, select, textarea {
width: 100%;
padding: 12px 16px;
border: 2px solid #e0e6ed;
border-radius: 12px;
font-size: 15px;
transition: all 0.2s;
font-family: monospace;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102,126,234,0.1);
}
.help-text {
font-size: 12px;
color: #6c757d;
margin-top: 6px;
}
.current-values {
background: #f8f9fa;
border-radius: 12px;
padding: 15px;
margin-bottom: 25px;
border: 1px solid #e9ecef;
}
.current-values h4 {
font-size: 13px;
color: #6c757d;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1px;
}
.current-values .value-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #e9ecef;
}
.current-values .value-row:last-child {
border-bottom: none;
}
.current-values .label {
font-weight: 500;
color: #495057;
}
.current-values .value {
font-family: monospace;
font-weight: bold;
color: #1e2a3e;
}
.current-values .empty {
color: #adb5bd;
font-style: italic;
}
.btn-group {
display: flex;
gap: 12px;
margin-top: 10px;
}
.btn {
flex: 1;
padding: 12px 24px;
border: none;
border-radius: 12px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
text-align: center;
display: inline-block;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102,126,234,0.4);
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-warning {
background: #ffc107;
color: #1e2a3e;
}
.btn-warning:hover {
background: #e0a800;
}
.footer {
text-align: center;
padding: 20px;
background: #f8f9fa;
font-size: 12px;
color: #6c757d;
}
.info-note {
background: #e7f3ff;
padding: 12px;
border-radius: 10px;
font-size: 13px;
margin-top: 20px;
}
hr {
margin: 20px 0;
border: none;
border-top: 1px solid #e9ecef;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="card-header">
<h1>?? Add Stock Details</h1>
<p>Enter ISIN, quantity purchased & depot</p>
<div class="symbol-badge"><?php echo htmlspecialchars($symbol ?: '?'); ?></div>
</div>
<div class="card-body">
<?php if ($message): ?>
<div class="alert alert-<?php echo $messageType === 'success' ? 'success' : 'error'; ?>">
<?php echo $message; ?>
</div>
<?php endif; ?>
<?php if ($stockIndex === null && !empty($symbol)): ?>
<div class="alert alert-error">
Stock '<?php echo htmlspecialchars($symbol); ?>' was not found in your portfolio.<br><br>
<a href="javascript:history.back()" style="color: #721c24;">? Go back</a>
</div>
<?php elseif (empty($symbol)): ?>
<div class="alert alert-info">
Please provide a stock symbol: <code>buy.php?go=AAPL</code>
</div>
<?php else: ?>
<?php if ($currentIsin || $currentNrbght || $currentDepot || $currentPricetarget): ?>
<div class="current-values">
<h4>?? Currently Saved</h4>
<div class="value-row">
<span class="label">ISIN:</span>
<span class="value"><?php echo $currentIsin ? htmlspecialchars($currentIsin) : '<span class="empty">Not set</span>'; ?></span>
</div>
<div class="value-row">
<span class="label">Quantity Bought:</span>
<span class="value"><?php echo $currentNrbght ? number_format($currentNrbght) : '<span class="empty">Not set</span>'; ?></span>
</div>
<div class="value-row">
<span class="label">Depot:</span>
<span class="value"><?php echo $currentDepot ? htmlspecialchars($currentDepot) : '<span class="empty">Not set</span>'; ?></span>
</div>
<div class="value-row">
<span class="label">Price Target:</span>
<span class="value"><?php echo $currentPricetarget !== '' ? htmlspecialchars($currentPricetarget) : '<span class="empty">Not set</span>'; ?></span>
</div>
</div>
<?php endif; ?>
<form method="POST" action="">
<div class="form-group">
<label>ISIN Code <span class="required">*</span></label>
<input type="text"
name="isin"
value="<?php echo htmlspecialchars($currentIsin); ?>"
placeholder="e.g., US0378331005"
maxlength="12"
pattern="[A-Z]{2}[A-Z0-9]{9}[0-9]"
title="ISIN must be 12 characters: 2 letters, then 9 alphanumeric, ending with a number">
<div class="help-text">
International Securities Identification Number (12 characters: 2 letters + 9 alphanumeric + 1 check digit)
</div>
</div>
<div class="form-group">
<label>Number Bought <span class="required">*</span></label>
<input type="number"
name="nrbght"
value="<?php echo htmlspecialchars($currentNrbght); ?>"
placeholder="e.g., 100"
min="1"
step="1"
required>
<div class="help-text">Total quantity of shares purchased</div>
</div>
<div class="form-group">
<label>Depot <span class="required">*</span></label>
<select name="depot" required>
<option value="scalablecapital" <?php echo $currentDepot === 'scalablecapital' ? 'selected' : ''; ?>>scalablecapital</option>
<option value="comdirect" <?php echo $currentDepot === 'comdirect' ? 'selected' : ''; ?>>comdirect</option>
<option value="ingdiba" <?php echo $currentDepot === 'ingdiba' ? 'selected' : ''; ?>>ING DiBa</option>
<option value="traderepublic" <?php echo $currentDepot === 'traderepublic' ? 'selected' : ''; ?>>TradeRepublic</option>
</select>
<div class="help-text">Select the depot/broker where this stock is held</div>
</div>
<div class="form-group">
<label>Price Target <span class="optional">(optional)</span></label>
<input type="number"
name="pricetarget"
value="<?php echo htmlspecialchars($currentPricetarget); ?>"
placeholder="e.g., 250.00"
step="any">
<div class="help-text">Leave empty to skip. If filled in, it will be saved as "pricetarget" in the JSON.</div>
</div>
<div class="btn-group">
<button type="submit" class="btn btn-primary">?? Save to JSON</button>
<a href="javascript:history.back()" class="btn btn-secondary">? Cancel</a>
</div>
</form>
<hr>
<div class="info-note">
<strong>?? What happens next?</strong><br>
The ISIN, quantity, and depot will be saved directly to <code>stocks.json</code> under the entry for
<strong><?php echo htmlspecialchars($symbol); ?></strong>. These fields are stored as "isin", "nrbght", and "depot".
If you fill in a price target, it will also be saved as "pricetarget".
</div>
<?php endif; ?>
</div>
<div class="footer">
<span>?? Data stored in stocks.json | ?? <a href="stocks.json" target="_blank" style="color:#667eea;">View JSON</a></span>
</div>
</div>
</div>
</body>
</html>