// State Management const state = { currentModule: 'dashboard', currentSubModule: null, locationFilter: 'all', data: { financeAccounting: { accountsPayable: { invoices: [ { invoice_id: 'INV-AP-001', vendor: 'PT Pakan Berkah', vendor_id: 'VEN-001', po_number: 'PO-2025-001', invoice_no: 'INV/PKB/2025/001', invoice_date: '2025-11-01', due_date: '2025-11-15', amount: 750000000, currency: 'IDR', status: 'Approved', payment_status: 'Unpaid', grn_matched: true }, { invoice_id: 'INV-AP-002', vendor: 'CV Ternak Maju', vendor_id: 'VEN-002', po_number: 'PO-2025-002', invoice_no: 'INV/TM/2025/002', invoice_date: '2025-11-03', due_date: '2025-11-17', amount: 4200000000, currency: 'IDR', status: 'In Transit', payment_status: 'Unpaid', grn_matched: false }, { invoice_id: 'INV-AP-003', vendor: 'PT Medika Veteriner', vendor_id: 'VEN-003', po_number: 'PO-2025-003', invoice_no: 'INV/MV/2025/003', invoice_date: '2025-10-28', due_date: '2025-11-11', amount: 125000000, currency: 'IDR', status: 'Received', payment_status: 'Unpaid', grn_matched: true }, { invoice_id: 'INV-AP-004', vendor: 'PT Pakan Berkah', vendor_id: 'VEN-001', po_number: 'PO-2025-004', invoice_no: 'INV/PKB/2025/004', invoice_date: '2025-10-20', due_date: '2025-11-03', amount: 450000000, currency: 'IDR', status: 'Received', payment_status: 'Paid', grn_matched: true, paid_date: '2025-11-02' }, { invoice_id: 'INV-AP-005', vendor: 'CV Ternak Maju', vendor_id: 'VEN-002', po_number: 'PO-2025-005', invoice_no: 'INV/TM/2025/005', invoice_date: '2025-09-15', due_date: '2025-09-29', amount: 320000000, currency: 'IDR', status: 'Received', payment_status: 'Overdue', grn_matched: true, days_overdue: 41 } ], summary: { total_payable: 5845000000, paid: 450000000, unpaid: 5395000000, overdue: 320000000 } }, accountsReceivable: { invoices: [ { invoice_id: 'INV-AR-001', customer: 'PT Daging Prima', customer_id: 'CUST-001', invoice_no: 'INV/DP/2025/001', invoice_date: '2025-11-01', due_date: '2025-11-15', amount: 3200000000, currency: 'IDR', status: 'Delivered', payment_status: 'Unpaid', days_outstanding: 9 }, { invoice_id: 'INV-AR-002', customer: 'CV Karkas Jaya', customer_id: 'CUST-002', invoice_no: 'INV/KJ/2025/002', invoice_date: '2025-10-28', due_date: '2025-11-11', amount: 2100000000, currency: 'IDR', status: 'Delivered', payment_status: 'Unpaid', days_outstanding: 12 }, { invoice_id: 'INV-AR-003', customer: 'PT Daging Prima', customer_id: 'CUST-001', invoice_no: 'INV/DP/2025/003', invoice_date: '2025-10-15', due_date: '2025-10-29', amount: 1800000000, currency: 'IDR', status: 'Delivered', payment_status: 'Paid', paid_date: '2025-10-28' }, { invoice_id: 'INV-AR-004', customer: 'CV Karkas Jaya', customer_id: 'CUST-002', invoice_no: 'INV/KJ/2025/004', invoice_date: '2025-09-20', due_date: '2025-10-04', amount: 1600000000, currency: 'IDR', status: 'Delivered', payment_status: 'Overdue', days_overdue: 37 } ], summary: { total_receivable: 8700000000, collected: 1800000000, outstanding: 6900000000, overdue: 1600000000, dso: 42 } }, assets: { fixedAssets: [ { asset_id: 'ASSET-001', name: 'Gudang Pakan Lampung', category: 'Building', location: 'Lampung Selatan', acquisition_date: '2020-03-15', original_cost: 5000000000, useful_life: 20, accumulated_depreciation: 1250000000, book_value: 3750000000, status: 'Active', depreciation_method: 'Straight Line' }, { asset_id: 'ASSET-002', name: 'Kandang Utama Lampung', category: 'Building', location: 'Lampung Selatan', acquisition_date: '2019-06-10', original_cost: 8000000000, useful_life: 20, accumulated_depreciation: 2400000000, book_value: 5600000000, status: 'Active', depreciation_method: 'Straight Line' }, { asset_id: 'ASSET-003', name: 'Forklift Hidraulis #1', category: 'Equipment', location: 'Lampung Selatan', acquisition_date: '2021-08-20', original_cost: 450000000, useful_life: 10, accumulated_depreciation: 67500000, book_value: 382500000, status: 'Active', depreciation_method: 'Straight Line' }, { asset_id: 'ASSET-004', name: 'Skala Digital Otomatis', category: 'Equipment', location: 'Medan', acquisition_date: '2022-02-14', original_cost: 185000000, useful_life: 5, accumulated_depreciation: 55500000, book_value: 129500000, status: 'Active', depreciation_method: 'Straight Line' }, { asset_id: 'ASSET-005', name: 'Kendaraan Dinas Pickup #1', category: 'Vehicle', location: 'Lampung Selatan', acquisition_date: '2021-11-05', original_cost: 320000000, useful_life: 5, accumulated_depreciation: 96000000, book_value: 224000000, status: 'Active', depreciation_method: 'Straight Line' } ], summary: { total_assets: 16086000000, total_depreciation: 3869000000, net_book_value: 10386000000 } }, cashBank: { bankAccounts: [ { account_id: 'BANK-001', bank_name: 'Bank BCA', account_number: '0012345678', account_type: 'Checking', balance: 5200000000, currency: 'IDR', status: 'Active', last_reconciled: '2025-11-09' }, { account_id: 'BANK-002', bank_name: 'Bank Mandiri', account_number: '0087654321', account_type: 'Savings', balance: 2500000000, currency: 'IDR', status: 'Active', last_reconciled: '2025-11-09' }, { account_id: 'BANK-003', bank_name: 'Bank BNI', account_number: '0011223344', account_type: 'Checking', balance: 1000000000, currency: 'IDR', status: 'Active', last_reconciled: '2025-11-09' } ], cash_on_hand: 50000000, total_liquid_assets: 8750000000 }, budgeting: { budgets: [ { budget_id: 'BUD-2025-001', department: 'Operations - Lampung', category: 'Feed Cost', budgeted_amount: 15000000000, spent_amount: 12450000000, remaining: 2550000000, variance_percent: -17.0, status: 'On Track' }, { budget_id: 'BUD-2025-002', department: 'Operations - Medan', category: 'Feed Cost', budgeted_amount: 8000000000, spent_amount: 6200000000, remaining: 1800000000, variance_percent: -22.5, status: 'On Track' }, { budget_id: 'BUD-2025-003', department: 'Animal Health', category: 'Veterinary & Drugs', budgeted_amount: 2000000000, spent_amount: 1850000000, remaining: 150000000, variance_percent: -7.5, status: 'Caution' }, { budget_id: 'BUD-2025-004', department: 'HR & Payroll', category: 'Employee Salaries', budgeted_amount: 5400000000, spent_amount: 5400000000, remaining: 0, variance_percent: 0, status: 'Fully Spent' }, { budget_id: 'BUD-2025-005', department: 'Logistics', category: 'Transportation', budgeted_amount: 1200000000, spent_amount: 540000000, remaining: 660000000, variance_percent: -55.0, status: 'On Track' } ], summary: { total_budget: 31600000000, total_spent: 26440000000, total_remaining: 5160000000, utilization_percent: 83.7 } }, generalLedger: { accounts: [ { account_code: '1100', account_name: 'Cash & Bank', account_type: 'Asset', balance: 8750000000 }, { account_code: '1200', account_name: 'Accounts Receivable', account_type: 'Asset', balance: 6900000000 }, { account_code: '1300', account_name: 'Inventory', account_type: 'Asset', balance: 18500000000 }, { account_code: '1500', account_name: 'Fixed Assets (Net)', account_type: 'Asset', balance: 10386000000 }, { account_code: '2100', account_name: 'Accounts Payable', account_type: 'Liability', balance: 5395000000 }, { account_code: '2200', account_name: 'Short-term Loans', account_type: 'Liability', balance: 5000000000 }, { account_code: '3100', account_name: 'Equity', account_type: 'Equity', balance: 45236000000 } ] }, reports: { incomeStatement: { period: 'Nov 1 - Nov 9, 2025', revenue: 12100000000, cogs: 8470000000, gross_profit: 3630000000, gross_margin_percent: 30.0, operating_expenses: 1820000000, operating_profit: 1810000000, net_profit: 1627750000 }, balanceSheet: { period: '2025-11-09', total_assets: 44536000000, total_liabilities: 10395000000, total_equity: 45236000000 } } }, livestock: [ { id: 'C001', tag: 'RFID-8450', breed: 'Brahman', weight: 425, age_months: 18, location: 'Lampung-Pen A1', health: 'Good', adg: 1.32 }, { id: 'C002', tag: 'RFID-8451', breed: 'Simmental', weight: 398, age_months: 16, location: 'Lampung-Pen A1', health: 'Good', adg: 1.28 }, { id: 'C003', tag: 'RFID-8452', breed: 'Limousin', weight: 412, age_months: 17, location: 'Lampung-Pen A2', health: 'Under Treatment', adg: 0.95 }, { id: 'C004', tag: 'RFID-8453', breed: 'Brahman', weight: 445, age_months: 19, location: 'Medan-Pen B1', health: 'Good', adg: 1.41 }, { id: 'C005', tag: 'RFID-8454', breed: 'Angus Cross', weight: 388, age_months: 15, location: 'Medan-Pen B1', health: 'Good', adg: 1.22 } ], inventory: [ { sku: 'FEED-001', name: 'Jagung Giling', category: 'Pakan', quantity: 45000, unit: 'kg', location: 'Lampung-WH1', reorder_point: 20000 }, { sku: 'FEED-002', name: 'Konsentrat Protein', category: 'Pakan', quantity: 12000, unit: 'kg', location: 'Lampung-WH1', reorder_point: 5000 }, { sku: 'MED-001', name: 'Vaksin PMK', category: 'Obat', quantity: 450, unit: 'dosis', location: 'Med Storage', reorder_point: 200 }, { sku: 'MED-002', name: 'Antibiotik Broad Spectrum', category: 'Obat', quantity: 85, unit: 'botol', location: 'Med Storage', reorder_point: 50 }, { sku: 'SUPP-001', name: 'Vitamin Premix', category: 'Suplemen', quantity: 2400, unit: 'kg', location: 'Lampung-WH1', reorder_point: 1000 } ], purchaseOrders: [ { po_no: 'PO-2025-001', vendor: 'PT Pakan Berkah', item: 'Jagung Giling', quantity: 50000, unit: 'kg', value: 'IDR 750M', status: 'Approved', delivery_date: '2025-11-15' }, { po_no: 'PO-2025-002', vendor: 'CV Ternak Maju', item: 'Sapi Bakalan', quantity: 200, unit: 'ekor', value: 'IDR 4.2B', status: 'In Transit', delivery_date: '2025-11-12' }, { po_no: 'PO-2025-003', vendor: 'PT Medika Veteriner', item: 'Vaksin & Obat', quantity: 1, unit: 'paket', value: 'IDR 125M', status: 'Pending', delivery_date: '2025-11-20' } ], employees: [ { emp_id: 'EMP-001', name: 'Budi Santoso', position: 'Farm Manager', location: 'Lampung', department: 'Operations', status: 'Active' }, { emp_id: 'EMP-002', name: 'Dr. Siti Rahayu', position: 'Veterinarian', location: 'Lampung', department: 'Animal Health', status: 'Active' }, { emp_id: 'EMP-003', name: 'Ahmad Fauzi', position: 'Warehouse Supervisor', location: 'Lampung', department: 'Logistics', status: 'Active' }, { emp_id: 'EMP-004', name: 'Dewi Lestari', position: 'Finance Manager', location: 'Jakarta', department: 'Finance', status: 'Active' }, { emp_id: 'EMP-005', name: 'Rudi Hermawan', position: 'Procurement Officer', location: 'Jakarta', department: 'Procurement', status: 'Active' } ], healthRecords: [ { mrn: 'MRN-00123', cattle_id: 'C003', date: '2025-11-08', diagnosis: 'Mild Respiratory Infection', treatment: 'Antibiotik 10ml IM', status: 'Under Treatment', vet: 'Dr. Siti Rahayu' }, { mrn: 'MRN-00124', cattle_id: 'C027', date: '2025-11-09', diagnosis: 'Routine Vaccination', treatment: 'Vaksin PMK', status: 'Completed', vet: 'Dr. Siti Rahayu' }, { mrn: 'MRN-00125', cattle_id: 'C089', date: '2025-11-09', diagnosis: 'Lameness', treatment: 'Anti-inflammatory, Rest', status: 'Under Observation', vet: 'Dr. Ahmad Kusuma' } ], vendors: [ { vendor_id: 'VEN-001', name: 'PT Pakan Berkah', category: 'Feed Supplier', otif: '94.5%', rating: 4.5 }, { vendor_id: 'VEN-002', name: 'CV Ternak Maju', category: 'Livestock Supplier', otif: '92.0%', rating: 4.2 }, { vendor_id: 'VEN-003', name: 'PT Medika Veteriner', category: 'Medical Supplies', otif: '96.8%', rating: 4.8 } ], customers: [ { customer_id: 'CUST-001', name: 'PT Daging Prima', type: 'Butcher/Processor', location: 'Jakarta', total_orders: 24, lifetime_value: 'IDR 12.5B' }, { customer_id: 'CUST-002', name: 'CV Karkas Jaya', type: 'Distributor', location: 'Surabaya', total_orders: 18, lifetime_value: 'IDR 8.2B' } ] } }; // Initialize App function init() { setupEventListeners(); loadModule('dashboard'); updateLastSync(); setInterval(updateLastSync, 60000); } // Event Listeners function setupEventListeners() { // Menu toggle document.getElementById('menuToggle').addEventListener('click', () => { const sidebar = document.getElementById('sidebar'); const mainContent = document.getElementById('mainContent'); sidebar.classList.toggle('collapsed'); mainContent.classList.toggle('expanded'); }); // Logo click to dashboard const logoLink = document.querySelector('.logo-link'); if (logoLink) { logoLink.addEventListener('click', (e) => { e.preventDefault(); document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active')); document.querySelector('.nav-item[data-module="dashboard"]').classList.add('active'); loadModule('dashboard'); }); } // Navigation document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', (e) => { e.preventDefault(); const module = item.dataset.module; document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active')); item.classList.add('active'); loadModule(module); }); }); // Location filter document.getElementById('locationFilter').addEventListener('change', (e) => { state.locationFilter = e.target.value; loadModule(state.currentModule); }); // Modal close document.getElementById('modalClose').addEventListener('click', closeModal); document.getElementById('modal').addEventListener('click', (e) => { if (e.target.id === 'modal') closeModal(); }); } // Module Loader function loadModule(module) { state.currentModule = module; const content = document.getElementById('mainContent'); switch(module) { case 'dashboard': content.innerHTML = renderDashboard(); initDashboardCharts(); break; case 'finance': content.innerHTML = renderFinance(); initFinanceCharts(); break; case 'inventory': content.innerHTML = renderInventory(); break; case 'warehouse': content.innerHTML = renderWarehouse(); break; case 'procurement': content.innerHTML = renderProcurement(); break; case 'hr': content.innerHTML = renderHR(); break; case 'crm': content.innerHTML = renderCRM(); initCRMCharts(); break; case 'scm': content.innerHTML = renderSCM(); break; case 'feedlot': content.innerHTML = renderFeedlot(); initFeedlotCharts(); break; case 'bi': content.innerHTML = renderBI(); initBICharts(); break; } setupModuleListeners(); } // Dashboard Module function renderDashboard() { return `
Total Revenue 💰
IDR 45.2B
+12.5% from last month
Total Expenses 📊
IDR 32.8B
+8.3% from last month
Net Profit 📈
IDR 12.4B
+24.1% from last month
Total Cattle 🐄
8,450
+234 this month
Active Employees 👥
342
+8 new hires
Stock Value 📦
IDR 18.5B
+5.2% from last week

