Wersja przed ENG

This commit is contained in:
Flatlogic Bot 2025-12-12 18:01:13 +00:00
parent 09395ccf2c
commit 044ab71d5f
14 changed files with 246 additions and 131 deletions

View File

@ -8,28 +8,36 @@ $pdo = db();
$message = ''; $message = '';
// Handle form submission // Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['client_id'], $_POST['product_id'], $_POST['price'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['client_id'], $_POST['product_id'])) {
$clientId = $_POST['client_id']; $clientId = $_POST['client_id'];
$productId = $_POST['product_id']; $productId = $_POST['product_id'];
$price = $_POST['price']; $priceNet = isset($_POST['price_net']) && is_numeric($_POST['price_net']) ? (float)$_POST['price_net'] : null;
$priceGross = isset($_POST['price_gross']) && is_numeric($_POST['price_gross']) ? (float)$_POST['price_gross'] : null;
if (!empty($clientId) && !empty($productId) && is_numeric($price)) { // Server-side validation and calculation
if ($priceGross !== null) {
$priceNet = round($priceGross / 1.23, 2);
} elseif ($priceNet !== null) {
$priceGross = round($priceNet * 1.23, 2);
}
if (!empty($clientId) && !empty($productId) && $priceNet !== null && $priceGross !== null) {
// Upsert logic // Upsert logic
$stmt = $pdo->prepare("SELECT COUNT(*) FROM client_prices WHERE client_id = :client_id AND product_id = :product_id"); $stmt = $pdo->prepare("SELECT COUNT(*) FROM client_prices WHERE client_id = :client_id AND product_id = :product_id");
$stmt->execute(['client_id' => $clientId, 'product_id' => $productId]); $stmt->execute(['client_id' => $clientId, 'product_id' => $productId]);
$exists = $stmt->fetchColumn() > 0; $exists = $stmt->fetchColumn() > 0;
if ($exists) { if ($exists) {
$stmt = $pdo->prepare("UPDATE client_prices SET price = :price WHERE client_id = :client_id AND product_id = :product_id"); $stmt = $pdo->prepare("UPDATE client_prices SET price_net = :price_net, price_gross = :price_gross WHERE client_id = :client_id AND product_id = :product_id");
$stmt->execute(['price' => $price, 'client_id' => $clientId, 'product_id' => $productId]); $stmt->execute(['price_net' => $priceNet, 'price_gross' => $priceGross, 'client_id' => $clientId, 'product_id' => $productId]);
$message = '<div class="alert alert-success">Cena została zaktualizowana.</div>'; $message = '<div class="alert alert-success">Cena została zaktualizowana.</div>';
} else { } else {
$stmt = $pdo->prepare("INSERT INTO client_prices (client_id, product_id, price) VALUES (:client_id, :product_id, :price)"); $stmt = $pdo->prepare("INSERT INTO client_prices (client_id, product_id, price_net, price_gross) VALUES (:client_id, :product_id, :price_net, :price_gross)");
$stmt->execute(['client_id' => $clientId, 'product_id' => $productId, 'price' => $price]); $stmt->execute(['client_id' => $clientId, 'product_id' => $productId, 'price_net' => $priceNet, 'price_gross' => $priceGross]);
$message = '<div class="alert alert-success">Nowa cena została dodana.</div>'; $message = '<div class="alert alert-success">Nowa cena została dodana.</div>';
} }
} else { } else {
$message = '<div class="alert alert-danger">Wszystkie pola są wymagane.</div>'; $message = '<div class="alert alert-danger">Wszystkie pola są wymagane, a ceny muszą być prawidłowymi liczbami.</div>';
} }
} }
@ -42,7 +50,8 @@ $pricesStmt = $pdo->query("
SELECT SELECT
cp.client_id, cp.client_id,
cp.product_id, cp.product_id,
cp.price, cp.price_net,
cp.price_gross,
c.name as client_name, c.name as client_name,
p.name as product_name p.name as product_name
FROM client_prices cp FROM client_prices cp
@ -80,7 +89,7 @@ $existingPrices = $pricesStmt->fetchAll(PDO::FETCH_ASSOC);
<div class="card-body"> <div class="card-body">
<form method="POST" action="client_prices.php"> <form method="POST" action="client_prices.php">
<div class="row"> <div class="row">
<div class="col-md-4 mb-3"> <div class="col-md-3 mb-3">
<label for="client_id" class="form-label">Klient:</label> <label for="client_id" class="form-label">Klient:</label>
<select name="client_id" id="client_id" class="form-select" required> <select name="client_id" id="client_id" class="form-select" required>
<option value="">Wybierz klienta</option> <option value="">Wybierz klienta</option>
@ -89,7 +98,7 @@ $existingPrices = $pricesStmt->fetchAll(PDO::FETCH_ASSOC);
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div class="col-md-4 mb-3"> <div class="col-md-3 mb-3">
<label for="product_id" class="form-label">Produkt:</label> <label for="product_id" class="form-label">Produkt:</label>
<select name="product_id" id="product_id" class="form-select" required> <select name="product_id" id="product_id" class="form-select" required>
<option value="">Wybierz produkt</option> <option value="">Wybierz produkt</option>
@ -99,8 +108,12 @@ $existingPrices = $pricesStmt->fetchAll(PDO::FETCH_ASSOC);
</select> </select>
</div> </div>
<div class="col-md-2 mb-3"> <div class="col-md-2 mb-3">
<label for="price" class="form-label">Cena:</label> <label for="price_net" class="form-label">Cena netto (PLN):</label>
<input type="number" step="0.01" name="price" id="price" class="form-control" required> <input type="number" step="0.01" name="price_net" id="price_net" class="form-control">
</div>
<div class="col-md-2 mb-3">
<label for="price_gross" class="form-label">Cena brutto (PLN):</label>
<input type="number" step="0.01" name="price_gross" id="price_gross" class="form-control">
</div> </div>
<div class="col-md-2 d-flex align-items-end"> <div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Zapisz</button> <button type="submit" class="btn btn-primary w-100">Zapisz</button>
@ -117,20 +130,22 @@ $existingPrices = $pricesStmt->fetchAll(PDO::FETCH_ASSOC);
<tr> <tr>
<th>Klient</th> <th>Klient</th>
<th>Produkt</th> <th>Produkt</th>
<th>Cena (PLN)</th> <th>Cena netto (PLN)</th>
<th>Cena brutto (PLN)</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php if (empty($existingPrices)): ?> <?php if (empty($existingPrices)): ?>
<tr> <tr>
<td colspan="3" class="text-center">Brak zdefiniowanych cen indywidualnych.</td> <td colspan="4" class="text-center">Brak zdefiniowanych cen indywidualnych.</td>
</tr> </tr>
<?php else: ?> <?php else: ?>
<?php foreach ($existingPrices as $price): ?> <?php foreach ($existingPrices as $price): ?>
<tr> <tr>
<td><?php echo htmlspecialchars($price['client_name']); ?></td> <td><?php echo htmlspecialchars($price['client_name']); ?></td>
<td><?php echo htmlspecialchars($price['product_name']); ?></td> <td><?php echo htmlspecialchars($price['product_name']); ?></td>
<td><?php echo number_format($price['price'], 2, ',', ' '); ?></td> <td><?php echo number_format($price['price_net'], 2, ',', ' '); ?></td>
<td><?php echo number_format($price['price_gross'], 2, ',', ' '); ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>
@ -141,4 +156,30 @@ $existingPrices = $pricesStmt->fetchAll(PDO::FETCH_ASSOC);
</div> </div>
</div> </div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const priceNetInput = document.getElementById('price_net');
const priceGrossInput = document.getElementById('price_gross');
const vatRate = 1.23;
priceNetInput.addEventListener('input', function () {
const netValue = parseFloat(this.value);
if (!isNaN(netValue)) {
priceGrossInput.value = (netValue * vatRate).toFixed(2);
} else {
priceGrossInput.value = '';
}
});
priceGrossInput.addEventListener('input', function () {
const grossValue = parseFloat(this.value);
if (!isNaN(grossValue)) {
priceNetInput.value = (grossValue / vatRate).toFixed(2);
} else {
priceNetInput.value = '';
}
});
});
</script>
<?php include __DIR__ . '/../includes/footer.php'; ?> <?php include __DIR__ . '/../includes/footer.php'; ?>

