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

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

AlamatPerum BCL Jln Kenanga Raya
Emailkenangakreasindo@gmail.com
RuntimePHP
Total Order
Produksi Aktif
Barang Selesai
Piutang

Penerimaan Order

Buat order + SPK

* wajib diisi. Nomor order, SPK, dan invoice otomatis.

Produksi & Monitoring

Daftar order terbaru

Lihat order baru
Belum ada order.Buat order pertama untuk menghasilkan SPK, tracking, dan invoice.
OrderCustomerPekerjaanStatusTagihanAksi
· pcs
Bayar

Customer Portal

Cek progres order

Customer tidak perlu login. Masukkan nomor order dan telepon yang tercatat di order.

Hasil tracking muncul di sini.Gunakan nomor order dari admin/Kasir.
Order tidak ditemukan. Pastikan nomor order dan telepon sudah benar.
Order

, pcs

    $s): ?>