0 ? 'replaced' : 'posted'; $reference = trim((string)($data['reference'] ?? '')); $description = trim((string)($data['description'] ?? '')); $journal_date = trim((string)($data['journal_date'] ?? '')); $lines_raw = $data['lines'] ?? []; if (is_string($lines_raw)) { $lines_raw = json_decode($lines_raw, true) ?: []; } // Convert d/m/Y → Y-m-d if (preg_match('#^(\d{2})/(\d{2})/(\d{4})$#', $journal_date, $m)) { $journal_date = "{$m[3]}-{$m[2]}-{$m[1]}"; } if (!$journal_date || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $journal_date)) { $answer['message'] = 'Valid journal date is required.'; exit(json_encode($answer)); } $period = substr($journal_date, 0, 7); // YYYY-MM // Validate and normalise lines $lines = []; $total_debit = 0; $total_credit = 0; foreach ($lines_raw as $l) { $account_code = trim((string)($l['account_code'] ?? '')); $debit = (float)($l['debit'] ?? 0); $credit = (float)($l['credit'] ?? 0); $desc = trim((string)($l['description'] ?? '')); if ($account_code === '') continue; if ($debit == 0 && $credit == 0) continue; $lines[] = ['account_code' => $account_code, 'department_id' => 0, 'debit' => $debit, 'credit' => $credit, 'description' => $desc]; $total_debit += $debit; $total_credit += $credit; } if (count($lines) < 2) { $answer['message'] = 'At least two journal lines are required.'; exit(json_encode($answer)); } if (abs($total_debit - $total_credit) > 0.005) { $answer['message'] = 'Journal is not balanced. Debit ' . number_format($total_debit, 2) . ' ≠ Credit ' . number_format($total_credit, 2) . '.'; exit(json_encode($answer)); } try { $pdo2->beginTransaction(); $guard = new PostingWindowGuard($pdo1, $company_id); $gl = new GlManager($pdo2, $company_id, $guard); if ($gl_id > 0) { $gl->replaceManual($gl_id, $reference, $description, $journal_date, $period, $lines); $answer['message'] = 'Journal entry updated.'; } else { $number_mgr = new DocumentNumberManager($pdo2, $company_id); if ($reference === '') { $reference = $number_mgr->generate('manual', trim((string)($data['doc_number_prefix'] ?? $data['document_prefix'] ?? ''))); } else { $reference = $number_mgr->validateManual('manual', $reference, 'td_gl', 'reference'); } $gl_id = $gl->postManual($reference, $description, $journal_date, $period, $lines); $answer['message'] = 'Journal entry saved.'; } $pdo2->commit(); $answer['success'] = 1; $answer['gl_id'] = $gl_id; notify_node('gl_posted', gl_posted_payload('manual', (int)$gl_id, $event_action, $lines), $company_id); } catch (Exception $e) { if ($pdo2->inTransaction()) $pdo2->rollBack(); $answer['message'] = $e->getMessage(); } exit(json_encode($answer));