View File

@ -1,8 +1,8 @@
<?php <?php
session_start();
require_once __DIR__ . '/../includes/auth.php'; require_once __DIR__ . '/../includes/auth.php';
require_role('admin'); require_role('admin');
require_once __DIR__ . '/../includes/helpers.php'; require_once __DIR__ . '/../includes/helpers.php';
require_once __DIR__ . '/menu.php';
$db = db(); $db = db();
@ -92,6 +92,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head> </head>
<body> <body>
<?php require_once __DIR__ . '/menu.php'; ?>
<div class="container mt-4"> <div class="container mt-4">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">

View File

@ -99,16 +99,23 @@ $pageTitle = 'Szczegóły zamówienia #' . htmlspecialchars($order['id']);
<tr> <tr>
<th>Produkt</th> <th>Produkt</th>
<th>Ilość</th> <th>Ilość</th>
<th>Cena jednostkowa</th> <th>Cena jedn. netto</th>
<th>Suma</th> <th>Cena jedn. brutto</th>
<th>Suma (brutto)</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($order_items as $item): ?> <?php
$vatRate = 1.23;
foreach ($order_items as $item):
$unit_price_gross = (float)$item['unit_price'];
$unit_price_net = $unit_price_gross / $vatRate;
?>
<tr> <tr>
<td><?php echo htmlspecialchars($item['product_name']); ?></td> <td><?php echo htmlspecialchars($item['product_name']); ?></td>
<td><?php echo htmlspecialchars($item['quantity']); ?></td> <td><?php echo htmlspecialchars($item['quantity']); ?></td>
<td><?php echo number_format($item['unit_price'], 2, ',', ' '); ?> zł</td> <td><?php echo number_format($unit_price_net, 2, ',', ' '); ?> zł</td>
<td><?php echo number_format($unit_price_gross, 2, ',', ' '); ?> zł</td>
<td><?php echo number_format($item['line_total'], 2, ',', ' '); ?> zł</td> <td><?php echo number_format($item['line_total'], 2, ',', ' '); ?> zł</td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
@ -125,7 +132,7 @@ $pageTitle = 'Szczegóły zamówienia #' . htmlspecialchars($order['id']);
<p><strong>Klient:</strong> <?php echo htmlspecialchars($order['client_company_name'] ?? 'Brak'); ?></p> <p><strong>Klient:</strong> <?php echo htmlspecialchars($order['client_company_name'] ?? 'Brak'); ?></p>
<p><strong>Data:</strong> <?php echo date('d.m.Y H:i', strtotime($order['created_at'])); ?></p> <p><strong>Data:</strong> <?php echo date('d.m.Y H:i', strtotime($order['created_at'])); ?></p>
<p><strong>Metoda płatności:</strong> <span class="badge bg-secondary"><?php echo htmlspecialchars(get_payment_method_translation_local($order['payment_method'], $i18n)); ?></span></p> <p><strong>Metoda płatności:</strong> <span class="badge bg-secondary"><?php echo htmlspecialchars(get_payment_method_translation_local($order['payment_method'], $i18n)); ?></span></p>
<p><strong>Suma:</strong> <strong class="fs-5"><?php echo number_format($order['total_amount'], 2, ',', ' '); ?> zł</strong></p> <p><strong>Suma (brutto):</strong> <strong class="fs-5"><?php echo number_format($order['total_amount'], 2, ',', ' '); ?> zł</strong></p>
</div> </div>
</div> </div>
<div class="card"> <div class="card">

