Ringkasan Utama
Mengotomatiskan piutang usaha di ERP kustom bermuara pada empat pekerjaan yang saling terhubung: kirim setiap invoice beserta tautan pembayaran, jalankan jadwal pengingat yang memperhitungkan zona waktu dari T-7 sampai T+30, kelompokkan invoice terbuka ke dalam laporan aging, dan pantau DSO. Dengan begitu, penagihan lewat spreadsheet dan WhatsApp berubah menjadi konfigurasi, dan tim keuangan tinggal menangani pengecualian.
Di banyak UKM Indonesia yang pernah saya tangani, pengelolaan piutang usaha dilakukan dengan kombinasi spreadsheet Excel dan follow-up WhatsApp manual. Tim keuangan mencatat siapa berutang berapa, mengirim pengingat satu per satu, dan menghabiskan berjam-jam setiap bulan untuk mencocokkan bukti pembayaran dengan invoice yang belum lunas. Ketika saya membangun modul AR untuk ERP kustom klien sebagai bagian dari pekerjaan konsultasi ERP lepas saya, tujuannya adalah mengotomatiskan seluruh siklus piutang — dari pembuatan invoice hingga eskalasi keterlambatan — sehingga tim keuangan bisa fokus pada pengecualian, bukan follow-up rutin.
Siklus Hidup AR: Apa yang Perlu Diotomatiskan
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.
Pembuatan Invoice dan Pengiriman PDF
Langkah pertama adalah membuat invoice PDF yang profesional dan mengirimkannya ke pelanggan melalui email. Dalam implementasi NestJS yang saya bangun, saya memakai Puppeteer untuk me-render template HTML menjadi PDF — cara ini memberi kontrol desain penuh tanpa keterbatasan library PDF. Template HTML menarik data dari entitas invoice dan di-render lewat instance Chrome headless. PDF yang dihasilkan disimpan di Google Cloud Storage dan tautan unduhannya disertakan di email pengiriman. Bagi penjual PKP, invoice ini bukan faktur pajak: Pasal 26 PP 44/2022 juga mewajibkan Faktur Pajak dibuat saat penyerahan melalui sistem DJP (Coretax sejak 1 Januari 2025), sehingga catatan AR menyimpan nomornya.
Desain Jadwal Pengingat
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 di email pengingat. Dalam pekerjaan konsultasi ERP lepas saya, saya mengintegrasikan payment gateway lokal (Midtrans): pelanggan membayar dari email lewat virtual account, QRIS, e-wallet, atau kartu, dan Midtrans mengirim notifikasi ke ERP, sehingga tidak ada yang perlu mencocokkan transfer berdasarkan nomor referensi. Midtrans hanya menerima setiap order_id satu kali, jadi buat satu tautan per invoice dan pakai ulang di setiap pengingat.
Mengimplementasikan Scheduler di NestJS
Modul @nestjs/schedule di NestJS menyediakan scheduler berbasis cron yang berjalan di dalam proses aplikasi. Saya mengaturnya agar menjalankan job pengingat AR setiap pagi pukul 08.00 WIB (UTC+7) dengan memberikan timeZone: 'Asia/Jakarta' pada decorator @Cron; tanpa opsi itu, library cron di bawahnya memakai waktu lokal server. Job ini mencari invoice yang jatuh tempo dalam 7, 3, dan 0 hari, serta invoice yang sudah terlambat. Untuk setiap invoice yang cocok, job mengirim template email yang sesuai, memperbarui status invoice, dan mencatat tindakan itu ke audit trail.
Pembuatan Laporan Aging
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, { timeZone: 'Asia/Jakarta' })
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`
);
}
}Pencatatan Pembayaran dan Rekonsiliasi
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 (UTC+7), WITA (UTC+8), dan WIT (UTC+9) — sementara waktu server sering memakai UTC. Cron yang dimaksudkan untuk pukul 08.00 tetapi berjalan dalam UTC akan menyala pukul 15.00 WIB, 16.00 WITA, dan 17.00 WIT, menjelang akhir jam kerja pelanggan, dan antara pukul 17.00 hingga tengah malam UTC, tanggal UTC sudah tertinggal satu hari dari WIB. Selalu simpan tanggal jatuh tempo sebagai DATE (bukan TIMESTAMP) di PostgreSQL dan konversikan secara eksplisit ke zona waktu pelanggan saat menghitung status keterlambatan.
Pelaporan dan Integrasi Dashboard
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.
Hasil Setelah 6 Bulan Produksi
Setelah sistem AR otomatis berjalan di produksi untuk satu klien selama enam bulan, waktu tim keuangan untuk follow-up manual jauh berkurang, lebih sedikit invoice yang lewat jatuh tempo, dan waktu penagihan menjadi lebih singkat. Persentase dari satu klien tidak bisa dipindahkan begitu saja ke bisnis Anda, jadi catat DSO, porsi invoice yang dibayar terlambat, dan jam follow-up mingguan sebelum go-live, lalu bandingkan. Keuntungan terbesarnya bukan otomatisasi itu sendiri, melainkan visibilitas — manajer keuangan dapat melihat gambaran piutang secara real time, bukan menyusunnya manual di Excel setiap akhir bulan.
Sumber & Bacaan Lebih Lanjut
- NestJS Docs — Task Scheduling with @nestjs/schedule
- TypeORM — Official Documentation for PostgreSQL integration
- Midtrans — Create Payment Link API reference
- cron (kelektiv/node-cron) — timeZone option, default local time
- JDIH BPK — PP No. 44 Tahun 2022 (Article 26: Faktur Pajak)
- DJP — Keterangan Tertulis Implementasi Coretax DJP