Health Alerts

🚨
Sapi C089 dalam perawatan (Medan)
2025-11-10 14:30 - High severity
⚠️
Feed konsentrat di bawah reorder point
2025-11-10 13:45 - Medium severity
💰
AP Rp 320M overdue (INV-AP-005)
2025-11-10 09:00 - High severity

Revenue Trend

Cattle Growth

Recent Activity

PO Created - PO-2025-006 created for PT Pakan Berkah
2025-11-10 10:15
Payment Received - Rp 1.8B from PT Daging Prima (INV-AR-003)
2025-11-09 16:30
Asset Maintenance - Forklift Hidraulis #1 scheduled maintenance
2025-11-08 08:00
New cattle shipment - 200 heads arrived at Medan facility
2025-11-07 14:20

Quick Actions

`; } function renderGeneralLedger() { const accounts = state.data.financeAccounting.generalLedger.accounts; const totalAssets = accounts.filter(a => a.account_type === 'Asset').reduce((sum, a) => sum + a.balance, 0); const totalLiabilities = accounts.filter(a => a.account_type === 'Liability').reduce((sum, a) => sum + a.balance, 0); const totalEquity = accounts.filter(a => a.account_type === 'Equity').reduce((sum, a) => sum + a.balance, 0); return `
Total Assets 📊
${formatCurrency(totalAssets)}
${accounts.filter(a => a.account_type === 'Asset').length} accounts
Total Liabilities 📉
${formatCurrency(totalLiabilities)}
${accounts.filter(a => a.account_type === 'Liability').length} accounts
Total Equity 💼
${formatCurrency(totalEquity)}
${accounts.filter(a => a.account_type === 'Equity').length} accounts
Current Ratio 📈
1.42
Healthy position

