This commit is contained in:
Flatlogic Bot 2025-10-24 09:40:09 +00:00
parent 2a30fbdcdb
commit b88fb2e6d7
12 changed files with 853 additions and 154 deletions

75
assets/css/custom.css Normal file
View File

@ -0,0 +1,75 @@
/* Custom Styles for Stock Management App */
body {
font-family: 'Roboto', sans-serif;
background-color: #121212;
color: #FFFFFF;
}
.navbar {
background-color: #1E1E1E;
border-bottom: 1px solid #FFA500;
}
.navbar-brand {
color: #FFA500 !important;
font-weight: bold;
}
.stat-card {
background-color: #1E1E1E;
border: 1px solid #333;
border-radius: .5rem;
padding: 1.5rem;
margin-bottom: 1rem;
color: #FFFFFF;
}
.stat-card h5 {
color: #A0A0A0;
font-size: 1rem;
text-transform: uppercase;
}
.stat-card .stat-value {
font-size: 2.5rem;
font-weight: bold;
color: #FFA500;
}
.btn-primary {
background-color: #FFA500;
border-color: #FFA500;
color: #121212;
font-weight: bold;
}
.btn-primary:hover {
background-color: #FFC107;
border-color: #FFC107;
color: #121212;
}
.table {
background-color: #1E1E1E;
color: #FFFFFF;
}
thead th {
border-bottom: 2px solid #FFA500 !important;
color: #FFFFFF;
}
tbody td {
border-color: #333 !important;
}
.table-hover tbody tr:hover {
background-color: #2a2a2a;
color: #FFA500;
}
.badge-warning {
background-color: #FFA500;
color: #121212;
}

5
assets/js/main.js Normal file
View File

@ -0,0 +1,5 @@
// Custom JS for Stock Management App
document.addEventListener('DOMContentLoaded', function () {
// Future interactive scripts will go here
});

View File

