Mengotomatiskan Piutang Usaha dalam ERP Kustom: Pengingat, Laporan Aging, dan Pelacakan Pembayaran

Foto oleh Unsplash
Puppeteer me-render template HTML melalui instance Chrome tanpa kepala (headless), sehingga memberikan kontrol desain penuh tanpa keterbatasan tata letak library PDF khusus. PDF yang dihasilkan disimpan di Google Cloud Storage dan tautan unduhan disertakan dalam email pengiriman ke pelanggan.
Invoice yang menyertakan tautan pembayaran langsung — yang terintegrasi dengan Midtrans, payment gateway lokal — dibayar 40% lebih cepat dibandingkan invoice yang mengharuskan transfer bank manual dengan nomor referensi. Itulah mengapa setiap email pengingat dalam sistem selalu menyertakan tautan pembayaran, terlepas dari tahap pengingatnya.
Untuk pelanggan NET_30, jadwalnya berjalan: pengingat ramah di T-7, pengingat lebih tegas di T-3, pemberitahuan keterlambatan di T+0, tindak lanjut di T+7, dan notifikasi credit hold di T+30 keterlambatan. Nada email berubah dari ramah menjadi formal, dan email T+30 menyalin (CC) manajer akun penjualan untuk mendorong intervensi manual.
Indonesia memiliki tiga zona waktu (WIB, WITA, WIT) sementara waktu server biasanya UTC, sehingga cron job yang diset pukul 08.00 UTC akan mengirimkan email pengingat pada pukul 03.00 dini hari waktu lokal — yang terkesan tidak profesional. Solusinya adalah menyimpan tanggal jatuh tempo sebagai DATE (bukan TIMESTAMP) di PostgreSQL dan secara eksplisit mengonversi ke zona waktu pelanggan saat menghitung status keterlambatan.
Setelah enam bulan di Commsult Indonesia, waktu follow-up manual tim keuangan turun sekitar 70%, tingkat keterlambatan pembayaran turun dari sekitar 45% menjadi 22%, dan waktu penagihan rata-rata membaik dari 38 hari menjadi 26 hari. Keuntungan terbesar adalah visibilitas real-time atas seluruh gambaran piutang, menggantikan rekonsiliasi Excel manual di akhir bulan.