View File

@ -125,7 +125,7 @@ $pageTitle = "Zarządzanie zamówieniami";
<th>Data</th> <th>Data</th>
<th>Status</th> <th>Status</th>
<th>Źródło</th> <th>Źródło</th>
<th>Suma</th> <th>Suma (brutto)</th>
<th>Akcje</th> <th>Akcje</th>
</tr> </tr>
</thead> </thead>

View File

@ -18,38 +18,32 @@ if (!empty($cart)) {
try { try {
$pdo = db(); $pdo = db();
$client_id = $_SESSION['client_id'] ?? null; $client_id = $_SESSION['client_id'] ?? null;
$params = [];
$sql = 'SELECT p.*, '; $stmt = $pdo->prepare("SELECT * FROM products WHERE id IN ($placeholders)");
if ($client_id) { $stmt->execute($product_ids);
$sql .= 'COALESCE(cp.price, p.price) as final_price FROM products p';
$sql .= ' LEFT JOIN client_prices cp ON p.id = cp.product_id AND cp.client_id = ?';
$params[] = $client_id;
} else {
$sql .= 'p.price as final_price FROM products p';
}
$sql .= " WHERE p.id IN ($placeholders)";
$params = array_merge($params, $product_ids);
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$products = $stmt->fetchAll(PDO::FETCH_ASSOC); $products = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($products as $product) { foreach ($products as $product) {
$quantity = $cart[$product['id']]; $quantity = $cart[$product['id']];
$line_total = $product['final_price'] * $quantity;
// Use the new centralized price function
$price_info = getEffectivePrice($pdo, $product['id'], $client_id);
$price_net = $price_info['net'];
$price_gross = $price_info['gross'];
$line_total_gross = $price_gross * $quantity;
$cart_products[] = [ $cart_products[] = [
'id' => $product['id'], 'id' => $product['id'],
'name' => $product['name'], 'name' => $product['name'],
'price' => $product['final_price'], 'price_net' => $price_net,
'price_gross' => $price_gross,
'quantity' => $quantity, 'quantity' => $quantity,
'line_total' => $line_total, 'line_total' => $line_total_gross, // Use gross for calculations
]; ];
$total_price += $line_total;
$total_price += $line_total_gross; // Sum up the gross total
} }
} catch (PDOException $e) { } catch (PDOException $e) {
die("Błąd połączenia z bazą danych: " . $e->getMessage()); die("Błąd połączenia z bazą danych: " . $e->getMessage());
@ -126,7 +120,8 @@ $user_role = get_user_role();
<thead> <thead>
<tr> <tr>
<th>Produkt</th> <th>Produkt</th>
<th>Cena</th> <th>Cena netto</th>
<th>Cena brutto</th>
<th>Ilość</th> <th>Ilość</th>
<th>Razem</th> <th>Razem</th>
<th></th> <th></th>
@ -136,7 +131,8 @@ $user_role = get_user_role();
<?php foreach ($cart_products as $item): ?> <?php foreach ($cart_products as $item): ?>
<tr> <tr>
<td><?php echo htmlspecialchars($item['name']); ?></td> <td><?php echo htmlspecialchars($item['name']); ?></td>
<td><?php echo number_format($item['price'], 2, ',', ' '); ?> zł</td> <td><?php echo number_format($item['price_net'], 2, ',', ' '); ?> zł</td>
<td><?php echo number_format($item['price_gross'], 2, ',', ' '); ?> zł</td>
<td> <td>
<form action="cart_actions.php" method="POST" class="d-inline-flex align-items-center"> <form action="cart_actions.php" method="POST" class="d-inline-flex align-items-center">
<input type="hidden" name="action" value="update"> <input type="hidden" name="action" value="update">
@ -161,7 +157,7 @@ $user_role = get_user_role();
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="text-end"><strong>Razem:</strong></td> <td colspan="4" class="text-end"><strong>Razem (brutto):</strong></td>
<td colspan="2"><strong><?php echo number_format($total_price, 2, ',', ' '); ?> zł</strong></td> <td colspan="2"><strong><?php echo number_format($total_price, 2, ',', ' '); ?> zł</strong></td>
</tr> </tr>
</tfoot> </tfoot>

View File

@ -30,20 +30,25 @@ if (!empty($cart)) {
$credit_info = $stmt->fetch(PDO::FETCH_ASSOC); $credit_info = $stmt->fetch(PDO::FETCH_ASSOC);
} }
$sql = "SELECT p.id, p.name, p.units_per_pallet, COALESCE(cp.price, p.price_gross) as price FROM products p LEFT JOIN client_prices cp ON p.id = cp.product_id AND cp.client_id = ? WHERE p.id IN ($placeholders)"; $sql = "SELECT p.id, p.name, p.units_per_pallet FROM products p WHERE p.id IN ($placeholders)";
$stmt = $pdo->prepare($sql); $stmt = $pdo->prepare($sql);
$params = array_merge([$client_id], $product_ids); $stmt->execute($product_ids);
$stmt->execute($params);
$products = $stmt->fetchAll(PDO::FETCH_ASSOC); $products = $stmt->fetchAll(PDO::FETCH_ASSOC);
$is_supplier_delivery = false; $is_supplier_delivery = false;
foreach ($products as $product) { foreach ($products as $product) {
$quantity = $cart[$product['id']]; $quantity = $cart[$product['id']];
$line_total = $product['price'] * $quantity;
$price_info = getEffectivePrice($pdo, $product['id'], $client_id);
$price_net = $price_info['net'];
$price_gross = $price_info['gross'];
$line_total = $price_gross * $quantity;
$cart_products[] = [ $cart_products[] = [
'id' => $product['id'], 'id' => $product['id'],
'name' => $product['name'], 'name' => $product['name'],
'price' => $product['price'], 'price_net' => $price_net,
'price_gross' => $price_gross,
'quantity' => $quantity, 'quantity' => $quantity,
'line_total' => $line_total, 'line_total' => $line_total,
]; ];
@ -134,8 +139,9 @@ $user_role = get_user_role();
<tr> <tr>
<th>Produkt</th> <th>Produkt</th>
<th>Ilość</th> <th>Ilość</th>
<th>Cena jedn.</th> <th>Cena netto</th>
<th>Suma</th> <th>Cena brutto</th>
<th>Suma (brutto)</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -143,14 +149,15 @@ $user_role = get_user_role();
<tr> <tr>
<td><?php echo htmlspecialchars($item['name']); ?></td> <td><?php echo htmlspecialchars($item['name']); ?></td>
<td><?php echo $item['quantity']; ?></td> <td><?php echo $item['quantity']; ?></td>
<td><?php echo number_format($item['price'], 2, ',', ' '); ?> zł</td> <td><?php echo number_format($item['price_net'], 2, ',', ' '); ?> zł</td>
<td><?php echo number_format($item['price_gross'], 2, ',', ' '); ?> zł</td>
<td><?php echo number_format($item['line_total'], 2, ',', ' '); ?> zł</td> <td><?php echo number_format($item['line_total'], 2, ',', ' '); ?> zł</td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="text-end"><strong>Suma:</strong></td> <td colspan="4" class="text-end"><strong>Suma (brutto):</strong></td>
<td><strong><?php echo number_format($total_price, 2, ',', ' '); ?> zł</strong></td> <td><strong><?php echo number_format($total_price, 2, ',', ' '); ?> zł</strong></td>
</tr> </tr>
</tfoot> </tfoot>

View File

@ -0,0 +1,10 @@
-- Add net and gross price columns to client_prices
ALTER TABLE client_prices
ADD COLUMN price_net DECIMAL(10, 2) NULL,
ADD COLUMN price_gross DECIMAL(10, 2) NULL;
-- Migrate existing price to price_gross (assuming it was gross)
UPDATE client_prices SET price_gross = price WHERE price IS NOT NULL;
-- Calculate price_net from price_gross
UPDATE client_prices SET price_net = ROUND(price_gross / 1.23, 2) WHERE price_gross IS NOT NULL;

View File

@ -75,3 +75,58 @@ function upload_error_message($error_code) {
return 'Unknown upload error'; return 'Unknown upload error';
} }
} }
function getEffectivePrice(PDO $db, int $productId, ?int $clientId): array {
$vatRate = 1.23;
$net = null;
$gross = null;
$priceFound = false;
// Priority A: Try to fetch from client_prices
if ($clientId) {
$stmt = $db->prepare("SELECT price_net, price_gross FROM client_prices WHERE client_id = :client_id AND product_id = :product_id LIMIT 1");
$stmt->execute(['client_id' => $clientId, 'product_id' => $productId]);
$priceRow = $stmt->fetch(PDO::FETCH_ASSOC);
if ($priceRow) {
$net = $priceRow['price_net'] !== null ? (float)$priceRow['price_net'] : null;
$gross = $priceRow['price_gross'] !== null ? (float)$priceRow['price_gross'] : null;
if ($net !== null || $gross !== null) {
$priceFound = true;
}
}
}
// Priority B: Fallback to product base prices if no client-specific price was found
if (!$priceFound) {
$stmt = $db->prepare("SELECT price_net, price_gross FROM products WHERE id = :product_id");
$stmt->execute(['product_id' => $productId]);
$priceRow = $stmt->fetch(PDO::FETCH_ASSOC);
if ($priceRow) {
$net = $priceRow['price_net'] !== null ? (float)$priceRow['price_net'] : null;
$gross = $priceRow['price_gross'] !== null ? (float)$priceRow['price_gross'] : null;
}
}
// If we have one price, calculate the other
if ($gross !== null && $net === null) {
$net = round($gross / $vatRate, 2);
} elseif ($net !== null && $gross === null) {
$gross = round($net * $vatRate, 2);
}
// Sanity check: gross must not be less than net. If so, log it and fix it.
if ($gross !== null && $net !== null && $gross < $net) {
error_log("Price inconsistency for product ID $productId: gross ($gross) is less than net ($net). Recalculating net from gross.");
$net = round($gross / $vatRate, 2);
}
// Final check for nulls before returning
if ($net === null || $gross === null) {
return ['net' => 0.0, 'gross' => 0.0];
}
return ['net' => $net, 'gross' => $gross];
}

View File

@ -12,8 +12,6 @@ try {
// Fetch products and their primary images // Fetch products and their primary images
$sql = "SELECT $sql = "SELECT
p.*, p.*,
COALESCE(cp.price, p.price_gross) as final_price,
p.price_net as final_price_net,
(SELECT file_path (SELECT file_path
FROM product_images FROM product_images
WHERE product_id = p.id WHERE product_id = p.id
@ -21,17 +19,13 @@ try {
LIMIT 1) AS image_path LIMIT 1) AS image_path
FROM FROM
products p products p
LEFT JOIN
users u ON u.id = :user_id
LEFT JOIN
client_prices cp ON cp.product_id = p.id AND cp.client_id = u.client_id
WHERE WHERE
p.is_active = 1 p.is_active = 1
ORDER BY ORDER BY
CASE p.product_role WHEN 'membrana' THEN 1 WHEN 'akcesoria' THEN 2 ELSE 3 END, p.name ASC"; CASE p.product_role WHEN 'membrana' THEN 1 WHEN 'akcesoria' THEN 2 ELSE 3 END, p.name ASC";
$stmt = $pdo->prepare($sql); $stmt = $pdo->prepare($sql);
$stmt->execute(['user_id' => $_SESSION['user_id']]); $stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC); $products = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Separate products into main and accessories // Separate products into main and accessories
@ -133,6 +127,7 @@ $page_title = 'Katalog';
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4"> <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4">
<?php foreach ($main_products as $product): <?php foreach ($main_products as $product):
$prices = getEffectivePrice($pdo, $product['id'], $_SESSION['client_id']);
$image_url = !empty($product['image_path']) $image_url = !empty($product['image_path'])
? 'uploads/products/' . htmlspecialchars($product['image_path']) ? 'uploads/products/' . htmlspecialchars($product['image_path'])
: 'https://placehold.co/600x400/EEE/31343C?text=Brak+zdj%C4%99cia'; : 'https://placehold.co/600x400/EEE/31343C?text=Brak+zdj%C4%99cia';
@ -152,7 +147,10 @@ $page_title = 'Katalog';
$desc = $product['description']; $desc = $product['description'];
echo htmlspecialchars(strlen($desc) > 100 ? substr($desc, 0, 100) . '...' : $desc); echo htmlspecialchars(strlen($desc) > 100 ? substr($desc, 0, 100) . '...' : $desc);
?></p> ?></p>
<p class="card-text fw-bold fs-5 mt-auto"><?= htmlspecialchars(number_format($product['final_price'], 2, ',', ' ')) ?> PLN / <?= htmlspecialchars($product['unit']) ?></p> <div class="mt-auto">
<p class="card-text text-muted small mb-0"><?= htmlspecialchars(number_format($prices['net'], 2, ',', ' ')) ?> zł netto</p>
<p class="card-text fw-bold fs-5"><?= htmlspecialchars(number_format($prices['gross'], 2, ',', ' ')) ?> zł brutto</p>
</div>
</div> </div>
<div class="card-footer bg-white border-top-0 pb-3"> <div class="card-footer bg-white border-top-0 pb-3">
<form action="cart_actions.php" method="POST" class="d-grid"> <form action="cart_actions.php" method="POST" class="d-grid">
@ -177,6 +175,7 @@ $page_title = 'Katalog';
<h2 class="mb-4">Akcesoria i produkty uzupełniające</h2> <h2 class="mb-4">Akcesoria i produkty uzupełniające</h2>
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-4 row-cols-lg-5 g-4"> <div class="row row-cols-1 row-cols-sm-2 row-cols-md-4 row-cols-lg-5 g-4">
<?php foreach ($accessories as $product): <?php foreach ($accessories as $product):
$prices = getEffectivePrice($pdo, $product['id'], $_SESSION['client_id']);
$image_url = !empty($product['image_path']) $image_url = !empty($product['image_path'])
? 'uploads/products/' . htmlspecialchars($product['image_path']) ? 'uploads/products/' . htmlspecialchars($product['image_path'])
: 'https://placehold.co/600x400/EEE/31343C?text=Brak+zdj%C4%99cia'; : 'https://placehold.co/600x400/EEE/31343C?text=Brak+zdj%C4%99cia';
@ -192,7 +191,10 @@ $page_title = 'Katalog';
<?= htmlspecialchars($product['name']) ?> <?= htmlspecialchars($product['name']) ?>
</a> </a>
</h6> </h6>
<p class="card-text fw-bold mt-auto"><?= htmlspecialchars(number_format($product['final_price'], 2, ',', ' ')) ?> PLN / <?= htmlspecialchars($product['unit']) ?></p> <div class="mt-auto">
<p class="card-text text-muted small mb-0"><?= htmlspecialchars(number_format($prices['net'], 2, ',', ' ')) ?> zł netto</p>
<p class="card-text fw-bold"><?= htmlspecialchars(number_format($prices['gross'], 2, ',', ' ')) ?> zł brutto</p>
</div>
</div> </div>
<div class="card-footer bg-white border-top-0 pb-3"> <div class="card-footer bg-white border-top-0 pb-3">
<form action="cart_actions.php" method="POST" class="d-grid"> <form action="cart_actions.php" method="POST" class="d-grid">

View File

@ -161,7 +161,7 @@ $lang = 'pl';
<p><strong>Data zamówienia:</strong> <?php echo date('d.m.Y H:i', strtotime($order['created_at'])); ?></p> <p><strong>Data zamówienia:</strong> <?php echo date('d.m.Y H:i', strtotime($order['created_at'])); ?></p>
<p><strong>Status:</strong> <span class="badge bg-info"><?php echo htmlspecialchars(get_polish_status_translation($order['status'])); ?></span></p> <p><strong>Status:</strong> <span class="badge bg-info"><?php echo htmlspecialchars(get_polish_status_translation($order['status'])); ?></span></p>
<p><strong>Metoda płatności:</strong> <?php echo htmlspecialchars(get_polish_status_translation($order['payment_method'])); ?></p> <p><strong>Metoda płatności:</strong> <?php echo htmlspecialchars(get_polish_status_translation($order['payment_method'])); ?></p>
<p><strong>Suma:</strong> <?php echo number_format($order['total_amount'], 2, ',', ' '); ?> zł</p> <p><strong>Suma (brutto):</strong> <?php echo number_format($order['total_amount'], 2, ',', ' '); ?> zł</p>
<p><strong>Uwagi:</strong> <?php echo nl2br(htmlspecialchars($order['notes'])); ?></p> <p><strong>Uwagi:</strong> <?php echo nl2br(htmlspecialchars($order['notes'])); ?></p>
</div> </div>
</div> </div>
@ -174,19 +174,25 @@ $lang = 'pl';
<tr> <tr>
<th>Zdjęcie</th> <th>Zdjęcie</th>
<th>Produkt</th> <th>Produkt</th>
<th>Cena jednostkowa</th> <th>Cena jedn. netto</th>
<th>Cena jedn. brutto</th>
<th>Ilość</th> <th>Ilość</th>
<th>Suma częściowa</th> <th>Suma (brutto)</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($order_items as $item): <?php
$vatRate = 1.23;
foreach ($order_items as $item):
$image_url = $product_images[$item['product_id']] ?? 'https://placehold.co/100x100/EEE/31343C?text=' . urlencode('Brak zdjęcia'); $image_url = $product_images[$item['product_id']] ?? 'https://placehold.co/100x100/EEE/31343C?text=' . urlencode('Brak zdjęcia');
$unit_price_gross = (float)$item['unit_price'];
$unit_price_net = $unit_price_gross / $vatRate;
?> ?>
<tr> <tr>
<td><img src="<?php echo htmlspecialchars($image_url); ?>" alt="<?php echo htmlspecialchars($item['product_name']); ?>" style="width: 50px; height: 50px; object-fit: cover;"></td> <td><img src="<?php echo htmlspecialchars($image_url); ?>" alt="<?php echo htmlspecialchars($item['product_name']); ?>" style="width: 50px; height: 50px; object-fit: cover;"></td>
<td><?php echo htmlspecialchars($item['product_name']); ?></td> <td><?php echo htmlspecialchars($item['product_name']); ?></td>
<td><?php echo number_format($item['unit_price'], 2, ',', ' '); ?> zł</td> <td><?php echo number_format($unit_price_net, 2, ',', ' '); ?> zł</td>
<td><?php echo number_format($unit_price_gross, 2, ',', ' '); ?> zł</td>
<td><?php echo $item['quantity']; ?></td> <td><?php echo $item['quantity']; ?></td>
<td><?php echo number_format($item['line_total'], 2, ',', ' '); ?> zł</td> <td><?php echo number_format($item['line_total'], 2, ',', ' '); ?> zł</td>
</tr> </tr>

View File

@ -31,27 +31,28 @@ try {
$products_by_id = $stmt->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC); $products_by_id = $stmt->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC);
// 2. Calculate total amount & total pallets // 2. Calculate total amount
$total_amount = 0; $total_amount_gross = 0;
$is_supplier_delivery = false;
$client_id = $_SESSION['client_id'] ?? null; $client_id = $_SESSION['client_id'] ?? null;
$product_prices = []; $order_items_data = [];
if ($client_id) {
$price_placeholders = implode(',', array_fill(0, count($product_ids), '?'));
$sql = "SELECT p.id, COALESCE(cp.price, p.price) as price FROM products p LEFT JOIN client_prices cp ON p.id = cp.product_id AND cp.client_id = ? WHERE p.id IN ($price_placeholders)";
$stmt = $pdo->prepare($sql);
$params = array_merge([$client_id], $product_ids);
$stmt->execute($params);
$product_prices = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
}
$is_supplier_delivery = false; $is_supplier_delivery = false;
foreach ($cart as $product_id => $quantity) { foreach ($cart as $product_id => $quantity) {
if (isset($products_by_id[$product_id])) { if (isset($products_by_id[$product_id])) {
$product = $products_by_id[$product_id]; $product = $products_by_id[$product_id];
$price = $product_prices[$product_id] ?? $product['price'];
$total_amount += $price * $quantity; $price_info = getEffectivePrice($pdo, $product_id, $client_id);
$price_gross = $price_info['gross'];
$total_amount_gross += $price_gross * $quantity;
$order_items_data[] = [
'product_id' => $product_id,
'quantity' => $quantity,
'unit_price' => $price_gross, // Save gross price
'line_total' => $price_gross * $quantity,
];
$units_per_pallet = $product['units_per_pallet']; $units_per_pallet = $product['units_per_pallet'];
if (isset($units_per_pallet) && $units_per_pallet > 0) { if (isset($units_per_pallet) && $units_per_pallet > 0) {
@ -69,11 +70,11 @@ try {
$stmt->execute([$client_id]); $stmt->execute([$client_id]);
$credit_info = $stmt->fetch(); $credit_info = $stmt->fetch();
if (!$credit_info || !$credit_info['credit_enabled'] || $credit_info['credit_balance'] < $total_amount) { if (!$credit_info || !$credit_info['credit_enabled'] || $credit_info['credit_balance'] < $total_amount_gross) {
throw new Exception('Invalid payment method or insufficient credit.'); throw new Exception('Invalid payment method or insufficient credit.');
} }
$new_balance = $credit_info['credit_balance'] - $total_amount; $new_balance = $credit_info['credit_balance'] - $total_amount_gross;
$stmt = $pdo->prepare('UPDATE clients SET credit_balance = ? WHERE id = ?'); $stmt = $pdo->prepare('UPDATE clients SET credit_balance = ? WHERE id = ?');
$stmt->execute([$new_balance, $client_id]); $stmt->execute([$new_balance, $client_id]);
} }
@ -84,7 +85,7 @@ try {
); );
$stmt->execute([ $stmt->execute([
$client_id, $client_id,
$total_amount, $total_amount_gross,
$_POST['payment_method'], $_POST['payment_method'],
$delivery_source, $delivery_source,
$_POST['notes'], $_POST['notes'],
@ -96,18 +97,14 @@ try {
$stmt = $pdo->prepare( $stmt = $pdo->prepare(
'INSERT INTO order_items (order_id, product_id, quantity, unit_price, line_total) VALUES (?, ?, ?, ?, ?)' 'INSERT INTO order_items (order_id, product_id, quantity, unit_price, line_total) VALUES (?, ?, ?, ?, ?)'
); );
foreach ($cart as $product_id => $quantity) { foreach ($order_items_data as $item) {
if (isset($products_by_id[$product_id])) { $stmt->execute([
$product = $products_by_id[$product_id]; $order_id,
$price = $product_prices[$product_id] ?? $product['price']; $item['product_id'],
$stmt->execute([ $item['quantity'],
$order_id, $item['unit_price'],
$product_id, $item['line_total'],
$quantity, ]);
$price,
$price * $quantity
]);
}
} }
// 5. Commit the transaction // 5. Commit the transaction

View File

@ -126,7 +126,7 @@ $lang = 'pl';
<th>Numer zamówienia</th> <th>Numer zamówienia</th>
<th>Data zamówienia</th> <th>Data zamówienia</th>
<th>Status</th> <th>Status</th>
<th>Suma</th> <th>Suma (brutto)</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>

View File

@ -15,14 +15,8 @@ if (!$product_id) {
try { try {
$pdo = db(); $pdo = db();
$stmt = $pdo->prepare("SELECT p.*, $stmt = $pdo->prepare("SELECT p.* FROM products p WHERE p.id = :product_id");
COALESCE(cp.price, p.price_gross) as final_price, $stmt->execute(['product_id' => $product_id]);
p.price_net as final_price_net
FROM products p
LEFT JOIN users u ON u.id = :user_id
LEFT JOIN client_prices cp ON cp.product_id = p.id AND cp.client_id = u.client_id
WHERE p.id = :product_id");
$stmt->execute(['user_id' => $_SESSION['user_id'], 'product_id' => $product_id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC); $product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) { if (!$product) {
@ -30,10 +24,8 @@ try {
exit; exit;
} }
// If client-specific price is used, re-calculate net price from it // Get the correct price pair using the centralized function
if (!empty($product['final_price']) && empty($product['final_price_net'])) { $prices = getEffectivePrice($pdo, $product['id'], $_SESSION['client_id']);
$product['final_price_net'] = round($product['final_price'] / 1.23, 2);
}
// Fetch product images // Fetch product images
@ -139,9 +131,10 @@ $user_role = get_user_role();
<h1 class="mb-3"><?= htmlspecialchars($product['name']) ?></h1> <h1 class="mb-3"><?= htmlspecialchars($product['name']) ?></h1>
<div class="bg-light p-4 rounded mb-4"> <div class="bg-light p-4 rounded mb-4">
<h2 class="h4 fw-bold"><?= htmlspecialchars(number_format($product['final_price'], 2, ',', ' ')) ?> PLN / <?= htmlspecialchars($product['unit']) ?></h2> <p class="h4 fw-bold mb-1"><?= htmlspecialchars(number_format($prices['gross'], 2, ',', ' ')) ?> zł <span class="fs-6 fw-normal">brutto</span></p>
<small class="text-muted">Cena brutto</small> <p class="text-muted mb-0"><?= htmlspecialchars(number_format($prices['net'], 2, ',', ' ')) ?> zł <span class="fs-6 fw-normal">netto</span></p>
<p class="mb-0"><?= htmlspecialchars(number_format($product['final_price_net'], 2, ',', ' ')) ?> PLN netto</p> <hr class="my-2">
<small class="text-muted">Cena za: <?= htmlspecialchars($product['unit']) ?></small>
</div> </div>
<form action="cart_actions.php" method="post" class="d-flex align-items-center"> <form action="cart_actions.php" method="post" class="d-flex align-items-center">

View File

@ -27,15 +27,13 @@ $stmt = $db->prepare("
p.name, p.name,
p.unit, p.unit,
p.price_net, p.price_net,
COALESCE(cp.price, p.price_gross) as final_price, p.price_gross,
pi.file_path AS primary_image pi.file_path AS primary_image
FROM products p FROM products p
LEFT JOIN users u ON u.id = :user_id
LEFT JOIN client_prices cp ON cp.product_id = p.id AND cp.client_id = u.client_id
LEFT JOIN product_images pi ON pi.product_id = p.id AND pi.is_primary = 1 LEFT JOIN product_images pi ON pi.product_id = p.id AND pi.is_primary = 1
WHERE p.id = :product_id WHERE p.id = :product_id
"); ");
$stmt->execute(['user_id' => $user_id, 'product_id' => $product_id]); $stmt->execute(['product_id' => $product_id]);
$added_product = $stmt->fetch(PDO::FETCH_ASSOC); $added_product = $stmt->fetch(PDO::FETCH_ASSOC);
// If product somehow doesn't exist, redirect away // If product somehow doesn't exist, redirect away
@ -43,6 +41,8 @@ if (!$added_product) {
header('Location: cart.php'); header('Location: cart.php');
exit; exit;
} }
$added_product_price = getEffectivePrice($db, $added_product['id'], $_SESSION['client_id'] ?? null);
// If image is not found, use a placeholder // If image is not found, use a placeholder
if (empty($added_product['primary_image'])) { if (empty($added_product['primary_image'])) {
@ -57,16 +57,14 @@ $related_products_stmt = $db->prepare("
p.name, p.name,
p.unit, p.unit,
p.price_net, p.price_net,
COALESCE(cp.price, p.price_gross) as final_price, p.price_gross,
pi.file_path as primary_image pi.file_path as primary_image
FROM products p FROM products p
JOIN product_relations pr ON p.id = pr.related_product_id JOIN product_relations pr ON p.id = pr.related_product_id
LEFT JOIN users u ON u.id = :user_id
LEFT JOIN client_prices cp ON cp.product_id = p.id AND cp.client_id = u.client_id
LEFT JOIN product_images pi ON p.id = pi.product_id AND pi.is_primary = 1 LEFT JOIN product_images pi ON p.id = pi.product_id AND pi.is_primary = 1
WHERE pr.product_id = :product_id AND p.product_role = 'akcesoria' WHERE pr.product_id = :product_id AND p.product_role = 'akcesoria'
"); ");
$related_products_stmt->execute(['user_id' => $user_id, 'product_id' => $product_id]); $related_products_stmt->execute(['product_id' => $product_id]);
$related_products = $related_products_stmt->fetchAll(PDO::FETCH_ASSOC); $related_products = $related_products_stmt->fetchAll(PDO::FETCH_ASSOC);
$user_role = get_user_role(); $user_role = get_user_role();
@ -165,8 +163,8 @@ $page_title = 'Dodano do koszyka';
<div class="col-md-5"> <div class="col-md-5">
<div class="d-flex justify-content-end align-items-center"> <div class="d-flex justify-content-end align-items-center">
<div class="text-end"> <div class="text-end">
<p class="mb-0 h5"><strong><?= number_format($added_product['final_price'], 2, ',', ' '); ?> zł</strong> <small>brutto</small></p> <p class="mb-0 h5"><strong><?= number_format($added_product_price['gross'], 2, ',', ' '); ?> zł</strong> <small>brutto</small></p>
<p class="mb-0 text-muted"><?= number_format($added_product['price_net'], 2, ',', ' '); ?> zł <small>netto</small></p> <p class="mb-0 text-muted"><?= number_format($added_product_price['net'], 2, ',', ' '); ?> zł <small>netto</small></p>
</div> </div>
</div> </div>
</div> </div>
@ -178,7 +176,9 @@ $page_title = 'Dodano do koszyka';
<?php if (!empty($related_products)): ?> <?php if (!empty($related_products)): ?>
<h3 class="mt-5 mb-4">Polecamy także produkty powiązane:</h3> <h3 class="mt-5 mb-4">Polecamy także produkty powiązane:</h3>
<div class="list-group"> <div class="list-group">
<?php foreach ($related_products as $product): ?> <?php foreach ($related_products as $product):
$effective_price = getEffectivePrice($db, $product['id'], $_SESSION['client_id'] ?? null);
?>
<div class="list-group-item list-group-item-action"> <div class="list-group-item list-group-item-action">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-md-2 text-center"> <div class="col-md-2 text-center">
@ -198,8 +198,8 @@ $page_title = 'Dodano do koszyka';
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="text-end"> <div class="text-end">
<p class="mb-0 h5"><strong><?= number_format($product['final_price'], 2, ',', ' '); ?> zł</strong> <small>brutto</small></p> <p class="mb-0 h5"><strong><?= number_format($effective_price['gross'], 2, ',', ' '); ?> zł</strong> <small>brutto</small></p>
<p class="mb-0 text-muted"><?= number_format($product['price_net'], 2, ',', ' '); ?> zł <small>netto</small></p> <p class="mb-0 text-muted"><?= number_format($effective_price['net'], 2, ',', ' '); ?> zł <small>netto</small></p>
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">