@ -7,11 +7,29 @@ define('DB_PASS', '9eb17a13-4a89-4e11-8517-0c201096e935');
function db() {
static $pdo;
if (!$pdo) {
if ($pdo) {
return $pdo;
}
try {
// Connect to MySQL server without specifying a database
$pdo_temp = new PDO('mysql:host='.DB_HOST, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// Create the database if it doesn't exist
$pdo_temp->exec("CREATE DATABASE IF NOT EXISTS `".DB_NAME."`");
// Now connect to the specific database
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
// If connection fails, it might be a more serious issue
die("Database connection failed: " . $e->getMessage());
}
return $pdo;
}

22
db/migrate.php Normal file
View File

@ -0,0 +1,22 @@
<?php
require_once __DIR__ . '/config.php';
echo "Running migrations...\n";
try {
$pdo = db();
$migration_files = glob(__DIR__ . '/migrations/*.sql');
sort($migration_files);
foreach ($migration_files as $file) {
echo "- Applying " . basename($file) . "...\n";
$sql = file_get_contents($file);
$pdo->exec($sql);
}
echo "All migrations completed successfully!\n";
} catch (PDOException $e) {
die("Migration failed: " . $e->getMessage() . "\n");
}

View File

@ -0,0 +1,31 @@
-- Initial Schema for Stock Management App
CREATE TABLE IF NOT EXISTS `products` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`type` ENUM('jant', 'lastik', 'akü') NOT NULL,
`stock_quantity` INT NOT NULL DEFAULT 0,
`purchase_price` DECIMAL(10, 2) NOT NULL,
`sale_price` DECIMAL(10, 2) NOT NULL,
`low_stock_threshold` INT NOT NULL DEFAULT 5,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `sales` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`total_amount` DECIMAL(10, 2) NOT NULL,
`profit_amount` DECIMAL(10, 2) NOT NULL,
`payment_method` VARCHAR(50) DEFAULT 'nakit',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `sale_items` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`sale_id` INT NOT NULL,
`product_id` INT NOT NULL,
`quantity` INT NOT NULL,
`unit_price` DECIMAL(10, 2) NOT NULL,
`total_price` DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (`sale_id`) REFERENCES `sales`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

108
edit-product.php Normal file
View File

@ -0,0 +1,108 @@
<?php
require_once 'db/config.php';
$product_id = $_GET['id'] ?? null;
// ID yoksa veya geçersizse ana sayfaya yönlendir
if (!$product_id) {
$_SESSION['error'] = "Geçersiz ürün ID'si.";
header('Location: products.php');
exit();
}
$pdo = db();
// Form gönderildiğinde (POST request)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
$type = $_POST['type'] ?? '';
$stock_quantity = $_POST['stock_quantity'] ?? 0;
$purchase_price = $_POST['purchase_price'] ?? 0.0;
$sale_price = $_POST['sale_price'] ?? 0.0;
$low_stock_threshold = $_POST['low_stock_threshold'] ?? 5;
if (!empty($name) && !empty($type) && is_numeric($stock_quantity) && is_numeric($purchase_price) && is_numeric($sale_price)) {
try {
$stmt = $pdo->prepare("UPDATE products SET name = ?, type = ?, stock_quantity = ?, purchase_price = ?, sale_price = ?, low_stock_threshold = ? WHERE id = ?");
$stmt->execute([$name, $type, $stock_quantity, $purchase_price, $sale_price, $low_stock_threshold, $product_id]);
$_SESSION['notification'] = "Ürün başarıyla güncellendi.";
header('Location: products.php');
exit();
} catch (PDOException $e) {
$_SESSION['error'] = "Veritabanı hatası: " . $e->getMessage();
}
} else {
$_SESSION['error'] = "Lütfen tüm alanları doğru bir şekilde doldurun.";
}
// Hata durumunda aynı sayfada kal, form tekrar dolsun
header("Location: edit-product.php?id=" . $product_id);
exit();
}
// Sayfa ilk yüklendiğinde (GET request)
try {
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$product_id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) {
$_SESSION['error'] = "Ürün bulunamadı.";
header('Location: products.php');
exit();
}
} catch (PDOException $e) {
$_SESSION['error'] = "Veritabanı hatası: " . $e->getMessage();
header('Location: products.php');
exit();
}
require_once 'partials/header.php';
?>
<h1 class="mb-4">Ürünü Düzenle</h1>
<div class="card">
<div class="card-header">
<?php echo htmlspecialchars($product['name']); ?>
</div>
<div class="card-body">
<form action="edit-product.php?id=<?php echo $product_id; ?>" method="POST">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Ürün Adı</label>
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($product['name']); ?>" required>
</div>
<div class="col-md-6 mb-3">
<label for="type" class="form-label">Ürün Tipi</label>
<select class="form-select" id="type" name="type" required>
<option value="jant" <?php echo ($product['type'] === 'jant') ? 'selected' : ''; ?>>Jant</option>
<option value="lastik" <?php echo ($product['type'] === 'lastik') ? 'selected' : ''; ?>>Lastik</option>
<option value="akü" <?php echo ($product['type'] === 'akü') ? 'selected' : ''; ?>>Akü</option>
</select>
</div>
</div>
<div class="row">
<div class="col-md-3 mb-3">
<label for="stock_quantity" class="form-label">Stok Miktarı</label>
<input type="number" class="form-control" id="stock_quantity" name="stock_quantity" value="<?php echo htmlspecialchars($product['stock_quantity']); ?>" required>
</div>
<div class="col-md-3 mb-3">
<label for="purchase_price" class="form-label">Alış Fiyatı</label>
<input type="number" step="0.01" class="form-control" id="purchase_price" name="purchase_price" value="<?php echo htmlspecialchars($product['purchase_price']); ?>" required>
</div>
<div class="col-md-3 mb-3">
<label for="sale_price" class="form-label">Satış Fiyatı</label>
<input type="number" step="0.01" class="form-control" id="sale_price" name="sale_price" value="<?php echo htmlspecialchars($product['sale_price']); ?>" required>
</div>
<div class="col-md-3 mb-3">
<label for="low_stock_threshold" class="form-label">Düşük Stok Uyarısı</label>
<input type="number" class="form-control" id="low_stock_threshold" name="low_stock_threshold" value="<?php echo htmlspecialchars($product['low_stock_threshold']); ?>" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Değişiklikleri Kaydet</button>
<a href="products.php" class="btn btn-secondary">İptal</a>
</form>
</div>
</div>
<?php require_once 'partials/footer.php'; ?>

339
index.php
View File