Foto oleh Unsplash
Di sebagian besar UKM Indonesia, manajemen piutang usaha ditangani melalui kombinasi spreadsheet Excel dan follow-up WhatsApp manual. Ketika saya membangun modul AR untuk ERP kustom klien sebagai bagian dari pekerjaan konsultasi ERP freelance saya, tujuannya adalah mengotomatiskan seluruh siklus hidup piutang — dari pembuatan invoice hingga eskalasi keterlambatan — sehingga tim keuangan dapat fokus pada pengecualian daripada follow-up rutin.
Otomatisasi AR yang lengkap mencakup empat tahap: pembuatan dan pengiriman invoice, pengingat pembayaran otomatis pada interval yang ditentukan, deteksi dan eskalasi keterlambatan, dan pencatatan pembayaran dengan rekonsiliasi.
Langkah pertama adalah menghasilkan invoice PDF yang profesional dan mengirimkannya ke pelanggan melalui email. Dalam implementasi NestJS yang saya bangun, saya menggunakan Puppeteer untuk me-render template HTML ke PDF. PDF yang dihasilkan disimpan di Google Cloud Storage dan tautan unduhan disertakan dalam email pengiriman.
Jadwal pengingat dapat dikonfigurasi per syarat pembayaran pelanggan. Untuk pelanggan NET_30: pengingat ramah di T-7, pengingat lebih tegas di T-3, pemberitahuan keterlambatan di T+0, tindak lanjut di T+7, dan notifikasi credit hold di T+30 keterlambatan.
Accounts Receivable Automation Flow
Invoice Created
│
▼
┌──────────────────┐
│ Generate PDF │◄── NestJS PDF service (Puppeteer)
│ & Send Email │──► Customer inbox
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Due Date T-7 │──► Reminder email #1 (automated)
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Due Date T-3 │──► Reminder email #2 (escalation tone)
└────────┬─────────┘
│
▼
┌──────────────────┐ ┌─────────────────────┐
│ Due Date T+0 │─────►│ Mark as OVERDUE │
└────────┬─────────┘ │ Notify AR Manager │
│ └─────────────────────┘
▼
┌──────────────────┐
│ T+7 Overdue │──► Manual follow-up flag
│ T+30 Overdue │──► Credit hold trigger
└──────────────────┘Selalu sertakan tautan pembayaran langsung dalam email pengingat. Dalam pekerjaan konsultasi ERP freelance saya, saya mengintegrasikan payment gateway lokal (Midtrans) sehingga pelanggan dapat membayar langsung dari email. Invoice dengan tautan pembayaran dibayar 40% lebih cepat.
Modul @nestjs/schedule NestJS menyediakan scheduler berbasis cron yang berjalan di dalam proses aplikasi. Saya mengatur scheduler ini untuk menjalankan tugas pengingat AR setiap pagi pukul 08.00 WIB (UTC+7). Tugas ini melakukan kueri untuk invoice yang jatuh tempo dalam 7, 3, dan 0 hari, serta untuk invoice yang sudah terlambat.
Laporan aging mengkategorikan semua invoice yang belum dibayar berdasarkan seberapa terlambatnya: lancar (belum jatuh tempo), 1–30 hari, 31–60 hari, 61–90 hari, dan 90+ hari. Saya membuat laporan ini secara on-demand dan juga pra-komputasi snapshot harian untuk performa.
// NestJS: AR automated reminder scheduler
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, LessThanOrEqual, In } from 'typeorm';
import { Invoice } from './invoice.entity';
import { EmailService } from '../email/email.service';
import { addDays, startOfDay } from 'date-fns';
@Injectable()
export class ArReminderService {
private readonly logger = new Logger(ArReminderService.name);
constructor(
@InjectRepository(Invoice)
private invoiceRepo: Repository<Invoice>,
private emailService: EmailService,
) {}
@Cron(CronExpression.EVERY_DAY_AT_8AM)
async sendReminders() {
const today = startOfDay(new Date());
// T-7: first reminder
const upcomingInvoices = await this.invoiceRepo.find({
where: {
dueDate: addDays(today, 7),
status: In(['SENT', 'PARTIALLY_PAID']),
},
relations: ['customer'],
});
for (const invoice of upcomingInvoices) {
await this.emailService.sendArReminder(invoice, 'REMINDER_7_DAYS');
this.logger.log(`Sent T-7 reminder for invoice ${invoice.invoiceNumber}`);
}
// T+0: mark overdue
const overdueInvoices = await this.invoiceRepo.find({
where: {
dueDate: LessThanOrEqual(today),
status: In(['SENT', 'PARTIALLY_PAID']),
},
relations: ['customer'],
});
for (const invoice of overdueInvoices) {
await this.invoiceRepo.update(invoice.id, { status: 'OVERDUE' });
await this.emailService.sendArReminder(invoice, 'OVERDUE_NOTICE');
}
this.logger.log(
`Processed ${upcomingInvoices.length} reminders, ${overdueInvoices.length} overdue`
);
}
}Ketika pelanggan melakukan pembayaran, staf keuangan mencatatnya di ERP terhadap invoice. Sistem mendukung pembayaran parsial. Ketika invoice mencapai saldo nol, secara otomatis beralih ke status DIBAYAR dan kuitansi dikirim melalui email ke pelanggan.
Berhati-hatilah dengan penanganan zona waktu di scheduler. Indonesia memiliki tiga zona waktu (WIB, WITA, WIT) dan waktu server sering UTC. Selalu simpan tanggal jatuh tempo sebagai DATE (bukan TIMESTAMP) di PostgreSQL dan secara eksplisit konversi ke zona waktu pelanggan saat menghitung status keterlambatan.
Modul AR berkontribusi ke dashboard keuangan dengan tiga metrik utama: Days Sales Outstanding (DSO), tingkat penagihan per periode, dan jumlah terlambat per tier pelanggan. DSO yang meningkat menandakan masalah penagihan sebelum menjadi krisis arus kas.
Setelah menjalankan sistem AR otomatis di produksi untuk klien selama 6 bulan: waktu follow-up manual turun 70%, tingkat keterlambatan turun dari 45% ke 22%, dan waktu penagihan rata-rata meningkat dari 38 hari menjadi 26 hari.