Skip to content

feat(finance): manual income tracking included in revenue - #53

Merged
Dev869 merged 1 commit into
mainfrom
feat/manual-income
May 19, 2026
Merged

feat(finance): manual income tracking included in revenue#53
Dev869 merged 1 commit into
mainfrom
feat/manual-income

Conversation

@Dev869

@Dev869 Dev869 commented May 19, 2026

Copy link
Copy Markdown
Owner

Summary

Adds manual income tracking to the Transactions page and rolls it into revenue/income KPIs.

Part 1 — Service layer

  • createManualIncome(input) in web/src/services/firestore.ts — writes a transactions doc with provider:'manual', type:'income', isManual:true, positive amount, date, description, category (defaults to Manual Income), ownerId (current uid), createdAt/updatedAt. Mirrors the existing createManualExpense conventions (addDoc + Timestamp). The Transaction model has no client field, so source belongs in description (no model redesign).
  • subscribeTransactions(callback) — onSnapshot listener over the user's transactions, mirroring subscribeReceipts.
  • useTransactions() hook in web/src/hooks/useFirestore.ts, mirroring useReceipts/useConnectedAccounts.

Part 2 — Transactions page UI

  • New web/src/components/finance/AddIncomeModal.tsx, mirroring NewInvoiceModal styling tokens and validation conventions. Fields: amount (required, > 0), date (required, defaults to today), description/source (required), category (optional, defaults to Manual Income).
  • "Add Income" button added to the Transactions page header next to "Import CSV"; on submit it calls createManualIncome and refetches the list using the existing pagination refetch pattern.
  • Manual income rows surface automatically in the existing list and are visually distinguishable: TransactionRow already renders a "Manual" source badge for provider==='manual' and styles positive amounts (income) green with a + prefix.

Part 3 — Revenue/Dashboard inclusion + double-count rule

  • calculateRevenue(items, range, transactions = []) in web/src/lib/finance.ts now adds the sum of manual income transactions (provider==='manual' && type==='income') whose date is in range. Signature is backward compatible (optional transactions defaulting to []).
  • Double-count handling: manual income transactions with a matchedWorkItemId are EXCLUDED, since those are tied to an already-counted invoice.
  • FinanceOverview.tsx now passes useTransactions() data into calculateRevenue (current and previous period).
  • Dashboard.tsx "revenue this month" adds in-range manual income with the same exclusion rule (kept the existing inline invoice predicate untouched to avoid changing invoice behavior).

Constraints respected

No changes to Plaid/Stripe sync, invoice/work-order predicates, routing (no new route — lives on the Transactions page), or unrelated finance logic. Existing styling tokens, TS conventions, and immutable patterns followed. No new dependencies.

Test plan

  • cd web && npx tsc -b — exit 0.
  • npx vitest run — 75/75 pass (full suite).
  • finance.test.ts extended with calculateRevenue cases:
    • in-range manual income added to paid invoices
    • manual income with matchedWorkItemId excluded (double-count guard)
    • out-of-range manual income excluded
    • non-manual / expense transactions ignored
    • backward compatible when no transactions arg passed
  • ESLint: no new errors in touched files vs main (pre-existing repo errors unchanged; new files lint clean).
  • Manual QA: add income via modal, confirm row appears with Manual badge + green amount, and Finance/Dashboard revenue increases by the entered amount.

@github-actions

Copy link
Copy Markdown

🔥 Firebase Hosting preview: https://open-ten99--pr-53-e52c29ui.web.app
(expires in 7 days)

@Dev869
Dev869 merged commit 05b1944 into main May 19, 2026
3 checks passed
@Dev869
Dev869 deleted the feat/manual-income branch May 19, 2026 03:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant