39728-vm/print_labels.php
2026-04-19 10:09:23 +00:00

279 lines
11 KiB
PHP

<?php
require_once __DIR__ . '/includes/app.php';
require_permission('stock', 'show');
$pdo = db();
$items = [];
$skus = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['skus'])) {
$skus = is_array($_POST['skus']) ? $_POST['skus'] : explode(',', $_POST['skus']);
} elseif (!empty($_GET['sku'])) {
$skus = [$_GET['sku']];
}
if (!empty($skus)) {
$placeholders = str_repeat('?,', count($skus) - 1) . '?';
$stmt = $pdo->prepare("SELECT * FROM items WHERE sku IN ($placeholders)");
$stmt->execute($skus);
$results = $stmt->fetchAll();
// Index by SKU
foreach ($results as $row) {
$items[$row['sku']] = $row;
}
}
// Templates configuration
$templates = [
'avery_65' => [
'name' => 'Avery L7651 - 65 Labels (38.1 x 21.2 mm)',
'cols' => 5,
'rows' => 13,
'width' => '38.1mm',
'height' => '21.2mm',
'margin_top' => '10.5mm',
'margin_left' => '4.7mm',
'gap_x' => '2.5mm',
'gap_y' => '0mm',
],
'avery_54' => [
'name' => 'Avery 54 Labels (32 x 25.4 mm)',
'cols' => 6,
'rows' => 9,
'width' => '32mm',
'height' => '25.4mm',
'margin_top' => '10mm',
'margin_left' => '5mm',
'gap_x' => '2mm',
'gap_y' => '0mm',
],
'avery_45' => [
'name' => 'Avery 45 Labels (38.1 x 29.6 mm)',
'cols' => 5,
'rows' => 9,
'width' => '38.1mm',
'height' => '29.6mm',
'margin_top' => '15mm',
'margin_left' => '5mm',
'gap_x' => '2mm',
'gap_y' => '0mm',
]
];
$templateId = $_REQUEST['template'] ?? 'avery_65';
$tpl = $templates[$templateId] ?? $templates['avery_65'];
// Prepare labels to print
$labelsToPrint = [];
if (!empty($items)) {
// Check if quantities are posted
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['qty'])) {
foreach ($_POST['qty'] as $sku => $qty) {
if (isset($items[$sku])) {
for ($i = 0; $i < (int)$qty; $i++) {
$labelsToPrint[] = $items[$sku];
}
}
}
} else {
// Default 1 per item
foreach ($skus as $sku) {
if (isset($items[$sku])) {
$labelsToPrint[] = $items[$sku];
}
}
}
}
?>
<!DOCTYPE html>
<html lang="<?= current_lang() ?>" dir="<?= current_lang() === 'ar' ? 'rtl' : 'ltr' ?>">
<head>
<meta charset="UTF-8">
<title><?= h(tr('طباعة ملصقات', 'Print Labels')) ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
<style>
body { background: #f0f2f5; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
.no-print { display: block; }
.print-only { display: none; }
.page {
width: 210mm;
min-height: 297mm;
padding-top: <?= $tpl['margin_top'] ?>;
padding-left: <?= $tpl['margin_left'] ?>;
padding-right: <?= $tpl['margin_left'] ?>;
margin: 10mm auto;
border: 1px solid #D3D3D3;
border-radius: 5px;
background: white;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
display: grid;
grid-template-columns: repeat(<?= $tpl['cols'] ?>, <?= $tpl['width'] ?>);
grid-auto-rows: <?= $tpl['height'] ?>;
column-gap: <?= $tpl['gap_x'] ?>;
row-gap: <?= $tpl['gap_y'] ?>;
box-sizing: border-box;
page-break-after: always;
}
.label-item {
width: <?= $tpl['width'] ?>;
height: <?= $tpl['height'] ?>;
border: 1px dashed #ccc;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
box-sizing: border-box;
padding: 2px;
text-align: center;
}
.label-name { font-size: 9px; font-weight: bold; line-height: 1.1; margin-bottom: 2px; max-height: 20px; overflow: hidden; }
.label-price { font-size: 10px; font-weight: bold; margin-top: 1px; }
.label-sku { font-size: 8px; color: #555; }
.label-barcode { margin: 0; display: flex; align-items: center; justify-content: center; }
.label-barcode svg { width: 100%; height: 100%; max-height: 14px; }
@media print {
body { background: white; margin: 0; padding: 0; }
.no-print { display: none !important; }
.print-only { display: block; }
.page {
margin: 0;
border: initial;
border-radius: initial;
width: 210mm;
min-height: 297mm;
box-shadow: initial;
background: initial;
page-break-after: always;
}
.label-item { border: none; }
@page { size: A4 portrait; margin: 0; }
}
</style>
</head>
<body>
<div class="container mt-4 no-print">
<div class="card shadow-sm">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h5 class="mb-0"><?= h(tr('إعدادات الطباعة', 'Print Settings')) ?></h5>
<a href="stock.php" class="btn btn-sm btn-light"><?= h(tr('رجوع للمخزون', 'Back to Stock')) ?></a>
</div>
<div class="card-body">
<form method="POST">
<div class="row g-3 align-items-end mb-3">
<div class="col-md-4">
<label class="form-label fw-bold"><?= h(tr('قالب الملصقات', 'Label Template')) ?></label>
<select name="template" class="form-select" onchange="this.form.submit()">
<?php foreach($templates as $key => $t): ?>
<option value="<?= $key ?>" <?= $templateId === $key ? 'selected' : '' ?>><?= $t['name'] ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-8 text-end">
<button type="button" class="btn btn-success" onclick="window.print()">
<i class="bi bi-printer"></i> <?= h(tr('طباعة الآن', 'Print Now')) ?>
</button>
</div>
</div>
<div class="table-responsive border rounded">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th>SKU</th>
<th><?= h(tr('الصنف', 'Item')) ?></th>
<th><?= h(tr('السعر', 'Price')) ?></th>
<th width="150" class="text-center"><?= h(tr('عدد الملصقات', 'Labels Count')) ?></th>
<th width="80" class="text-center"><?= h(tr('إزالة', 'Remove')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach($items as $sku => $item): ?>
<tr>
<td class="fw-bold text-secondary">
<?= h($sku) ?>
<input type="hidden" name="skus[]" value="<?= h($sku) ?>">
</td>
<td><?= h($item['name']) ?></td>
<td><span class="badge bg-light text-dark border"><?= h(currency($item['price'])) ?></span></td>
<td>
<input type="number" name="qty[<?= h($sku) ?>]" class="form-control text-center" value="<?= isset($_POST['qty'][$sku]) ? (int)$_POST['qty'][$sku] : 1 ?>" min="1">
</td>
<td class="text-center">
<button type="button" class="btn btn-sm btn-outline-danger rounded-circle" onclick="this.closest('tr').remove();" title="<?= h(tr('إزالة', 'Remove')) ?>">
<i class="bi bi-x-lg"></i>
</button>
</td>
</tr>
<?php endforeach; ?>
<?php if(empty($items)): ?>
<tr>
<td colspan="5" class="text-center text-muted py-4">
<i class="bi bi-inbox fs-3 d-block mb-2"></i>
<?= h(tr('لم يتم تحديد أصناف', 'No items selected')) ?>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if(!empty($items)): ?>
<div class="mt-3">
<button type="submit" class="btn btn-primary">
<i class="bi bi-arrow-clockwise"></i> <?= h(tr('تحديث المعاينة', 'Update Preview')) ?>
</button>
</div>
<?php endif; ?>
</form>
</div>
</div>
</div>
<?php if (!empty($labelsToPrint)): ?>
<div class="print-preview mt-4">
<h5 class="text-center no-print text-muted mb-3"><i class="bi bi-view-list"></i> <?= h(tr('معاينة الطباعة', 'Print Preview')) ?></h5>
<?php
$labelsPerPage = $tpl['cols'] * $tpl['rows'];
$pages = array_chunk($labelsToPrint, $labelsPerPage);
foreach ($pages as $pageIdx => $pageLabels):
?>
<div class="page bg-white">
<?php foreach ($pageLabels as $label): ?>
<div class="label-item">
<div class="label-name"><?= h($label['name']) ?></div>
<div class="label-barcode">
<svg class="barcode"
jsbarcode-format="code128"
jsbarcode-value="<?= h($label['sku']) ?>"
jsbarcode-displayvalue="false"
jsbarcode-width="1"
jsbarcode-height="15"
jsbarcode-margin="0">
</svg>
</div>
<div class="label-sku"><?= h($label['sku']) ?></div>
<div class="label-price"><?= h(currency($label['price'])) ?></div>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<script>
document.addEventListener("DOMContentLoaded", function() {
if (typeof JsBarcode !== 'undefined') {
JsBarcode(".barcode").init();
}
});
</script>
</body>
</html>