Chart of Accounts

${accounts.map(acc => ` `).join('')}
Account Code Account Name Type Balance (IDR)
${acc.account_code} ${acc.account_name} ${acc.account_type} ${formatCurrency(acc.balance)}

Financial Ratios

Debt-to-Equity Ratio
0.23
✓ Low leverage
Working Capital
${formatCurrency(totalAssets - totalLiabilities)}
✓ Strong position

Quick Stats

Total Accounts ${accounts.length}
Asset Accounts ${accounts.filter(a => a.account_type === 'Asset').length}
Liability Accounts ${accounts.filter(a => a.account_type === 'Liability').length}
Equity Accounts ${accounts.filter(a => a.account_type === 'Equity').length}
`; } function renderAccountsPayable() { const ap = state.data.financeAccounting.accountsPayable; const invoices = ap.invoices; return `
Total Payable 📤
${formatCurrency(ap.summary.total_payable)}
${invoices.length} open invoices
Unpaid
${formatCurrency(ap.summary.unpaid)}
${invoices.filter(i => i.payment_status === 'Unpaid').length} invoices
Overdue 🚨
${formatCurrency(ap.summary.overdue)}
${invoices.filter(i => i.payment_status === 'Overdue').length} invoice overdue
Paid This Month
${formatCurrency(ap.summary.paid)}
${invoices.filter(i => i.payment_status === 'Paid').length} invoice paid

AP Aging Analysis

Vendor Invoices

${invoices.map(inv => { const daysOverdue = inv.days_overdue || 0; const statusBadge = inv.payment_status === 'Paid' ? 'status-good' : inv.payment_status === 'Overdue' ? 'status-error' : 'status-warning'; return ` `; }).join('')}
Invoice ID Vendor PO Number Invoice Date Due Date Amount Status Payment Actions
${inv.invoice_id} ${inv.vendor} ${inv.po_number} ${inv.invoice_date} ${inv.due_date} ${formatCurrency(inv.amount)} ${inv.status} ${inv.payment_status}${daysOverdue > 0 ? ` ⚠️` : ''} ${inv.payment_status !== 'Paid' ? '' : ''}
`; } function initDashboardCharts() { // Revenue Chart const revenueCtx = document.getElementById('revenueChart'); if (revenueCtx) { new Chart(revenueCtx, { type: 'line', data: { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov'], datasets: [{ label: 'Revenue (Billion IDR)', data: [32, 35, 38, 36, 39, 41, 40, 42, 43, 44, 45.2], borderColor: '#1FB8CD', backgroundColor: 'rgba(31, 184, 205, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } } } }); } // Cattle Chart const cattleCtx = document.getElementById('cattleChart'); if (cattleCtx) { new Chart(cattleCtx, { type: 'bar', data: { labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov'], datasets: [{ label: 'Total Cattle', data: [7200, 7350, 7500, 7680, 7820, 7950, 8100, 8200, 8300, 8400, 8450], backgroundColor: '#FFC185' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } } } }); } } // Finance Module function renderFinance() { state.currentSubModule = state.currentSubModule || 'gl'; const subModule = state.currentSubModule; return `
${renderFinanceSubModule(subModule)}
`; } function renderFinanceSubModule(subModule) { switch(subModule) { case 'gl': return renderGeneralLedger(); case 'ap': return renderAccountsPayable(); case 'ar': return renderAccountsReceivable(); case 'assets': return renderAssets(); case 'cashbank': return renderCashBank(); case 'budget': return renderBudgeting(); case 'reports': return renderFinanceReports(); default: return renderGeneralLedger(); } } function renderGeneralLedger() { const glData = { accounts: [ { account_code: '1100', account_name: 'Cash & Bank', account_type: 'Asset', balance: 8750000000, prior_balance: 8200000000, change: 550000000 }, { account_code: '1200', account_name: 'Accounts Receivable', account_type: 'Asset', balance: 6900000000, prior_balance: 5400000000, change: 1500000000 }, { account_code: '1300', account_name: 'Inventory', account_type: 'Asset', balance: 18500000000, prior_balance: 17800000000, change: 700000000 }, { account_code: '1500', account_name: 'Fixed Assets (Net)', account_type: 'Asset', balance: 10386000000, prior_balance: 10512000000, change: -126000000 }, { account_code: '2100', account_name: 'Accounts Payable', account_type: 'Liability', balance: 5395000000, prior_balance: 4200000000, change: 1195000000 }, { account_code: '2200', account_name: 'Short-term Loans', account_type: 'Liability', balance: 5000000000, prior_balance: 5000000000, change: 0 }, { account_code: '3100', account_name: 'Equity', account_type: 'Equity', balance: 45236000000, prior_balance: 42112000000, change: 3124000000 } ] }; const accounts = glData.accounts; const totalAssets = accounts.filter(a => a.account_type === 'Asset').reduce((sum, a) => sum + a.balance, 0); const totalLiabilities = accounts.filter(a => a.account_type === 'Liability').reduce((sum, a) => sum + a.balance, 0); const totalEquity = accounts.filter(a => a.account_type === 'Equity').reduce((sum, a) => sum + a.balance, 0); const currentRatio = 1.42; function getTypeBadgeStyle(type) { if (type === 'Asset') return 'background: #E3F2FD; color: #0066CC; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 500;'; if (type === 'Liability') return 'background: #FFEBEE; color: #CC0000; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 500;'; if (type === 'Equity') return 'background: #E8F5E9; color: #008000; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 500;'; return ''; } function formatChange(change) { if (change > 0) return `+${formatCurrency(change)}`; if (change < 0) return `${formatCurrency(change)}`; return `0`; } return `

General Ledger & Chart of Accounts

Financial Position as of 2025-11-10

Total Assets 📊
${formatCurrency(totalAssets)}
${accounts.filter(a => a.account_type === 'Asset').length} accounts
Total Liabilities 📉
${formatCurrency(totalLiabilities)}
${accounts.filter(a => a.account_type === 'Liability').length} accounts
Total Equity 💼
${formatCurrency(totalEquity)}
${accounts.filter(a => a.account_type === 'Equity').length} accounts
Current Ratio 📈
${currentRatio.toFixed(2)}
Healthy position

Chart of Accounts

${accounts.map(acc => ` `).join('')}
Account Code Account Name Account Type Balance Prior Balance Change
${acc.account_code} ${acc.account_name} ${acc.account_type} ${formatCurrency(acc.balance)} ${formatCurrency(acc.prior_balance)} ${formatChange(acc.change)}

Financial Ratios

Debt-to-Equity Ratio
0.23
✓ Low leverage
Working Capital
${formatCurrency(totalAssets - totalLiabilities)}
✓ Strong position

Quick Stats

Total Accounts ${accounts.length}
Asset Accounts ${accounts.filter(a => a.account_type === 'Asset').length}
Liability Accounts ${accounts.filter(a => a.account_type === 'Liability').length}
Equity Accounts ${accounts.filter(a => a.account_type === 'Equity').length}
`; } function showAPInvoiceDetail(invoiceId) { const invoice = state.data.financeAccounting.accountsPayable.invoices.find(i => i.invoice_id === invoiceId); if (invoice) { const content = `

Invoice Information

Invoice ID: ${invoice.invoice_id}
Invoice No: ${invoice.invoice_no}
Vendor: ${invoice.vendor}
PO Reference: ${invoice.po_number}
Invoice Date: ${invoice.invoice_date}
Due Date: ${invoice.due_date}
Amount: ${formatCurrency(invoice.amount)}
Status: ${invoice.status}
Payment Status: ${invoice.payment_status}
GRN Matched: ${invoice.grn_matched ? 'Yes ✓' : 'No ✗'}

3-Way Match Status

✅ PO Match: Verified
${invoice.grn_matched ? '✅' : '⏳'} GRN Match: ${invoice.grn_matched ? 'Verified' : 'Pending'}
✅ Invoice Match: Verified
${invoice.payment_status !== 'Paid' ? '' : ''}
`; showModal('Invoice Details: ' + invoice.invoice_id, content); } } function showARInvoiceDetail(invoiceId) { const invoice = state.data.financeAccounting.accountsReceivable.invoices.find(i => i.invoice_id === invoiceId); if (invoice) { const content = `

Invoice Information

Invoice ID: ${invoice.invoice_id}
Invoice No: ${invoice.invoice_no}
Customer: ${invoice.customer}
Invoice Date: ${invoice.invoice_date}
Due Date: ${invoice.due_date}
Amount: ${formatCurrency(invoice.amount)}
Status: ${invoice.status}
Payment Status: ${invoice.payment_status}
${invoice.days_overdue ? `
Days Overdue: ${invoice.days_overdue}
` : ''} ${invoice.days_outstanding ? `
Days Outstanding: ${invoice.days_outstanding}
` : ''}

Collection Status

${invoice.payment_status === 'Paid' ? 'Payment received and recorded' : invoice.payment_status === 'Overdue' ? 'Follow-up required - payment overdue' : 'Monitoring for payment'}
${invoice.payment_status !== 'Paid' ? '' : ''} ${invoice.payment_status !== 'Paid' ? '' : ''}
`; showModal('Invoice Details: ' + invoice.invoice_id, content); } } function showAssetDetail(assetId) { const asset = state.data.financeAccounting.assets.fixedAssets.find(a => a.asset_id === assetId); if (asset) { const monthlyDepreciation = asset.original_cost / (asset.useful_life * 12); const content = `

Asset Information

Asset ID: ${asset.asset_id}
Name: ${asset.name}
Category: ${asset.category}
Location: ${asset.location}
Acquisition Date: ${asset.acquisition_date}
Status: ${asset.status}
Original Cost: ${formatCurrency(asset.original_cost)}
Useful Life: ${asset.useful_life} years
Method: ${asset.depreciation_method}
Accumulated Depr: ${formatCurrency(asset.accumulated_depreciation)}
Book Value: ${formatCurrency(asset.book_value)}
Monthly Depr: ${formatCurrency(monthlyDepreciation)}

Depreciation Summary

Depreciation Rate: ${((asset.accumulated_depreciation / asset.original_cost) * 100).toFixed(1)}% of original cost
Remaining Life: ${(asset.useful_life - (asset.accumulated_depreciation / (asset.original_cost / asset.useful_life))).toFixed(1)} years
`; showModal('Asset Details: ' + asset.name, content); } } function formatCurrency(amount) { if (amount >= 1000000000) { return 'IDR ' + (amount / 1000000000).toFixed(2) + 'B'; } else if (amount >= 1000000) { return 'IDR ' + (amount / 1000000).toFixed(0) + 'M'; } else { return 'IDR ' + amount.toLocaleString('id-ID'); } } function renderAccountsReceivable() { const ar = state.data.financeAccounting.accountsReceivable; const invoices = ar.invoices; return `
Total Receivable 📥
${formatCurrency(ar.summary.total_receivable)}
${invoices.length} customer invoices
Outstanding
${formatCurrency(ar.summary.outstanding)}
${invoices.filter(i => i.payment_status === 'Unpaid').length} invoices
Overdue 🚨
${formatCurrency(ar.summary.overdue)}
${invoices.filter(i => i.payment_status === 'Overdue').length} invoice overdue
DSO (Days) 📊
${ar.summary.dso}
Days Sales Outstanding

AR Aging Analysis

Customer Invoices

${invoices.map(inv => { const daysOverdue = inv.days_overdue || 0; const statusBadge = inv.payment_status === 'Paid' ? 'status-good' : inv.payment_status === 'Overdue' ? 'status-error' : 'status-warning'; return ` `; }).join('')}
Invoice ID Customer Invoice Date Due Date Amount Status Payment Actions
${inv.invoice_id} ${inv.customer} ${inv.invoice_date} ${inv.due_date} ${formatCurrency(inv.amount)} ${inv.status} ${inv.payment_status}${daysOverdue > 0 ? ` (${daysOverdue}d)` : ''} ${inv.payment_status !== 'Paid' ? '' : ''}
`; } function renderAssets() { const assets = state.data.financeAccounting.assets; const fixedAssets = assets.fixedAssets; return `
Total Assets Value 🏢
${formatCurrency(assets.summary.total_assets)}
Original cost
Net Book Value 💎
${formatCurrency(assets.summary.net_book_value)}
After depreciation
Accumulated Depreciation 📉
${formatCurrency(assets.summary.total_depreciation)}
${((assets.summary.total_depreciation / assets.summary.total_assets) * 100).toFixed(1)}% of original
Active Assets
${fixedAssets.filter(a => a.status === 'Active').length}
Total items

Assets by Category

Fixed Asset Register

${fixedAssets.map(asset => ` `).join('')}
Asset ID Name Category Location Acquisition Date Original Cost Acc. Depreciation Book Value Status
${asset.asset_id} ${asset.name} ${asset.category} ${asset.location} ${asset.acquisition_date} ${formatCurrency(asset.original_cost)} ${formatCurrency(asset.accumulated_depreciation)} ${formatCurrency(asset.book_value)} ${asset.status}
`; } function renderCashBank() { const cb = state.data.financeAccounting.cashBank; const accounts = cb.bankAccounts; return `
Total Liquid Assets 💵
${formatCurrency(cb.total_liquid_assets)}
Cash + Bank
Bank Accounts 🏦
${formatCurrency(cb.total_liquid_assets - cb.cash_on_hand)}
${accounts.length} accounts
Cash on Hand 💰
${formatCurrency(cb.cash_on_hand)}
Physical cash
Last Reconciliation
${accounts[0].last_reconciled}
All accounts

Cash Flow Forecast (30 Days)

Bank Accounts

${accounts.map(acc => ` `).join('')}
Bank Account Number Type Balance Status
${acc.bank_name} ${acc.account_number} ${acc.account_type} ${formatCurrency(acc.balance)} ${acc.status}

Recent Transactions

Customer Payment +IDR 1.8B
2025-11-09 | Bank BCA
Vendor Payment -IDR 750M
2025-11-08 | Bank Mandiri
Payroll Transfer -IDR 450M
2025-11-07 | Bank BCA
Bank Interest +IDR 12.5M
2025-11-05 | Bank Mandiri
`; } function renderBudgeting() { const budgeting = state.data.financeAccounting.budgeting; const budgets = budgeting.budgets; return `
Total Budget 📊
${formatCurrency(budgeting.summary.total_budget)}
FY 2025
Total Spent 💸
${formatCurrency(budgeting.summary.total_spent)}
${budgeting.summary.utilization_percent.toFixed(1)}% utilized
Remaining 💰
${formatCurrency(budgeting.summary.total_remaining)}
${(100 - budgeting.summary.utilization_percent).toFixed(1)}% available
Budget Items 📋
${budgets.length}
${budgets.filter(b => b.status === 'Caution' || b.status === 'Fully Spent').length} need attention

Budget vs Actual by Department

Budget Analysis

