-
-
Notifications
You must be signed in to change notification settings - Fork 291
Add contributions report query and allow filtering by order transaction charge date #11342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
9f536d7
116fe24
0e27cc9
bdad713
81e22f4
65b6dbc
aa58169
e6ae514
cea8652
2e15ff9
9e91d81
6793169
2984c66
9d8e1ff
a8f3fbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| 'use strict'; | ||
|
|
||
| /** @type {import('sequelize-cli').Migration} */ | ||
| module.exports = { | ||
| async up(queryInterface) { | ||
| await queryInterface.sequelize.query(` | ||
| CREATE INDEX CONCURRENTLY IF NOT EXISTS "transactions__contributions_date" | ||
| ON "Transactions" (COALESCE("clearedAt", "createdAt")) | ||
| INCLUDE ("OrderId", "HostCollectiveId") | ||
| WHERE | ||
| "kind" IN ('CONTRIBUTION', 'ADDED_FUNDS') AND | ||
| type = 'CREDIT' AND | ||
| "isRefund" = false AND "RefundTransactionId" IS NULL AND "deletedAt" IS NULL; | ||
| `); | ||
|
|
||
| await queryInterface.sequelize.query(` | ||
| CREATE INDEX CONCURRENTLY IF NOT EXISTS "transactions__contributions_host_id" | ||
| ON "Transactions" ("HostCollectiveId", COALESCE("clearedAt", "createdAt")) | ||
| INCLUDE ("OrderId") | ||
| WHERE | ||
| "kind" IN ('CONTRIBUTION', 'ADDED_FUNDS') AND | ||
| type = 'CREDIT' AND | ||
| "isRefund" = false AND "RefundTransactionId" IS NULL and "deletedAt" IS NULL; | ||
| `); | ||
| }, | ||
|
|
||
| async down(queryInterface) { | ||
| await queryInterface.sequelize.query(` | ||
| DROP INDEX CONCURRENTLY IF EXISTS "transactions__contributions_date"; | ||
| `); | ||
|
|
||
| await queryInterface.sequelize.query(` | ||
| DROP INDEX CONCURRENTLY IF EXISTS "transactions__contributions_host_id"; | ||
| `); | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| import { Association, IncludeOptions, Model, ModelAttributes, ModelStatic, Sequelize } from 'sequelize'; | ||
|
|
||
| declare module 'sequelize' { | ||
| interface CteOption { | ||
| as: string; | ||
| query: string; | ||
| } | ||
|
|
||
| interface QueryOptions { | ||
| /** @experimental Use with caution. Not tested with all query types. */ | ||
| cte?: CteOption[]; | ||
| } | ||
|
|
||
| interface CountOptions { | ||
| /** @experimental Use with caution. Not tested with all query types. */ | ||
| cte?: CteOption[]; | ||
| } | ||
| } | ||
|
|
||
| // | ||
|
|
||
| /** | ||
| * Adds a sequelize hook to prepend optional CTEs to a query. | ||
| * CTEs can be added via the `cte` option in the query options. | ||
| * | ||
| */ | ||
| export function sequelizeCte(sequelize: Sequelize) { | ||
| sequelize.addHook('beforeQuery', (options, query) => { | ||
| if (options.cte && options.cte.length > 0) { | ||
| const queryGenerator = sequelize.getQueryInterface().queryGenerator as { | ||
| quoteIdentifier: (identifier: string) => string; | ||
| }; | ||
| const runFn = query['run']; | ||
| (query as unknown)['run'] = async function (sql, bindParams) { | ||
| const ctes = options.cte.map(cte => `${queryGenerator.quoteIdentifier(cte.as)} AS (${cte.query})`).join(', '); | ||
| const result = await Reflect.apply(runFn, query, [`WITH ${ctes} ${sql}`, bindParams]); | ||
| return result; | ||
| }; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Adds a CTE to an include option. | ||
| * Used to include a CTE in a query. CTEs are first added via the `cte` option in the query options. | ||
| * @param tableName - The name of the CTE table | ||
| * @param attributes - The attributes of the CTE table | ||
| * @param leftModel - The model to join the CTE to | ||
| * @param leftKey - The key on the left model to join the CTE to | ||
| * @returns The include options for the CTE | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const cte = [ | ||
| * { | ||
| * query: 'SELECT "OrderId" as "id" FROM "Trasactions" t WHERE ...', | ||
| * as: 'TransactionsCTE', | ||
| * }, | ||
| * ]; | ||
| * const include = [ | ||
| * includeCte('TransactionsCTE', { | ||
| * id: { | ||
| * type: DataTypes.INTEGER, | ||
| * }, | ||
| * }, models.Order, 'id'), | ||
| * ]; | ||
| * | ||
| * models.Order.findAll({ | ||
| * cte, | ||
| * include, | ||
| * }); | ||
| * ``` | ||
| * | ||
| * ```sql | ||
| * | ||
| * The query will be: | ||
| * WITH "TransactionsCTE" AS (SELECT "OrderId" as "id" FROM "Trasactions" t WHERE ...) | ||
| * SELECT * FROM "Orders" | ||
| * INNER JOIN "TransactionsCTE" ON "Orders"."id" = "TransactionsCTE"."id" | ||
| * ``` | ||
| */ | ||
| export function includeCte( | ||
| tableName: string, | ||
| attributes: ModelAttributes<Model, unknown>, | ||
| leftModel: ModelStatic<Model>, | ||
| leftKey: string, | ||
| options: Partial<IncludeOptions> = {}, | ||
| ): IncludeOptions { | ||
| return { | ||
| as: tableName, | ||
| association: { | ||
| source: leftModel, | ||
| identifierField: leftKey, | ||
| } as unknown as Association<Model, Model>, | ||
| _pseudo: true, | ||
| model: { | ||
| rawAttributes: attributes, | ||
| getTableName: () => tableName, | ||
| tableAttributes: Object.keys(attributes), | ||
| _injectDependentVirtualAttributes: () => { | ||
| return []; | ||
| }, | ||
| primaryKeyAttribute: Object.entries(attributes).find(([, value]) => value['primaryKey'])?.[0] ?? 'id', | ||
| _virtualAttributes: new Set(), | ||
| options: { | ||
| paranoid: false, | ||
| }, | ||
| _expandAttributes: options => { | ||
| options.attributes = []; | ||
| }, | ||
| } as unknown as ModelStatic<Model>, | ||
|
||
| ...options, | ||
| } as unknown as IncludeOptions; | ||
| } | ||
This comment was marked as outdated.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.