prepare("SELECT COUNT(*) AS total FROM print_orders WHERE $column LIKE :prefix");
$stmt->execute([':prefix' => $prefix . '%']);
$count = (int)($stmt->fetch()['total'] ?? 0) + 1;
return $prefix . str_pad((string)$count, 5, '0', STR_PAD_LEFT);
}
function statusBadge(string $status): string {
return match ($status) {
'Order diterima' => 'secondary',
'SPK dibuat' => 'info',
'Produksi berjalan' => 'primary',
'QC / Finishing' => 'warning',
'Barang selesai' => 'success',
'Invoice / Tagihan' => 'dark',
default => 'secondary',
};
}
function ensureSchema(PDO $pdo): void {
$pdo->exec("CREATE TABLE IF NOT EXISTS print_orders (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(40) NOT NULL UNIQUE,
spk_no VARCHAR(40) NOT NULL UNIQUE,
invoice_no VARCHAR(40) NOT NULL UNIQUE,
customer_name VARCHAR(140) NOT NULL,
customer_phone VARCHAR(40) NOT NULL,
customer_email VARCHAR(160) NULL,
project_name VARCHAR(180) NOT NULL,
category VARCHAR(100) NOT NULL,
quantity INT UNSIGNED NOT NULL DEFAULT 1,
size_info VARCHAR(120) NULL,
material VARCHAR(160) NULL,
finishing VARCHAR(180) NULL,
deadline DATE NULL,
operator_name VARCHAR(120) NULL,
cs_name VARCHAR(120) NULL,
status VARCHAR(40) NOT NULL DEFAULT 'Order diterima',
progress_note TEXT NULL,
unit_price DECIMAL(14,2) NOT NULL DEFAULT 0,
discount DECIMAL(14,2) NOT NULL DEFAULT 0,
paid_amount DECIMAL(14,2) NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_phone (customer_phone),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
}
$pdo = db();
ensureSchema($pdo);
$errors = [];
$notice = '';
$createdId = 0;
$statuses = ['Order diterima', 'SPK dibuat', 'Produksi berjalan', 'QC / Finishing', 'Barang selesai', 'Invoice / Tagihan'];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'create_order') {
$customerName = trim((string)($_POST['customer_name'] ?? ''));
$customerPhone = preg_replace('/[^0-9+]/', '', (string)($_POST['customer_phone'] ?? ''));
$customerEmail = trim((string)($_POST['customer_email'] ?? ''));
$project = trim((string)($_POST['project_name'] ?? ''));
$category = trim((string)($_POST['category'] ?? ''));
$qty = max(1, (int)($_POST['quantity'] ?? 1));
$deadline = trim((string)($_POST['deadline'] ?? '')) ?: null;
$unitPrice = max(0, (float)($_POST['unit_price'] ?? 0));
$discount = max(0, (float)($_POST['discount'] ?? 0));
$paid = max(0, (float)($_POST['paid_amount'] ?? 0));
foreach ([
'Nama customer' => $customerName,
'Nomor telepon' => $customerPhone,
'Nama pekerjaan' => $project,
'Kategori' => $category,
] as $label => $value) {
if ($value === '') { $errors[] = $label . ' wajib diisi.'; }
}
if ($customerEmail !== '' && !filter_var($customerEmail, FILTER_VALIDATE_EMAIL)) { $errors[] = 'Format email customer tidak valid.'; }
if (!$errors) {
$year = date('y');
$month = date('m');
$orderNo = nextNumber($pdo, 'ORD-' . date('Y') . '-', 'order_no');
$invoiceNo = nextNumber($pdo, 'VI-' . $year . '-', 'invoice_no');
$spkPrefix = str_pad((string)(((int)$pdo->query('SELECT COUNT(*) FROM print_orders')->fetchColumn()) + 1), 2, '0', STR_PAD_LEFT) . '-SPK-' . $month . '-' . date('Y');
$spkNo = $spkPrefix;
$stmt = $pdo->prepare('INSERT INTO print_orders (order_no, spk_no, invoice_no, customer_name, customer_phone, customer_email, project_name, category, quantity, size_info, material, finishing, deadline, operator_name, cs_name, status, progress_note, unit_price, discount, paid_amount) VALUES (:order_no, :spk_no, :invoice_no, :customer_name, :customer_phone, :customer_email, :project_name, :category, :quantity, :size_info, :material, :finishing, :deadline, :operator_name, :cs_name, :status, :progress_note, :unit_price, :discount, :paid_amount)');
$stmt->execute([
':order_no' => $orderNo,
':spk_no' => $spkNo,
':invoice_no' => $invoiceNo,
':customer_name' => $customerName,
':customer_phone' => $customerPhone,
':customer_email' => $customerEmail ?: null,
':project_name' => $project,
':category' => $category,
':quantity' => $qty,
':size_info' => trim((string)($_POST['size_info'] ?? '')) ?: null,
':material' => trim((string)($_POST['material'] ?? '')) ?: null,
':finishing' => trim((string)($_POST['finishing'] ?? '')) ?: null,
':deadline' => $deadline,
':operator_name' => trim((string)($_POST['operator_name'] ?? '')) ?: null,
':cs_name' => trim((string)($_POST['cs_name'] ?? '')) ?: null,
':status' => 'SPK dibuat',
':progress_note' => trim((string)($_POST['progress_note'] ?? 'Order diterima dan SPK siap dicetak.')),
':unit_price' => $unitPrice,
':discount' => $discount,
':paid_amount' => $paid,
]);
$createdId = (int)$pdo->lastInsertId();
$notice = 'Order berhasil dibuat. SPK dan Invoice sudah otomatis tersedia.';
}
} elseif ($action === 'update_status') {
$id = (int)($_POST['id'] ?? 0);
$status = (string)($_POST['status'] ?? '');
$note = trim((string)($_POST['progress_note'] ?? ''));
if ($id > 0 && in_array($status, $statuses, true)) {
$stmt = $pdo->prepare('UPDATE print_orders SET status = :status, progress_note = :note WHERE id = :id');
$stmt->execute([':status' => $status, ':note' => $note, ':id' => $id]);
$notice = 'Status produksi berhasil diperbarui.';
}
}
}
$trackResult = null;
if (isset($_GET['track_order'], $_GET['track_phone'])) {
$trackOrder = trim((string)$_GET['track_order']);
$trackPhone = preg_replace('/[^0-9+]/', '', (string)$_GET['track_phone']);
$stmt = $pdo->prepare('SELECT * FROM print_orders WHERE order_no = :order_no AND customer_phone = :phone LIMIT 1');
$stmt->execute([':order_no' => $trackOrder, ':phone' => $trackPhone]);
$trackResult = $stmt->fetch() ?: false;
}
$orders = $pdo->query('SELECT * FROM print_orders ORDER BY created_at DESC LIMIT 25')->fetchAll();
$totalOrders = (int)$pdo->query('SELECT COUNT(*) FROM print_orders')->fetchColumn();
$activeOrders = (int)$pdo->query("SELECT COUNT(*) FROM print_orders WHERE status NOT IN ('Barang selesai','Invoice / Tagihan')")->fetchColumn();
$finishedOrders = (int)$pdo->query("SELECT COUNT(*) FROM print_orders WHERE status = 'Barang selesai'")->fetchColumn();
$finance = $pdo->query('SELECT COALESCE(SUM(quantity * unit_price - discount),0) AS billed, COALESCE(SUM(paid_amount),0) AS paid FROM print_orders')->fetch();
$receivable = max(0, (float)$finance['billed'] - (float)$finance['paid']);
?>
= e($projectName) ?> — Order, SPK, Produksi & Invoice
= $errors ? e(implode(' ', $errors)) : e($notice) ?>
Sistem Percetakan
Order masuk, SPK, produksi, invoice, dan tracking customer dalam satu alur.
MVP awal untuk Percetakan Kenanga Kreasindo: admin membuat order, sistem menerbitkan SPK dan invoice, produksi memperbarui status, customer mengecek progres memakai nomor order + telepon.
Percetakan
Kenanga Kreasindo
Alamat Perum BCL Jln Kenanga Raya
Email kenangakreasindo@gmail.com
Runtime PHP = e(PHP_VERSION) ?>
Total Order = $totalOrders ?>
Produksi Aktif = $activeOrders ?>
Barang Selesai = $finishedOrders ?>
Piutang = e(rupiah($receivable)) ?>
Penerimaan Order
Buat order + SPK
Produksi & Monitoring
Daftar order terbaru
Lihat order baru
Belum ada order. Buat order pertama untuk menghasilkan SPK, tracking, dan invoice.
Order Customer Pekerjaan Status Tagihan Aksi
= e($order['order_no']) ?> = e(date('d M Y', strtotime($order['created_at']))) ?>
= e($order['customer_name']) ?>= e($order['customer_phone']) ?>
= e($order['project_name']) ?>= e($order['category']) ?> · = (int)$order['quantity'] ?> pcs
= e($order['status']) ?>
= e(rupiah(max(0, $amount))) ?>Bayar = e(rupiah((float)$order['paid_amount'])) ?>
Hasil tracking muncul di sini. Gunakan nomor order dari admin/Kasir.
Order tidak ditemukan. Pastikan nomor order dan telepon sudah benar.
Order
= e($trackResult['order_no']) ?> = e($trackResult['status']) ?>
= e($trackResult['project_name']) ?> — = e($trackResult['category']) ?>, = (int)$trackResult['quantity'] ?> pcs
= nl2br(e((string)$trackResult['progress_note'])) ?>
$s): ?>
= e($s) ?>