Cara yang bisa diandalkan untuk menghasilkan laporan Excel bergaya dari Delphi adalah memulainya dari workbook yang sudah dibuat oleh seorang designer. Seseorang di bagian finance menata tata letak invoice di Excel: logo, header kolom, border pada detail band, baris totals yang tebal, format mata uang. Kode Anda membuka file itu, memasukkan data live ke sel-sel yang sudah dicadangkan designer untuknya, lalu menyimpan hasilnya. Tampilannya milik mereka; angkanya milik Anda. HotXLS, pustaka native Delphi dan C++Builder yang membaca dan menulis workbook XLS dan XLSX tanpa mengendalikan Excel, memberi Anda tiga operasi yang dibutuhkan pendekatan ini: mencari sel berdasarkan teksnya, menyalin sebuah rentang beserta style dan formulanya utuh, dan menyisipkan baris agar segala sesuatu di bawahnya ikut bergeser turun bersama data
Satu aturan yang membedakan generator yang tetap bertahan terhadap perubahan template dari generator yang rusak pada perubahan pertama adalah: jangan pernah mengalamatkan sel dengan angka baris dan kolom secara literal. Template adalah dokumen yang diedit orang lain. Tim finance menambahkan baris pajak, menaikkan tinggi baris logo, menyusun ulang blok alamat, dan format file sama sekali tidak membantu Anda: penyimpanan BIFF atau OOXML tetap berhasil terlepas apakah baris 10 masih berarti sama seperti kuartal lalu. Sebuah generator yang menulis baris detail pertama ke baris 10 yang di-hard-code akan, pada kali pertama seseorang menyisipkan sebuah blok di atas bagian detail, mencap item baris di atas sel yang salah dan menjumlahkan rentang totals yang sudah tidak lagi mencakup data. Tidak ada apa pun yang melempar error, setiap penyimpanan mengembalikan sukses, dan satu-satunya sinyal adalah pelanggan yang menyadari sebuah invoice yang keliru
Jangkarkan Setiap Koordinat ke Token Placeholder
Perbaikannya adalah membuat template membawa koordinatnya sendiri. Designer menulis token seperti {{CUSTOMER}}, {{DATE}}, dan {{DETAIL_START}} ke dalam sel-sel yang harus disentuh generator, dan generator menghitung setiap posisi saat runtime dari tempat ia menemukan token-token itu. Perubahan layout tidak lagi menjadi masalah, karena token itu ikut berpindah bersama sel tempatnya berada. Separuh kedua dari kontrak ini adalah aturan kegagalan: jika sebuah token wajib hilang, job harus berhenti sebelum satu pun data pelanggan sampai ke file. Template yang sudah melenceng seharusnya menghasilkan tiket job yang gagal, bukan dokumen yang terkirim
Menemukan Token: FindText dan ReplaceText
Kedua keluarga class HotXLS sama-sama mengekspos pencarian tingkat worksheet. FindText mengembalikan baris dan kolom dari sel pertama yang teksnya cocok, dengan sebuah overload yang menambahkan sensitivitas huruf besar/kecil. ReplaceText menukar setiap kemunculan dan mengembalikan berapa banyak yang diubahnya. Keduanya mencakup dua jenis token yang biasanya Anda miliki. Sebuah anchor tunggal seperti nama pelanggan Anda temukan sekali lalu tulis di sebelahnya; sebuah token yang seharusnya muncul tepat satu kali, seperti tanggal laporan, Anda ganti lalu periksa jumlahnya. Pada sisi XLSX, sebuah pengisian yang menjangkarkan dirinya seperti ini bentuknya seperti berikut:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
R, C: Integer;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('invoice-template.xlsx') <> 1 then
raise Exception.Create('Cannot open invoice template');
Sheet := Book.Sheets[0]; // TXLSXSheets.Items berbasis 0
if not Sheet.FindText('{{CUSTOMER}}', R, C) then
raise Exception.Create('Template drift: {{CUSTOMER}} anchor missing');
Sheet.Cells[R, C].Value := 'ACME Corp';
if Sheet.ReplaceText('{{DATE}}',
FormatDateTime('yyyy-mm-dd', Date)) = 0 then
raise Exception.Create('Template drift: {{DATE}} token missing');
// ekspansi detail dan penyimpanan menyusul di bawah
finally
Book.Free;
end;
end;
Dua detail penting di sini. Pertama, FindText dan ReplaceText mencocokkan nilai teks sebuah sel; sebuah token yang tertanam di dalam string formula tidak terlihat oleh keduanya, sehingga token placeholder harus berada di sel biasa, tidak pernah di dalam formula. Kedua, jumlah penggantian adalah detektor drift Anda. Template yang seharusnya mengandung tepat satu token {{DATE}} tetapi melaporkan nol penggantian berarti sudah diedit, dan memunculkan exception pada saat itulah yang mengubah drift layout yang senyap menjadi kegagalan yang terlihat
Mengkloning Baris Detail Tanpa Kehilangan Style atau Formula
Bagian detail sebuah invoice bertumbuh mengikuti data. Menulis nilai langsung ke baris kosong di bawah baris sampel akan membuang semua yang telah disiapkan designer: border, format angka, formula per baris. Pola yang mempertahankan semua itu adalah menyisakan satu baris sampel yang telah diformat sepenuhnya di dalam template dan mengkloningnya untuk setiap item. CopyRange menduplikasi style dan formula dalam satu panggilan, setelah itu generator hanya menimpa sel-sel nilainya saja
const
DetailRow = 10; // baris sampel yang sudah diformat di dalam template
var
I: Integer;
begin
// Buka ruang sebelum blok totals lebih dulu, agar rentang SUM
// di bawah detail band ikut merentang bersama data.
if Length(Items) > 1 then
Sheet.InsertRows(DetailRow + 1, Length(Items) - 1);
for I := 0 to High(Items) do
begin
if I > 0 then // kloning style + formula dari baris sampel
Sheet.CopyRange(DetailRow, 1, DetailRow, 5, DetailRow + I, 1);
Sheet.Cells[DetailRow + I, 1].Value := Items[I].Name;
Sheet.Cells[DetailRow + I, 2].Value := Items[I].Qty;
Sheet.Cells[DetailRow + I, 3].Value := Items[I].UnitPrice;
Sheet.Cells[DetailRow + I, 4].Formula :=
Format('B%d*C%d', [DetailRow + I, DetailRow + I]); // tanpa awalan '='
end;
end;
Perhatikan penetapan formula ini baik-baik. Properti Formula pada XLSX menerima ekspresi tanpa tanda sama dengan di depan, sementara facade XLS mengharapkan '=B10*C10' ditetapkan melalui Value. Mencampur kedua konvensi itu adalah kesalahan porting paling umum antara kedua keluarga class, dan kegagalannya tanpa keluhan apa pun: selnya hanya menyimpan string literal yang oleh Excel ditampilkan sebagai teks. Jika template menghiasi detail band dengan baris judul yang digabung (merged), ingat bahwa hanya sel paling kiri-atas dari sebuah area gabungan yang membawa nilai. Aturan layout dalam artikel pendamping tentang sel gabungan dalam template laporan berbasis layout menjelaskan mengapa region gabungan seharusnya sama sekali berada di luar detail band
Apa yang Dipindahkan InsertRows, dan Apa yang Ditinggalkannya
Menyisipkan baris sebelum blok totals itulah yang menjaga rentang SUM tetap merentang seiring bertumbuhnya bagian detail. Pada sisi XLSX, InsertRows membawa turun daftar panjang struktur yang bergantung bersama sel-selnya: rentang gabungan, tinggi baris, hyperlink, komentar, frozen pane, rentang autofilter, conditional format, validasi data, tabel, defined name, serta anchor gambar dan chart. Ada satu batas dalam daftar itu yang layak dihafal. Penulisan ulang formula hanya menjangkau referensi di dalam sheet yang sama. Sebuah formula pada sheet ringkasan yang menunjuk ke region yang dipindahkan tetap mempertahankan koordinat lamanya dan diam-diam membaca sel yang salah, itulah sebabnya totals yang ditarik lintas sheet lebih aman diungkapkan melalui nama tingkat workbook. Artikel pendamping tentang defined name dan formula lintas sheet membahas pola tersebut secara menyeluruh
Format XLS lawas menarik garis batas di tempat yang lebih keras. HotXLS mempertahankan pivot table, query table, dan koneksi data eksternal di dalam file BIFF sebagai blok byte mentah. Semua itu bertahan tanpa berubah melalui open dan save, tetapi tidak dimodelkan, sehingga penyisipan baris tidak pernah menyentuhnya. Sebuah template yang menaruh pivot table di bawah blok detail yang terus bertumbuh akan tersimpan tanpa peringatan apa pun sementara persegi sumber pivot itu melenceng menjauh dari data. Jalan keluarnya bersifat struktural, bukan defensif: jaga agar konten pivot dan query tetap berada di sheet yang tidak pernah disisipi generator, dan kebasian data itu tidak akan pernah terjadi
Rekalkulasi Sebelum Pengiriman, atau Ketahui Mengapa Anda Melewatinya
HotXLS tidak mengevaluasi formula selama SaveAs. Ketika seseorang membuka file itu, Excel merekalkulasi semuanya (facade XLS mengekspos CalculationMode dan RecalcOnSave jika Anda perlu mengaturnya), sehingga laporan yang ditujukan ke inbox manusia tidak membutuhkan apa pun lagi dari Anda. Gambarannya berubah begitu workbook itu memberi makan program lain. Ekspor CSV menuliskan formula sebagai teks literalnya dan tidak pernah menghitungnya, dan parser hilir mana pun yang memercayai nilai cache akan membaca angka yang basi atau kosong. Untuk jalur-jalur itu, hitung di server dengan Calculate, yang mengevaluasi sebuah ekspresi sembarang terhadap workbook yang sudah dimuat dan mengembalikan hasilnya:
var
Total: Variant;
LastDetail: Integer;
begin
LastDetail := DetailRow + Length(Items) - 1;
Total := Book.Calculate(Format('SUM(Invoice!D%d:D%d)',
[DetailRow, LastDetail]));
if (not VarIsNumeric(Total)) or
(Abs(Total - ExpectedTotal) > 0.005) then
raise Exception.Create('Invoice total does not match the order record');
if Book.SaveAs('invoice-2026-0611.xlsx') <> 1 then
raise Exception.Create('Save failed: check output path and permissions');
end;
Memeriksa total hasil perhitungan terhadap catatan order sebelum penyimpanan adalah asuransi murah dengan hasil yang bagus. Ini mengubah invoice yang salah menjadi job yang gagal. Seorang operator bisa mencoba ulang job yang gagal dalam hitungan detik; sebuah invoice yang salah yang sudah berada di kotak surat pelanggan menghabiskan biaya berupa permintaan maaf dan koreksi dari seorang account manager
Dua Keluarga Class, Satu Algoritma
Logika yang sama diportabel antar format, tetapi bukan kode yang sama. TXLSWorkbook untuk .xls lawas berbasis interface dan reference-counted, dengan pengindeksan sheet berbasis 1, dan Anda tidak pernah membebaskannya secara manual. TXLSXWorkbook untuk .xlsx adalah objek biasa yang harus Anda bebaskan dalam try..finally, dengan pengindeksan sheet berbasis 0 dan konvensi formula yang ditunjukkan di atas. FindText, ReplaceText, CopyRange, dan InsertRows sama-sama tersedia di kedua sisi, sehingga bentuk anchor-clone-recalculate ini terbawa dengan bersih. Saran praktisnya adalah berkomitmen pada satu format per pipeline, atau menyembunyikan kedua lifecycle objek itu di balik adapter tipis buatan Anda sendiri, alih-alih menyebarkan perbedaannya ke seluruh generator
Ukuran jarang menjadi masalah untuk jenis laporan yang dihasilkan pola ini. Mengkloning satu baris bergaya beberapa ribu kali bukan apa-apa bagi hardware masa kini. Jalur penyimpanan baru menjadi bottleneck ketika sebuah detail band mencapai enam digit baris, dan pada titik itu menyetel StreamingWrite mengirim XML worksheet langsung ke paket output alih-alih membufernya; artikel tentang streaming write untuk job batch server membahas kapan pertukaran itu layak dilakukan. Chart berperilaku seperti bagian layout lainnya: pada sisi XLSX, baik anchor chart maupun referensi series-nya sama-sama berpindah ketika InsertRows berjalan di atasnya, sehingga sebuah chart di bawah baris totals tetap terikat pada data yang benar, sementara pada sisi XLS chart berada di chart sheet-nya sendiri dan, seperti pivot table, tidak pernah bergeser. Itu satu alasan lagi untuk menjaga sheet presentasi tetap bersih dari sheet yang diperluas generator
Pendekatan anchor-clone-recalculate ini membiarkan seorang designer memiliki kendali atas tampilan sebuah workbook sementara kode Anda memiliki kendali atas apa yang dikatakannya, dan itu biasanya yang membuat output Excel hasil generate layak dipertahankan. Panggilan pencarian, penyalinan, dan penyisipan yang ditunjukkan di sini, bersama mesin formula yang digunakan untuk pemeriksaan total sebelum pengiriman, disertakan dalam HotXLS Delphi Component untuk Delphi dan C++Builder