@ -1,150 +1,199 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
require_once 'db/config.php';
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
$pdo = db();
// İstatistikleri çek
$stats = [
'total_revenue' => 0,
'total_profit' => 0,
'product_count' => 0,
'low_stock_count' => 0
];
try {
// Toplam Gelir ve Kâr
$stmt = $pdo->query("SELECT SUM(total_amount) as total_revenue, SUM(profit_amount) as total_profit FROM sales");
$sales_stats = $stmt->fetch(PDO::FETCH_ASSOC);
if ($sales_stats) {
$stats['total_revenue'] = $sales_stats['total_revenue'] ?? 0;
$stats['total_profit'] = $sales_stats['total_profit'] ?? 0;
}
// Toplam Ürün Sayısı
$stats['product_count'] = $pdo->query("SELECT count(*) FROM products")->fetchColumn();
// Düşük Stoktaki Ürün Sayısı
$stats['low_stock_count'] = $pdo->query("SELECT count(*) FROM products WHERE stock_quantity <= low_stock_threshold")->fetchColumn();
// Son Satışlar (Son 5)
$recent_sales_stmt = $pdo->query("
SELECT p.name AS product_name, si.quantity, si.total_price, s.created_at
FROM sale_items si
JOIN sales s ON si.sale_id = s.id
JOIN products p ON si.product_id = p.id
ORDER BY s.created_at DESC
LIMIT 5
");
$recent_sales = $recent_sales_stmt->fetchAll(PDO::FETCH_ASSOC);
// Düşük Stoktaki Ürünler
$low_stock_products_stmt = $pdo->query("SELECT id, name, stock_quantity, low_stock_threshold FROM products WHERE stock_quantity <= low_stock_threshold ORDER BY stock_quantity ASC");
$low_stock_products = $low_stock_products_stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$_SESSION['error'] = "Dashboard verileri çekilirken bir hata oluştu: " . $e->getMessage();
}
require_once 'partials/header.php';
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
<h1 class="mb-4">Ana Panel</h1>
<!-- İstatistik Kartları -->
<div class="row">
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Toplam Gelir</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo number_format($stats['total_revenue'], 2); ?> TL</div>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
<div class="col-auto">
<i class="bi bi-cash-coin fs-2 text-gray-300"></i>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
</body>
</html>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-success shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Toplam Kâr</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo number_format($stats['total_profit'], 2); ?> TL</div>
</div>
<div class="col-auto">
<i class="bi bi-graph-up-arrow fs-2 text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Toplam Ürün Çeşidi</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo $stats['product_count']; ?></div>
</div>
<div class="col-auto">
<i class="bi bi-box-seam fs-2 text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Düşük Stok Uyarısı</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo $stats['low_stock_count']; ?> Ürün</div>
</div>
<div class="col-auto">
<i class="bi bi-exclamation-triangle-fill fs-2 text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- İçerik Alanı -->
<div class="row">
<!-- Son Satışlar -->
<div class="col-lg-7 mb-4">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Son Satışlar</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Ürün</th>
<th>Adet</th>
<th>Tutar</th>
<th>Tarih</th>
</tr>
</thead>
<tbody>
<?php if (empty($recent_sales)): ?>
<tr><td colspan="4" class="text-center">Henüz satış yok.</td></tr>
<?php else: ?>
<?php foreach ($recent_sales as $sale): ?>
<tr>
<td><?php echo htmlspecialchars($sale['product_name']); ?></td>
<td><?php echo htmlspecialchars($sale['quantity']); ?></td>
<td><?php echo number_format($sale['total_price'], 2); ?> TL</td>
<td><?php echo date('d/m/Y', strtotime($sale['created_at'])); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<a href="sales.php" class="btn btn-primary btn-sm">Tüm Satışları Gör &rarr;</a>
</div>
</div>
</div>
<!-- Düşük Stoktaki Ürünler -->
<div class="col-lg-5 mb-4">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-warning">Stoğu Azalan Ürünler</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead>
<tr>
<th>Ürün</th>
<th>Kalan Stok</th>
</tr>
</thead>
<tbody>
<?php if (empty($low_stock_products)): ?>
<tr><td colspan="2" class="text-center">Stoğu azalan ürün yok.</td></tr>
<?php else: ?>
<?php foreach ($low_stock_products as $product): ?>
<tr class="table-warning">
<td>
<a href="edit-product.php?id=<?php echo $product['id']; ?>">
<?php echo htmlspecialchars($product['name']); ?>
</a>
</td>
<td><strong><?php echo htmlspecialchars($product['stock_quantity']); ?></strong> / <?php echo htmlspecialchars($product['low_stock_threshold']); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<a href="products.php" class="btn btn-primary btn-sm">Ürünleri Yönet &rarr;</a>
</div>
</div>
</div>
</div>
<?php require_once 'partials/footer.php'; ?>

6
partials/footer.php Normal file
View File

@ -0,0 +1,6 @@
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

56
partials/header.php Normal file
View File

@ -0,0 +1,56 @@
<?php
session_start();
$current_page = basename($_SERVER['PHP_SELF']);
?>
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stok Yönetim Sistemi</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<div class="d-flex">
<div class="sidebar d-flex flex-column flex-shrink-0 p-3">
<a href="index.php" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<i class="bi bi-box-seam-fill me-2"></i>
<span class="fs-4">Stok Yönetimi</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="index.php" class="nav-link <?php echo ($current_page == 'index.php') ? 'active' : ''; ?>">
<i class="bi bi-speedometer2 me-2"></i> Ana Panel
</a>
</li>
<li>
<a href="products.php" class="nav-link <?php echo ($current_page == 'products.php') ? 'active' : ''; ?>">
<i class="bi bi-grid me-2"></i> Ürünler
</a>
</li>
<li>
<a href="sales.php" class="nav-link <?php echo ($current_page == 'sales.php') ? 'active' : ''; ?>">
<i class="bi bi-receipt me-2"></i> Satışlar
</a>
</li>
</ul>
<hr>
</div>
<div class="main-content flex-grow-1 p-4">
<?php if (isset($_SESSION['notification'])): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?php echo $_SESSION['notification']; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php unset($_SESSION['notification']); ?>
<?php endif; ?>
<?php if (isset($_SESSION['error'])): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<?php echo $_SESSION['error']; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php unset($_SESSION['error']); ?>
<?php endif; ?>

161
products.php Normal file
View File

@ -0,0 +1,161 @@
<?php
require_once 'db/config.php';
// Form gönderildi mi kontrolü
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = db();
// Yeni ürün ekleme işlemi
if (isset($_POST['name'])) {
$name = $_POST['name'] ?? '';
$type = $_POST['type'] ?? '';
$stock_quantity = $_POST['stock_quantity'] ?? 0;
$purchase_price = $_POST['purchase_price'] ?? 0.0;
$sale_price = $_POST['sale_price'] ?? 0.0;
$low_stock_threshold = $_POST['low_stock_threshold'] ?? 5;
if (!empty($name) && !empty($type) && is_numeric($stock_quantity) && is_numeric($purchase_price) && is_numeric($sale_price)) {
try {
$stmt = $pdo->prepare("INSERT INTO products (name, type, stock_quantity, purchase_price, sale_price, low_stock_threshold) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $type, $stock_quantity, $purchase_price, $sale_price, $low_stock_threshold]);
$_SESSION['notification'] = "Ürün başarıyla eklendi!";
} catch (PDOException $e) {
$_SESSION['error'] = "Veritabanı hatası: " . $e->getMessage();
}
} else {
$_SESSION['error'] = "Lütfen tüm alanları doğru bir şekilde doldurun.";
}
}
// Ürün silme işlemi
if (isset($_POST['action']) && $_POST['action'] === 'delete') {
$product_id = $_POST['product_id'] ?? null;
if ($product_id) {
try {
$stmt = $pdo->prepare("DELETE FROM products WHERE id = ?");
$stmt->execute([$product_id]);
$_SESSION['notification'] = "Ürün başarıyla silindi.";
} catch (PDOException $e) {
$_SESSION['error'] = "Hata: Ürün silinemedi. İlişkili satış kayıtları olabilir.";
}
}
}
// PRG Pattern: Sayfayı yeniden yönlendirerek formun tekrar gönderilmesini engelle
header("Location: products.php");
exit();
}
// Ürünleri veritabanından çek
$products = [];
try {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM products ORDER BY created_at DESC");
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// Hata mesajını oturuma kaydet ve sayfayı yenilemekten kaçın
$_SESSION['error'] = "Ürünler çekilirken hata oluştu: " . $e->getMessage();
}
require_once 'partials/header.php';
?>
<h1 class="mb-4">Ürün Yönetimi</h1>
<!-- Yeni Ürün Ekleme Formu -->
<div class="card mb-4">
<div class="card-header">
Yeni Ürün Ekle
</div>
<div class="card-body">
<form action="products.php" method="POST">
<div class="row">
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Ürün Adı</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="col-md-6 mb-3">
<label for="type" class="form-label">Ürün Tipi</label>
<select class="form-select" id="type" name="type" required>
<option value="jant">Jant</option>
<option value="lastik">Lastik</option>
<option value="akü">Akü</option>
</select>
</div>
</div>
<div class="row">
<div class="col-md-3 mb-3">
<label for="stock_quantity" class="form-label">Stok Miktarı</label>
<input type="number" class="form-control" id="stock_quantity" name="stock_quantity" required>
</div>
<div class="col-md-3 mb-3">
<label for="purchase_price" class="form-label">Alış Fiyatı</label>
<input type="number" step="0.01" class="form-control" id="purchase_price" name="purchase_price" required>
</div>
<div class="col-md-3 mb-3">
<label for="sale_price" class="form-label">Satış Fiyatı</label>
<input type="number" step="0.01" class="form-control" id="sale_price" name="sale_price" required>
</div>
<div class="col-md-3 mb-3">
<label for="low_stock_threshold" class="form-label">Düşük Stok Uyarısı</label>
<input type="number" class="form-control" id="low_stock_threshold" name="low_stock_threshold" value="5" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Ürünü Ekle</button>
</form>
</div>
</div>
<!-- Ürün Listesi -->
<div class="card">
<div class="card-header">
Mevcut Ürünler
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Ad</th>
<th>Tip</th>
<th>Stok</th>
<th>Alış Fiyatı</th>
<th>Satış Fiyatı</th>
<th>Eklendiği Tarih</th>
<th>İşlemler</th>
</tr>
</thead>
<tbody>
<?php if (empty($products)):
<tr>
<td colspan="8" class="text-center">Henüz hiç ürün eklenmemiş.</td>
</tr>
<?php else: ?>
<?php foreach ($products as $product): ?>
<tr>
<td><?php echo htmlspecialchars($product['id']); ?></td>
<td><?php echo htmlspecialchars($product['name']); ?></td>
<td><?php echo htmlspecialchars($product['type']); ?></td>
<td><?php echo htmlspecialchars($product['stock_quantity']); ?></td>
<td><?php echo htmlspecialchars($product['purchase_price']); ?></td>
<td><?php echo htmlspecialchars($product['sale_price']); ?></td>
<td><?php echo htmlspecialchars($product['created_at']); ?></td>
<td>
<a href="edit-product.php?id=<?php echo $product['id']; ?>" class="btn btn-sm btn-warning">Düzenle</a>
<form action="products.php" method="POST" class="d-inline" onsubmit="return confirm('Bu ürünü silmek istediğinizden emin misiniz?');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="product_id" value="<?php echo $product['id']; ?>">
<button type="submit" class="btn btn-sm btn-danger">Sil</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once 'partials/footer.php'; ?>

162
sales.php Normal file
View File

@ -0,0 +1,162 @@
<?php
require_once 'db/config.php';
// Yeni satış ekleme işlemi
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_sale'])) {
$product_id = $_POST['product_id'] ?? null;
$quantity = $_POST['quantity'] ?? 0;
if (empty($product_id) || empty($quantity) || !is_numeric($quantity) || $quantity <= 0) {
$_SESSION['error'] = "Lütfen geçerli bir ürün ve adet girin.";
header("Location: sales.php");
exit();
}
$pdo = db();
$pdo->beginTransaction();
try {
// 1. Ürün bilgilerini ve stok durumunu kontrol et
$stmt = $pdo->prepare("SELECT name, sale_price, purchase_price, stock_quantity FROM products WHERE id = ? FOR UPDATE");
$stmt->execute([$product_id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) {
throw new Exception("Ürün bulunamadı.");
}
if ($product['stock_quantity'] < $quantity) {
throw new Exception("Yetersiz stok! Mevcut stok: " . $product['stock_quantity']);
}
// 2. Satış hesaplamalarını yap
$total_amount = $product['sale_price'] * $quantity;
$profit_amount = ($product['sale_price'] - $product['purchase_price']) * $quantity;
// 3. 'sales' tablosuna ana satış kaydını ekle
$stmt = $pdo->prepare("INSERT INTO sales (total_amount, profit_amount) VALUES (?, ?)");
$stmt->execute([$total_amount, $profit_amount]);
$sale_id = $pdo->lastInsertId();
// 4. 'sale_items' tablosuna satılan ürünü ekle
$stmt = $pdo->prepare("INSERT INTO sale_items (sale_id, product_id, quantity, unit_price, total_price) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$sale_id, $product_id, $quantity, $product['sale_price'], $total_amount]);
// 5. Ürün stoğunu güncelle
$stmt = $pdo->prepare("UPDATE products SET stock_quantity = stock_quantity - ? WHERE id = ?");
$stmt->execute([$quantity, $product_id]);
// Her şey yolundaysa işlemi onayla
$pdo->commit();
$_SESSION['notification'] = "Satış başarıyla kaydedildi.";
} catch (Exception $e) {
// Bir hata olursa tüm işlemleri geri al
$pdo->rollBack();
$_SESSION['error'] = "Hata: " . $e->getMessage();
}
header("Location: sales.php");
exit();
}
// Sayfa içeriğini hazırlama
$pdo = db();
// Form için ürünleri çek
$products_for_form = [];
try {
$products_stmt = $pdo->query("SELECT id, name, sale_price, stock_quantity FROM products WHERE stock_quantity > 0 ORDER BY name ASC");
$products_for_form = $products_stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$_SESSION['error'] = "Ürünler getirilemedi: " . $e->getMessage();
}
// Görüntülemek için geçmiş satışları çek (JOIN ile)
$sales_list = [];
try {
$sales_stmt = $pdo->query("
SELECT si.id, p.name AS product_name, si.quantity, si.total_price, s.created_at AS sale_date
FROM sale_items si
JOIN sales s ON si.sale_id = s.id
JOIN products p ON si.product_id = p.id
ORDER BY s.created_at DESC
");
$sales_list = $sales_stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$_SESSION['error'] = "Satışlar getirilemedi: " . $e->getMessage();
}
require_once 'partials/header.php';
?>
<h1 class="mb-4">Satış Yönetimi</h1>
<!-- Yeni Satış Ekleme Formu -->
<div class="card mb-4">
<div class="card-header">Yeni Satış Ekle</div>
<div class="card-body">
<form action="sales.php" method="POST">
<input type="hidden" name="add_sale" value="1">
<div class="row">
<div class="col-md-8 mb-3">
<label for="product_id" class="form-label">Ürün Seçin</label>
<select class="form-select" id="product_id" name="product_id" required>
<option value="">-- Bir Ürün Seçin --</option>
<?php foreach ($products_for_form as $product): ?>
<option value="<?php echo htmlspecialchars($product['id']); ?>">
<?php echo htmlspecialchars($product['name']); ?>
(Stok: <?php echo htmlspecialchars($product['stock_quantity']); ?> |
Fiyat: <?php echo htmlspecialchars(number_format($product['sale_price'], 2)); ?> TL)
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4 mb-3">
<label for="quantity" class="form-label">Adet</label>
<input type="number" class="form-control" id="quantity" name="quantity" min="1" required>
</div>
</div>
<button type="submit" class="btn btn-primary">Satış Ekle</button>
</form>
</div>
</div>
<!-- Geçmiş Satışlar -->
<div class="card">
<div class="card-header">Geçmiş Satışlar</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Ürün Adı</th>
<th>Adet</th>
<th>Toplam Fiyat</th>
<th>Satış Tarihi</th>
</tr>
</thead>
<tbody>
<?php if (empty($sales_list)): ?>
<tr>
<td colspan="5" class="text-center">Henüz hiç satış yapılmamış.</td>
</tr>
<?php else: ?>
<?php foreach ($sales_list as $sale): ?>
<tr>
<td><?php echo htmlspecialchars($sale['id']); ?></td>
<td><?php echo htmlspecialchars($sale['product_name']); ?></td>
<td><?php echo htmlspecialchars($sale['quantity']); ?></td>
<td><?php echo htmlspecialchars(number_format($sale['total_price'], 2)); ?> TL</td>
<td><?php echo htmlspecialchars($sale['sale_date']); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once 'partials/footer.php'; ?>