AI assistant for Indonesian water utility (PDAM) accounting systems using SAKEP standards, complete with automated journaling, tax processing, and financial reporting.
This skill has safety concerns that you should review before use. Some patterns were detected that may pose a risk.Safety score: 60/100.
KillerSkills scans all public content for safety. Use caution before installing or executing flagged content.
AI assistant specialized in developing accounting systems for Indonesian water utilities (PDAM, BUMDes Air) following SAKEP (Standar Akuntansi Keuangan Entitas Privat) standards.
This skill guides development of integrated, automated accounting solutions for water utility companies with:
The SAKEP chart of accounts uses a three-level hierarchy:
1. **NO_KEL** (Group Number): Main account category (10, 20, 30, etc.)
2. **NO_REK** (Account Number): Specific account within group (1101, 1102, etc.)
3. **NO_BANTU** (Auxiliary Number): Sub-account detail (10, 20, 30, etc.)
| NO_KEL | NAMA_KEL | Type |
|--------|----------|------|
| 10 | Aktiva Lancar (Current Assets) | Asset |
| 20 | Investasi Jk. Panjang (Long-term Investments) | Asset |
| 30 | Aktiva Tetap (Fixed Assets) | Asset |
| 40 | Aktiva Lain-lain (Other Assets) | Asset |
| 50 | Kewajiban Jk. Pendek (Short-term Liabilities) | Liability |
| 60 | Kewajiban Jk. Panjang (Long-term Liabilities) | Liability |
| 70 | Modal dan Cadangan (Equity & Reserves) | Equity |
| 80 | Pendapatan (Revenue) | Revenue |
| 88 | Pendapatan Diluar Usaha (Non-operating Income) | Revenue |
| 91 | Biaya Sumber Air (Water Source Costs) | Expense |
| 92 | Biaya Pengolahan Air (Water Treatment Costs) | Expense |
| 93 | Biaya Transmisi dan Distribusi (Distribution Costs) | Expense |
| 96 | Biaya Administrasi dan Umum (Admin & General) | Expense |
| 98 | Biaya Diluar Usaha (Non-operating Expenses) | Expense |
Create tables following this structure:
```php
// account_groups
Schema::create('account_groups', function (Blueprint $table) {
$table->string('no_kel', 2)->primary();
$table->string('nama_kel', 100);
$table->enum('type', ['asset', 'liability', 'equity', 'revenue', 'expense']);
$table->timestamps();
});
// accounts
Schema::create('accounts', function (Blueprint $table) {
$table->id();
$table->string('no_kel', 2);
$table->string('no_rek', 4)->unique();
$table->string('nama_rek', 100);
$table->enum('normal_balance', ['D', 'K']); // Debit/Kredit
$table->boolean('is_active')->default(true);
$table->foreign('no_kel')->references('no_kel')->on('account_groups');
$table->timestamps();
});
// account_auxiliaries
Schema::create('account_auxiliaries', function (Blueprint $table) {
$table->id();
$table->string('no_kel', 2);
$table->string('no_rek', 4);
$table->string('no_bantu', 2);
$table->string('nm_bantu', 100);
$table->enum('normal_balance', ['D', 'K']);
$table->foreign('no_rek')->references('no_rek')->on('accounts');
$table->timestamps();
});
// journals
Schema::create('journals', function (Blueprint $table) {
$table->id();
$table->date('transaction_date');
$table->string('reference', 50)->nullable();
$table->text('description');
$table->boolean('is_closed')->default(false);
$table->softDeletes();
$table->timestamps();
});
// journal_details
Schema::create('journal_details', function (Blueprint $table) {
$table->id();
$table->foreignId('journal_id')->constrained()->cascadeOnDelete();
$table->string('no_rek', 4);
$table->string('no_bantu', 2)->nullable();
$table->decimal('debit', 15, 2)->default(0);
$table->decimal('credit', 15, 2)->default(0);
$table->foreign('no_rek')->references('no_rek')->on('accounts');
$table->timestamps();
});
```
When generating code for water sales transactions:
```php
// Automatic journal entry for water sales
public function recordWaterSale($customer, $volume_m3, $tariff, $is_vat_registered = false)
{
$amount = $volume_m3 * $tariff;
$vat = $is_vat_registered ? $amount * 0.11 : 0;
$total = $amount + $vat;
$journal = Journal::create([
'transaction_date' => now(),
'description' => "Penjualan air - {$customer->name}",
'reference' => "SAL-" . now()->format('Ymd') . "-" . $customer->id
]);
// Debit: Piutang Usaha
$journal->details()->create([
'no_rek' => '1301',
'debit' => $total,
'credit' => 0
]);
// Credit: Pendapatan Air
$journal->details()->create([
'no_rek' => '8101',
'no_bantu' => '10',
'debit' => 0,
'credit' => $amount
]);
// Credit: PPN Keluaran (if applicable)
if ($vat > 0) {
$journal->details()->create([
'no_rek' => '5006',
'no_bantu' => '10',
'debit' => 0,
'credit' => $vat
]);
}
}
```
Implement automatic tax calculations:
| Tax Type | Calculation | Account |
|----------|-------------|---------|
| PPN (VAT) | 11% × Taxable Revenue | 5006-10 |
| PPh 21 (Employee Tax) | Progressive rates | 5006-40 |
| PPh 23 (Service Tax) | 2% × Service Purchases | 1402-40 |
```php
public function calculateVAT($taxable_amount)
{
return $taxable_amount * 0.11;
}
public function calculatePPh23($service_amount)
{
return $service_amount * 0.02;
}
```
```php
// Mark bank statement as reconciled
public function reconcileBankStatement($statement_id, $journal_detail_id)
{
BankStatement::find($statement_id)->update([
'reconciled_at' => now(),
'journal_detail_id' => $journal_detail_id
]);
}
```
Generate reports by querying journal details:
```php
// Balance Sheet (Neraca)
public function generateBalanceSheet($period_end)
{
return DB::table('journal_details')
->join('accounts', 'journal_details.no_rek', '=', 'accounts.no_rek')
->join('account_groups', 'accounts.no_kel', '=', 'account_groups.no_kel')
->whereIn('account_groups.type', ['asset', 'liability', 'equity'])
->where('journals.transaction_date', '<=', $period_end)
->selectRaw('
accounts.no_rek,
accounts.nama_rek,
SUM(journal_details.debit) - SUM(journal_details.credit) as balance
')
->groupBy('accounts.no_rek', 'accounts.nama_rek')
->get();
}
// Income Statement (Laba Rugi)
public function generateIncomeStatement($period_start, $period_end)
{
return DB::table('journal_details')
->join('accounts', 'journal_details.no_rek', '=', 'accounts.no_rek')
->join('account_groups', 'accounts.no_kel', '=', 'account_groups.no_kel')
->whereIn('account_groups.type', ['revenue', 'expense'])
->whereBetween('journals.transaction_date', [$period_start, $period_end])
->selectRaw('
accounts.no_rek,
accounts.nama_rek,
SUM(journal_details.credit) - SUM(journal_details.debit) as amount
')
->groupBy('accounts.no_rek', 'accounts.nama_rek')
->get();
}
```
```php
public function closePeriod($period_end)
{
// Calculate net income
$net_income = $this->calculateNetIncome($period_end);
// Transfer to retained earnings
$journal = Journal::create([
'transaction_date' => $period_end,
'description' => 'Penutupan periode - Transfer laba/rugi',
'is_closed' => true
]);
// Debit revenue accounts, Credit expense accounts,
// Net to Retained Earnings (7011)
// Lock all journals in period
Journal::where('transaction_date', '<=', $period_end)
->update(['is_closed' => true]);
}
```
```php
// Define roles using Spatie Permission
Role::create(['name' => 'super_admin']);
Role::create(['name' => 'akuntan']);
Role::create(['name' => 'kasir']);
Role::create(['name' => 'direktur']);
// Assign permissions
Permission::create(['name' => 'view journals']);
Permission::create(['name' => 'create journals']);
Permission::create(['name' => 'close period']);
Permission::create(['name' => 'manage reports']);
```
Always enforce these rules:
```php
// Journal must balance
public function validateJournalBalance($journal_id)
{
$totals = JournalDetail::where('journal_id', $journal_id)
->selectRaw('SUM(debit) as total_debit, SUM(credit) as total_credit')
->first();
if ($totals->total_debit != $totals->total_credit) {
throw new Exception('Journal tidak balance: Debit ≠ Kredit');
}
}
// Prevent modifying closed journals
public function ensureNotClosed($journal_id)
{
if (Journal::find($journal_id)->is_closed) {
throw new Exception('Tidak dapat mengubah jurnal yang sudah ditutup');
}
}
```
1. **Setup**: Configure account groups, accounts, and auxiliaries
2. **Daily Operations**: Record water sales, purchases, payroll
3. **Automation**: Auto-generate journals from meter readings, bank feeds
4. **Reconciliation**: Match bank statements with journals
5. **Period Adjustments**: Accrue expenses, calculate depreciation
6. **Tax Processing**: Calculate PPN, PPh, generate e-Faktur
7. **Reporting**: Generate Balance Sheet, Income Statement, Cash Flow
8. **Period Close**: Transfer net income, lock journals
9. **Compliance**: Export to PDF/Excel for DJP/BPK/Local Government
When creating custom Artisan commands:
```bash
php artisan sakep:journal "Penjualan Air Bulan Juni"
php artisan sakep:close-period 2024-06
php artisan sakep:generate-efaktur --month=06 --year=2024
php artisan sakep:report --type=neraca --format=pdf
php artisan sakep:reconcile-bank --statement-id=123
```
Implement these key metrics:
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/sakep-accounting-system-guide/raw