From d3b454a093cda7d8ffa576afd878655383ec95ca Mon Sep 17 00:00:00 2001 From: "Joseph T. French" Date: Fri, 1 May 2026 14:43:54 -0500 Subject: [PATCH] feat: implement event block and agent queries; add tests for event block retrieval --- clients/LedgerClient.test.ts | 215 ++++++++ clients/LedgerClient.ts | 106 ++++ clients/graphql/generated/graphql.ts | 489 ++++++++++++++++++ clients/graphql/queries/ledger/agent.ts | 32 ++ clients/graphql/queries/ledger/agents.ts | 46 ++ clients/graphql/queries/ledger/eventBlock.ts | 39 ++ clients/graphql/queries/ledger/eventBlocks.ts | 58 +++ 7 files changed, 985 insertions(+) create mode 100644 clients/graphql/queries/ledger/agent.ts create mode 100644 clients/graphql/queries/ledger/agents.ts create mode 100644 clients/graphql/queries/ledger/eventBlock.ts create mode 100644 clients/graphql/queries/ledger/eventBlocks.ts diff --git a/clients/LedgerClient.test.ts b/clients/LedgerClient.test.ts index 363ee72..629848c 100644 --- a/clients/LedgerClient.test.ts +++ b/clients/LedgerClient.test.ts @@ -441,6 +441,221 @@ describe('LedgerClient', () => { }) }) + describe('listEventBlocks', () => { + it('returns the eventBlocks array', async () => { + mockFetch.mockResolvedValueOnce( + gqlResponse({ + eventBlocks: [ + { + id: 'evt_1', + eventType: 'invoice_issued', + eventCategory: 'sales', + eventClass: 'Receivable', + status: 'captured', + occurredAt: '2026-04-15T00:00:00Z', + effectiveAt: null, + source: 'quickbooks', + externalId: 'qb_inv_1', + externalUrl: null, + amount: 50000, + currency: 'USD', + description: 'Invoice #1001', + metadata: { entries: [] }, + dimensionIds: [], + agentId: 'agt_1', + resourceType: null, + resourceElementId: null, + replacedByEventId: null, + replacesEventId: null, + obligatedByEventId: null, + dischargesEventId: null, + createdAt: '2026-04-15T01:00:00Z', + createdBy: 'sync', + }, + ], + }) + ) + const events = await client.listEventBlocks('graph_1', { status: 'captured' }) + expect(events).toHaveLength(1) + expect(events[0].eventType).toBe('invoice_issued') + expect(events[0].agentId).toBe('agt_1') + }) + + it('forwards filter args as GraphQL variables', async () => { + mockFetch.mockResolvedValueOnce(gqlResponse({ eventBlocks: [] })) + await client.listEventBlocks('graph_1', { + eventType: 'bill_received', + eventCategory: 'purchase', + status: 'captured', + agentId: 'agt_2', + source: 'quickbooks', + limit: 25, + offset: 50, + }) + const body = JSON.parse((mockFetch.mock.calls[0][1] as RequestInit).body as string) + expect(body.variables).toMatchObject({ + eventType: 'bill_received', + eventCategory: 'purchase', + status: 'captured', + agentId: 'agt_2', + source: 'quickbooks', + limit: 25, + offset: 50, + }) + }) + + it('defaults isActive-equivalent filters to nulls and pagination to 50/0', async () => { + mockFetch.mockResolvedValueOnce(gqlResponse({ eventBlocks: [] })) + await client.listEventBlocks('graph_1') + const body = JSON.parse((mockFetch.mock.calls[0][1] as RequestInit).body as string) + expect(body.variables).toMatchObject({ + eventType: null, + status: null, + limit: 50, + offset: 0, + }) + }) + }) + + describe('getEventBlock', () => { + it('returns the event block detail', async () => { + mockFetch.mockResolvedValueOnce( + gqlResponse({ + eventBlock: { + id: 'evt_1', + eventType: 'invoice_issued', + eventCategory: 'sales', + eventClass: 'Receivable', + status: 'captured', + occurredAt: '2026-04-15T00:00:00Z', + effectiveAt: null, + source: 'quickbooks', + externalId: 'qb_inv_1', + externalUrl: null, + amount: 50000, + currency: 'USD', + description: 'Invoice #1001', + metadata: { + entries: [ + { + memo: 'Sale to Acme', + posting_date: '2026-04-15', + line_items: [ + { element_external_id: '11000', debit_amount: 500, credit_amount: 0 }, + { element_external_id: '40000', debit_amount: 0, credit_amount: 500 }, + ], + }, + ], + }, + dimensionIds: [], + agentId: 'agt_1', + resourceType: null, + resourceElementId: null, + replacedByEventId: null, + replacesEventId: null, + obligatedByEventId: null, + dischargesEventId: null, + createdAt: '2026-04-15T01:00:00Z', + createdBy: 'sync', + }, + }) + ) + const evt = await client.getEventBlock('graph_1', 'evt_1') + expect(evt?.id).toBe('evt_1') + const meta = evt?.metadata as { entries: Array<{ line_items: unknown[] }> } + expect(meta.entries).toHaveLength(1) + expect(meta.entries[0].line_items).toHaveLength(2) + }) + + it('returns null when event block is missing', async () => { + mockFetch.mockResolvedValueOnce(gqlResponse({ eventBlock: null })) + expect(await client.getEventBlock('graph_1', 'evt_missing')).toBeNull() + }) + }) + + describe('listAgents', () => { + it('returns the agents array', async () => { + mockFetch.mockResolvedValueOnce( + gqlResponse({ + agents: [ + { + id: 'agt_1', + agentType: 'customer', + name: 'Acme Corp', + legalName: 'Acme Corporation Inc.', + taxId: '12-3456789', + registrationNumber: null, + duns: null, + lei: null, + email: 'billing@acme.test', + phone: '+1 555 0100', + address: { Line1: '1 Main St' }, + source: 'quickbooks', + externalId: 'qb_cust_1', + isActive: true, + is1099Recipient: false, + createdAt: '2026-04-01T00:00:00Z', + updatedAt: '2026-04-01T00:00:00Z', + createdBy: 'sync', + }, + ], + }) + ) + const agents = await client.listAgents('graph_1', { agentType: 'customer' }) + expect(agents).toHaveLength(1) + expect(agents[0].name).toBe('Acme Corp') + }) + + it('defaults isActive to true and forwards explicit overrides', async () => { + mockFetch.mockResolvedValueOnce(gqlResponse({ agents: [] })) + await client.listAgents('graph_1') + let body = JSON.parse((mockFetch.mock.calls[0][1] as RequestInit).body as string) + expect(body.variables.isActive).toBe(true) + + mockFetch.mockResolvedValueOnce(gqlResponse({ agents: [] })) + await client.listAgents('graph_1', { isActive: null }) + body = JSON.parse((mockFetch.mock.calls[1][1] as RequestInit).body as string) + expect(body.variables.isActive).toBeNull() + }) + }) + + describe('getAgent', () => { + it('returns the agent detail', async () => { + mockFetch.mockResolvedValueOnce( + gqlResponse({ + agent: { + id: 'agt_1', + agentType: 'vendor', + name: 'Office Supplies Co', + legalName: null, + taxId: null, + registrationNumber: null, + duns: null, + lei: null, + email: null, + phone: null, + address: null, + source: 'quickbooks', + externalId: 'qb_vend_1', + isActive: true, + is1099Recipient: false, + createdAt: '2026-04-01T00:00:00Z', + updatedAt: '2026-04-01T00:00:00Z', + createdBy: 'sync', + }, + }) + ) + const agt = await client.getAgent('graph_1', 'agt_1') + expect(agt?.agentType).toBe('vendor') + expect(agt?.name).toBe('Office Supplies Co') + }) + + it('returns null when agent is missing', async () => { + mockFetch.mockResolvedValueOnce(gqlResponse({ agent: null })) + expect(await client.getAgent('graph_1', 'agt_missing')).toBeNull() + }) + }) + describe('getFiscalCalendar', () => { it('returns the calendar state', async () => { mockFetch.mockResolvedValueOnce( diff --git a/clients/LedgerClient.ts b/clients/LedgerClient.ts index 2dc49a8..df9e47d 100644 --- a/clients/LedgerClient.ts +++ b/clients/LedgerClient.ts @@ -107,8 +107,10 @@ import { GetInformationBlockDocument, GetLedgerAccountRollupsDocument, GetLedgerAccountTreeDocument, + GetLedgerAgentDocument, GetLedgerClosingBookStructuresDocument, GetLedgerEntityDocument, + GetLedgerEventBlockDocument, GetLedgerFiscalCalendarDocument, GetLedgerMappedTrialBalanceDocument, GetLedgerMappingCoverageDocument, @@ -125,8 +127,10 @@ import { GetLedgerTrialBalanceDocument, ListInformationBlocksDocument, ListLedgerAccountsDocument, + ListLedgerAgentsDocument, ListLedgerElementsDocument, ListLedgerEntitiesDocument, + ListLedgerEventBlocksDocument, ListLedgerMappingsDocument, ListLedgerPublishListsDocument, ListLedgerReportsDocument, @@ -137,8 +141,10 @@ import { type GetInformationBlockQuery, type GetLedgerAccountRollupsQuery, type GetLedgerAccountTreeQuery, + type GetLedgerAgentQuery, type GetLedgerClosingBookStructuresQuery, type GetLedgerEntityQuery, + type GetLedgerEventBlockQuery, type GetLedgerFiscalCalendarQuery, type GetLedgerMappedTrialBalanceQuery, type GetLedgerMappingCoverageQuery, @@ -155,8 +161,10 @@ import { type GetLedgerTrialBalanceQuery, type ListInformationBlocksQuery, type ListLedgerAccountsQuery, + type ListLedgerAgentsQuery, type ListLedgerElementsQuery, type ListLedgerEntitiesQuery, + type ListLedgerEventBlocksQuery, type ListLedgerMappingsQuery, type ListLedgerPublishListsQuery, type ListLedgerReportsQuery, @@ -192,6 +200,12 @@ export type LedgerTransactionList = NonNullable +export type LedgerEventBlock = ListLedgerEventBlocksQuery['eventBlocks'][number] +export type LedgerEventBlockDetail = NonNullable + +export type LedgerAgent = ListLedgerAgentsQuery['agents'][number] +export type LedgerAgentDetail = NonNullable + export type LedgerReportingTaxonomy = NonNullable< GetLedgerReportingTaxonomyQuery['reportingTaxonomy'] > @@ -577,6 +591,98 @@ export class LedgerClient { ) } + // ── Event Blocks (Inbox) ──────────────────────────────────────────── + + /** + * List captured event blocks for the inbox surface. + * + * Pass `status: 'captured'` for the default un-reviewed view. Use + * `eventType` filters like `'invoice_issued'` / `'bill_received'` to + * narrow by source class. Approve/reject via `updateEventBlock`. + */ + async listEventBlocks( + graphId: string, + options?: { + eventType?: string + eventCategory?: string + status?: string + agentId?: string + source?: string + limit?: number + offset?: number + } + ): Promise { + return this.gqlQuery( + graphId, + ListLedgerEventBlocksDocument, + { + eventType: options?.eventType ?? null, + eventCategory: options?.eventCategory ?? null, + status: options?.status ?? null, + agentId: options?.agentId ?? null, + source: options?.source ?? null, + limit: options?.limit ?? 50, + offset: options?.offset ?? 0, + }, + 'List event blocks', + (data) => data.eventBlocks + ) + } + + /** Get event block detail (carries the metadata blob with nested entries). */ + async getEventBlock(graphId: string, eventId: string): Promise { + return this.gqlQuery( + graphId, + GetLedgerEventBlockDocument, + { id: eventId }, + 'Get event block', + (data) => data.eventBlock + ) + } + + // ── Agents (REA counterparties) ───────────────────────────────────── + + /** + * List agents — REA-aligned counterparties (customers, vendors, + * employees). Defaults to active agents only; pass `isActive: null` + * to include deactivated ones. + */ + async listAgents( + graphId: string, + options?: { + agentType?: string + source?: string + isActive?: boolean | null + limit?: number + offset?: number + } + ): Promise { + return this.gqlQuery( + graphId, + ListLedgerAgentsDocument, + { + agentType: options?.agentType ?? null, + source: options?.source ?? null, + isActive: options?.isActive === undefined ? true : options.isActive, + limit: options?.limit ?? 50, + offset: options?.offset ?? 0, + }, + 'List agents', + (data) => data.agents + ) + } + + /** Get agent detail by id. */ + async getAgent(graphId: string, agentId: string): Promise { + return this.gqlQuery( + graphId, + GetLedgerAgentDocument, + { id: agentId }, + 'Get agent', + (data) => data.agent + ) + } + // ── Trial Balance ────────────────────────────────────────────────── /** Trial balance by raw CoA account. */ diff --git a/clients/graphql/generated/graphql.ts b/clients/graphql/generated/graphql.ts index 6b0f02c..7b71437 100644 --- a/clients/graphql/generated/graphql.ts +++ b/clients/graphql/generated/graphql.ts @@ -1801,6 +1801,64 @@ export type ListLedgerAccountsQuery = { } | null } +export type GetLedgerAgentQueryVariables = Exact<{ + id: Scalars['String']['input'] +}> + +export type GetLedgerAgentQuery = { + agent: { + id: string + agentType: string + name: string + legalName: string | null + taxId: string | null + registrationNumber: string | null + duns: string | null + lei: string | null + email: string | null + phone: string | null + address: any | null + source: string + externalId: string | null + isActive: boolean + is1099Recipient: boolean + createdAt: any | null + updatedAt: any | null + createdBy: string | null + } | null +} + +export type ListLedgerAgentsQueryVariables = Exact<{ + agentType: InputMaybe + source: InputMaybe + isActive?: InputMaybe + limit?: Scalars['Int']['input'] + offset?: Scalars['Int']['input'] +}> + +export type ListLedgerAgentsQuery = { + agents: Array<{ + id: string + agentType: string + name: string + legalName: string | null + taxId: string | null + registrationNumber: string | null + duns: string | null + lei: string | null + email: string | null + phone: string | null + address: any | null + source: string + externalId: string | null + isActive: boolean + is1099Recipient: boolean + createdAt: any | null + updatedAt: any | null + createdBy: string | null + }> +} + export type GetLedgerClosingBookStructuresQueryVariables = Exact<{ [key: string]: never }> export type GetLedgerClosingBookStructuresQuery = { @@ -1918,6 +1976,78 @@ export type GetLedgerEntityQuery = { } | null } +export type GetLedgerEventBlockQueryVariables = Exact<{ + id: Scalars['String']['input'] +}> + +export type GetLedgerEventBlockQuery = { + eventBlock: { + id: string + eventType: string + eventCategory: string + eventClass: string + status: string + occurredAt: any + effectiveAt: any | null + source: string + externalId: string | null + externalUrl: string | null + amount: number | null + currency: string + description: string | null + metadata: any + dimensionIds: Array + agentId: string | null + resourceType: string | null + resourceElementId: string | null + replacedByEventId: string | null + replacesEventId: string | null + obligatedByEventId: string | null + dischargesEventId: string | null + createdAt: any + createdBy: string + } | null +} + +export type ListLedgerEventBlocksQueryVariables = Exact<{ + eventType: InputMaybe + eventCategory: InputMaybe + status: InputMaybe + agentId: InputMaybe + source: InputMaybe + limit?: Scalars['Int']['input'] + offset?: Scalars['Int']['input'] +}> + +export type ListLedgerEventBlocksQuery = { + eventBlocks: Array<{ + id: string + eventType: string + eventCategory: string + eventClass: string + status: string + occurredAt: any + effectiveAt: any | null + source: string + externalId: string | null + externalUrl: string | null + amount: number | null + currency: string + description: string | null + metadata: any + dimensionIds: Array + agentId: string | null + resourceType: string | null + resourceElementId: string | null + replacedByEventId: string | null + replacesEventId: string | null + obligatedByEventId: string | null + dischargesEventId: string | null + createdAt: any + createdBy: string + }> +} + export type GetLedgerFiscalCalendarQueryVariables = Exact<{ [key: string]: never }> export type GetLedgerFiscalCalendarQuery = { @@ -3900,6 +4030,170 @@ export const ListLedgerAccountsDocument = { }, ], } as unknown as DocumentNode +export const GetLedgerAgentDocument = { + kind: 'Document', + definitions: [ + { + kind: 'OperationDefinition', + operation: 'query', + name: { kind: 'Name', value: 'GetLedgerAgent' }, + variableDefinitions: [ + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'id' } }, + type: { + kind: 'NonNullType', + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'agent' }, + arguments: [ + { + kind: 'Argument', + name: { kind: 'Name', value: 'id' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'id' } }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { kind: 'Field', name: { kind: 'Name', value: 'id' } }, + { kind: 'Field', name: { kind: 'Name', value: 'agentType' } }, + { kind: 'Field', name: { kind: 'Name', value: 'name' } }, + { kind: 'Field', name: { kind: 'Name', value: 'legalName' } }, + { kind: 'Field', name: { kind: 'Name', value: 'taxId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'registrationNumber' } }, + { kind: 'Field', name: { kind: 'Name', value: 'duns' } }, + { kind: 'Field', name: { kind: 'Name', value: 'lei' } }, + { kind: 'Field', name: { kind: 'Name', value: 'email' } }, + { kind: 'Field', name: { kind: 'Name', value: 'phone' } }, + { kind: 'Field', name: { kind: 'Name', value: 'address' } }, + { kind: 'Field', name: { kind: 'Name', value: 'source' } }, + { kind: 'Field', name: { kind: 'Name', value: 'externalId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'isActive' } }, + { kind: 'Field', name: { kind: 'Name', value: 'is1099Recipient' } }, + { kind: 'Field', name: { kind: 'Name', value: 'createdAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'updatedAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'createdBy' } }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode +export const ListLedgerAgentsDocument = { + kind: 'Document', + definitions: [ + { + kind: 'OperationDefinition', + operation: 'query', + name: { kind: 'Name', value: 'ListLedgerAgents' }, + variableDefinitions: [ + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'agentType' } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'source' } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'isActive' } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'Boolean' } }, + defaultValue: { kind: 'BooleanValue', value: true }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'limit' } }, + type: { + kind: 'NonNullType', + type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } }, + }, + defaultValue: { kind: 'IntValue', value: '50' }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'offset' } }, + type: { + kind: 'NonNullType', + type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } }, + }, + defaultValue: { kind: 'IntValue', value: '0' }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'agents' }, + arguments: [ + { + kind: 'Argument', + name: { kind: 'Name', value: 'agentType' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'agentType' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'source' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'source' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'isActive' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'isActive' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'limit' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'limit' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'offset' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'offset' } }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { kind: 'Field', name: { kind: 'Name', value: 'id' } }, + { kind: 'Field', name: { kind: 'Name', value: 'agentType' } }, + { kind: 'Field', name: { kind: 'Name', value: 'name' } }, + { kind: 'Field', name: { kind: 'Name', value: 'legalName' } }, + { kind: 'Field', name: { kind: 'Name', value: 'taxId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'registrationNumber' } }, + { kind: 'Field', name: { kind: 'Name', value: 'duns' } }, + { kind: 'Field', name: { kind: 'Name', value: 'lei' } }, + { kind: 'Field', name: { kind: 'Name', value: 'email' } }, + { kind: 'Field', name: { kind: 'Name', value: 'phone' } }, + { kind: 'Field', name: { kind: 'Name', value: 'address' } }, + { kind: 'Field', name: { kind: 'Name', value: 'source' } }, + { kind: 'Field', name: { kind: 'Name', value: 'externalId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'isActive' } }, + { kind: 'Field', name: { kind: 'Name', value: 'is1099Recipient' } }, + { kind: 'Field', name: { kind: 'Name', value: 'createdAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'updatedAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'createdBy' } }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode export const GetLedgerClosingBookStructuresDocument = { kind: 'Document', definitions: [ @@ -4198,6 +4492,201 @@ export const GetLedgerEntityDocument = { }, ], } as unknown as DocumentNode +export const GetLedgerEventBlockDocument = { + kind: 'Document', + definitions: [ + { + kind: 'OperationDefinition', + operation: 'query', + name: { kind: 'Name', value: 'GetLedgerEventBlock' }, + variableDefinitions: [ + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'id' } }, + type: { + kind: 'NonNullType', + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'eventBlock' }, + arguments: [ + { + kind: 'Argument', + name: { kind: 'Name', value: 'id' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'id' } }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { kind: 'Field', name: { kind: 'Name', value: 'id' } }, + { kind: 'Field', name: { kind: 'Name', value: 'eventType' } }, + { kind: 'Field', name: { kind: 'Name', value: 'eventCategory' } }, + { kind: 'Field', name: { kind: 'Name', value: 'eventClass' } }, + { kind: 'Field', name: { kind: 'Name', value: 'status' } }, + { kind: 'Field', name: { kind: 'Name', value: 'occurredAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'effectiveAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'source' } }, + { kind: 'Field', name: { kind: 'Name', value: 'externalId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'externalUrl' } }, + { kind: 'Field', name: { kind: 'Name', value: 'amount' } }, + { kind: 'Field', name: { kind: 'Name', value: 'currency' } }, + { kind: 'Field', name: { kind: 'Name', value: 'description' } }, + { kind: 'Field', name: { kind: 'Name', value: 'metadata' } }, + { kind: 'Field', name: { kind: 'Name', value: 'dimensionIds' } }, + { kind: 'Field', name: { kind: 'Name', value: 'agentId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'resourceType' } }, + { kind: 'Field', name: { kind: 'Name', value: 'resourceElementId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'replacedByEventId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'replacesEventId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'obligatedByEventId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'dischargesEventId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'createdAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'createdBy' } }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode +export const ListLedgerEventBlocksDocument = { + kind: 'Document', + definitions: [ + { + kind: 'OperationDefinition', + operation: 'query', + name: { kind: 'Name', value: 'ListLedgerEventBlocks' }, + variableDefinitions: [ + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'eventType' } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'eventCategory' } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'status' } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'agentId' } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'source' } }, + type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'limit' } }, + type: { + kind: 'NonNullType', + type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } }, + }, + defaultValue: { kind: 'IntValue', value: '50' }, + }, + { + kind: 'VariableDefinition', + variable: { kind: 'Variable', name: { kind: 'Name', value: 'offset' } }, + type: { + kind: 'NonNullType', + type: { kind: 'NamedType', name: { kind: 'Name', value: 'Int' } }, + }, + defaultValue: { kind: 'IntValue', value: '0' }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'eventBlocks' }, + arguments: [ + { + kind: 'Argument', + name: { kind: 'Name', value: 'eventType' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'eventType' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'eventCategory' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'eventCategory' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'status' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'status' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'agentId' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'agentId' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'source' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'source' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'limit' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'limit' } }, + }, + { + kind: 'Argument', + name: { kind: 'Name', value: 'offset' }, + value: { kind: 'Variable', name: { kind: 'Name', value: 'offset' } }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { kind: 'Field', name: { kind: 'Name', value: 'id' } }, + { kind: 'Field', name: { kind: 'Name', value: 'eventType' } }, + { kind: 'Field', name: { kind: 'Name', value: 'eventCategory' } }, + { kind: 'Field', name: { kind: 'Name', value: 'eventClass' } }, + { kind: 'Field', name: { kind: 'Name', value: 'status' } }, + { kind: 'Field', name: { kind: 'Name', value: 'occurredAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'effectiveAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'source' } }, + { kind: 'Field', name: { kind: 'Name', value: 'externalId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'externalUrl' } }, + { kind: 'Field', name: { kind: 'Name', value: 'amount' } }, + { kind: 'Field', name: { kind: 'Name', value: 'currency' } }, + { kind: 'Field', name: { kind: 'Name', value: 'description' } }, + { kind: 'Field', name: { kind: 'Name', value: 'metadata' } }, + { kind: 'Field', name: { kind: 'Name', value: 'dimensionIds' } }, + { kind: 'Field', name: { kind: 'Name', value: 'agentId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'resourceType' } }, + { kind: 'Field', name: { kind: 'Name', value: 'resourceElementId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'replacedByEventId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'replacesEventId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'obligatedByEventId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'dischargesEventId' } }, + { kind: 'Field', name: { kind: 'Name', value: 'createdAt' } }, + { kind: 'Field', name: { kind: 'Name', value: 'createdBy' } }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode export const GetLedgerFiscalCalendarDocument = { kind: 'Document', definitions: [ diff --git a/clients/graphql/queries/ledger/agent.ts b/clients/graphql/queries/ledger/agent.ts new file mode 100644 index 0000000..30e5a83 --- /dev/null +++ b/clients/graphql/queries/ledger/agent.ts @@ -0,0 +1,32 @@ +import { gql } from 'graphql-request' + +/** + * Get a single agent by id (the agents detail view). + * + * `address` is a JSON dict — typically QB-shaped with `Line1`/`City`/ + * `CountrySubDivisionCode`/`PostalCode` keys. + */ +export const GET_AGENT = gql` + query GetLedgerAgent($id: String!) { + agent(id: $id) { + id + agentType + name + legalName + taxId + registrationNumber + duns + lei + email + phone + address + source + externalId + isActive + is1099Recipient + createdAt + updatedAt + createdBy + } + } +` diff --git a/clients/graphql/queries/ledger/agents.ts b/clients/graphql/queries/ledger/agents.ts new file mode 100644 index 0000000..b7ea827 --- /dev/null +++ b/clients/graphql/queries/ledger/agents.ts @@ -0,0 +1,46 @@ +import { gql } from 'graphql-request' + +/** + * List agents (REA-aligned counterparty graph). + * + * `agentType` filters to `customer`, `vendor`, or `employee`. `source` + * scopes to a single ingestion source (e.g. `quickbooks`). `isActive` + * defaults to `true` on the resolver — pass explicit `false` to see + * deactivated agents, or `null` to see both. + */ +export const LIST_AGENTS = gql` + query ListLedgerAgents( + $agentType: String + $source: String + $isActive: Boolean = true + $limit: Int! = 50 + $offset: Int! = 0 + ) { + agents( + agentType: $agentType + source: $source + isActive: $isActive + limit: $limit + offset: $offset + ) { + id + agentType + name + legalName + taxId + registrationNumber + duns + lei + email + phone + address + source + externalId + isActive + is1099Recipient + createdAt + updatedAt + createdBy + } + } +` diff --git a/clients/graphql/queries/ledger/eventBlock.ts b/clients/graphql/queries/ledger/eventBlock.ts new file mode 100644 index 0000000..5260d8e --- /dev/null +++ b/clients/graphql/queries/ledger/eventBlock.ts @@ -0,0 +1,39 @@ +import { gql } from 'graphql-request' + +/** + * Get a single event block by id (the inbox detail view). + * + * `metadata` is a JSON dict — for QB-sourced events it carries the + * nested `entries[]` shape (memo + posting_date + line_items) that the + * journal_entry_recorded handler consumes on approve. + */ +export const GET_EVENT_BLOCK = gql` + query GetLedgerEventBlock($id: String!) { + eventBlock(id: $id) { + id + eventType + eventCategory + eventClass + status + occurredAt + effectiveAt + source + externalId + externalUrl + amount + currency + description + metadata + dimensionIds + agentId + resourceType + resourceElementId + replacedByEventId + replacesEventId + obligatedByEventId + dischargesEventId + createdAt + createdBy + } + } +` diff --git a/clients/graphql/queries/ledger/eventBlocks.ts b/clients/graphql/queries/ledger/eventBlocks.ts new file mode 100644 index 0000000..42a6f49 --- /dev/null +++ b/clients/graphql/queries/ledger/eventBlocks.ts @@ -0,0 +1,58 @@ +import { gql } from 'graphql-request' + +/** + * List captured event blocks (the inbox surface). + * + * Filters are independent. `status` defaults to all on the wire, but + * the inbox UI typically passes `'captured'` to show un-reviewed events. + * `eventType` matches values like `invoice_issued`, `bill_received`, + * `payment_received`, `bill_paid`, `sales_receipt_recorded`, + * `journal_entry_recorded`. `eventCategory` groups them: `sales`, + * `purchase`, `adjustment`. + */ +export const LIST_EVENT_BLOCKS = gql` + query ListLedgerEventBlocks( + $eventType: String + $eventCategory: String + $status: String + $agentId: String + $source: String + $limit: Int! = 50 + $offset: Int! = 0 + ) { + eventBlocks( + eventType: $eventType + eventCategory: $eventCategory + status: $status + agentId: $agentId + source: $source + limit: $limit + offset: $offset + ) { + id + eventType + eventCategory + eventClass + status + occurredAt + effectiveAt + source + externalId + externalUrl + amount + currency + description + metadata + dimensionIds + agentId + resourceType + resourceElementId + replacedByEventId + replacesEventId + obligatedByEventId + dischargesEventId + createdAt + createdBy + } + } +`