diff --git a/src/graphql/schemaV2.graphql b/src/graphql/schemaV2.graphql index 1b1811d6..0e4e678e 100644 --- a/src/graphql/schemaV2.graphql +++ b/src/graphql/schemaV2.graphql @@ -223,6 +223,16 @@ interface Account { """ updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -951,6 +961,41 @@ interface Account { """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -1691,6 +1736,23 @@ enum ImageFormat { svg } +input AccountReferenceInput { + """ + The public id identifying the account (ie: dgm9bnk8-0437xqry-ejpvzeol-jdayw5re) + """ + id: String + + """ + The internal id of the account (ie: 580) + """ + legacyId: Int @deprecated(reason: "2020-01-01: should only be used during the transition to GraphQL API v2.") + + """ + The slug identifying the account (ie: babel for https://opencollective.com/babel) + """ + slug: String +} + """ A collection of "Members" (ie: Organization backing a Collective) """ @@ -2612,6 +2674,16 @@ type Host implements Account & AccountWithContributions { createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -3356,6 +3428,41 @@ type Host implements Account & AccountWithContributions { """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -4052,23 +4159,6 @@ type Contributor { publicMessage: String } -input AccountReferenceInput { - """ - The public id identifying the account (ie: dgm9bnk8-0437xqry-ejpvzeol-jdayw5re) - """ - id: String - - """ - The internal id of the account (ie: 580) - """ - legacyId: Int @deprecated(reason: "2020-01-01: should only be used during the transition to GraphQL API v2.") - - """ - The slug identifying the account (ie: babel for https://opencollective.com/babel) - """ - slug: String -} - """ A legal document (e.g. W9, W8BEN, W8BEN-E) """ @@ -6111,6 +6201,41 @@ enum ActivityClassType { REPORTS } +""" +A collection of Transactions groups +""" +type TransactionGroupCollection implements Collection { + offset: Int + limit: Int + totalCount: Int + nodes: [TransactionGroup!]! +} + +""" +Transaction group +""" +type TransactionGroup { + id: String! + totalAmount: Amount! + host: Account + + """ + The account on the main side of the transaction (CREDIT -> recipient, DEBIT -> sender) + """ + account: Account + + """ + The primary transaction in the group + """ + primaryTransaction: Transaction + + """ + The transactions in the group + """ + transactions: [Transaction] + createdAt: DateTime +} + """ EXPERIMENTAL (this may change or be deleted): Host transaction report """ @@ -6780,6 +6905,16 @@ type TransactionsImport { """ updatedAt: DateTime! + """ + When the import was last synced + """ + lastSyncAt: DateTime + + """ + Connected account linked to the import + """ + connectedAccount: ConnectedAccount + """ List of rows in the import """ @@ -7515,6 +7650,16 @@ type Bot implements Account { createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -8259,6 +8404,41 @@ type Bot implements Account { """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -8327,6 +8507,16 @@ type Collective implements Account & AccountWithHost & AccountWithContributions createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -9071,6 +9261,41 @@ type Collective implements Account & AccountWithHost & AccountWithContributions """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -9118,6 +9343,11 @@ type Collective implements Account & AccountWithHost & AccountWithContributions """ approvedAt: DateTime + """ + Date when the collective was last unfrozen by current Fiscal Host + """ + unfrozenAt: DateTime + """ Returns whether it's approved by the Fiscal Host """ @@ -9137,6 +9367,17 @@ type Collective implements Account & AccountWithHost & AccountWithContributions """ offset: Int! = 0 ): AgreementCollection + summary( + """ + Calculate amount after this date + """ + dateFrom: DateTime + + """ + Calculate amount before this date + """ + dateTo: DateTime + ): HostedAccountSummary """ Number of unique financial contributors. @@ -9226,6 +9467,11 @@ interface AccountWithHost { """ approvedAt: DateTime + """ + Date when the collective was last unfrozen by current Fiscal Host + """ + unfrozenAt: DateTime + """ Returns whether it's approved by the Fiscal Host """ @@ -9250,6 +9496,31 @@ interface AccountWithHost { """ offset: Int! = 0 ): AgreementCollection + summary( + """ + Calculate amount after this date + """ + dateFrom: DateTime + + """ + Calculate amount before this date + """ + dateTo: DateTime + ): HostedAccountSummary +} + +""" +Return a summary of transaction info about a given account within the context of its current fiscal host +""" +type HostedAccountSummary { + expenseCount: Int + expenseTotal: Amount + expenseMaxValue: Amount + expenseDistinctPayee: Int + contributionCount: Int + contributionTotal: Amount + hostFeeTotal: Amount + spentTotal: Amount } """ @@ -9592,6 +9863,16 @@ type Event implements Account & AccountWithHost & AccountWithContributions & Acc createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -10337,31 +10618,66 @@ type Event implements Account & AccountWithHost & AccountWithContributions & Acc duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! """ - EXPERIMENTAL (this may change or be removed) + [!] Warning: this query is currently in beta and the API might change """ - transactionReports(timeUnit: TimeUnit = MONTH, dateFrom: DateTime, dateTo: DateTime): TransactionReports - webhooks( + transactionGroups( """ The number of results to fetch (default 10, max 1000) """ - limit: Int! = 10 + limit: Int! = 100 """ The offset to use to fetch """ offset: Int! = 0 - account: AccountReferenceInput! - ): WebhookCollection! - - """ - Returns the Fiscal Host - """ - host: Host - """ - Describe how the host charges the collective - """ - hostFeesStructure: HostFeeStructure + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + + """ + EXPERIMENTAL (this may change or be removed) + """ + transactionReports(timeUnit: TimeUnit = MONTH, dateFrom: DateTime, dateTo: DateTime): TransactionReports + webhooks( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 10 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + account: AccountReferenceInput! + ): WebhookCollection! + + """ + Returns the Fiscal Host + """ + host: Host + + """ + Describe how the host charges the collective + """ + hostFeesStructure: HostFeeStructure """ Fees percentage that the host takes for this collective @@ -10383,6 +10699,11 @@ type Event implements Account & AccountWithHost & AccountWithContributions & Acc """ approvedAt: DateTime + """ + Date when the collective was last unfrozen by current Fiscal Host + """ + unfrozenAt: DateTime + """ Returns whether it's approved by the Fiscal Host """ @@ -10402,6 +10723,17 @@ type Event implements Account & AccountWithHost & AccountWithContributions & Acc """ offset: Int! = 0 ): AgreementCollection + summary( + """ + Calculate amount after this date + """ + dateFrom: DateTime + + """ + Calculate amount before this date + """ + dateTo: DateTime + ): HostedAccountSummary """ Number of unique financial contributors. @@ -10639,6 +10971,16 @@ type Individual implements Account { createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -11381,6 +11723,41 @@ type Individual implements Account { """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -11657,6 +12034,16 @@ type Organization implements Account & AccountWithContributions { createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -12399,6 +12786,41 @@ type Organization implements Account & AccountWithContributions { """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -12564,6 +12986,16 @@ type Vendor implements Account & AccountWithContributions { createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -13308,6 +13740,41 @@ type Vendor implements Account & AccountWithContributions { """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -14431,6 +14898,61 @@ type Query { accountingCategory: [String] ): TransactionCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroup( + """ + The public id identifying the transaction group (ie: rvelja97-pkzqbgq7-bbzyx6wd-50o8n4rm) + """ + groupId: String! + + """ + Account associated to the transaction group + """ + account: AccountReferenceInput! + ): TransactionGroup + + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + Reference of the account(s) assigned to the main side of the transaction group (CREDIT -> recipient, DEBIT -> sender) + """ + account: AccountReferenceInput! + + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ Fetch a transactions import """ @@ -16050,6 +16572,16 @@ type Fund implements Account & AccountWithHost & AccountWithContributions { createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -16794,6 +17326,41 @@ type Fund implements Account & AccountWithHost & AccountWithContributions { """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -16841,6 +17408,11 @@ type Fund implements Account & AccountWithHost & AccountWithContributions { """ approvedAt: DateTime + """ + Date when the collective was last unfrozen by current Fiscal Host + """ + unfrozenAt: DateTime + """ Returns whether it's approved by the Fiscal Host """ @@ -16860,6 +17432,17 @@ type Fund implements Account & AccountWithHost & AccountWithContributions { """ offset: Int! = 0 ): AgreementCollection + summary( + """ + Calculate amount after this date + """ + dateFrom: DateTime + + """ + Calculate amount before this date + """ + dateTo: DateTime + ): HostedAccountSummary """ Number of unique financial contributors. @@ -16975,6 +17558,16 @@ type Project implements Account & AccountWithHost & AccountWithContributions & A createdAt: DateTime updatedAt: DateTime + """ + Date of unhosting by a given Fiscal Host. + """ + unhostedAt( + """ + The host account this collective was hosted by + """ + host: AccountReferenceInput! + ): DateTime + """ Returns whether this account is archived """ @@ -17719,6 +18312,41 @@ type Project implements Account & AccountWithHost & AccountWithContributions & A """ duplicatedAccounts(limit: Int! = 100, offset: Int! = 0): AccountCollection! + """ + [!] Warning: this query is currently in beta and the API might change + """ + transactionGroups( + """ + The number of results to fetch (default 10, max 1000) + """ + limit: Int! = 100 + + """ + The offset to use to fetch + """ + offset: Int! = 0 + + """ + Filter transaction groups by the type of the primary transaction + """ + type: TransactionType + + """ + Filter transaction groups by the kind of the primary transaction + """ + kind: TransactionKind + + """ + Only return transaction groups that were created after this date + """ + dateFrom: DateTime + + """ + Only return transaction groups that were created before this date + """ + dateTo: DateTime + ): TransactionGroupCollection! + """ EXPERIMENTAL (this may change or be removed) """ @@ -17766,6 +18394,11 @@ type Project implements Account & AccountWithHost & AccountWithContributions & A """ approvedAt: DateTime + """ + Date when the collective was last unfrozen by current Fiscal Host + """ + unfrozenAt: DateTime + """ Returns whether it's approved by the Fiscal Host """ @@ -17785,6 +18418,17 @@ type Project implements Account & AccountWithHost & AccountWithContributions & A """ offset: Int! = 0 ): AgreementCollection + summary( + """ + Calculate amount after this date + """ + dateFrom: DateTime + + """ + Calculate amount before this date + """ + dateTo: DateTime + ): HostedAccountSummary """ Number of unique financial contributors. @@ -19501,6 +20145,26 @@ type Mutation { type: TransactionsImportType! ): TransactionsImport! + """ + Edit an import + """ + editTransactionsImport( + """ + ID of the import to edit + """ + id: NonEmptyString! + + """ + Source of the import (e.g. "Bank of America", "Eventbrite", etc...) + """ + source: NonEmptyString + + """ + Name of the import (e.g. "Contributions May 2021", "Tickets for Mautic Conference 2024") + """ + name: NonEmptyString + ): TransactionsImport! + """ Import transactions, manually or from a CSV file """ @@ -19541,6 +20205,16 @@ type Mutation { rows: [TransactionsImportRowUpdateInput!]! ): TransactionsImport! + """ + Delete an import and all its associated rows + """ + deleteTransactionsImport( + """ + ID of the import to delete + """ + id: NonEmptyString! + ): Boolean! + """ Create update. Scope: "updates". """ diff --git a/src/server/controllers/hosted-collectives.ts b/src/server/controllers/hosted-collectives.ts index b6d4625e..111a6406 100644 --- a/src/server/controllers/hosted-collectives.ts +++ b/src/server/controllers/hosted-collectives.ts @@ -16,6 +16,44 @@ function json2csv(data, opts) { return parser.parse(data); } +type Fields = + | 'name' + | 'slug' + | 'type' + | 'legalName' + | 'description' + | 'website' + | 'currency' + | 'approvedAt' + | 'balance' + | 'hostFeePercent' + | 'adminEmails' + | 'adminCount' + | 'firstContributionDate' + | 'lastContributionDate' + | 'firstExpenseDate' + | 'lastExpenseDate' + | 'status' + | 'dateApplied' + | 'unhostedAt' + | 'unfrozenAt' + | 'numberOfExpensesYear' + | 'valueOfExpensesYear' + | 'maxExpenseValueYear' + | 'numberOfPayeesYear' + | 'numberOfContributionsYear' + | 'valueOfContributionsYear' + | 'valueOfHostFeeYear' + | 'spentTotalYear' + | 'numberOfExpensesAllTime' + | 'valueOfExpensesAllTime' + | 'maxExpenseValueAllTime' + | 'numberOfPayeesAllTime' + | 'numberOfContributionsAllTime' + | 'valueOfContributionsAllTime' + | 'valueOfHostFeeAllTime' + | 'spentTotalAllTime'; + export const hostedCollectivesQuery = gqlV2` query HostedCollectives( $hostSlug: String! @@ -31,6 +69,9 @@ export const hostedCollectivesQuery = gqlV2` $balance: AmountRangeInput $consolidatedBalance: AmountRangeInput $currencies: [String] + $includeYearSummary: Boolean! + $lastYear: DateTime! + $includeAllTimeSummary: Boolean! ) { host(slug: $hostSlug) { id @@ -70,6 +111,7 @@ export const hostedCollectivesQuery = gqlV2` tags settings createdAt + unhostedAt(host: { slug: $hostSlug }) stats { id balance { @@ -84,15 +126,26 @@ export const hostedCollectivesQuery = gqlV2` value currency } + } policies { id COLLECTIVE_ADMINS_CAN_SEE_PAYOUT_METHODS } ... on AccountWithHost { + host { + id + legacyId + slug + } hostFeesStructure hostFeePercent approvedAt + unfrozenAt + hostApplication { + id + createdAt + } hostAgreements { totalCount nodes { @@ -106,6 +159,26 @@ export const hostedCollectivesQuery = gqlV2` } } } + yearSummary: summary(dateFrom: $lastYear) @include(if: $includeYearSummary) { + expenseTotal { valueInCents, value, currency } + expenseCount + expenseMaxValue { valueInCents, value, currency } + expenseDistinctPayee + contributionCount + contributionTotal { valueInCents, value, currency } + hostFeeTotal { valueInCents, value, currency } + spentTotal { valueInCents, value, currency} + } + allTimeSummary: summary @include(if: $includeAllTimeSummary) { + expenseTotal { valueInCents, value, currency } + expenseCount + expenseMaxValue { valueInCents, value, currency } + expenseDistinctPayee + contributionCount + contributionTotal { valueInCents, value, currency } + hostFeeTotal { valueInCents, value, currency } + spentTotal { valueInCents, value, currency} + } } admins: members(role: [ADMIN]) { totalCount @@ -162,7 +235,7 @@ export const hostedCollectivesQuery = gqlV2` } `; -const csvMapping = { +const csvMapping: Record = { name: 'name', slug: 'slug', type: 'type', @@ -177,12 +250,46 @@ const csvMapping = { adminCount: (account) => account.admins?.totalCount, firstContributionDate: (account) => shortDate(account.firstContributionReceived?.nodes[0]?.createdAt), lastContributionDate: (account) => shortDate(account.lastContributionReceived?.nodes[0]?.createdAt), - totalAmountOfContributions: (account) => - account.stats.totalAmountReceived && amountAsString(account.stats.totalAmountReceived), - totalNumberOfContributions: (account) => account.numberOfContributions?.totalCount, firstExpenseDate: (account) => shortDate(account.firstExpenseReceived?.nodes[0]?.createdAt), lastExpenseDate: (account) => shortDate(account.lastExpenseReceived?.nodes[0]?.createdAt), - numberOfExpenses: (account) => account.numberOfExpenses?.totalCount, + status: (account, host) => { + if (account.host?.id !== host.id) { + return 'UNHOSTED'; + } else if (account.isFrozen) { + return 'FROZEN'; + } else { + return 'ACTIVE'; + } + }, + // Added fields + dateApplied: (account) => shortDate(account.hostApplication?.createdAt), + unhostedAt: (account) => account.unhostedAt && shortDate(account.unhostedAt), + unfrozenAt: (account) => account.unfrozenAtd && shortDate(account.unfrozenAt), + numberOfExpensesYear: (account) => account.yearSummary?.expenseCount, + valueOfExpensesYear: (account) => + account.yearSummary?.expenseTotal && amountAsString(account.yearSummary.expenseTotal), + maxExpenseValueYear: (account) => + account.yearSummary?.expenseMaxValue && amountAsString(account.yearSummary.expenseMaxValue), + numberOfPayeesYear: (account) => account.yearSummary?.expenseDistinctPayee, + numberOfContributionsYear: (account) => account.yearSummary?.contributionCount, + valueOfContributionsYear: (account) => + account.yearSummary?.contributionTotal && amountAsString(account.yearSummary.contributionTotal), + valueOfHostFeeYear: (account) => + account.yearSummary?.hostFeeTotal && amountAsString(account.yearSummary.hostFeeTotal), + spentTotalYear: (account) => account.yearSummary?.spentTotal && amountAsString(account.yearSummary.spentTotal), + numberOfExpensesAllTime: (account) => account.allTimeSummary?.expenseCount, + valueOfExpensesAllTime: (account) => + account.allTimeSummary?.expenseTotal && amountAsString(account.allTimeSummary.expenseTotal), + maxExpenseValueAllTime: (account) => + account.allTimeSummary?.expenseMaxValue && amountAsString(account.allTimeSummary.expenseMaxValue), + numberOfPayeesAllTime: (account) => account.allTimeSummary?.expenseDistinctPayee, + numberOfContributionsAllTime: (account) => account.allTimeSummary?.contributionCount, + valueOfContributionsAllTime: (account) => + account.allTimeSummary?.contributionTotal && amountAsString(account.allTimeSummary.contributionTotal), + valueOfHostFeeAllTime: (account) => + account.allTimeSummary?.hostFeeTotal && amountAsString(account.allTimeSummary.hostFeeTotal), + spentTotalAllTime: (account) => + account.allTimeSummary?.spentTotal && amountAsString(account.allTimeSummary.spentTotal), }; const hostedCollectives: RequestHandler<{ slug: string; format: 'csv' | 'json' }> = async (req, res) => { @@ -210,11 +317,17 @@ const hostedCollectives: RequestHandler<{ slug: string; format: 'csv' | 'json' } const hostSlug = req.params.slug; assert(hostSlug, 'Please provide a slug'); + const fields = (get(req.query, 'fields', '') as string) + .split(',') + .map(trim) + .filter((v) => !!v) as Fields[]; + const variables = { hostSlug, limit: req.method === 'HEAD' ? 0 : req.query.limit ? toNumber(req.query.limit) : 1000, offset: req.query.offset ? toNumber(req.query.offset) : 0, - sort: req.query.sort, + sort: req.query.sort && JSON.parse(req.query.sort as string), + consolidatedBalance: req.query.consolidatedBalance && JSON.parse(req.query.consolidatedBalance as string), hostFeesStructure: req.query.hostFeesStructure, searchTerm: req.query.searchTerm, type: splitEnums(req.query.type as string), @@ -222,14 +335,32 @@ const hostedCollectives: RequestHandler<{ slug: string; format: 'csv' | 'json' } isFrozen: req.query.isFrozen ? parseToBooleanDefaultTrue(req.query.isFrozen as string) : undefined, isUnhosted: req.query.isUnhosted ? parseToBooleanDefaultTrue(req.query.isUnhosted as string) : undefined, currencies: splitEnums(req.query.currencies as string), + lastYear: moment().subtract(1, 'year').toISOString(), + includeYearSummary: fields.some((field) => + [ + 'numberOfExpensesYear', + 'valueOfExpensesYear', + 'maxExpenseValueYear', + 'numberOfPayeesYear', + 'numberOfContributionsYear', + 'valueOfContributionsYear', + 'valueOfHostFeeYear', + ].includes(field), + ), + includeAllTimeSummary: fields.some((field) => + [ + 'numberOfExpensesAllTime', + 'valueOfExpensesAllTime', + 'maxExpenseValueAllTime', + 'numberOfPayeesAllTime', + 'numberOfContributionsAllTime', + 'valueOfContributionsAllTime', + 'valueOfHostFeeAllTime', + ].includes(field), + ), }; const fetchAll = variables.offset ? false : parseToBooleanDefaultFalse(req.query.fetchAll as string); - logger.debug('hostedCollectives:query', variables); - - const fields = (get(req.query, 'fields', '') as string) - .split(',') - .map(trim) - .filter((v) => !!v); + logger.debug('hostedCollectives:query', { variables, headers }); let result = await graphqlRequest(hostedCollectivesQuery, variables, { version: 'v2', headers }); @@ -259,7 +390,7 @@ const hostedCollectives: RequestHandler<{ slug: string; format: 'csv' | 'json' } } const mapping = pick(csvMapping, fields); - const mappedTransactions = result.host.hostedAccounts.nodes.map((t) => applyMapping(mapping, t)); + const mappedTransactions = result.host.hostedAccounts.nodes.map((t) => applyMapping(mapping, t, result.host)); res.write(json2csv(mappedTransactions, null)); res.write(`\n`); @@ -268,7 +399,9 @@ const hostedCollectives: RequestHandler<{ slug: string; format: 'csv' | 'json' } do { variables.offset += result.host.hostedAccounts.limit; result = await graphqlRequest(hostedCollectivesQuery, variables, { version: 'v2', headers }); - const mappedTransactions = result.host.hostedAccounts.nodes.map((t) => applyMapping(mapping, t)); + const mappedTransactions = result.host.hostedAccounts.nodes.map((t) => + applyMapping(mapping, t, result.host), + ); res.write(json2csv(mappedTransactions, { header: false })); res.write(`\n`); } while ( diff --git a/src/server/lib/utils.ts b/src/server/lib/utils.ts index 68120057..18a80b2f 100644 --- a/src/server/lib/utils.ts +++ b/src/server/lib/utils.ts @@ -92,12 +92,12 @@ export const splitIds = (str?: string) => str?.split(',').map(trim) || []; export const splitEnums = (str?: string) => splitIds(str).map(toUpper); -export const applyMapping = (mapping, row) => { +export const applyMapping = (mapping, row, meta?) => { const res = {}; Object.keys(mapping).map((key) => { const val = mapping[key]; if (typeof val === 'function') { - return (res[key] = val(row)); + return (res[key] = val(row, meta)); } else { return (res[key] = get(row, val)); }