${budgets.map(budget => { const statusBadge = budget.status === 'On Track' ? 'status-good' : budget.status === 'Caution' ? 'status-warning' : 'status-error'; return ` `; }).join('')}
Department Category Budgeted Spent Remaining Variance % Status
${budget.department} ${budget.category} ${formatCurrency(budget.budgeted_amount)} ${formatCurrency(budget.spent_amount)} ${formatCurrency(budget.remaining)} ${Math.abs(budget.variance_percent).toFixed(1)}% ${budget.status}
`; } function renderFinanceReports() { const reports = state.data.financeAccounting.reports; const is = reports.incomeStatement; const bs = reports.balanceSheet; return `
Revenue (MTD) 💰
${formatCurrency(is.revenue)}
${is.period}
Gross Profit 📊
${formatCurrency(is.gross_profit)}
${is.gross_margin_percent}% margin
Net Profit 💎
${formatCurrency(is.net_profit)}
${((is.net_profit / is.revenue) * 100).toFixed(1)}% of revenue
Total Assets 🏢
${formatCurrency(bs.total_assets)}
Balance Sheet

Income Statement

Period: ${is.period}
Revenue ${formatCurrency(is.revenue)}
Cost of Goods Sold (${formatCurrency(is.cogs)})
Gross Profit ${formatCurrency(is.gross_profit)}
Operating Expenses (${formatCurrency(is.operating_expenses)})
Operating Profit ${formatCurrency(is.operating_profit)}
Net Profit ${formatCurrency(is.net_profit)}

Balance Sheet

As of: ${bs.period}
ASSETS ${formatCurrency(bs.total_assets)}
Current Assets ${formatCurrency(8750000000 + 6900000000 + 18500000000)}
Fixed Assets (Net) ${formatCurrency(10386000000)}
LIABILITIES ${formatCurrency(bs.total_liabilities)}
EQUITY ${formatCurrency(bs.total_equity)}
Total Liab. + Equity ${formatCurrency(bs.total_liabilities + bs.total_equity)}

Available Reports

📊
Profit & Loss Statement
Monthly/Quarterly/YTD
🏢
Balance Sheet
Assets, Liabilities, Equity
💵
Cash Flow Statement
Operating/Investing/Financing
📤
AP Aging Report
Payables by due date
📥
AR Aging Report
Receivables by due date
⚖️
Trial Balance
All account balances
`; } function initFinanceCharts() { const subModule = state.currentSubModule || 'gl'; if (subModule === 'ap') { const apAgingCtx = document.getElementById('apAgingChart'); if (apAgingCtx) { new Chart(apAgingCtx, { type: 'bar', data: { labels: ['Current', '1-30 Days', '31-60 Days', '60+ Days'], datasets: [{ label: 'Amount (IDR Billion)', data: [4.2, 0.75, 0.125, 0.32], backgroundColor: ['#1FB8CD', '#FFC185', '#D2BA4C', '#B4413C'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } } } }); } } else if (subModule === 'ar') { const arAgingCtx = document.getElementById('arAgingChart'); if (arAgingCtx) { new Chart(arAgingCtx, { type: 'bar', data: { labels: ['Current', '1-30 Days', '31-60 Days', '60+ Days'], datasets: [{ label: 'Amount (IDR Billion)', data: [3.2, 2.1, 0, 1.6], backgroundColor: ['#1FB8CD', '#FFC185', '#D2BA4C', '#B4413C'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } } } }); } } else if (subModule === 'assets') { const assetsCategoryCtx = document.getElementById('assetsCategoryChart'); if (assetsCategoryCtx) { new Chart(assetsCategoryCtx, { type: 'doughnut', data: { labels: ['Building', 'Equipment', 'Vehicle'], datasets: [{ data: [9.35, 0.512, 0.224], backgroundColor: ['#1FB8CD', '#FFC185', '#B4413C'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right' } } } }); } } else if (subModule === 'cashbank') { const cashFlowCtx = document.getElementById('cashFlowChart'); if (cashFlowCtx) { new Chart(cashFlowCtx, { type: 'line', data: { labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'], datasets: [{ label: 'Projected Balance (IDR Billion)', data: [8.75, 9.2, 8.9, 9.5], borderColor: '#1FB8CD', backgroundColor: 'rgba(31, 184, 205, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } } } }); } } else if (subModule === 'budget') { const budgetCtx = document.getElementById('budgetVsActualChart'); new Chart(budgetCtx, { type: 'bar', data: { labels: ['Ops Lampung', 'Ops Medan', 'Animal Health', 'HR Payroll', 'Logistics'], datasets: [ { label: 'Budget (IDR Billion)', data: [15, 8, 2, 5.4, 1.2], backgroundColor: '#B4413C' }, { label: 'Actual (IDR Billion)', data: [12.45, 6.2, 1.85, 5.4, 0.54], backgroundColor: '#1FB8CD' } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top' } } } }); } } // Inventory Module function renderInventory() { const items = state.data.inventory; return `
Total Stock Value 💎
IDR 18.5B
Across all locations
Total Items 📦
${items.length}
Active SKUs
Low Stock Items ⚠️
${items.filter(i => i.quantity < i.reorder_point).length}
Need reorder
Stock Movements 📊
342
This month

Inventory Items

${items.map(item => ` `).join('')}
SKU Name Category Quantity Unit Location Status Actions
${item.sku} ${item.name} ${item.category} ${item.quantity.toLocaleString()} ${item.unit} ${item.location} ${item.quantity < item.reorder_point ? 'Low Stock' : 'In Stock'}
`; } // Warehouse Module function renderWarehouse() { return `
Storage Utilization 📊
78%
Of total capacity
Active Orders 📋
45
Being processed
Picking Tasks
23
Pending completion
Shipments Today 🚚
12
8 completed

Warehouse Zones

🏭
Zone A - Feed Storage
Capacity: 85% | Items: 145
💊
Zone B - Medical Storage
Capacity: 45% | Items: 67
📦
Zone C - General Supplies
Capacity: 62% | Items: 98

Today's Activity

Receipts 8 orders
2,500 items received
Putaway Tasks 15 completed
3 pending
Picks Completed 45 orders
95% accuracy rate
Shipments 12 dispatched
On-time: 100%
`; } // Procurement Module function renderProcurement() { const pos = state.data.purchaseOrders; return `
Open POs 📋
${pos.length}
Total value: IDR 5.1B
Active Vendors 🏢
${state.data.vendors.length}
Avg rating: 4.5/5
Pending Approvals
12
Awaiting review
OTIF Performance
94.5%
On-time in-full

Purchase Orders

${pos.map(po => ` `).join('')}
PO Number Vendor Item Quantity Value Status Delivery Date
${po.po_no} ${po.vendor} ${po.item} ${po.quantity.toLocaleString()} ${po.unit} ${po.value} ${po.status} ${po.delivery_date}

Vendor Performance

${state.data.vendors.map(v => ` `).join('')}
Vendor ID Name Category OTIF % Rating
${v.vendor_id} ${v.name} ${v.category} ${v.otif} ⭐ ${v.rating}/5.0
`; } // HR Module function renderHR() { const employees = state.data.employees; return `
Total Employees 👥
342
+8 this month
Attendance Rate 📅
96.8%
Above target
Open Positions 📢
7
Active recruitment
Training Completion 🎓
87%
This quarter

Employee Directory

${employees.map(emp => ` `).join('')}
Employee ID Name Position Department Location Status
${emp.emp_id} ${emp.name} ${emp.position} ${emp.department} ${emp.location} ${emp.status}
`; } // CRM Module function renderCRM() { const customers = state.data.customers; return `
Total Customers 🤝
${customers.length}
+2 new this month
Active Opportunities 💼
18
IDR 8.5B pipeline
Win Rate 🎯
68%
Last 90 days
Customer Satisfaction
4.7/5
Based on surveys

Sales Pipeline

Customer List

${customers.map(cust => ` `).join('')}
Customer ID Name Type Location Total Orders Lifetime Value
${cust.customer_id} ${cust.name} ${cust.type} ${cust.location} ${cust.total_orders} ${cust.lifetime_value}
`; } function initCRMCharts() { const pipelineCtx = document.getElementById('pipelineChart'); if (pipelineCtx) { new Chart(pipelineCtx, { type: 'doughnut', data: { labels: ['Prospecting', 'Qualification', 'Proposal', 'Negotiation', 'Closed Won'], datasets: [{ data: [12, 8, 6, 4, 10], backgroundColor: ['#1FB8CD', '#FFC185', '#B4413C', '#ECEBD5', '#5D878F'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right' } } } }); } } // SCM Module function renderSCM() { return `
Supply Chain Efficiency
92%
+3% vs last month
On-Time Delivery 🚚
94.5%
Above target
Active Shipments 📦
28
In transit
Perfect Order Rate
96.2%
This quarter

