Initial commit: multi-company financial management system
Root company app (src/, client/) — GL foundation, AR, and AP: - Segmented Chart of Accounts (Sage 300-style), double-entry PostingService with three-layer balance enforcement (app validation, CHECK constraints, deferred DB trigger), gap-free document numbering, RBAC with branch restrictions. - Accounts Receivable: Customers, Invoices, Credit Notes, Receipts with allocation, all posting through PostingService. - Accounts Payable: Suppliers, Supplier Invoices, Debit Notes, Payments with allocation, mirroring AR's posting pattern. - Tax Codes shared between AR and AP. - Company-app login is a token handoff from the Account Manager (RS256, single-use, company-scoped) rather than a local password. - Frontend reskinned to the approved mockup: navy sidebar, dashboard, tree-view Chart of Accounts, Customer/Supplier 360 pages. Account Manager (manager/, manager/client/) — central login gateway: - Password + TOTP 2FA + Google/Microsoft OAuth, company picker, and the RS256 handoff token issuance that the company app verifies. Covered by unit + integration tests against a real Postgres database (GL balance invariants, AR/AP posting correctness and allocation limits, the full handoff exchange including replay/cross-company/expiry/forgery rejection). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tq2DbEvzHzs13jDFhLaX44
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { createManualEntry, fetchJournalEntries, fetchJournalEntry, reverseJournalEntry } from '../api/journalEntries.api';
|
||||
import type { BranchSelection } from '../store/branchStore';
|
||||
|
||||
export function useJournalEntries(branchScope: BranchSelection) {
|
||||
return useQuery({ queryKey: ['journalEntries', branchScope], queryFn: () => fetchJournalEntries(branchScope) });
|
||||
}
|
||||
|
||||
export function useJournalEntry(id: number | undefined) {
|
||||
return useQuery({
|
||||
queryKey: ['journalEntry', id],
|
||||
queryFn: () => fetchJournalEntry(id as number),
|
||||
enabled: id !== undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateManualEntry() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: createManualEntry,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['journalEntries'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['glAccounts'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useReverseJournalEntry() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, reason }: { id: number; reason?: string }) => reverseJournalEntry(id, reason),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['journalEntries'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['journalEntry'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user