Updated GL System , Updated user manual
This commit is contained in:
@@ -1784,6 +1784,61 @@ async function main() {
|
||||
})).assignment;
|
||||
if (crossEmpAssign.employee_id) throw new Error('A company A assignment accepted a company B employee');
|
||||
|
||||
// ---- GL journal entries: CRUD + audit + import (conflict/merge) + export ----
|
||||
const glEntry = (await request('/api/books/GAAP/gl-entries', {
|
||||
method: 'POST', headers: companyHeaders(companyA),
|
||||
body: JSON.stringify({ entry_date: '2026-03-01', account: '1500', account_type: 'Asset', description: 'Equipment', reference: 'INV-1', debit: 1000, credit: 0, external_id: `GL-${suffix}-100` })
|
||||
})).entry;
|
||||
if (!glEntry || glEntry.entity_id !== companyA) throw new Error('GL entry was not created/scoped');
|
||||
const glListed = (await request('/api/books/GAAP/gl-entries?year=2026', { headers: companyHeaders(companyA) }));
|
||||
if (!glListed.entries.some((e) => e.id === glEntry.id) || glListed.summary.debit < 1000) throw new Error('GL entry did not list with totals');
|
||||
|
||||
// Update it (full before/after must be audited).
|
||||
await request(`/api/books/GAAP/gl-entries/${glEntry.id}`, {
|
||||
method: 'PUT', headers: companyHeaders(companyA), body: JSON.stringify({ debit: 1200, description: 'Equipment (revised)' })
|
||||
});
|
||||
const glAudit = await request('/api/audit-logs?entity_type=gl_entry', { headers: auth });
|
||||
if (glAudit.total < 2) throw new Error('GL changes were not audited');
|
||||
const updateLog = glAudit.rows.find((r) => r.action === 'update' && r.entity_id === String(glEntry.id));
|
||||
if (!updateLog || !updateLog.before_json || !updateLog.after_json) throw new Error('GL update did not capture full before/after');
|
||||
|
||||
// Import preview: one row matches the existing entry's external id (update), one is brand new.
|
||||
const csv = `Date,Account,Account Type,Description,Reference,Debit,Credit,External Id\n` +
|
||||
`2026-03-01,1500,Asset,Equipment changed,INV-1,2000,0,GL-${suffix}-100\n` +
|
||||
`2026-03-05,6400,Expense,Depreciation,INV-2,0,250,GL-${suffix}-200\n`;
|
||||
const previewForm = new FormData();
|
||||
previewForm.append('file', new Blob([csv], { type: 'text/csv' }), 'gl.csv');
|
||||
const glPreview = await (await fetch(`${baseUrl}/api/books/GAAP/gl-entries/import/preview`, { method: 'POST', headers: companyHeaders(companyA), body: previewForm })).json();
|
||||
if (glPreview.summary.new !== 1 || glPreview.summary.update !== 1) {
|
||||
throw new Error(`Import preview misclassified rows: ${JSON.stringify(glPreview.summary)}`);
|
||||
}
|
||||
|
||||
// Commit with merge: insert the new row, update the matched one.
|
||||
const glCommit = await request('/api/books/GAAP/gl-entries/import/commit', {
|
||||
method: 'POST', headers: companyHeaders(companyA), body: JSON.stringify({ rows: glPreview.rows, strategy: 'merge' })
|
||||
});
|
||||
if (glCommit.inserted !== 1 || glCommit.updated !== 1) throw new Error(`Import commit applied wrong counts: ${JSON.stringify(glCommit)}`);
|
||||
|
||||
// Export CSV + Excel.
|
||||
const glCsv = await fetch(`${baseUrl}/api/books/GAAP/gl-entries/export?year=2026&format=csv`, { headers: companyHeaders(companyA) });
|
||||
if (!(glCsv.headers.get('content-type') || '').includes('text/csv')) throw new Error('GL CSV export was not text/csv');
|
||||
if (!(await glCsv.text()).startsWith('Date,Account')) throw new Error('GL CSV header was malformed');
|
||||
const glXlsx = await fetch(`${baseUrl}/api/books/GAAP/gl-entries/export?year=2026&format=xlsx`, { headers: companyHeaders(companyA) });
|
||||
if (!(glXlsx.headers.get('content-type') || '').includes('spreadsheet')) throw new Error('GL Excel export had the wrong content-type');
|
||||
|
||||
// Per-entry change history (create + update events with before/after).
|
||||
const glHistory = (await request(`/api/books/GAAP/gl-entries/${glEntry.id}/history`, { headers: companyHeaders(companyA) })).history;
|
||||
if (glHistory.length < 2 || !glHistory.some((h) => h.action === 'update' && h.before && h.after)) {
|
||||
throw new Error('GL entry history did not return the create + update timeline');
|
||||
}
|
||||
// History is company-scoped (company B can't read company A's entry history).
|
||||
const crossHistory = await fetch(`${baseUrl}/api/books/GAAP/gl-entries/${glEntry.id}/history`, { headers: companyHeaders(companyB) });
|
||||
if (crossHistory.status !== 404) throw new Error('Cross-company GL history should 404');
|
||||
|
||||
// Isolation: company B's GAAP book sees none of company A's entries.
|
||||
const glB = (await request('/api/books/GAAP/gl-entries', { headers: companyHeaders(companyB) })).entries;
|
||||
if (glB.some((e) => e.id === glEntry.id)) throw new Error('Company B sees company A GL entries');
|
||||
|
||||
console.log('Smoke test passed');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user