Skip to main content

Neary Integration

Neary is the first source system that ships financial events into Moonlight via @tkgreg/moonlight-sdk. Moonlight is the sole writer to QuickBooks Online for the Neary workspace; the legacy Neary→QuickBooks path is being sunset (see docs/MOONLIGHT_INTEGRATION.md in neary-turbo).

Data Flow

Event Catalogue (v1)

The Neary bridge emits these outbound operations:

OperationMaps toNotes
Purchase.status='paid'INCOME entry on the Neary business partyImplicit VAT split into PRODUCT + TAX entry_item rows. FX block carries USD→KHR rate from Invoice.exchangeRate.
Transaction(refund/reversal)EXPENSE entry, relation_type=REFUND/CORRECTION, relates_to_external_id setRefund inserted before original sale fails fast — bridge re-enqueues until the parent exists.
AffiliateEventEXPENSE (bonus/withdrawal/referral_bonus) or INCOME (claw_back)entry.from_party / to_party use the affiliate party.
Invoice.pdfUrl setattachDocumentByUrl to the corresponding entry, type BASIS, status APPROVEDThe invoice PDF is the official Cambodian tax document.

Inbound Events Consumed

The Neary webhook handler (apps/api/src/routes/moonlight-webhook-routes.ts) reacts to:

EventNeary action
entry.review.approved (affiliate withdrawal)Unlock the affiliate's pending balance, queue Telegram notification
entry.review.rejectedOpen finance Ticket with the rejection reason
entry.review.changes_requestedOpen finance Ticket with the requested change
entry.document.requestedOpen MOONLIGHT_DOCUMENT_REQUEST Ticket targeting the source aggregate
entry.voidedMark Purchase.metadata.moonlightVoided=true, alert finance
accounting.sync.failedSlack/Telegram alert in #finance-ops

External-id Mapping

Neary entityexternal_id
Purchaseneary:purchase:{id}
Transaction (refund/reversal)neary:tx:{id}
AffiliateEventneary:affiliate-event:{id}
User → CUSTOMER partyneary:user:{id}
Affiliate → EMPLOYEE partyneary:affiliate:{id}
Provider → VENDOR/BANK partyneary:provider:{id}
Neary entity (BUSINESS party)neary:business:1

external_source is always "neary".

Project Routing

Neary SalesChannel.code (e.g. TG, FB) is forwarded as project_code on the ingest payload. Moonlight resolves (workspace_id, code) from the project table; if the code is unknown the ingest is rejected with a clear error so the operator can create the project before re-running.

Backfill

The Neary side ships three CLI scripts (run from the neary-turbo repo):

pnpm moonlight:backfill --phase=sales --from=2024-01-01 --to=2024-12-31
pnpm moonlight:backfill --phase=refunds --from=2024-01-01 --to=2024-12-31
pnpm moonlight:backfill --phase=affiliate --from=2024-01-01 --to=2024-12-31
pnpm moonlight:backfill --phase=documents --from=2024-01-01 --to=2024-12-31

Phases must run in order — refunds and documents reference parent entries by external_id. Phases use partial_per_row=true, so a single malformed row does not block the rest of the batch.

Reconciliation

pnpm moonlight:reconcile --from=2024-01-01 --to=2024-12-31 --verbose

Compares Neary purchase paid rows against Moonlight entries with external_source='neary'. The script exits non-zero on any monthly diff or any orphan id, suitable for a nightly cron.

See Also