Shipment Tracking

SHIP-2025-0234 In Transit
Lampung → Jakarta | ETA: Nov 12, 2025
SHIP-2025-0235 Delayed
Medan → Surabaya | ETA: Nov 13, 2025
SHIP-2025-0236 Delivered
Lampung → Bandung | Delivered: Nov 10, 2025

Supply Chain KPIs

Order Cycle Time 3.2 days
Cash-to-Cash Cycle 42 days
Inventory Turnover 8.5x
Supply Chain Cost Ratio 18.5%
`; } // Feedlot Operations Module (Most Important) function renderFeedlot() { const cattle = state.data.livestock; const healthRecords = state.data.healthRecords; return `
Total Cattle 🐄
8,450
+234 this month
Average Daily Gain 📈
1.28 kg
Above target (1.2 kg)
Feed Conversion Ratio 🌾
6.2:1
Efficient
Health Rate 💊
99.2%
${healthRecords.filter(h => h.status === 'Under Treatment').length} under treatment
Harvest Ready
145
Target weight reached
Mortality Rate 📊
0.8%
Below industry avg

Weight Growth Trend

Cattle Registry

${cattle.map(c => ` `).join('')}
Cattle ID RFID Tag Breed Weight (kg) Age (months) Location ADG (kg/day) Health Status
${c.id} ${c.tag} ${c.breed} ${c.weight} ${c.age_months} ${c.location} ${c.adg} ${c.health}

Recent Health Records

${healthRecords.map(h => ` `).join('')}
MRN Cattle ID Date Diagnosis Treatment Veterinarian Status
${h.mrn} ${h.cattle_id} ${h.date} ${h.diagnosis} ${h.treatment} ${h.vet} ${h.status}
`; } function initFeedlotCharts() { const weightCtx = document.getElementById('weightChart'); if (weightCtx) { new Chart(weightCtx, { type: 'line', data: { labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5', 'Week 6', 'Week 7', 'Week 8'], datasets: [ { label: 'Actual Weight (kg)', data: [320, 335, 352, 368, 385, 401, 415, 428], borderColor: '#1FB8CD', backgroundColor: 'rgba(31, 184, 205, 0.1)', tension: 0.4 }, { label: 'Target Weight (kg)', data: [320, 332, 344, 356, 368, 380, 392, 404], borderColor: '#B4413C', borderDash: [5, 5], backgroundColor: 'transparent', tension: 0.4 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top' } } } }); } } // BI Module function renderBI() { return `
Revenue Growth 📈
+12.5%
Year over year
Profit Margin 💹
27.4%
+2.1% vs target
ROI 💰
34.8%
On feedlot operations
Efficiency Score
92/100
Industry leading

Revenue by Category

Cost Analysis

Predictive Analytics

🔮 Disease Outbreak Prediction
Low Risk
Based on environmental factors, vaccination coverage, and historical data
Confidence: 87%
📊 Weight Gain Forecast
1.32 kg/day
Expected ADG for next 30 days based on feed quality and weather patterns
Confidence: 92%
🌾 Feed Demand Forecast
52,000 kg
Projected feed requirement for next week across all facilities
Confidence: 89%
💹 Price Trend Prediction
+5.2%
Expected cattle price increase in next quarter based on market analysis
Confidence: 78%

Active Alerts & Recommendations

⚠️ Inventory Alert
Vitamin Premix stock below reorder point. Recommend ordering 5,000 kg.
💡 Optimization Opportunity
Feed cost can be reduced by 8% by switching to alternative supplier without quality impact.
✅ Performance Achievement
Medan facility achieved 98% health rate this month, exceeding target by 3%.
`; } function initBICharts() { const revByCat = document.getElementById('revenueByCategory'); if (revByCat) { new Chart(revByCat, { type: 'pie', data: { labels: ['Cattle Sales', 'Feed Sales', 'Breeding Services', 'Consulting', 'Other'], datasets: [{ data: [65, 20, 8, 5, 2], backgroundColor: ['#1FB8CD', '#FFC185', '#B4413C', '#ECEBD5', '#5D878F'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right' } } } }); } const costAnal = document.getElementById('costAnalysis'); if (costAnal) { new Chart(costAnal, { type: 'doughnut', data: { labels: ['Feed', 'Labor', 'Veterinary', 'Transport', 'Utilities', 'Other'], datasets: [{ data: [45, 25, 12, 8, 6, 4], backgroundColor: ['#DB4545', '#D2BA4C', '#964325', '#944454', '#13343B', '#5D878F'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right' } } } }); } } // Helper Functions function setupModuleListeners() { // Sub-navigation listeners document.querySelectorAll('.sub-nav-item').forEach(item => { item.addEventListener('click', (e) => { if (state.currentModule === 'finance') { e.preventDefault(); document.querySelectorAll('.sub-nav-item').forEach(i => i.classList.remove('active')); item.classList.add('active'); state.currentSubModule = item.dataset.subnav; const subContent = document.getElementById('financeSubContent'); if (subContent) { subContent.innerHTML = renderFinanceSubModule(state.currentSubModule); initFinanceCharts(); } } else { document.querySelectorAll('.sub-nav-item').forEach(i => i.classList.remove('active')); item.classList.add('active'); const subModule = item.dataset.subnav; showToast(`Switched to ${item.textContent}`); } }); }); } function showModal(title, content) { const modal = document.getElementById('modal'); const modalTitle = document.getElementById('modalTitle'); const modalBody = document.getElementById('modalBody'); modalTitle.textContent = title; modalBody.innerHTML = `

${content}

`; modal.classList.add('active'); } function closeModal() { document.getElementById('modal').classList.remove('active'); } function showToast(message) { const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toastMessage'); toastMessage.textContent = message; toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); }, 3000); } function updateLastSync() { const lastSyncEl = document.getElementById('lastSync'); if (lastSyncEl) { const now = new Date(); lastSyncEl.textContent = now.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit' }); } } function showInventoryDetail(sku) { const item = state.data.inventory.find(i => i.sku === sku); if (item) { const content = `
SKU: ${item.sku}
Name: ${item.name}
Category: ${item.category}
Quantity: ${item.quantity.toLocaleString()} ${item.unit}
Location: ${item.location}
Reorder Point: ${item.reorder_point.toLocaleString()} ${item.unit}
Status: ${item.quantity < item.reorder_point ? 'Low Stock - Reorder Required' : 'In Stock'}
`; showModal('Item Details: ' + item.name, content); } } function showCattleDetail(id) { const cattle = state.data.livestock.find(c => c.id === id); if (cattle) { const content = `

Individual Cattle Profile

Cattle ID: ${cattle.id}
RFID Tag: ${cattle.tag}
Breed: ${cattle.breed}
Weight: ${cattle.weight} kg
Age: ${cattle.age_months} months
Location: ${cattle.location}
ADG: ${cattle.adg} kg/day
Health Status: ${cattle.health}

Weight History

Last 8 weeks of weight measurements

Entry weights would be displayed here in a chart or table format

Health Records

Vaccination history, treatments, and medical notes

Medical history for this animal would be displayed here
`; showModal('Cattle Profile: ' + cattle.id, content); } } // AI Chatbot Logic const chatbot = { isOpen: false, conversationHistory: [], init() { this.setupEventListeners(); this.showWelcomeNotification(); }, setupEventListeners() { const toggleBtn = document.getElementById('chatbotToggle'); const closeBtn = document.getElementById('chatCloseBtn'); const minimizeBtn = document.getElementById('chatMinimizeBtn'); const clearBtn = document.getElementById('chatClearBtn'); const sendBtn = document.getElementById('chatbotSendBtn'); const input = document.getElementById('chatbotInput'); toggleBtn.addEventListener('click', () => this.toggleChat()); closeBtn.addEventListener('click', () => this.closeChat()); minimizeBtn.addEventListener('click', () => this.closeChat()); clearBtn.addEventListener('click', () => this.clearConversation()); sendBtn.addEventListener('click', () => this.sendMessage()); input.addEventListener('keypress', (e) => { if (e.key === 'Enter') this.sendMessage(); }); // Quick action buttons document.querySelectorAll('.quick-action-btn').forEach(btn => { btn.addEventListener('click', (e) => { const action = e.target.dataset.action; this.handleQuickAction(action); }); }); // Suggestion buttons document.querySelectorAll('.suggestion-btn').forEach(btn => { btn.addEventListener('click', (e) => { const question = e.target.textContent; this.askQuestion(question); }); }); }, showWelcomeNotification() { const badge = document.getElementById('chatNotificationBadge'); badge.style.display = 'block'; }, toggleChat() { const widget = document.getElementById('chatbotWidget'); const badge = document.getElementById('chatNotificationBadge'); this.isOpen = !this.isOpen; widget.classList.toggle('active'); if (this.isOpen) { badge.style.display = 'none'; document.getElementById('chatbotInput').focus(); } }, closeChat() { const widget = document.getElementById('chatbotWidget'); widget.classList.remove('active'); this.isOpen = false; }, clearConversation() { if (confirm('Hapus semua percakapan?')) { this.conversationHistory = []; const messagesContainer = document.getElementById('chatbotMessages'); messagesContainer.innerHTML = ''; document.querySelector('.chatbot-welcome').style.display = 'block'; showToast('Percakapan telah dihapus'); } }, sendMessage() { const input = document.getElementById('chatbotInput'); const message = input.value.trim(); if (!message) return; this.addMessage(message, 'user'); input.value = ''; // Hide welcome screen document.querySelector('.chatbot-welcome').style.display = 'none'; // Show typing indicator this.showTyping(); // Process query and respond setTimeout(() => { this.hideTyping(); this.processQuery(message); }, 1000 + Math.random() * 1000); }, askQuestion(question) { document.getElementById('chatbotInput').value = question; this.sendMessage(); }, handleQuickAction(action) { const queries = { dashboard: 'Tampilkan ringkasan dashboard sistem', inventory: 'Cek status inventory semua lokasi', health: 'Tampilkan health alerts saat ini', financial: 'Berikan ringkasan keuangan bulan ini' }; this.askQuestion(queries[action]); }, addMessage(text, sender) { const messagesContainer = document.getElementById('chatbotMessages'); const messageDiv = document.createElement('div'); messageDiv.className = `chat-message ${sender}`; const avatar = document.createElement('div'); avatar.className = `message-avatar ${sender}`; avatar.textContent = sender === 'bot' ? '🤖' : '👤'; const content = document.createElement('div'); content.className = 'message-content'; const bubble = document.createElement('div'); bubble.className = 'message-bubble'; bubble.innerHTML = text; const time = document.createElement('div'); time.className = 'message-time'; const now = new Date(); time.textContent = now.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit' }); content.appendChild(bubble); content.appendChild(time); messageDiv.appendChild(avatar); messageDiv.appendChild(content); messagesContainer.appendChild(messageDiv); messagesContainer.scrollTop = messagesContainer.scrollHeight; this.conversationHistory.push({ text, sender, timestamp: now }); return content; }, showTyping() { const messagesContainer = document.getElementById('chatbotMessages'); const typingDiv = document.createElement('div'); typingDiv.className = 'chat-message bot'; typingDiv.id = 'typingIndicator'; const avatar = document.createElement('div'); avatar.className = 'message-avatar bot'; avatar.textContent = '🤖'; const typing = document.createElement('div'); typing.className = 'typing-indicator'; typing.innerHTML = '
'; typingDiv.appendChild(avatar); typingDiv.appendChild(typing); messagesContainer.appendChild(typingDiv); messagesContainer.scrollTop = messagesContainer.scrollHeight; }, hideTyping() { const typing = document.getElementById('typingIndicator'); if (typing) typing.remove(); }, processQuery(query) { const lowerQuery = query.toLowerCase(); let response = ''; // Livestock/Cattle queries if (lowerQuery.match(/berapa.*sapi|total.*cattle|jumlah.*sapi/)) { response = this.getCattleCountResponse(); } else if (lowerQuery.match(/sapi.*sakit|sick.*cattle|kesehatan.*sapi/)) { response = this.getSickCattleResponse(); } else if (lowerQuery.match(/sapi.*panen|harvest.*ready|siap.*panen/)) { response = this.getHarvestReadyResponse(); } else if (lowerQuery.match(/adg|average.*daily.*gain|pertambahan.*bobot/)) { response = this.getADGResponse(); } // Inventory queries else if (lowerQuery.match(/stok.*jagung|corn.*stock|jagung/)) { response = this.getCornStockResponse(); } else if (lowerQuery.match(/stock.*out|stok.*habis|inventory.*critical/)) { response = this.getCriticalStockResponse(); } else if (lowerQuery.match(/inventory.*status|status.*inventory|cek.*inventory/)) { response = this.getInventoryStatusResponse(); } // Purchase Order queries else if (lowerQuery.match(/status.*po|po.*status|purchase.*order/)) { response = this.getPOStatusResponse(); } else if (lowerQuery.match(/po.*pending|pending.*po/)) { response = this.getPendingPOResponse(); } // Financial queries else if (lowerQuery.match(/revenue|pendapatan|penjualan.*bulan/)) { response = this.getRevenueResponse(); } else if (lowerQuery.match(/profit|laba|margin/)) { response = this.getProfitResponse(); } else if (lowerQuery.match(/cash.*balance|saldo.*kas/)) { response = this.getCashBalanceResponse(); } else if (lowerQuery.match(/financial.*summary|ringkasan.*keuangan/)) { response = this.getFinancialSummaryResponse(); } else if (lowerQuery.match(/ap.*balance|accounts.*payable|hutang/)) { response = this.getAPBalanceResponse(); } else if (lowerQuery.match(/ar.*balance|accounts.*receivable|piutang/)) { response = this.getARBalanceResponse(); } else if (lowerQuery.match(/asset.*value|nilai.*aset/)) { response = this.getAssetValueResponse(); } // HR queries else if (lowerQuery.match(/karyawan|employee|attendance/)) { response = this.getEmployeeResponse(); } // Dashboard/Summary else if (lowerQuery.match(/dashboard|ringkasan.*sistem|summary/)) { response = this.getDashboardSummaryResponse(); } // Report generation else if (lowerQuery.match(/generate.*report|buat.*laporan|laporan/)) { response = this.getReportGenerationResponse(); } // Health alerts else if (lowerQuery.match(/health.*alert|alert.*kesehatan/)) { response = this.getHealthAlertsResponse(); } // Default response else { response = this.getDefaultResponse(query); } const messageContent = this.addMessage(response, 'bot'); this.addActionButtons(messageContent, lowerQuery); }, getCattleCountResponse() { return `📊 Total Sapi - Semua Lokasi

📍 Lampung: 5,200 ekor
📍 Medan: 3,250 ekor
Total: 8,450 ekor

📈 Trend: +234 ekor bulan ini (+2.8%)
Health Rate: 99.2%
🎯 Harvest Ready: 145 ekor`; }, getSickCattleResponse() { return `🏥 Sapi Sakit - Minggu Ini (Nov 3-9, 2025)

📍 Lampung: 5 sapi
• 3 respirasi
• 2 digestif

📍 Medan: 2 sapi
• 1 mastitis
• 1 lameness

Total: 7 ekor (0.08% dari populasi)
🔔 1 case butuh immediate attention - sapi C089 di Medan
📊 Trend: Naik 2 kasus vs minggu lalu`; }, getHarvestReadyResponse() { return `✅ Sapi Siap Panen

Total: 145 ekor telah mencapai target weight

📍 Lampung: 92 ekor (avg 485 kg)
📍 Medan: 53 ekor (avg 478 kg)

📅 Estimasi Revenue: IDR 950M
💡 Rekomendasi: Harvest dalam 7-10 hari untuk optimal margin`; }, getADGResponse() { return `📈 Average Daily Gain (ADG) Analysis

ADG Rata-rata: 1.28 kg/hari
🎯 Target: 1.2 kg/hari ✅

📍 Lampung: 1.32 kg/hari
📍 Medan: 1.22 kg/hari

Top Performers (ADG > 1.4):
• C004: 1.41 kg/hari (Brahman)
• C012: 1.38 kg/hari (Simmental)

⚠️ Low ADG (<1.0): 12 sapi need review`; }, getCornStockResponse() { return `🌽 Jagung Giling (FEED-001) - Stock Summary

📍 Lampung-WH1: 45,000 kg (Stock Aman ✓)
📍 Medan-WH1: 12,500 kg (⚠️ Cukup untuk 2.1 hari)
Total: 57,500 kg

📈 Daily Consumption: ~5,800 kg
🔮 Forecast: Medan stock habis dalam 2.1 hari

💡 Rekomendasi: Transfer 20,000 kg dari Lampung atau order baru segera`; }, getCriticalStockResponse() { return `⚠️ Critical Stock Alert

ItemStockDays Left
Jagung (Medan)12,500 kg2.1
Antibiotik85 btl4.2
Vitamin Premix2,400 kg5.8

🚨 Action Required: 3 items perlu immediate reorder`; }, getInventoryStatusResponse() { return `📦 Inventory Status - All Locations

Total Stock Value: IDR 18.5B

In Stock: 287 SKUs
⚠️ Low Stock: 23 SKUs
🚨 Stock Out: 0 SKUs

By Category:
• Pakan: IDR 12.2B (66%)
• Obat: IDR 3.8B (21%)
• Suplemen: IDR 2.5B (13%)

📊 Inventory Turnover: 8.5x/year`; }, getPOStatusResponse() { return `📋 Purchase Order Status

PO NumberStatusValue
PO-2025-001ApprovedIDR 750M
PO-2025-002In TransitIDR 4.2B
PO-2025-003PendingIDR 125M

Total Open POs: 3
Total Value: IDR 5.1B`; }, getPendingPOResponse() { return `⏳ Pending Purchase Orders

PO-2025-003
Vendor: PT Medika Veteriner
Item: Vaksin & Obat
Value: IDR 125M
Status: Awaiting approval
Expected Delivery: Nov 20, 2025

💡 Action: Review dan approve untuk avoid stock out`; }, getRevenueResponse() { return `💰 Revenue Bulan Ini (November 2025)

Total: IDR 45.2B
📈 +12.5% vs Oktober (IDR 40.2B)

By Category:
• Cattle Sales: IDR 29.4B (65%)
• Feed Sales: IDR 9.0B (20%)
• Breeding: IDR 3.6B (8%)
• Other: IDR 3.2B (7%)

🎯 Target: IDR 42B ✅ (108% achieved)`; }, getProfitResponse() { return `📊 Profitability Analysis - November 2025

Revenue: IDR 45.2B
COGS: IDR 32.8B
Gross Profit: IDR 12.4B
Gross Margin: 27.4%

By Location:
📍 Lampung: 29.2% margin
📍 Medan: 24.1% margin

📈 Margin improvement: +2.1% vs target`; }, getCashBalanceResponse() { return `🏦 Cash & Bank Balance

Available Cash: IDR 8.5B

💵 Cash in Hand: IDR 450M
🏦 Bank Accounts: IDR 8.05B

Upcoming:
📥 Receivables (30d): IDR 12.2B
📤 Payables (30d): IDR 8.8B

Net Position: Healthy`; }, getAPBalanceResponse() { const ap = state.data.financeAccounting.accountsPayable; return `📤 Accounts Payable Summary

Total Payable: ${formatCurrency(ap.summary.total_payable)}

✅ Paid: ${formatCurrency(ap.summary.paid)}
⏳ Unpaid: ${formatCurrency(ap.summary.unpaid)}
🚨 Overdue: ${formatCurrency(ap.summary.overdue)}

Recent Invoices:
${ap.invoices.slice(0, 3).map(inv => `• ${inv.vendor}: ${formatCurrency(inv.amount)} - ${inv.payment_status}`).join('
')}

💡 ${ap.invoices.filter(i => i.payment_status === 'Overdue').length} invoice(s) need immediate attention`; }, getARBalanceResponse() { const ar = state.data.financeAccounting.accountsReceivable; return `📥 Accounts Receivable Summary

Total Receivable: ${formatCurrency(ar.summary.total_receivable)}

✅ Collected: ${formatCurrency(ar.summary.collected)}
⏳ Outstanding: ${formatCurrency(ar.summary.outstanding)}
🚨 Overdue: ${formatCurrency(ar.summary.overdue)}

📊 DSO: ${ar.summary.dso} days

Top Outstanding:
${ar.invoices.filter(i => i.payment_status !== 'Paid').slice(0, 3).map(inv => `• ${inv.customer}: ${formatCurrency(inv.amount)}`).join('
')}`; }, getAssetValueResponse() { const assets = state.data.financeAccounting.assets; return `🏢 Fixed Assets Summary

Total Original Cost: ${formatCurrency(assets.summary.total_assets)}
Net Book Value: ${formatCurrency(assets.summary.net_book_value)}

📉 Accumulated Depreciation: ${formatCurrency(assets.summary.total_depreciation)}
✅ Active Assets: ${assets.fixedAssets.filter(a => a.status === 'Active').length}

By Category:
• Building: ${assets.fixedAssets.filter(a => a.category === 'Building').length} assets
• Equipment: ${assets.fixedAssets.filter(a => a.category === 'Equipment').length} assets
• Vehicle: ${assets.fixedAssets.filter(a => a.category === 'Vehicle').length} assets`; }, getFinancialSummaryResponse() { return `💹 Financial Summary - November 2025

Revenue: IDR 45.2B (+12.5%)
Expenses: IDR 32.8B (+8.3%)
Net Profit: IDR 12.4B
Profit Margin: 27.4%

Key Metrics:
• ROI: 34.8%
• Cash Balance: IDR 8.5B
• AR Outstanding: IDR 12.2B
• AP Outstanding: IDR 8.8B

✅ All metrics above target`; }, getEmployeeResponse() { return `👥 Employee & Attendance Summary

Total Employees: 342
✅ Active: 342
📅 New Hires (Nov): 8

Attendance Rate: 96.8%
🎯 Target: 95% ✅

By Department:
• Operations: 185
• Animal Health: 45
• Logistics: 52
• Finance: 28
• Other: 32`; }, getDashboardSummaryResponse() { return `🎯 Dashboard Summary - JJAA ERP System

Business Highlights:
💰 Revenue: IDR 45.2B (+12.5%)
🐄 Total Cattle: 8,450 (+234)
👥 Employees: 342 (96.8% attendance)
📦 Stock Value: IDR 18.5B

Key Alerts:
🚨 8 health alerts (3 critical)
⚠️ 23 low stock items
⏳ 12 pending PO approvals

Performance:
✅ ADG: 1.28 kg/day (target: 1.2)
✅ Health Rate: 99.2%
✅ Profit Margin: 27.4%`; }, getHealthAlertsResponse() { return `🚨 Health Alerts - Current Status

Critical (3):
🔴 Sapi C089 - Lameness (immediate attention)
🔴 Sapi C145 - High fever (monitoring)
🔴 Sapi C203 - Respiratory distress

Under Treatment (5):
🟡 5 sapi receiving antibiotics

Vaccination Due (12):
🟢 12 sapi scheduled for PMK vaccine

📊 Overall Health Rate: 99.2%`; }, getReportGenerationResponse() { return `📄 Report Generation Ready

Pilih report yang ingin Anda generate:

Available Reports:
• Weekly Health Summary
• Monthly Profitability Report
• Inventory Valuation Report
• FCR Analysis Report
• Vendor Performance Report

💡 Klik tombol di bawah untuk generate report yang dipilih`; }, getDefaultResponse(query) { return `Terima kasih atas pertanyaan Anda: "${query}"

Saya dapat membantu Anda dengan:
🐄 Data livestock & kesehatan sapi
📦 Status inventory & stock
💰 Informasi keuangan & revenue
📋 Purchase order & procurement
👥 Data karyawan & attendance
📊 Report generation

Silakan coba pertanyaan yang lebih spesifik atau pilih dari suggested questions di atas.`; }, addActionButtons(messageContent, query) { const actionsDiv = document.createElement('div'); actionsDiv.className = 'message-actions'; // Add context-specific action buttons if (query.match(/inventory|stok|stock/)) { actionsDiv.innerHTML = ` `; } else if (query.match(/cattle|sapi|livestock/)) { actionsDiv.innerHTML = ` `; } else if (query.match(/po|purchase|procurement/)) { actionsDiv.innerHTML = ` `; } else if (query.match(/revenue|profit|financial|keuangan/)) { actionsDiv.innerHTML = ` `; } else if (query.match(/dashboard|summary/)) { actionsDiv.innerHTML = ` `; } else if (query.match(/report|laporan/)) { actionsDiv.innerHTML = ` `; } if (actionsDiv.innerHTML) { messageContent.appendChild(actionsDiv); } } }; // Initialize on page load document.addEventListener('DOMContentLoaded', () => { init(); chatbot.init(); });