diff --git a/.idea/misc.xml b/.idea/misc.xml index c1d50387..8dc5c6c7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,6 @@ + diff --git a/reports/README.md b/reports/README.md new file mode 100644 index 00000000..33e465c8 --- /dev/null +++ b/reports/README.md @@ -0,0 +1,152 @@ +# Kill Bill Reports Overview + +This document provides details about the built-in reports provided by the analytics plugin. It also provides instructions for installing the reports as well as details of the script that can be used to install all the reports. + +## Reports Creation + +All the built-in reports are present in the [reports](https://github.com/killbill/killbill-analytics-plugin/reports) directory. Within this directory, there are separate directories for each report. + +The following files are present in each report directory: + +* `v_report_xxx` - This is the DDL for the view corresponding to the report +* `report_xxx` - This is the DDL corresponding to the database table/stored procedure corresponding to the report +* `README` - This includes the documentation for the report as well as the `curl` command to create the report +* `xxx.png` - This is a screenshot for the report + +In order to create a report, you need to do the following: + +1. Run the DDL inside the `v_report_xxx` to create the view. +2. Run the DDL inside `report_xxx` to create the stored procedure. +3. Run the `curl` command within the README + + +## Report Script + +In addition, we also provide the [reports_setup.sh](reports_setup.sh) script. This script automatically creates all the reports in the `reports` directory. + +### Prerequisites + +- **Bash shell** (Linux, macOS, or Windows Git Bash) +- **MySQL client** installed and accessible in PATH +- Kill Bill running with the Kill Bill Analytics plugin installed +- Appropriate permissions for the MySQL database and Kill Bill API + +--- + +### Usage + +Run the script as follows: + +```bash +./setup_reports.sh +``` + +By default, the script installs DDLs and creates all reports. + +--- + +### Environment Variables + +The script uses environment variables to configure MySQL and Kill Bill settings. Defaults are provided if variables are not set: + +| Variable | Default | Description | +|--------------------------|--------------------------|---------------------------------------------------------------------------| +| `KILLBILL_HTTP_PROTOCOL` | `http` | Kill Bill API protocol | +| `KILLBILL_HOST` | `127.0.0.1` | Kill Bill host | +| `KILLBILL_PORT` | `8080` | Kill Bill port | +| `KILLBILL_USER` | `admin` | Kill Bill username | +| `KILLBILL_PASSWORD` | `password` | Kill Bill password | +| `KILLBILL_API_KEY` | `bob` | Kill Bill API key | +| `KILLBILL_API_SECRET` | `lazar` | Kill Bill API secret | +| `MYSQL_HOST` | `127.0.0.1` | MySQL host | +| `MYSQL_USER` | `root` | MySQL user | +| `MYSQL_PASSWORD` | `killbill` | MySQL password | +| `MYSQL_DATABASE` | `killbill` | MySQL database name | +| `INSTALL_DDL` | `true` | Whether to install DDL files (`true` or `false`) | +| `DROP_EXISTING_REPORT` | `true` | Whether to drop existing reports before creating them (`true` or `false`) | + +You can export environment variables before running the script to override defaults: + +```bash +export KILLBILL_HOST=192.168.1.10 +export MYSQL_PASSWORD=mysecret +export INSTALL_DDL=false + +./setup_reports.sh +``` + +--- + +### Examples + +- **Run with default configuration:** + +```bash +./setup_reports.sh +``` + +- **Skip DDL installation:** + +```bash +export INSTALL_DDL=false +./setup_reports.sh +``` + +- **Disable dropping existing reports:** + +```bash +export DROP_EXISTING_REPORT=false +./setup_reports.sh +``` + +- **Override Kill Bill host and MySQL password:** + +```bash +export KILLBILL_HOST=192.168.1.10 +export MYSQL_PASSWORD=mysecret +./setup_reports.sh +``` + +## Reports Overview + +The following table provides an overview of all the available reports. + +| Report Name | Underlying Report Table | Report Description | +|--------------------------------------------------------------------------------------|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [accounts_summary](accounts_summary/README.md) | report_accounts_summary | Provides an account summary. Provides details like account balance, account status, currency, etc. | +| [active_by_product_term_monthly](active_by_product_term_monthly/README.md) | report_active_by_product_term_monthly | Compute (at the end of each month) the total number of active subscriptions per product and billing period. | +| [bundles_summary](bundles_summary/README.md) | report_bundles_summary | Provides a subscription bundle summary. Provides details like CTD, plan name, price, for the base subscription in a bundle. | +| [cancellations_daily](cancellations_daily/README.md) | report_cancellations_daily | Compute the total number of cancellations per day per phase. | +| [chargebacks_daily](chargebacks_daily/README.md) | report_chargebacks_daily | Compute the total value (in the reference currency) of chargebacks per day per currency. | +| [churn](churn/README.md) | report_churn_percent | Shows the churn percentage for monthly and annual subscriptions on a per-tenant, per-month basis. | +| [churn](churn/README.md) | report_churn_total_usd | Shows the total churned revenue for monthly and annual subscriptions on a per-tenant, per-month basis. | +| [conversion-total-dollar-amount](conversion-total-dollar-amount/README.md) | report_conversion-total-dollar-amount | Compute (monthly) the total revenue from subscriptions converting out of trial, grouped by tenant and billing period. | +| [invoice_aging](invoice_aging/README.md) | report_invoice_aging | This report lists all customer invoice aging with remaining balances, breaking them into standard aging buckets and converting amounts to USD for easy comparison. | +| [invoice_aging_no_payment](invoice_aging_no_payment/README.md) | report_invoice_aging_no_payment | This report lists all customer invoices with no payments recorded, categorizing outstanding amounts into standard aging buckets and converting balances into USD for comparison. +| +| [invoice_credits_daily](invoice_credits_daily/README.md) | report_invoice_credits_daily | Total of invoice credits per tenant, per currency and per day. | +| [invoice_credits_monthly](invoice_credits_monthly/README.md) | report_invoice_credits_monthly | Report of all invoice credits from the previous month, showing amounts in both original currency and USD equivalents. | +| [invoice_item_adjustments_daily](invoice_item_adjustments_daily/README.md) | report_invoice_item_adjustments_daily | Total of invoice item adjustments per tenant, per currency and per day. | +| [invoice_item_adjustments_monthly](invoice_item_adjustments_monthly/README.md) | report_invoice_item_adjustments_monthly | Report of all invoice item adjustments from the previous month, showing amounts in both original currency and USD equivalents. | +| [invoice_items_monthly](invoice_items_monthly/README.md) | report_invoice_items_monthly | Report of all invoice items from the previous month, showing amounts in both original currency and USD equivalents. | +| [invoices_balance_daily](invoices_balance_daily/README.md) | report_invoices_balance_daily | Compute the total sum of invoices balance (in the reference currency) per invoice created day. +| +| [invoices_daily](invoices_daily/README.md) | report_invoices_daily | Compute the total invoice amount charged (in the reference currency) per day per currency. | +| [invoices_monthly](invoices_monthly/README.md) | report_invoices_monthly | Report of all invoices from the previous month, showing amounts in both original currency and USD equivalents. | +| [mrr_daily](mrr_daily/README.md) | report_mrr_daily | Computes the total active MRR (monthly recurring revenue), broken down both by product and as a tenant-wide total (ALL) for each tenant and each day. | +| [new_accounts_daily](new_accounts_daily/README.md) | report_new_accounts_daily | Compute the total amount of new accounts created per day for each tenant. | +| [overdue-states-count-daily](overdue-states-count-daily/README.md) | report_overdue-states-count-daily | Count of overdue states per tenant and per day. | +| [payment_provider_conversions](payment_provider_conversions/README.md) | report_payment_provider_conversions | Compare the total number of transactions and the number of successful transactions that have occurred in a recent 15 minutes period to the same metrics that occurred in the corresponding 15 minutes from 14 days ago. | +| [payment_provider_errors](payment_provider_errors/README.md) | report_payment_provider_errors | Compute the top errors per provider and currency, per day. | +| [payment_provider_monitor](payment_provider_monitor/README.md) | report_payment_provider_monitor | Compute the number of successful transactions that have occurred in the past hour, for each payment service provider that has had transactions within the last week. | +| [payments_by_provider](payments_by_provider/README.md) | report_payments_by_provider | Compute the number of payments by transaction state over different timeframes for each payment service provider (plugin). | +| [payments_by_provider](payments_by_provider/README.md) | report_payments_by_provider_last_24h_summary | Compute the number of payments by transaction state over different timeframes for each payment service provider (plugin). | +| [payments_monthly](payments_monthly/README.md) | report_payments_monthly | Report of all payments from the previous month, showing amounts in both original currency and USD equivalents. | +| [payments_summary](payments_summary/README.md) | report_payments_summary | Provides payment summary. Provides details like payment_id, amount, etc. | +| [payments_total_daily](payments_total_daily/README.md) | report_payments_total_daily | Compute the total value (in the reference currency) of payments per day per currency. | +| [refunds-monthly](refunds-monthly/README.md) | report_refunds-monthly | Report of all refunds from the previous month, showing amounts in both original currency and USD equivalents. | +| [refunds_total_daily](refunds_total_daily/README.md) | report_refunds_total_daily | Compute the total value (in the reference currency) of refunds per day per currency for each tenant. | +| [subscribers-vs-non-subscribers](subscribers-vs-non-subscribers/README.md) | report_subscribers-vs-non-subscribers | Compute the total number of active (i.e. with at least one active subscription) and non-active accounts per tenant. | +| [trial-starts-count-daily](trial-starts-count-daily/README.md) | report_trial-starts-count-daily | Count of new trial subscriptions per tenant, per day and per product. | +| [trial-to-no-trial-conversions_daily](trial-to-no-trial-conversions_daily/README.md) | report_trial-to-no-trial-conversions_daily | Count of subscriptions converting from trial to non-trial per tenant per day. | + diff --git a/reports/accounts_summary/README.md b/reports/accounts_summary/README.md index 029e971e..8c83c977 100644 --- a/reports/accounts_summary/README.md +++ b/reports/accounts_summary/README.md @@ -1,10 +1,10 @@ -# Accounts summary report +# Accounts Summary Report Provides an account summary. Provides details like account balance, account status, currency, etc. The snapshot view is: `v_report_accounts_summary` -## Pie chart configuration +## Report Creation ``` curl -v \ @@ -15,9 +15,15 @@ curl -v \ -H 'Content-Type: application/json' \ -d '{"reportName": "report_accounts_summary", "reportType": "TABLE", - "reportPrettyName": "Accounts summary", + "reportPrettyName": "Accounts Summary", "sourceTableName": "report_accounts_summary", "refreshProcedureName": "refresh_report_accounts_summary", "refreshFrequency": "HOURLY"}' \ "http://127.0.0.1:8080/plugins/killbill-analytics/reports" ``` + +## Report UI: + +![accounts-summary.png](accounts-summary.png) + + diff --git a/reports/accounts_summary/accounts-summary.png b/reports/accounts_summary/accounts-summary.png new file mode 100644 index 00000000..198607e3 Binary files /dev/null and b/reports/accounts_summary/accounts-summary.png differ diff --git a/reports/accounts_summary/v_report_account_summary.ddl b/reports/accounts_summary/v_report_accounts_summary.ddl similarity index 100% rename from reports/accounts_summary/v_report_account_summary.ddl rename to reports/accounts_summary/v_report_accounts_summary.ddl diff --git a/reports/active_by_product_term_monthly/README.md b/reports/active_by_product_term_monthly/README.md new file mode 100644 index 00000000..4cc33f94 --- /dev/null +++ b/reports/active_by_product_term_monthly/README.md @@ -0,0 +1,40 @@ +# Monthly Active Subscriptions Report + +Compute (at the end of each month) the total number of active subscriptions per product and billing period. + +The snapshot view is: `v_report_active_by_product_term_monthly` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_active_by_product_term_monthly", + "reportType": "TIMELINE", + "reportPrettyName": "Monthly Active Subscriptions by Product Term", + "sourceTableName": "report_active_by_product_term_monthly", + "refreshProcedureName": "refresh_active_by_product_term_monthly", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` +## Sample Data + +| tenant_record_id | day | product_name | billing_period | count | +|------------------|------------|--------------|----------------|-------| +| 1 | 2025-08-31 | Gold | MONTHLY | 3 | +| 2 | 2025-08-31 | Gold | MONTHLY | 1 | +| 1 | 2025-08-31 | Gold | ANNUAL | 2 | +| 2 | 2025-09-30 | Silver | MONTHLY | 4 | +| 2 | 2025-09-30 | Gold | QUARTERLY | 1 | + +The first row in the above table indicates that on `2025-08-31` the `tenant_record_id=1` had 3 Monthly subscriptions for the `Gold` product. + + + +## Report UI: + +![monthly-active-subs-by-product-term.png](monthly-active-subs-by-product-term.png) \ No newline at end of file diff --git a/reports/active_by_product_term_monthly/monthly-active-subs-by-product-term.png b/reports/active_by_product_term_monthly/monthly-active-subs-by-product-term.png new file mode 100644 index 00000000..3b169e45 Binary files /dev/null and b/reports/active_by_product_term_monthly/monthly-active-subs-by-product-term.png differ diff --git a/src/main/resources/reports/active_by_product_term_monthly/report_active_by_product_term_monthly.ddl b/reports/active_by_product_term_monthly/report_active_by_product_term_monthly.ddl similarity index 56% rename from src/main/resources/reports/active_by_product_term_monthly/report_active_by_product_term_monthly.ddl rename to reports/active_by_product_term_monthly/report_active_by_product_term_monthly.ddl index fdc00ba4..972619be 100644 --- a/src/main/resources/reports/active_by_product_term_monthly/report_active_by_product_term_monthly.ddl +++ b/reports/active_by_product_term_monthly/report_active_by_product_term_monthly.ddl @@ -11,27 +11,7 @@ DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; START TRANSACTION; delete from report_active_by_product_term_monthly; - insert into report_active_by_product_term_monthly - select - x.tenant_record_id - , cal.d day - , x.product_name - , x.billing_period - , x.count - from calendar cal - join ( - select - tenant_record_id - , last_day(day) day - , product_name - , billing_period - , count - from - v_report_active_by_product_term_monthly - where 1=1 - ) x on last_day(cal.d) = x.day - where 1=1 - ; + insert into report_active_by_product_term_monthly select * from v_report_active_by_product_term_monthly; COMMIT; END; diff --git a/src/main/resources/reports/active_by_product_term_monthly/v_report_active_by_product_term_monthly.ddl b/reports/active_by_product_term_monthly/v_report_active_by_product_term_monthly.ddl similarity index 100% rename from src/main/resources/reports/active_by_product_term_monthly/v_report_active_by_product_term_monthly.ddl rename to reports/active_by_product_term_monthly/v_report_active_by_product_term_monthly.ddl diff --git a/reports/bundles_summary/README.md b/reports/bundles_summary/README.md index f96c8447..91eb6619 100644 --- a/reports/bundles_summary/README.md +++ b/reports/bundles_summary/README.md @@ -4,7 +4,7 @@ Provides a subscription bundle summary. Provides details like CTD, plan name, pr The snapshot view is: `v_report_bundles_summary` -## Pie chart configuration +## Report Creation ``` curl -v \ @@ -15,9 +15,13 @@ curl -v \ -H 'Content-Type: application/json' \ -d '{"reportName": "report_bundles_summary", "reportType": "TABLE", - "reportPrettyName": "Bundles summary", + "reportPrettyName": "Bundles Summary", "sourceTableName": "report_bundles_summary", "refreshProcedureName": "refresh_report_bundles_summary", "refreshFrequency": "HOURLY"}' \ "http://127.0.0.1:8080/plugins/killbill-analytics/reports" ``` + +## Report UI: + +![bundles-summary.png](bundles-summary.png) \ No newline at end of file diff --git a/reports/bundles_summary/bundles-summary.png b/reports/bundles_summary/bundles-summary.png new file mode 100644 index 00000000..b6a3e683 Binary files /dev/null and b/reports/bundles_summary/bundles-summary.png differ diff --git a/reports/cancellations_daily/README.md b/reports/cancellations_daily/README.md new file mode 100644 index 00000000..a3cd9985 --- /dev/null +++ b/reports/cancellations_daily/README.md @@ -0,0 +1,39 @@ +# Daily Cancellations Report + +Compute the total number of cancellations per day per phase. + +The snapshot view is: `v_report_cancellations_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_cancellations_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Cancellations", + "sourceTableName": "report_cancellations_daily", + "refreshProcedureName": "refresh_report_cancellations_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` +## Sample Data + +| tenant_record_id | phase | day | count | +|------------------|-----------|------------|-------| +| 1 | EVERGREEN | 2025-09-10 | 9 | +| 1 | EVERGREEN | 2025-09-03 | 2 | +| 1 | EVERGREEN | 2025-09-22 | 5 | +| 2 | EVERGREEN | 2025-09-23 | 6 | +| 2 | EVERGREEN | 2025-09-24 | 6 | +| 1 | EVERGREEN | 2025-09-25 | 1 | + +The first row in the above table indicates that on `2025-09-10`, the `tenant_record_id=1` had 9 cancellations in `EVERGREEN` phase. + +## Report UI: + +![daily-cancellations.png](daily-cancellations.png) \ No newline at end of file diff --git a/reports/cancellations_daily/daily-cancellations.png b/reports/cancellations_daily/daily-cancellations.png new file mode 100644 index 00000000..1b66818f Binary files /dev/null and b/reports/cancellations_daily/daily-cancellations.png differ diff --git a/src/main/resources/reports/cancellations_daily/report_cancellations_daily.ddl b/reports/cancellations_daily/report_cancellations_daily.ddl similarity index 100% rename from src/main/resources/reports/cancellations_daily/report_cancellations_daily.ddl rename to reports/cancellations_daily/report_cancellations_daily.ddl diff --git a/src/main/resources/reports/cancellations_daily/v_report_cancellations_daily.ddl b/reports/cancellations_daily/v_report_cancellations_daily.ddl similarity index 100% rename from src/main/resources/reports/cancellations_daily/v_report_cancellations_daily.ddl rename to reports/cancellations_daily/v_report_cancellations_daily.ddl diff --git a/reports/chargebacks_daily/README.md b/reports/chargebacks_daily/README.md new file mode 100644 index 00000000..374c788a --- /dev/null +++ b/reports/chargebacks_daily/README.md @@ -0,0 +1,40 @@ +# Daily Chargebacks Report + +Compute the total value (in the reference currency) of chargebacks per day per currency. + +The snapshot view is: `v_report_chargebacks_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_chargebacks_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Chargebacks Value", + "sourceTableName": "report_chargebacks_daily", + "refreshProcedureName": "refresh_report_chargebacks_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| tenant_record_id | day | currency | count | +|------------------|------------|----------|----------| +| 1 | 2025-09-18 | USD | 100.0000 | +| 1 | 2025-09-26 | USD | 50.0000 | +| 2 | 2025-09-11 | USD | 100.0000 | + +The first row in the above table indicates that on `2025-09-18`, the `tenant_record_id=1` had a total chargeback value of `$100` + + +## Report UI: + +![chargebacks-daily.png](chargebacks-daily.png) + + diff --git a/reports/chargebacks_daily/chargebacks-daily.png b/reports/chargebacks_daily/chargebacks-daily.png new file mode 100644 index 00000000..abf8e3d2 Binary files /dev/null and b/reports/chargebacks_daily/chargebacks-daily.png differ diff --git a/src/main/resources/reports/chargebacks_daily/report_chargebacks_daily.ddl b/reports/chargebacks_daily/report_chargebacks_daily.ddl similarity index 100% rename from src/main/resources/reports/chargebacks_daily/report_chargebacks_daily.ddl rename to reports/chargebacks_daily/report_chargebacks_daily.ddl diff --git a/src/main/resources/reports/chargebacks_daily/v_report_chargebacks_daily.ddl b/reports/chargebacks_daily/v_report_chargebacks_daily.ddl similarity index 87% rename from src/main/resources/reports/chargebacks_daily/v_report_chargebacks_daily.ddl rename to reports/chargebacks_daily/v_report_chargebacks_daily.ddl index 2580c78c..01cccd1c 100644 --- a/src/main/resources/reports/chargebacks_daily/v_report_chargebacks_daily.ddl +++ b/reports/chargebacks_daily/v_report_chargebacks_daily.ddl @@ -3,7 +3,7 @@ select ac.tenant_record_id , date_format(ac.created_date,'%Y-%m-%d') as day , ac.currency -, sum(ac.converted_amount) as count +, sum(ac.amount) as count from analytics_payment_chargebacks ac where 1=1 diff --git a/reports/churn/README.md b/reports/churn/README.md new file mode 100644 index 00000000..0d931a2a --- /dev/null +++ b/reports/churn/README.md @@ -0,0 +1,54 @@ +# Churn (Monthly and Annual) Report + +This report tracks customer churn in dollar terms, showing both the total churned revenue and the churn percentage for monthly and annual subscriptions on a per-tenant, per-month basis. + +The snapshot view is: `v_report_churn_percent_and_total_usd` + +## Database Indices + +The churn reports requires some additional database indices to be created. See [churn-indices.ddl](churn-indices.ddl) + +## Churn Percent Configuration + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_churn_percent", + "reportType": "TIMELINE", + "reportPrettyName": "Churn Percent (Monthly and Annual)", + "sourceTableName": "report_churn_percent", + "refreshProcedureName": "refresh_report_churn_percent", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Churn Percentage Report UI + +![churn-percent.png.png](churn-percent.png) + +## Churn Amount Configuration + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_churn_total_usd", + "reportType": "TIMELINE", + "reportPrettyName": "Churn Total USD (Monthly and Annual)", + "sourceTableName": "report_churn_total_usd", + "refreshProcedureName": "refresh_report_churn_total_usd", + "refreshFrequency": "DAILY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Churn Amount Report UI + + +![churn-amount.png](churn-amount.png) \ No newline at end of file diff --git a/reports/churn/churn-amount.png b/reports/churn/churn-amount.png new file mode 100644 index 00000000..94d462f9 Binary files /dev/null and b/reports/churn/churn-amount.png differ diff --git a/reports/churn/churn-indices.ddl b/reports/churn/churn-indices.ddl new file mode 100644 index 00000000..af24f219 --- /dev/null +++ b/reports/churn/churn-indices.ddl @@ -0,0 +1,4 @@ +CREATE INDEX idx_ai_tenant_bundle ON analytics_invoice_items(tenant_record_id, bundle_id, invoice_original_amount_charged, invoice_balance); +CREATE INDEX idx_ast_bundle_tenant_event ON analytics_subscription_transitions(bundle_id, tenant_record_id, event, next_start_date, next_end_date, next_billing_period); +CREATE INDEX idx_subs_id_tenant ON subscriptions(id, tenant_record_id); +CREATE INDEX idx_se_sub_tenant_type ON subscription_events(subscription_id, tenant_record_id, user_type); \ No newline at end of file diff --git a/reports/churn/churn-percent.png b/reports/churn/churn-percent.png new file mode 100644 index 00000000..080e8ab7 Binary files /dev/null and b/reports/churn/churn-percent.png differ diff --git a/reports/churn/report_churn_percent.ddl b/reports/churn/report_churn_percent.ddl new file mode 100644 index 00000000..8ee9ba72 --- /dev/null +++ b/reports/churn/report_churn_percent.ddl @@ -0,0 +1,21 @@ +create table report_churn_percent as select tenant_record_id + , month day, billing_period, churn_pct count from v_report_churn_percent_and_total_usd limit 0; + +drop procedure if exists refresh_report_churn_percent; +DELIMITER // +CREATE PROCEDURE refresh_report_churn_percent() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_churn_percent; + insert into report_churn_percent select tenant_record_id + , month day, billing_period, churn_pct count from v_report_churn_percent_and_total_usd; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/churn/report_churn_total_usd.ddl b/reports/churn/report_churn_total_usd.ddl new file mode 100644 index 00000000..e9378f5f --- /dev/null +++ b/reports/churn/report_churn_total_usd.ddl @@ -0,0 +1,21 @@ +create table report_churn_total_usd as select tenant_record_id + , month day, billing_period, churn_dollars count from v_report_churn_percent_and_total_usd limit 0; + +drop procedure if exists refresh_report_churn_total_usd; +DELIMITER // +CREATE PROCEDURE refresh_report_churn_total_usd() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_churn_total_usd; + insert into report_churn_total_usd select tenant_record_id + , month day, billing_period, churn_dollars count from v_report_churn_percent_and_total_usd; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/churn/v_report_churn_percent_and_total_usd.ddl b/reports/churn/v_report_churn_percent_and_total_usd.ddl new file mode 100644 index 00000000..b748e367 --- /dev/null +++ b/reports/churn/v_report_churn_percent_and_total_usd.ddl @@ -0,0 +1,143 @@ +create or replace view v_report_churn_percent_and_total_usd as +with paid_bundles_monthly as ( + select distinct tenant_record_id, bundle_id + from analytics_invoice_items + where invoice_original_amount_charged > 0 + and invoice_balance = 0 +), +paid_bundles_annual as ( + select distinct tenant_record_id, bundle_id + from ( + select tenant_record_id, bundle_id + from analytics_invoice_items + where invoice_original_amount_charged > 0 + and invoice_balance = 0 + union + select s.tenant_record_id, s.bundle_id + from subscription_events se + join subscriptions s + on se.subscription_id = s.id + and se.tenant_record_id = s.tenant_record_id + where user_type in ('MIGRATE_ENTITLEMENT') + ) bundles +), +paid_bundles_annual2 as ( + select distinct tenant_record_id, bundle_id, charged_through_date + from ( + select tenant_record_id, bundle_id, end_date as charged_through_date + from analytics_invoice_items + where invoice_original_amount_charged > 0 + and invoice_balance = 0 + union + select s.tenant_record_id, s.bundle_id, effective_date as charged_through_date + from subscription_events se + join subscriptions s + on se.subscription_id = s.id + and se.tenant_record_id = s.tenant_record_id + where user_type in ('MIGRATE_ENTITLEMENT') + ) bundles +) +-- ============================ +-- MONTHLY PART +-- ============================ +select + active_sub_dollar.tenant_record_id, + active_sub_dollar.month, + round(churn_dollar.amount) as churn_dollars, + round(churn_dollar.amount / active_sub_dollar.amount, 4) as churn_pct, + 'MONTHLY' as billing_period +from ( + select + ast.tenant_record_id, + date_format(next_start_date, '%Y-%m-01') as month, + prev_billing_period, + sum(converted_prev_price) as amount + from analytics_subscription_transitions ast + join paid_bundles_monthly b + on ast.bundle_id = b.bundle_id + and ast.tenant_record_id = b.tenant_record_id + where report_group = 'default' + and next_service = 'entitlement-service' + and event like 'STOP_ENTITLEMENT%' + and prev_billing_period in ('MONTHLY') + group by 1,2,3 +) churn_dollar +join ( + select + ast.tenant_record_id, + cal.d as month, + next_billing_period, + sum(converted_next_price) as amount + from analytics_subscription_transitions ast + join calendar cal + on next_start_date < cal.d + and (next_end_date > cal.d or next_end_date is null) + and cal.d = date_format(cal.d, '%Y-%m-01') + and cal.d >= '2013-01-01' + and cal.d < sysdate() + join paid_bundles_monthly b + on ast.bundle_id = b.bundle_id + and ast.tenant_record_id = b.tenant_record_id + where report_group = 'default' + and next_service = 'entitlement-service' + and event not like 'STOP_ENTITLEMENT%' + and next_billing_period in ('MONTHLY') + group by 1,2,3 +) active_sub_dollar + on churn_dollar.month = active_sub_dollar.month + and churn_dollar.prev_billing_period = active_sub_dollar.next_billing_period + and churn_dollar.tenant_record_id = active_sub_dollar.tenant_record_id + +union all + +-- ============================ +-- ANNUAL PART +-- ============================ +select + churn_dollar.tenant_record_id, + churn_dollar.month, + churn_dollar.amount as churn_dollars, + round(churn_dollar.amount / active_sub_dollar.amount, 4) as churn_pct, + 'ANNUAL' as billing_period +from ( + select + ast.tenant_record_id, + date_format(next_start_date, '%Y-%m-01') as month, + prev_billing_period, + round(sum(converted_prev_price)) as amount + from analytics_subscription_transitions ast + join paid_bundles_annual b + on ast.bundle_id = b.bundle_id + and ast.tenant_record_id = b.tenant_record_id + where report_group = 'default' + and next_service = 'entitlement-service' + and event like 'STOP_ENTITLEMENT%' + and prev_billing_period in ('ANNUAL') + group by 1,2,3 +) churn_dollar +join ( + select + ast.tenant_record_id, + cal.d as month, + next_billing_period, + round(sum(converted_next_price)) as amount + from analytics_subscription_transitions ast + join calendar cal + on next_start_date < cal.d + and (next_end_date > cal.d or next_end_date is null) + and cal.d = date_format(cal.d, '%Y-%m-01') + and cal.d >= '2013-01-01' + and cal.d < sysdate() + join paid_bundles_annual2 b + on ast.bundle_id = b.bundle_id + and ast.tenant_record_id = b.tenant_record_id + where report_group = 'default' + and next_service = 'entitlement-service' + and event not like 'STOP_ENTITLEMENT%' + and next_billing_period in ('ANNUAL') + and extract(month from date_add(charged_through_date, interval 1 day)) = extract(month from cal.d) + group by 1,2,3 +) active_sub_dollar + on churn_dollar.month = active_sub_dollar.month + and churn_dollar.prev_billing_period = active_sub_dollar.next_billing_period + and churn_dollar.tenant_record_id = active_sub_dollar.tenant_record_id; diff --git a/reports/conversion-total-dollar-amount/README.md b/reports/conversion-total-dollar-amount/README.md new file mode 100644 index 00000000..a89de416 --- /dev/null +++ b/reports/conversion-total-dollar-amount/README.md @@ -0,0 +1,39 @@ +# Conversions Total Dollar Amount Report + +Compute (monthly) the total revenue from subscriptions converting out of trial, grouped by tenant and billing period. + +The snapshot view is: `v_report_conversions_total_dollar_monthly` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_conversions_total_dollar_monthly", + "reportType": "TIMELINE", + "reportPrettyName": "Conversions Total Dollar Amount", + "sourceTableName": "report_conversions_total_dollar_monthly", + "refreshProcedureName": "refresh_report_conversions_total_dollar_monthly", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +|Tenant Record Id|Day |Billing Period| Count| +|--|--|--|--| +|1|2025-06-01 |WEEKLY |30| +|1|2025-07-01 |MONTHLY |30| +|1| 2025-07-01 |QUARTERLY |70| +|6| 2025-01-01 |ANNUAL|200| +|1| 2025-04-01 |MONTHLY|30| + +Here `day` represents the first day of the month representing that subscription's conversion month. So if the subscription converts from TRIAL to EVERGREEN phase on `2025-04-15`, the `day` value will be `2025-04-01`. The first row in the above table indicates that in the month of June, a revenue of `$30` was generated when `WEEKLY` subscriptions moved out of `TRIAL` phase. + +## Report UI: + +![conversion-total-dollar-amount.png](conversion-total-dollar-amount.png) diff --git a/reports/conversion-total-dollar-amount/conversion-total-dollar-amount.png b/reports/conversion-total-dollar-amount/conversion-total-dollar-amount.png new file mode 100644 index 00000000..495f8ff5 Binary files /dev/null and b/reports/conversion-total-dollar-amount/conversion-total-dollar-amount.png differ diff --git a/reports/conversion-total-dollar-amount/report_conversions_total_dollar_monthly.ddl b/reports/conversion-total-dollar-amount/report_conversions_total_dollar_monthly.ddl new file mode 100644 index 00000000..a0a82d40 --- /dev/null +++ b/reports/conversion-total-dollar-amount/report_conversions_total_dollar_monthly.ddl @@ -0,0 +1,19 @@ +create table report_conversions_total_dollar_monthly as select * from v_report_conversions_total_dollar_monthly limit 0; + +drop procedure if exists refresh_report_conversions_total_dollar_monthly; +DELIMITER // +CREATE PROCEDURE refresh_report_conversions_total_dollar_monthly() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_conversions_total_dollar_monthly; + insert into report_conversions_total_dollar_monthly select * from v_report_conversions_total_dollar_monthly; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/conversion-total-dollar-amount/v_report_conversions_total_dollar_monthly.ddl b/reports/conversion-total-dollar-amount/v_report_conversions_total_dollar_monthly.ddl new file mode 100644 index 00000000..b9045e37 --- /dev/null +++ b/reports/conversion-total-dollar-amount/v_report_conversions_total_dollar_monthly.ddl @@ -0,0 +1,21 @@ +create or replace view v_report_conversions_total_dollar_monthly as +select + ast.tenant_record_id, + date_format(next_start_date, '%Y-%m-01') day, + next_billing_period billing_period, + round(sum(converted_next_price)) count +from analytics_subscription_transitions ast +join ( + select distinct tenant_record_id, bundle_id + from analytics_invoice_items + where invoice_original_amount_charged > 0 + and invoice_balance = 0 +) paid_bundles + on ast.bundle_id = paid_bundles.bundle_id + and ast.tenant_record_id = paid_bundles.tenant_record_id +where report_group = 'default' + and next_service = 'entitlement-service' + and prev_phase = 'TRIAL' + and next_phase != 'TRIAL' + and event not like 'STOP_ENTITLEMENT%' +group by 1,2,3; diff --git a/reports/invoice_aging/README.md b/reports/invoice_aging/README.md new file mode 100644 index 00000000..178cc0a6 --- /dev/null +++ b/reports/invoice_aging/README.md @@ -0,0 +1,31 @@ +# Invoice Aging Report + +This report lists all customer invoice aging with remaining balances, breaking them into standard aging buckets and converting amounts to USD for easy comparison. + +The snapshot view is: `v_report_invoice_aging` + +## Prerequisites + +This report requires the `analytics_currency_conversion` table to be populated. See [insertMonthlyCurrencyConversionRates.ddl](../utils/insertMonthlyCurrencyConversionRates.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_invoice_aging", + "reportType": "TABLE", + "reportPrettyName": "Invoice Aging Report", + "sourceTableName": "report_invoice_aging", + "refreshProcedureName": "refresh_report_invoice_aging", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Report UI: + +![invoice-aging.png](invoice-aging.png) diff --git a/reports/invoice_aging/invoice-aging.png b/reports/invoice_aging/invoice-aging.png new file mode 100644 index 00000000..e4f749ca Binary files /dev/null and b/reports/invoice_aging/invoice-aging.png differ diff --git a/reports/invoice_aging/report_invoice_aging.ddl b/reports/invoice_aging/report_invoice_aging.ddl new file mode 100644 index 00000000..f7fcd599 --- /dev/null +++ b/reports/invoice_aging/report_invoice_aging.ddl @@ -0,0 +1,19 @@ +create table report_invoice_aging as select * from v_report_invoice_aging limit 0; + +drop procedure if exists refresh_report_invoice_aging; +DELIMITER // +CREATE PROCEDURE refresh_report_invoice_aging() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_invoice_aging; + insert into report_invoice_aging select * from v_report_invoice_aging; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/invoice_aging/v_report_invoice_aging.ddl b/reports/invoice_aging/v_report_invoice_aging.ddl new file mode 100644 index 00000000..bc81af6e --- /dev/null +++ b/reports/invoice_aging/v_report_invoice_aging.ddl @@ -0,0 +1,77 @@ +CREATE OR REPLACE VIEW v_report_invoice_aging AS +WITH date_buckets AS ( + SELECT + CURRENT_DATE AS today, + CURRENT_DATE - INTERVAL 30 DAY AS d_0_30, + CURRENT_DATE - INTERVAL 60 DAY AS d_30_60, + CURRENT_DATE - INTERVAL 90 DAY AS d_60_90, + CURRENT_DATE - INTERVAL 120 DAY AS d_90_120, + CURRENT_DATE - INTERVAL 150 DAY AS d_120_150 +), +invoice_data AS ( + SELECT + ii.invoice_number, + ii.account_name, + ii.account_external_key, + CAST(ii.created_date AS DATE) AS invoice_creation_date, + CAST(ii.invoice_date AS DATE) AS invoice_date, + CAST(ii.start_date AS DATE) AS service_start_date, + CAST(ii.end_date AS DATE) AS service_end_date, + ii.bundle_external_key, + ii.product_name, + ii.slug, + ii.currency, + ii.invoice_original_amount_charged, + ii.invoice_balance, + ii.amount, + ii.created_date, + ii.invoice_item_record_id, + ii.tenant_record_id + FROM analytics_invoice_items ii + WHERE ii.invoice_date < CAST(DATE_FORMAT(SYSDATE(), '%Y-%m-01') AS DATE) + AND ii.report_group != 'test' + AND ii.invoice_balance > 0 +) +SELECT + a.account_name AS "Customer Name", + a.account_external_key AS "Account Number", + a.currency AS "Currency", + + -- Balance due buckets + CASE WHEN a.invoice_creation_date > b.d_0_30 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 0-30 Days", + CASE WHEN a.invoice_creation_date BETWEEN b.d_30_60 AND b.d_0_30 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 30-60 Days", + CASE WHEN a.invoice_creation_date BETWEEN b.d_60_90 AND b.d_30_60 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 60-90 Days", + CASE WHEN a.invoice_creation_date BETWEEN b.d_90_120 AND b.d_60_90 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 90-120 Days", + CASE WHEN a.invoice_creation_date BETWEEN b.d_120_150 AND b.d_90_120 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 120-150 Days", + CASE WHEN a.invoice_creation_date < b.d_120_150 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 150+ Days", + + a.invoice_original_amount_charged AS "Total Balance Due", + + -- Balance due in USD + CASE WHEN a.invoice_creation_date > b.d_0_30 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 0-30 Days USD", + CASE WHEN a.invoice_creation_date BETWEEN b.d_30_60 AND b.d_0_30 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 30-60 Days USD", + CASE WHEN a.invoice_creation_date BETWEEN b.d_60_90 AND b.d_30_60 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 60-90 Days USD", + CASE WHEN a.invoice_creation_date BETWEEN b.d_90_120 AND b.d_60_90 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 90-120 Days USD", + CASE WHEN a.invoice_creation_date BETWEEN b.d_120_150 AND b.d_90_120 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 120-150 Days USD", + CASE WHEN a.invoice_creation_date < b.d_120_150 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 150+ Days USD", + + CASE WHEN a.currency != 'USD' THEN cc.reference_rate * a.invoice_original_amount_charged ELSE a.invoice_original_amount_charged END AS "Total Balance Due USD" , + + a.invoice_number AS "Invoice Number", + a.bundle_external_key AS "Bundle External Key", + a.slug AS "Slug", + a.service_start_date AS "Service Start Date", + a.service_end_date AS "Service End Date", + a.invoice_date AS "Invoice Date", + a.invoice_original_amount_charged AS "Invoice Amount", + a.invoice_balance AS "Invoice Balance", + case when a.currency != 'USD' then ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) else a.invoice_original_amount_charged end AS "Invoice Amount USD", + case when a.currency != 'USD' then ROUND(cc.reference_rate * a.invoice_balance, 4) else a.invoice_balance end AS "Invoice Balance USD", + a.tenant_record_id +FROM invoice_data a +LEFT OUTER JOIN analytics_currency_conversion cc + ON a.created_date >= cc.start_date + AND a.created_date <= cc.end_date + AND cc.currency = a.currency +CROSS JOIN date_buckets b +ORDER BY a.account_name, a.invoice_number, a.invoice_item_record_id; \ No newline at end of file diff --git a/reports/invoice_aging_no_payment/README.md b/reports/invoice_aging_no_payment/README.md new file mode 100644 index 00000000..dff5c681 --- /dev/null +++ b/reports/invoice_aging_no_payment/README.md @@ -0,0 +1,31 @@ +# Invoice Aging No Payments Report + +This report lists all customer invoices with no payments recorded, categorizing outstanding amounts into standard aging buckets and converting balances into USD for comparison. + +The snapshot view is: `v_report_invoice_aging_no_payment` + +## Prerequisites + +This report requires the `analytics_currency_conversion` table to be populated. See [insertMonthlyCurrencyConversionRates.ddl](../utils/insertMonthlyCurrencyConversionRates.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_invoice_aging_no_payment", + "reportType": "TABLE", + "reportPrettyName": "Invoice Aging No Payments Report", + "sourceTableName": "report_invoice_aging_no_payment", + "refreshProcedureName": "refresh_report_invoice_aging_no_payment", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Report UI: + +![invoice-aging-no-payment.png](invoice-aging-no-payment.png) diff --git a/reports/invoice_aging_no_payment/invoice-aging-no-payment.png b/reports/invoice_aging_no_payment/invoice-aging-no-payment.png new file mode 100644 index 00000000..46fb60dd Binary files /dev/null and b/reports/invoice_aging_no_payment/invoice-aging-no-payment.png differ diff --git a/reports/invoice_aging_no_payment/report_invoice_aging_no_payment.ddl b/reports/invoice_aging_no_payment/report_invoice_aging_no_payment.ddl new file mode 100644 index 00000000..cd7baf83 --- /dev/null +++ b/reports/invoice_aging_no_payment/report_invoice_aging_no_payment.ddl @@ -0,0 +1,19 @@ +create table report_invoice_aging_no_payment as select * from v_report_invoice_aging_no_payment limit 0; + +drop procedure if exists refresh_report_invoice_aging_no_payment; +DELIMITER // +CREATE PROCEDURE refresh_report_invoice_aging_no_payment() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_invoice_aging_no_payment; + insert into report_invoice_aging_no_payment select * from v_report_invoice_aging_no_payment; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/invoice_aging_no_payment/v_report_invoice_aging_no_payment.ddl b/reports/invoice_aging_no_payment/v_report_invoice_aging_no_payment.ddl new file mode 100644 index 00000000..bf060190 --- /dev/null +++ b/reports/invoice_aging_no_payment/v_report_invoice_aging_no_payment.ddl @@ -0,0 +1,79 @@ +CREATE OR REPLACE VIEW v_report_invoice_aging_no_payment AS +WITH date_buckets AS ( + SELECT + CURRENT_DATE AS today, + CURRENT_DATE - INTERVAL 30 DAY AS d_0_30, + CURRENT_DATE - INTERVAL 60 DAY AS d_30_60, + CURRENT_DATE - INTERVAL 90 DAY AS d_60_90, + CURRENT_DATE - INTERVAL 120 DAY AS d_90_120, + CURRENT_DATE - INTERVAL 150 DAY AS d_120_150 +), +invoice_data AS ( + SELECT + ii.invoice_number, + ii.account_name, + ii.account_external_key, + CAST(ii.created_date AS DATE) AS invoice_creation_date, + CAST(ii.invoice_date AS DATE) AS invoice_date, + CAST(ii.start_date AS DATE) AS service_start_date, + CAST(ii.end_date AS DATE) AS service_end_date, + ii.bundle_external_key, + ii.product_name, + ii.slug, + ii.currency, + ii.invoice_original_amount_charged, + ii.invoice_balance, + ii.amount, + ii.created_date, + ii.invoice_item_record_id, + ii.tenant_record_id + FROM analytics_invoice_items ii + WHERE ii.invoice_date < CAST(DATE_FORMAT(SYSDATE(), '%Y-%m-01') AS DATE) + AND ii.report_group != 'test' + and ii.amount > 0 + and ii.invoice_amount_paid=0 + and ii.invoice_amount_charged=ii.invoice_original_amount_charged +) +SELECT + a.account_name AS "Customer Name", + a.account_external_key AS "Account Number", + a.currency AS "Currency", + + -- Balance due buckets + CASE WHEN a.invoice_creation_date > b.d_0_30 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 0-30 Days", + CASE WHEN a.invoice_creation_date BETWEEN b.d_30_60 AND b.d_0_30 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 30-60 Days", + CASE WHEN a.invoice_creation_date BETWEEN b.d_60_90 AND b.d_30_60 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 60-90 Days", + CASE WHEN a.invoice_creation_date BETWEEN b.d_90_120 AND b.d_60_90 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 90-120 Days", + CASE WHEN a.invoice_creation_date BETWEEN b.d_120_150 AND b.d_90_120 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 120-150 Days", + CASE WHEN a.invoice_creation_date < b.d_120_150 THEN a.invoice_original_amount_charged ELSE 0 END AS "Balance due 150+ Days", + + a.invoice_original_amount_charged AS "Total Balance Due", + + -- Balance due in USD + CASE WHEN a.invoice_creation_date > b.d_0_30 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 0-30 Days USD", + CASE WHEN a.invoice_creation_date BETWEEN b.d_30_60 AND b.d_0_30 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 30-60 Days USD", + CASE WHEN a.invoice_creation_date BETWEEN b.d_60_90 AND b.d_30_60 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 60-90 Days USD", + CASE WHEN a.invoice_creation_date BETWEEN b.d_90_120 AND b.d_60_90 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 90-120 Days USD", + CASE WHEN a.invoice_creation_date BETWEEN b.d_120_150 AND b.d_90_120 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 120-150 Days USD", + CASE WHEN a.invoice_creation_date < b.d_120_150 THEN CASE WHEN a.currency != 'USD' THEN ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) ELSE a.invoice_original_amount_charged END ELSE 0 END AS "Balance due 150+ Days USD", + + CASE WHEN a.currency != 'USD' THEN cc.reference_rate * a.invoice_original_amount_charged ELSE a.invoice_original_amount_charged END AS "Total Balance Due USD" , + + a.invoice_number AS "Invoice Number", + a.bundle_external_key AS "Bundle External Key", + a.slug AS "Slug", + a.service_start_date AS "Service Start Date", + a.service_end_date AS "Service End Date", + a.invoice_date AS "Invoice Date", + a.invoice_original_amount_charged AS "Invoice Amount", + a.invoice_balance AS "Invoice Balance", + case when a.currency != 'USD' then ROUND(cc.reference_rate * a.invoice_original_amount_charged, 4) else a.invoice_original_amount_charged end AS "Invoice Amount USD", + case when a.currency != 'USD' then ROUND(cc.reference_rate * a.invoice_balance, 4) else a.invoice_balance end AS "Invoice Balance USD", + a.tenant_record_id +FROM invoice_data a +LEFT OUTER JOIN analytics_currency_conversion cc + ON a.created_date >= cc.start_date + AND a.created_date <= cc.end_date + AND cc.currency = a.currency +CROSS JOIN date_buckets b +ORDER BY a.account_name, a.invoice_number, a.invoice_item_record_id; \ No newline at end of file diff --git a/reports/invoice_credits_daily/README.md b/reports/invoice_credits_daily/README.md new file mode 100644 index 00000000..d323c0d8 --- /dev/null +++ b/reports/invoice_credits_daily/README.md @@ -0,0 +1,41 @@ +# Daily Invoice Credits Report + +Total of invoice credits per tenant, per currency and per day. + +So if there is an unpaid invoice for `$20` on `2025-09-22`, and an account credit is added for `$100` on the same day, there is a invoice credit of `$80` which will be shown on this report. If however, a new charge of `$200` is created later on on the same day, the `$80` credit is consumed so the total credit displayed by the report for `2025-09-22` is now `$0`. + +The snapshot view is: [v_report_invoice_item_credits_daily](v_report_invoice_item_credits_daily.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_daily_invoice_credits", + "reportType": "TIMELINE", + "reportPrettyName": "Invoice Credits Daily", + "sourceTableName": "report_invoice_credits_daily", + "refreshProcedureName": "refresh_report_invoice_credits_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| Tenant Record Id | Currency | Day | Count | +|------------------|----------|------------|----------| +| 1 | USD | 2025-09-19 | 80.0000 | +| 22 | USD | 2025-09-19 | 125.0000 | +| 22 | EUR | 2025-09-22 | 45.0000 | +| 22 | USD | 2025-09-18 | -10.0000 | + +The first row in the above table indicates that on `2025-09-19`, there was a total invoice credit of `$80` for the tenant_record_id=1. + + +## Report UI: + +![invoice-credits-daily.png](invoice-credits-daily.png) \ No newline at end of file diff --git a/reports/invoice_credits_daily/invoice-credits-daily.png b/reports/invoice_credits_daily/invoice-credits-daily.png new file mode 100644 index 00000000..720d0fba Binary files /dev/null and b/reports/invoice_credits_daily/invoice-credits-daily.png differ diff --git a/reports/invoice_credits_daily/report_invoice_credits_daily.ddl b/reports/invoice_credits_daily/report_invoice_credits_daily.ddl new file mode 100644 index 00000000..a3906e67 --- /dev/null +++ b/reports/invoice_credits_daily/report_invoice_credits_daily.ddl @@ -0,0 +1,19 @@ +create table report_invoice_credits_daily as select * from v_report_invoice_credits_daily limit 0; + +drop procedure if exists refresh_report_invoice_credits_daily; +DELIMITER // +CREATE PROCEDURE refresh_report_invoice_credits_daily() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_invoice_credits_daily; + insert into report_invoice_credits_daily select * from v_report_invoice_credits_daily; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/reports/v_report_invoice_item_credits_daily.ddl b/reports/invoice_credits_daily/v_report_invoice_credits_daily.ddl similarity index 77% rename from src/main/resources/reports/v_report_invoice_item_credits_daily.ddl rename to reports/invoice_credits_daily/v_report_invoice_credits_daily.ddl index f8252cae..0f87f90e 100644 --- a/src/main/resources/reports/v_report_invoice_item_credits_daily.ddl +++ b/reports/invoice_credits_daily/v_report_invoice_credits_daily.ddl @@ -1,4 +1,4 @@ -create or replace view v_report_invoice_item_credits_daily as +create or replace view v_report_invoice_credits_daily as select aic.tenant_record_id , aic.currency diff --git a/reports/invoice_credits_monthly/README.md b/reports/invoice_credits_monthly/README.md new file mode 100644 index 00000000..24472ed8 --- /dev/null +++ b/reports/invoice_credits_monthly/README.md @@ -0,0 +1,31 @@ +# Invoice Credits Monthly Report + +Report of all invoice credits from the previous month, showing amounts in both original currency and USD equivalents. + +The snapshot view is: `v_report_invoice_credits_monthly` + +## Prerequisites + +This report requires the `analytics_currency_conversion` table to be populated. See [insertMonthlyCurrencyConversionRates.ddl](../utils/insertMonthlyCurrencyConversionRates.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_invoice_credits_monthly", + "reportType": "TABLE", + "reportPrettyName": "Invoice Credits Monthly Report", + "sourceTableName": "report_invoice_credits_monthly", + "refreshProcedureName": "refresh_report_invoice_credits_monthly", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Report UI: + +![invoice-credits-monthly.png](invoice-credits-monthly.png) diff --git a/reports/invoice_credits_monthly/invoice-credits-monthly.png b/reports/invoice_credits_monthly/invoice-credits-monthly.png new file mode 100644 index 00000000..b5ae99cb Binary files /dev/null and b/reports/invoice_credits_monthly/invoice-credits-monthly.png differ diff --git a/reports/invoice_credits_monthly/report_invoice_credits_monthly.ddl b/reports/invoice_credits_monthly/report_invoice_credits_monthly.ddl new file mode 100644 index 00000000..a4368082 --- /dev/null +++ b/reports/invoice_credits_monthly/report_invoice_credits_monthly.ddl @@ -0,0 +1,19 @@ +create table report_invoice_credits_monthly as select * from v_report_invoice_credits_monthly limit 0; + +drop procedure if exists refresh_report_invoice_credits_monthly; +DELIMITER // +CREATE PROCEDURE refresh_report_invoice_credits_monthly() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_invoice_credits_monthly; + insert into report_invoice_credits_monthly select * from v_report_invoice_credits_monthly; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/extracts/finance/invoice_credits_monthly.sql b/reports/invoice_credits_monthly/v_report_invoice_credits_monthly.ddl similarity index 57% rename from src/main/resources/extracts/finance/invoice_credits_monthly.sql rename to reports/invoice_credits_monthly/v_report_invoice_credits_monthly.ddl index 67a931b1..09829c9d 100644 --- a/src/main/resources/extracts/finance/invoice_credits_monthly.sql +++ b/reports/invoice_credits_monthly/v_report_invoice_credits_monthly.ddl @@ -1,3 +1,4 @@ +CREATE OR REPLACE VIEW v_report_invoice_credits_monthly AS select ic.invoice_number as "Invoice Number" , ic.account_name as "Customer Name" @@ -14,16 +15,17 @@ select , ic.invoice_original_amount_charged as "Invoice Amount" , ic.invoice_balance as "Invoice Balance" , ic.amount as "Invoice Credit Amount" -, round(cc.reference_rate * ic.invoice_original_amount_charged,4) as "Invoice Amount USD" -, round(cc.reference_rate * ic.invoice_balance,4) as "Invoice Balance USD" -, round(cc.reference_rate * ic.amount,4) as "Invoice Credit Amount USD" +, case when ic.currency != 'USD' THEN round(cc.reference_rate * ic.invoice_original_amount_charged,4) else invoice_original_amount_charged end as "Invoice Amount USD" +, case when ic.currency != 'USD' THEN round(cc.reference_rate * ic.invoice_balance,4) else invoice_balance end as "Invoice Balance USD" +, case when ic.currency != 'USD' THEN round(cc.reference_rate * ic.amount,4) else amount end as "Invoice Credit Amount USD" +, ic.tenant_record_id from analytics_invoice_credits ic - join analytics_currency_conversion cc on ic.created_date >= cc.start_date and ic.created_date <= cc.end_date and cc.currency =ic.currency + left outer join analytics_currency_conversion cc on ic.created_date >= cc.start_date and ic.created_date <= cc.end_date and cc.currency =ic.currency where 1=1 and ic.created_date >= cast(date_format(date_sub(sysdate(), interval '1' month), '%Y-%m-01') as date) and ic.created_date < cast(date_format(sysdate(), '%Y-%m-01') as date) and ic.report_group != 'test' order by invoice_number -, ic.invoice_item_record_id; -- just for well defined ordering +, ic.invoice_item_record_id; -- just for well defined ordering \ No newline at end of file diff --git a/reports/invoice_item_adjustments_daily/README.md b/reports/invoice_item_adjustments_daily/README.md new file mode 100644 index 00000000..fa1143a8 --- /dev/null +++ b/reports/invoice_item_adjustments_daily/README.md @@ -0,0 +1,45 @@ +# Daily Invoice Item Adjustments report + +Total of invoice item adjustments per tenant, per currency and per day. + +So if there is an invoice for `$100` created on `2025-09-21` and the invoice is adjusted for `$20`, this report will show a value of `-20` for the date `2025-09-21` for the particular tenant. If there are multiple invoice item adjustments for the tenant on the same day, they will be added up. + +The snapshot view is: [v_report_invoice_item_adjustments_daily](v_report_invoice_item_adjustments_daily.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_daily_invoice_item_adjustments", + "reportType": "TIMELINE", + "reportPrettyName": "Invoice Item Adjustments Daily", + "sourceTableName": "report_invoice_item_adjustments_daily", + "refreshProcedureName": "refresh_report_invoice_item_adjustments_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| Tenant Record Id | Currency | Date | Count | +|------------------|----------|------------|-----------| +| 1 | EUR | 2025-08-28 | -10.0000 | +| 1 | USD | 2025-08-29 | | +| 1 | EUR | 2025-08-29 | -50.0000 | +| 1 | EUR | 2025-09-01 | -16.0000 | +| 1 | USD | 2025-09-16 | | +| 1 | EUR | 2025-09-02 | -19.9500 | +| 5 | USD | 2025-09-05 | -109.9500 | +| 1 | USD | 2025-09-19 | -19.3100 | +| 489 | USD | 2025-09-19 | -10.0000 | + +The first row in the above table indicates that on the date `2025-08-28`, the tenant with record id=1 had a total invoice adjustments of EUR 10. + +## Report UI: + +![invoice_item_adjustments_daily.png](invoice_item_adjustments_daily.png) \ No newline at end of file diff --git a/reports/invoice_item_adjustments_daily/invoice_item_adjustments_daily.png b/reports/invoice_item_adjustments_daily/invoice_item_adjustments_daily.png new file mode 100644 index 00000000..863630e5 Binary files /dev/null and b/reports/invoice_item_adjustments_daily/invoice_item_adjustments_daily.png differ diff --git a/reports/invoice_item_adjustments_daily/report_invoice_item_adjustments_daily.ddl b/reports/invoice_item_adjustments_daily/report_invoice_item_adjustments_daily.ddl new file mode 100644 index 00000000..ec961ea9 --- /dev/null +++ b/reports/invoice_item_adjustments_daily/report_invoice_item_adjustments_daily.ddl @@ -0,0 +1,19 @@ +create table report_invoice_item_adjustments_daily as select * from v_report_invoice_item_adjustments_daily limit 0; + +drop procedure if exists refresh_report_invoice_item_adjustments_daily; +DELIMITER // +CREATE PROCEDURE refresh_report_invoice_item_adjustments_daily() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_invoice_item_adjustments_daily; + insert into report_invoice_item_adjustments_daily select * from v_report_invoice_item_adjustments_daily; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/reports/v_report_invoice_item_adjustments_daily.ddl b/reports/invoice_item_adjustments_daily/v_report_invoice_item_adjustments_daily.ddl similarity index 87% rename from src/main/resources/reports/v_report_invoice_item_adjustments_daily.ddl rename to reports/invoice_item_adjustments_daily/v_report_invoice_item_adjustments_daily.ddl index a6bd536b..ac67709d 100644 --- a/src/main/resources/reports/v_report_invoice_item_adjustments_daily.ddl +++ b/reports/invoice_item_adjustments_daily/v_report_invoice_item_adjustments_daily.ddl @@ -3,7 +3,7 @@ select aiia.tenant_record_id , aiia.currency , date_format(aiia.created_date,'%Y-%m-%d') as day -, sum(aiia.converted_amount) as count +, sum(aiia.amount) as count from analytics_invoice_item_adjustments aiia where 1=1 diff --git a/reports/invoice_item_adjustments_monthly/README.md b/reports/invoice_item_adjustments_monthly/README.md new file mode 100644 index 00000000..a540971b --- /dev/null +++ b/reports/invoice_item_adjustments_monthly/README.md @@ -0,0 +1,31 @@ +# Invoice Items Adjustments Monthly Report + +Report of all invoice item adjustments from the previous month, showing amounts in both original currency and USD equivalents. + +The snapshot view is: `v_report_invoice_item_adjustments_monthly` + +## Prerequisites + +This report requires the `analytics_currency_conversion` table to be populated. See [insertMonthlyCurrencyConversionRates.ddl](../utils/insertMonthlyCurrencyConversionRates.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_invoice_item_adjustments_monthly", + "reportType": "TABLE", + "reportPrettyName": "Invoice Item Adjustments Monthly Report", + "sourceTableName": "report_invoice_item_adjustments_monthly", + "refreshProcedureName": "refresh_report_invoice_item_adjustments_monthly", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Report UI: + +![invoice-item-adjustments-monthly.png.png](invoice-item-adjustments-monthly.png.png) diff --git a/reports/invoice_item_adjustments_monthly/invoice-item-adjustments-monthly.png b/reports/invoice_item_adjustments_monthly/invoice-item-adjustments-monthly.png new file mode 100644 index 00000000..9fe7b5a2 Binary files /dev/null and b/reports/invoice_item_adjustments_monthly/invoice-item-adjustments-monthly.png differ diff --git a/reports/invoice_item_adjustments_monthly/report_invoice_item_adjustments_monthly.ddl b/reports/invoice_item_adjustments_monthly/report_invoice_item_adjustments_monthly.ddl new file mode 100644 index 00000000..809fdbd4 --- /dev/null +++ b/reports/invoice_item_adjustments_monthly/report_invoice_item_adjustments_monthly.ddl @@ -0,0 +1,19 @@ +create table report_invoice_item_adjustments_monthly as select * from v_report_invoice_item_adjustments_monthly limit 0; + +drop procedure if exists refresh_report_invoice_item_adjustments_monthly; +DELIMITER // +CREATE PROCEDURE refresh_report_invoice_item_adjustments_monthly() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_invoice_item_adjustments_monthly; + insert into report_invoice_item_adjustments_monthly select * from v_report_invoice_item_adjustments_monthly; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/extracts/finance/invoice_item_adjustments_monthly.sql b/reports/invoice_item_adjustments_monthly/v_report_invoice_item_adjustments_monthly.ddl similarity index 51% rename from src/main/resources/extracts/finance/invoice_item_adjustments_monthly.sql rename to reports/invoice_item_adjustments_monthly/v_report_invoice_item_adjustments_monthly.ddl index 9b51848b..261c37c6 100644 --- a/src/main/resources/extracts/finance/invoice_item_adjustments_monthly.sql +++ b/reports/invoice_item_adjustments_monthly/v_report_invoice_item_adjustments_monthly.ddl @@ -1,3 +1,4 @@ +CREATE OR REPLACE VIEW v_report_invoice_item_adjustments_monthly AS select iia.invoice_number as "Invoice Number" , iia.account_name as "Customer Name" @@ -8,27 +9,26 @@ select , iia.bundle_external_key as "Bundle External Key" , iia.product_name as "Product" , iia.slug as "Slug" - -- , date_format(iia.start_date, '%m/%d/%Y') as "Service Start Date" - -- , date_format(iia.end_date, '%m/%d/%Y') as "Service End Date" -, date_format(ii.start_date, '%m/%d/%Y') as "Service Start Date" -, date_format(ii.end_date, '%m/%d/%Y') as "Service End Date" +, date_format(iia.start_date, '%m/%d/%Y') as "Service Start Date" +, date_format(iia.end_date, '%m/%d/%Y') as "Service End Date" , iia.currency as "Currency" , iia.invoice_original_amount_charged as "Invoice Amount" , iia.invoice_balance as "Invoice Balance" , iia.amount as "Invoice Item Adjustment Amount" , abs(iia.amount) as "Impact Amount" -, round(cc.reference_rate * iia.invoice_original_amount_charged,4) as "Invoice Amount USD" -, round(cc.reference_rate * iia.invoice_balance,4) as "Invoice Balance USD" -, round(cc.reference_rate * iia.amount,4) as "Invoice Item Adjustment Amount USD" -, abs(round(cc.reference_rate * iia.amount,4)) as "Impact Amount USD" +, case when iia.currency != 'USD' then round(cc.reference_rate * iia.invoice_original_amount_charged,4) else iia.invoice_original_amount_charged END as "Invoice Amount USD" +, case when iia.currency != 'USD' then round(cc.reference_rate * iia.invoice_balance,4) else iia.invoice_balance end as "Invoice Balance USD" +, case when iia.currency != 'USD' then round(cc.reference_rate * iia.amount,4) else iia.amount end as "Invoice Item Adjustment Amount USD" +, case when iia.currency != 'USD' then abs(round(cc.reference_rate * iia.amount,4)) else abs(iia.amount) end as "Impact Amount USD" , case when iia.amount < 0 then 'CREDIT' else 'CHARGE' end as "Adjustment Type" , 'PROCESSED' as "Invoice Item Adjustment Status" +, iia.tenant_record_id from analytics_invoice_item_adjustments iia join analytics_invoice_items ii on iia.linked_item_id = ii.item_id -- workaround - join analytics_currency_conversion cc on iia.created_date >= cc.start_date and iia.created_date <= cc.end_date and cc.currency = iia.currency + left outer join analytics_currency_conversion cc on iia.created_date >= cc.start_date and iia.created_date <= cc.end_date and cc.currency = iia.currency where 1=1 - and iia.created_date >= cast(date_format(date_sub(sysdate() - interval '1' month), '%Y-%m-01') as date) + and iia.created_date >= cast(date_format(date_sub(sysdate(), interval '1' month), '%Y-%m-01') as date) and iia.created_date < cast(date_format(sysdate(), '%Y-%m-01') as date) and iia.report_group != 'test' order by diff --git a/reports/invoice_items_monthly/README.md b/reports/invoice_items_monthly/README.md new file mode 100644 index 00000000..8da1c735 --- /dev/null +++ b/reports/invoice_items_monthly/README.md @@ -0,0 +1,31 @@ +# Invoice Items Monthly Report + +Report of all invoice items from the previous month, showing amounts in both original currency and USD equivalents. + +The snapshot view is: `v_report_invoice_items_monthly` + +## Prerequisites + +This report requires the `analytics_currency_conversion` table to be populated. See [insertMonthlyCurrencyConversionRates.ddl](../utils/insertMonthlyCurrencyConversionRates.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_invoice_items_monthly", + "reportType": "TABLE", + "reportPrettyName": "Invoice Items Monthly Report", + "sourceTableName": "report_invoice_items_monthly", + "refreshProcedureName": "refresh_report_invoice_items_monthly", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Report UI: + +![invoice-items-monthly.png](invoice-items-monthly.png) diff --git a/reports/invoice_items_monthly/invoice-items-monthly.png b/reports/invoice_items_monthly/invoice-items-monthly.png new file mode 100644 index 00000000..2fa3658c Binary files /dev/null and b/reports/invoice_items_monthly/invoice-items-monthly.png differ diff --git a/reports/invoice_items_monthly/report_invoice_items_monthly.ddl b/reports/invoice_items_monthly/report_invoice_items_monthly.ddl new file mode 100644 index 00000000..f323080c --- /dev/null +++ b/reports/invoice_items_monthly/report_invoice_items_monthly.ddl @@ -0,0 +1,19 @@ +create table report_invoice_items_monthly as select * from v_report_invoice_items_monthly limit 0; + +drop procedure if exists refresh_report_invoice_items_monthly; +DELIMITER // +CREATE PROCEDURE refresh_report_invoice_items_monthly() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_invoice_items_monthly; + insert into report_invoice_items_monthly select * from v_report_invoice_items_monthly; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/extracts/finance/invoice_items_monthly.sql b/reports/invoice_items_monthly/v_report_invoice_items_monthly.ddl similarity index 57% rename from src/main/resources/extracts/finance/invoice_items_monthly.sql rename to reports/invoice_items_monthly/v_report_invoice_items_monthly.ddl index 62c7394b..a0744f49 100644 --- a/src/main/resources/extracts/finance/invoice_items_monthly.sql +++ b/reports/invoice_items_monthly/v_report_invoice_items_monthly.ddl @@ -1,3 +1,4 @@ +CREATE OR REPLACE VIEW v_report_invoice_items_monthly AS select ii.invoice_number as "Invoice Number" , ii.account_name as "Customer Name" @@ -14,16 +15,17 @@ select , ii.invoice_original_amount_charged as "Invoice Amount" , ii.invoice_balance as "Invoice Balance" , ii.amount as "Invoice Item Amount" -, round(cc.reference_rate * ii.invoice_original_amount_charged,4) as "Invoice Amount USD" -, round(cc.reference_rate * ii.invoice_balance,4) as "Invoice Balance USD" -, round(cc.reference_rate * ii.amount,4) as "Invoice Item Amount USD" +, case when ii.currency != 'USD' THEN round(cc.reference_rate * ii.invoice_original_amount_charged,4) else ii.invoice_original_amount_charged END as "Invoice Amount USD" +, case when ii.currency != 'USD' THEN round(cc.reference_rate * ii.invoice_balance,4) else ii.invoice_balance END as "Invoice Balance USD" +, case when ii.currency != 'USD' THEN round(cc.reference_rate * ii.amount,4) else ii.amount END as "Invoice Item Amount USD" +, ii.tenant_record_id from analytics_invoice_items ii - join analytics_currency_conversion cc on ii.created_date >= cc.start_date and ii.created_date <= cc.end_date and cc.currency = ii.currency + left outer join analytics_currency_conversion cc on ii.created_date >= cc.start_date and ii.created_date <= cc.end_date and cc.currency = ii.currency where 1=1 and ii.invoice_date >= cast(date_format(sysdate() - interval '1' month, '%Y-%m-01') as date) and ii.invoice_date < cast(date_format(sysdate(), '%Y-%m-01') as date) and ii.report_group != 'test' order by invoice_number -, ii.invoice_item_record_id; -- just for well defined ordering +, ii.invoice_item_record_id; -- just for well defined ordering \ No newline at end of file diff --git a/reports/invoices_balance_daily/README.md b/reports/invoices_balance_daily/README.md new file mode 100644 index 00000000..914bf8c8 --- /dev/null +++ b/reports/invoices_balance_daily/README.md @@ -0,0 +1,40 @@ +# Daily Invoice Balance Report + +Compute the total sum of invoices balance (in the reference currency) per invoice created day. + +The snapshot view is: `v_report_invoices_balance_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_invoices_balance_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Invoices Balance", + "sourceTableName": "report_invoices_balance_daily", + "refreshProcedureName": "refresh_report_invoices_balance_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| tenant_record_id | currency | day | count | +|------------------|----------|------------|---------| +| 1 | EUR | 2025-09-15 | 15.0000 | +| 1 | EUR | 2025-09-01 | 0.0000 | +| 1 | USD | 2025-09-15 | 42.9000 | +| 2 | USD | 2025-09-02 | 59.5000 | +| 2 | EUR | 2025-09-16 | 0.0000 | + + +The first row in the above table indicates that on the date `2025-09-15`, the tenant with record id=1 had a total invoice balance of EUR 15. + +## Report UI: + +![invoice-balance-daily.png](invoice-balance-daily.png) \ No newline at end of file diff --git a/reports/invoices_balance_daily/invoice-balance-daily.png b/reports/invoices_balance_daily/invoice-balance-daily.png new file mode 100644 index 00000000..1dc78e5f Binary files /dev/null and b/reports/invoices_balance_daily/invoice-balance-daily.png differ diff --git a/src/main/resources/reports/invoices_balance_daily/report_invoices_balance_daily.ddl b/reports/invoices_balance_daily/report_invoices_balance_daily.ddl similarity index 100% rename from src/main/resources/reports/invoices_balance_daily/report_invoices_balance_daily.ddl rename to reports/invoices_balance_daily/report_invoices_balance_daily.ddl diff --git a/src/main/resources/reports/invoices_balance_daily/v_report_invoices_balance_daily.ddl b/reports/invoices_balance_daily/v_report_invoices_balance_daily.ddl similarity index 78% rename from src/main/resources/reports/invoices_balance_daily/v_report_invoices_balance_daily.ddl rename to reports/invoices_balance_daily/v_report_invoices_balance_daily.ddl index 7f60ffbc..35539cce 100644 --- a/src/main/resources/reports/invoices_balance_daily/v_report_invoices_balance_daily.ddl +++ b/reports/invoices_balance_daily/v_report_invoices_balance_daily.ddl @@ -1,11 +1,12 @@ create or replace view v_report_invoices_balance_daily as select ai.tenant_record_id +, ai.currency , date_format(ai.created_date,'%Y-%m-%d') as day -, sum(ai.converted_balance) as count +, sum(ai.balance) as count from analytics_invoices ai where 1=1 and ai.report_group='default' -group by 1,2 +group by 1,2,3 ; \ No newline at end of file diff --git a/reports/invoices_daily/README.md b/reports/invoices_daily/README.md new file mode 100644 index 00000000..6a7cb73f --- /dev/null +++ b/reports/invoices_daily/README.md @@ -0,0 +1,44 @@ +# Daily Invoices Report + +Compute the total invoice amount charged (in the reference currency) per day per currency. + +The snapshot view is: `v_report_invoices_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_invoices_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Invoices Value", + "sourceTableName": "report_invoices_daily", + "refreshProcedureName": "refresh_report_invoices_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| ID | Date | Currency | Amount | +|-----|------------|----------|-----------| +| 1 | 2025-09-05 | USD | 579.0700 | +| 1 | 2025-09-19 | USD | 728.4000 | +| 1 | 2025-09-22 | USD | 100.0000 | +| 489 | 2025-09-21 | USD | 250.0000 | +| 1 | 2025-09-25 | EUR | 59.9500 | +| 359 | 2025-09-25 | USD | 1009.3000 | +| 1 | 2025-09-11 | USD | 516.9500 | +| 1 | 2025-09-28 | EUR | 0.0000 | +| 359 | 2025-09-28 | USD | 356.6900 | + +The first row in the above table indicates that on the date `2025-09-05`, the tenant with record id=1 had a total invoice value of USD 579.0700. + + +## Report UI: + +![invoice-amount-daily.png](invoice-amount-daily.png) \ No newline at end of file diff --git a/reports/invoices_daily/invoice-amount-daily.png b/reports/invoices_daily/invoice-amount-daily.png new file mode 100644 index 00000000..6e05eeea Binary files /dev/null and b/reports/invoices_daily/invoice-amount-daily.png differ diff --git a/src/main/resources/reports/invoices_daily/report_invoices_daily.ddl b/reports/invoices_daily/report_invoices_daily.ddl similarity index 100% rename from src/main/resources/reports/invoices_daily/report_invoices_daily.ddl rename to reports/invoices_daily/report_invoices_daily.ddl diff --git a/src/main/resources/reports/invoices_daily/v_report_invoices_daily.ddl b/reports/invoices_daily/v_report_invoices_daily.ddl similarity index 81% rename from src/main/resources/reports/invoices_daily/v_report_invoices_daily.ddl rename to reports/invoices_daily/v_report_invoices_daily.ddl index af1a9153..ae6c0251 100644 --- a/src/main/resources/reports/invoices_daily/v_report_invoices_daily.ddl +++ b/reports/invoices_daily/v_report_invoices_daily.ddl @@ -3,7 +3,7 @@ select ai.tenant_record_id , date_format(ai.created_date,'%Y-%m-%d') as day , ai.currency -, sum(ai.converted_original_amount_charged) as count +, sum(ai.original_amount_charged) as count from analytics_invoices ai where 1=1 diff --git a/reports/invoices_monthly/README.md b/reports/invoices_monthly/README.md new file mode 100644 index 00000000..30541b85 --- /dev/null +++ b/reports/invoices_monthly/README.md @@ -0,0 +1,31 @@ +# Invoice Monthly Report + +Report of all invoices from the previous month, showing amounts in both original currency and USD equivalents. + +The snapshot view is: `v_report_invoices_monthly` + +## Prerequisites + +This report requires the `analytics_currency_conversion` table to be populated. See [insertMonthlyCurrencyConversionRates.ddl](../utils/insertMonthlyCurrencyConversionRates.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_invoices_monthly", + "reportType": "TABLE", + "reportPrettyName": "Invoices Monthly Report", + "sourceTableName": "report_invoices_monthly", + "refreshProcedureName": "refresh_report_invoices_monthly", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Report UI: + +![invoices-monthly.png](invoices-monthly.png) diff --git a/reports/invoices_monthly/invoices-monthly.png b/reports/invoices_monthly/invoices-monthly.png new file mode 100644 index 00000000..b2f58c3d Binary files /dev/null and b/reports/invoices_monthly/invoices-monthly.png differ diff --git a/reports/invoices_monthly/report_invoices_monthly.ddl b/reports/invoices_monthly/report_invoices_monthly.ddl new file mode 100644 index 00000000..11bc733a --- /dev/null +++ b/reports/invoices_monthly/report_invoices_monthly.ddl @@ -0,0 +1,19 @@ +create table report_invoices_monthly as select * from v_report_invoices_monthly limit 0; + +drop procedure if exists refresh_report_invoices_monthly; +DELIMITER // +CREATE PROCEDURE refresh_report_invoices_monthly() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_invoices_monthly; + insert into report_invoices_monthly select * from v_report_invoices_monthly; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/extracts/finance/invoices_monthly.sql b/reports/invoices_monthly/v_report_invoices_monthly.ddl similarity index 55% rename from src/main/resources/extracts/finance/invoices_monthly.sql rename to reports/invoices_monthly/v_report_invoices_monthly.ddl index be5d31ab..25e17186 100644 --- a/src/main/resources/extracts/finance/invoices_monthly.sql +++ b/reports/invoices_monthly/v_report_invoices_monthly.ddl @@ -1,3 +1,4 @@ +CREATE OR REPLACE VIEW v_report_invoices_monthly AS select inv.invoice_number as "Invoice Number" , inv.account_name as "Customer Name" @@ -8,15 +9,16 @@ select , inv.currency as "Currency" , inv.original_amount_charged as "Invoice Amount" , inv.balance as "Invoice Balance" -, round(cc.reference_rate * inv.original_amount_charged,4) as "Invoice Amount USD" -, round(cc.reference_rate * inv.balance,4) as "Invoice Balance USD" +, case when inv.currency != 'USD' THEN round(cc.reference_rate * inv.original_amount_charged,4) else inv.original_amount_charged END as "Invoice Amount USD" +, case when inv.currency != 'USD' THEN round(cc.reference_rate * inv.balance,4) else inv.balance END as "Invoice Balance USD" +, inv.tenant_record_id from analytics_invoices inv - join analytics_currency_conversion cc on inv.created_date >= cc.start_date and inv.created_date <= cc.end_date and cc.currency = inv.currency + left outer join analytics_currency_conversion cc on inv.created_date >= cc.start_date and inv.created_date <= cc.end_date and cc.currency = inv.currency where 1=1 and inv.invoice_date >= cast(date_format(date_sub(sysdate(), interval '1' month), '%Y-%m-01') as date) and inv.invoice_date < cast(date_format(sysdate(), '%Y-%m-01') as date) and inv.report_group != 'test' order by invoice_number -, inv.invoice_record_id; -- just for well defined ordering +, inv.invoice_record_id; -- just for well defined ordering \ No newline at end of file diff --git a/reports/mrr_daily/README.md b/reports/mrr_daily/README.md new file mode 100644 index 00000000..e8d4bd04 --- /dev/null +++ b/reports/mrr_daily/README.md @@ -0,0 +1,41 @@ +# Daily MRR + +Computes the total active MRR (monthly recurring revenue), broken down both by product and as a tenant-wide total (ALL) for each tenant and each day. + +The snapshot view is: `v_report_mrr_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_mrr_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily MRR", + "sourceTableName": "report_mrr_daily", + "refreshProcedureName": "refresh_report_mrr_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| tenant_record_id | currency | product | day | count | +|------------------|----------|---------------|------------|----------| +| 1 | USD | Pistol | 2025-08-07 | 224.6292 | +| 1 | USD | Blowdart | 2025-08-07 | 29.9500 | +| 1 | EUR | Knife | 2025-08-07 | 29.9500 | +| 1 | USD | Assault-Rifle | 2025-08-07 | 499.9958 | +| 2 | USD | Pistol | 2025-08-07 | 100.0000 | +| 2 | USD | All | 2025-08-07 | 100.0000 | + + +The first row in the above table indicates that on the date `2025-08-07`, the MRR for the `Pistol` product was $150 for `tenant_record_id=1`. + +## Report UI: + +![daily-mrr.png](daily-mrr.png) \ No newline at end of file diff --git a/reports/mrr_daily/daily-mrr.png b/reports/mrr_daily/daily-mrr.png new file mode 100644 index 00000000..9ed2a0dc Binary files /dev/null and b/reports/mrr_daily/daily-mrr.png differ diff --git a/src/main/resources/reports/mrr/report_mrr_daily.ddl b/reports/mrr_daily/report_mrr_daily.ddl similarity index 100% rename from src/main/resources/reports/mrr/report_mrr_daily.ddl rename to reports/mrr_daily/report_mrr_daily.ddl diff --git a/src/main/resources/reports/mrr/v_report_mrr_daily.ddl b/reports/mrr_daily/v_report_mrr_daily.ddl similarity index 88% rename from src/main/resources/reports/mrr/v_report_mrr_daily.ddl rename to reports/mrr_daily/v_report_mrr_daily.ddl index bf64697d..5937a0fb 100644 --- a/src/main/resources/reports/mrr/v_report_mrr_daily.ddl +++ b/reports/mrr_daily/v_report_mrr_daily.ddl @@ -1,9 +1,10 @@ create or replace view v_report_mrr_daily as select ast.tenant_record_id +, ast.next_currency as currency , ifnull(ast.next_product_name, ast.prev_product_name) as product , date_format(cal.d,'%Y-%m-%d') as day -, sum(ast.converted_next_mrr) as count +, sum(ast.next_mrr) as count from calendar cal left join analytics_subscription_transitions ast on cast(date_format(ast.next_start_date, '%Y-%m-%d') as date) <= cast(date_format(cal.d, '%Y-%m-%d') as date) @@ -13,12 +14,13 @@ where 1=1 and ast.report_group='default' and ast.next_service='entitlement-service' and ifnull(ast.next_mrr, 0) > 0 -group by 1,2,3 +group by 1,2,3,4 union select ast.tenant_record_id +, ast.next_currency as currency , 'ALL' as product , date_format(cal.d,'%Y-%m-%d') as day -, sum(ast.converted_next_mrr) as count +, sum(ast.next_mrr) as count from calendar cal left join analytics_subscription_transitions ast on cast(date_format(ast.next_start_date, '%Y-%m-%d') as date) <= cast(date_format(cal.d, '%Y-%m-%d') as date) @@ -28,5 +30,5 @@ where 1=1 and ast.report_group='default' and ast.next_service='entitlement-service' and ifnull(ast.next_mrr, 0) > 0 -group by 1,2,3 -; +group by 1,2,3,4 +; \ No newline at end of file diff --git a/reports/new_accounts_daily/README.md b/reports/new_accounts_daily/README.md new file mode 100644 index 00000000..39b97121 --- /dev/null +++ b/reports/new_accounts_daily/README.md @@ -0,0 +1,42 @@ +# Daily New Accounts Report + +Compute the total amount of new accounts created per day for each tenant. + +The snapshot view is: `v_report_new_accounts_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_new_accounts_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily New Accounts", + "sourceTableName": "report_new_accounts_daily", + "refreshProcedureName": "refresh_report_new_accounts_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| ID | Date | Value | +|----|------------|-------| +| 1 | 2025-09-18 | 4 | +| 1 | 2025-09-19 | 4 | +| 2 | 2025-09-19 | 4 | +| 1 | 2025-09-21 | 1 | +| 2 | 2025-09-21 | 1 | +| 1 | 2025-09-22 | 1 | + + +The first row in the above table indicates that on the date `2025-09-18`, the tenant with record id=1 had 4 new accounts created. + + +## Report UI: + +![daily-new-accounts.png](daily-new-accounts.png) diff --git a/reports/new_accounts_daily/daily-new-accounts.png b/reports/new_accounts_daily/daily-new-accounts.png new file mode 100644 index 00000000..0a27fe95 Binary files /dev/null and b/reports/new_accounts_daily/daily-new-accounts.png differ diff --git a/src/main/resources/reports/new_accounts_daily/report_new_accounts_daily.ddl b/reports/new_accounts_daily/report_new_accounts_daily.ddl similarity index 100% rename from src/main/resources/reports/new_accounts_daily/report_new_accounts_daily.ddl rename to reports/new_accounts_daily/report_new_accounts_daily.ddl diff --git a/src/main/resources/reports/new_accounts_daily/v_report_new_accounts_daily.ddl b/reports/new_accounts_daily/v_report_new_accounts_daily.ddl similarity index 100% rename from src/main/resources/reports/new_accounts_daily/v_report_new_accounts_daily.ddl rename to reports/new_accounts_daily/v_report_new_accounts_daily.ddl diff --git a/reports/new_subscriptions_daily/README.md b/reports/new_subscriptions_daily/README.md new file mode 100644 index 00000000..5e4947b7 --- /dev/null +++ b/reports/new_subscriptions_daily/README.md @@ -0,0 +1,44 @@ +# Daily New Subscriptions Report + +Compute the total amount of new subscriptions created per day for each tenant. + +The snapshot view is: `v_report_new_subscriptions_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_new_subscriptions_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily New Subscriptions", + "sourceTableName": "report_new_subscriptions_daily", + "refreshProcedureName": "refresh_report_new_subscriptions_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + + +## Sample Data + +| Tenant Record Id | Slug | Day | Count | +|------------------|------------------------------------|------------|-------| +| 1 | blowdart-monthly-notrial-evergreen | 2025-08-20 | 9 | +| 1 | pistol-monthly-notrial-evergreen | 2025-08-20 | 2 | +| 1 | pistol-monthly-notrial-evergreen | 2025-09-01 | 5 | +| 1 | blowdart-monthly-notrial-evergreen | 2025-09-02 | 6 | +| 25 | gold-monthly-notrial-evergreen | 2025-09-02 | 5 | +| 25 | silver-monthly-notrial-evergreen | 2025-09-03 | 6 | + + +The first row in the above table indicates that on the date `2025-08-20`, the tenant with record id=1 had 9 new subscriptions created. + + +## Report UI: + + +![daily-new-subscriptions.png](daily-new-subscriptions.png) diff --git a/reports/new_subscriptions_daily/daily-new-subscriptions.png b/reports/new_subscriptions_daily/daily-new-subscriptions.png new file mode 100644 index 00000000..c89f8e0b Binary files /dev/null and b/reports/new_subscriptions_daily/daily-new-subscriptions.png differ diff --git a/reports/new_subscriptions_daily/report_new_subscriptions_daily.ddl b/reports/new_subscriptions_daily/report_new_subscriptions_daily.ddl new file mode 100644 index 00000000..c9a9de3e --- /dev/null +++ b/reports/new_subscriptions_daily/report_new_subscriptions_daily.ddl @@ -0,0 +1,19 @@ +create table report_new_subscriptions_daily as select * from v_report_new_subscriptions_daily limit 0; + +drop procedure if exists refresh_report_new_subscriptions_daily; +DELIMITER // +CREATE PROCEDURE refresh_report_report_new_subscriptions_daily() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_new_subscriptions_daily; + insert into report_new_subscriptions_daily select * from v_report_new_subscriptions_daily; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/new_subscriptions_daily/v_report_new_subscriptions_daily.ddl b/reports/new_subscriptions_daily/v_report_new_subscriptions_daily.ddl new file mode 100644 index 00000000..ced1b864 --- /dev/null +++ b/reports/new_subscriptions_daily/v_report_new_subscriptions_daily.ddl @@ -0,0 +1,11 @@ +create or replace view v_report_new_subscriptions_daily AS +select ast.tenant_record_id AS tenant_record_id, +ast.next_slug AS slug, +date_format(ast.next_start_date,'%Y-%m-%d') AS day, +count(0) AS count +from analytics_subscription_transitions ast +where ((1 = 1) and +(ast.event = 'START_BILLING_BASE') and +(ast.report_group = 'default')) group by +ast.tenant_record_id,ast.next_slug, +date_format(ast.next_start_date,'%Y-%m-%d') \ No newline at end of file diff --git a/reports/overdue-states-count-daily/README.md b/reports/overdue-states-count-daily/README.md new file mode 100644 index 00000000..075a235d --- /dev/null +++ b/reports/overdue-states-count-daily/README.md @@ -0,0 +1,45 @@ +# Daily Overdue Count Report + +Count of overdue states per tenant and per day. + +The snapshot view is: [v_report_overdue_states_count_daily](v_report_overdue_states_count_daily.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_overdue_states_count_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Overdue Count", + "sourceTableName": "report_overdue_states_count_daily", + "refreshProcedureName": "refresh_report_overdue_states_count_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| Tenant Record Id | State | Day | Count | +|------------------|--------------|------------|-------| +| 1 | BLOCKED | 2025-09-15 | 5 | +| 1 | BLOCKED | 2025-09-16 | 5 | +| 22 | BLOCKED | 2025-09-17 | 6 | +| 22 | CANCELLATION | 2025-09-18 | 3 | +| 1 | BLOCKED | 2025-09-18 | 1 | +| 23 | BLOCKED | 2025-09-19 | 7 | +| 45 | CANCELLATION | 2025-09-19 | 5 | + + +The first row in the above table indicates that on the date `2025-09-15`, the tenant with record id=1 had 5 accounts in the `BLOCKED` overdue state. + +## Report UI: + +![overdue-states-count-daily.png](overdue-states-count-daily.png) + + + diff --git a/reports/overdue-states-count-daily/overdue-states-count-daily.png b/reports/overdue-states-count-daily/overdue-states-count-daily.png new file mode 100644 index 00000000..576a5c97 Binary files /dev/null and b/reports/overdue-states-count-daily/overdue-states-count-daily.png differ diff --git a/reports/overdue-states-count-daily/report_overdue_states_count_daily.ddl b/reports/overdue-states-count-daily/report_overdue_states_count_daily.ddl new file mode 100644 index 00000000..1400f5ec --- /dev/null +++ b/reports/overdue-states-count-daily/report_overdue_states_count_daily.ddl @@ -0,0 +1,19 @@ +create table report_overdue_states_count_daily as select * from v_report_overdue_states_count_daily limit 0; + +drop procedure if exists refresh_report_overdue_states_count_daily; +DELIMITER // +CREATE PROCEDURE refresh_report_overdue_states_count_daily() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_overdue_states_count_daily; + insert into report_overdue_states_count_daily select * from v_report_overdue_states_count_daily; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/reports/v_report_overdue_states_count_daily.ddl b/reports/overdue-states-count-daily/v_report_overdue_states_count_daily.ddl similarity index 100% rename from src/main/resources/reports/v_report_overdue_states_count_daily.ddl rename to reports/overdue-states-count-daily/v_report_overdue_states_count_daily.ddl diff --git a/reports/overdue_accounts_summary/README.md b/reports/overdue_accounts_summary/README.md new file mode 100644 index 00000000..4ef34641 --- /dev/null +++ b/reports/overdue_accounts_summary/README.md @@ -0,0 +1,43 @@ +# Overdue Accounts Summary Report + +Breakdown of current vs. overdue accounts by tenant. + +The snapshot view is: [v_report_overdue_account_summary](v_report_overdue_account_summary.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_overdue_accounts_summary", + "reportType": "COUNTERS", + "reportPrettyName": "Overdue Accounts Summary", + "sourceTableName": "report_overdue_accounts_summary", + "refreshProcedureName": "refresh_report_overdue_accounts_summary", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| Tenant Record Id | Label | Count | +|------------------|---------|-------| +| 515 | Overdue | 5 | +| 518 | Overdue | 1 | +| 1 | Overdue | 74 | +| 1 | Current | 23 | +| 256 | Overdue | 3 | + + +The first row in the above table indicates that the tenant with record id=1 had 5 accounts in the `Overdue` state. + +## Report UI: + +![overdue-accounts-summary.png](overdue-accounts-summary.png) + + + diff --git a/reports/overdue_accounts_summary/overdue-accounts-summary.png b/reports/overdue_accounts_summary/overdue-accounts-summary.png new file mode 100644 index 00000000..397cdb64 Binary files /dev/null and b/reports/overdue_accounts_summary/overdue-accounts-summary.png differ diff --git a/reports/overdue_accounts_summary/report_overdue_account_summary.ddl b/reports/overdue_accounts_summary/report_overdue_account_summary.ddl new file mode 100644 index 00000000..6f3faff1 --- /dev/null +++ b/reports/overdue_accounts_summary/report_overdue_account_summary.ddl @@ -0,0 +1,19 @@ +create table report_overdue_accounts_summary as select * from v_report_overdue_accounts_summary limit 0; + +drop procedure if exists refresh_report_overdue_accounts_summary; +DELIMITER // +CREATE PROCEDURE refresh_report_overdue_accounts_summary() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_overdue_accounts_summary; + insert into report_overdue_accounts_summary select * from v_report_overdue_accounts_summary; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/overdue_accounts_summary/v_report_overdue_account_summary.ddl b/reports/overdue_accounts_summary/v_report_overdue_account_summary.ddl new file mode 100644 index 00000000..f7571371 --- /dev/null +++ b/reports/overdue_accounts_summary/v_report_overdue_account_summary.ddl @@ -0,0 +1,10 @@ +create or replace view v_report_overdue_accounts_summary AS +select a.tenant_record_id AS tenant_record_id, +(case when (a.balance <= 0) then 'Current' else 'Overdue' end) AS label, +count(0) AS count +from analytics_accounts a +where +((1 = 1) and +(a.report_group = 'default')) +group by a.tenant_record_id, +(case when (a.balance <= 0) then 'Current' else 'Overdue' end) \ No newline at end of file diff --git a/src/main/resources/reports/payment_provider_conversion/README.md b/reports/payment_provider_conversions/README.md similarity index 76% rename from src/main/resources/reports/payment_provider_conversion/README.md rename to reports/payment_provider_conversions/README.md index c5e0dcc1..093227aa 100644 --- a/src/main/resources/reports/payment_provider_conversion/README.md +++ b/reports/payment_provider_conversions/README.md @@ -1,11 +1,10 @@ # Payment Provider Conversion report -Compare the total number of transactions and the number of successful transactions that have occurred in a recent 15 minutes -period to the same metrics that occurred in the corresponding 15 minutes from 14 days ago. +Compare the total number of transactions and the number of successful transactions that have occurred in a recent 15 minutes period to the same metrics that occurred in the corresponding 15 minutes from 14 days ago. The snapshot view is: `v_report_payment_provider_conversion`. -## History table configuration +## Report Creation ``` curl -v \ @@ -17,8 +16,8 @@ curl -v \ -d '{"reportName": "report_payment_provider_conversion", "reportType": "TABLE", "reportPrettyName": "Payment Provider Conversion", - "sourceTableName": "report_payment_provider_conversion_history", - "refreshProcedureName": "refresh_report_payment_provider_conversion_history", + "sourceTableName": "report_payment_provider_conversion", + "refreshProcedureName": "refresh_report_payment_provider_conversion", "refreshFrequency": "HOURLY"}' \ "http://127.0.0.1:8080/plugins/killbill-analytics/reports" ``` diff --git a/reports/payment_provider_conversions/report_payment_provider_conversions.ddl b/reports/payment_provider_conversions/report_payment_provider_conversions.ddl new file mode 100644 index 00000000..cdaa90fd --- /dev/null +++ b/reports/payment_provider_conversions/report_payment_provider_conversions.ddl @@ -0,0 +1,16 @@ +create table report_payment_provider_conversion as select * from v_report_payment_provider_conversion limit 0; + +drop procedure if exists refresh_report_payment_provider_conversion; +DELIMITER // +CREATE PROCEDURE refresh_report_payment_provider_conversion() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +insert into report_payment_provider_conversion select * from v_report_payment_provider_conversion; + +END; +// +DELIMITER ; diff --git a/reports/payment_provider_conversions/v_report_payment_provider_conversions.ddl b/reports/payment_provider_conversions/v_report_payment_provider_conversions.ddl new file mode 100644 index 00000000..266320f4 --- /dev/null +++ b/reports/payment_provider_conversions/v_report_payment_provider_conversions.ddl @@ -0,0 +1,122 @@ +create or replace view v_report_payment_provider_conversion_sub1 as +SELECT + apa.plugin_name +, ifnull(apa.plugin_property_4,'unknown') as merchant_account +, ifnull(apa.plugin_property_5,'unknown') as payment_method +, apa.tenant_record_id +, sum(case when apa.payment_transaction_status='SUCCESS' then 1 else 0 end) as current_success_count +, count(1) as current_transaction_count +, count(distinct apa.account_id) as current_customer_count +FROM + analytics_payment_auths apa +WHERE 1=1 +AND apa.created_date >= FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - 15*60 - (UNIX_TIMESTAMP(UTC_TIMESTAMP()) - 15*60)%(15*60)) +AND cast(FROM_UNIXTIME(UNIX_TIMESTAMP(apa.created_date) - UNIX_TIMESTAMP(apa.created_date)%(15*60)) as time) = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - 15*60 - (UNIX_TIMESTAMP(NOW()) - 15*60)%(15*60)) as time) +GROUP BY + apa.plugin_name +, ifnull(apa.plugin_property_4,'unknown') +, ifnull(apa.plugin_property_5,'unknown') +, apa.tenant_record_id +UNION +SELECT + app.plugin_name +, ifnull(app.plugin_property_4,'unknown') as merchant_account +, ifnull(app.plugin_property_5,'unknown') as payment_method +, app.tenant_record_id +, sum(case when app.payment_transaction_status='SUCCESS' then 1 else 0 end) as current_success_count +, count(1) as current_transaction_count +, count(distinct app.account_id) as current_customer_count +FROM + analytics_payment_purchases app +WHERE 1=1 +AND app.created_date >= FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - 15*60 - (UNIX_TIMESTAMP(UTC_TIMESTAMP()) - 15*60)%(15*60)) +AND cast(FROM_UNIXTIME(UNIX_TIMESTAMP(app.created_date) - UNIX_TIMESTAMP(app.created_date)%(15*60)) as time) = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - 15*60 - (UNIX_TIMESTAMP(NOW()) - 15*60)%(15*60)) as time) +GROUP BY + app.plugin_name +, ifnull(app.plugin_property_4,'unknown') +, ifnull(app.plugin_property_5,'unknown') +, app.tenant_record_id +; + +create or replace view v_report_payment_provider_conversion_sub2 as +SELECT + apa.plugin_name +, ifnull(apa.plugin_property_4,'unknown') as merchant_account +, ifnull(apa.plugin_property_5,'unknown') as payment_method +, apa.tenant_record_id +, sum(case when apa.payment_transaction_status='SUCCESS' then 1 else 0 end) as historical_success_count +, count(1) as historical_transaction_count +, count(distinct apa.account_id) as historical_customer_count +FROM + analytics_payment_auths apa +WHERE 1=1 +AND apa.created_date < FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60)%(15*60) + 15*60) +AND cast(FROM_UNIXTIME(UNIX_TIMESTAMP(apa.created_date) - UNIX_TIMESTAMP(apa.created_date)%(15*60)) as time) = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60)%(15*60)) as time) +AND apa.created_date >= FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60)%(15*60)) +GROUP BY + apa.plugin_name +, ifnull(apa.plugin_property_4,'unknown') +, ifnull(apa.plugin_property_5,'unknown') +, apa.tenant_record_id +UNION +SELECT + app.plugin_name +, ifnull(app.plugin_property_4,'unknown') as merchant_account +, ifnull(app.plugin_property_5,'unknown') as payment_method +, app.tenant_record_id +, sum(case when app.payment_transaction_status='SUCCESS' then 1 else 0 end) as historical_success_count +, count(1) as historical_transaction_count +, count(distinct app.account_id) as historical_customer_count +FROM + analytics_payment_purchases app +WHERE 1=1 +AND app.created_date < FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60)%(15*60) + 15*60) +AND cast(FROM_UNIXTIME(UNIX_TIMESTAMP(app.created_date) - UNIX_TIMESTAMP(app.created_date)%(15*60)) as time) = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60)%(15*60)) as time) +AND app.created_date >= FROM_UNIXTIME(UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(UTC_TIMESTAMP() - interval '14' day) - 15*60)%(15*60)) +GROUP BY + app.plugin_name +, ifnull(app.plugin_property_4,'unknown') +, ifnull(app.plugin_property_5,'unknown') +, app.tenant_record_id +; + +create or replace view v_report_payment_provider_conversion as +with agg as ( + select + rpccs1.plugin_name, + rpccs1.merchant_account, + rpccs1.payment_method, + rpccs1.tenant_record_id, + ifnull(sum(rpccs1.current_success_count),0) as current_success_count, + ifnull(sum(rpccs1.current_transaction_count),0) as current_transaction_count, + ifnull(sum(rpccs1.current_customer_count),0) as current_customer_count, + ifnull(sum(rpccs2.historical_success_count),0) as historical_success_count, + ifnull(sum(rpccs2.historical_transaction_count),0) as historical_transaction_count, + ifnull(sum(rpccs2.historical_customer_count),0) as historical_customer_count + from v_report_payment_provider_conversion_sub2 rpccs2 + left join v_report_payment_provider_conversion_sub1 rpccs1 + on rpccs1.plugin_name=rpccs2.plugin_name + and rpccs1.merchant_account=rpccs2.merchant_account + and rpccs1.payment_method=rpccs2.payment_method + and rpccs1.tenant_record_id=rpccs2.tenant_record_id + group by + rpccs1.plugin_name, + rpccs1.merchant_account, + rpccs1.payment_method, + rpccs1.tenant_record_id +) +select *, + case when historical_success_count > 0 + then concat(round(((current_success_count-historical_success_count)/historical_success_count)*100,2),'%') + else '0%' + end as success_delta, + case when historical_transaction_count > 0 + then concat(round(((current_transaction_count-historical_transaction_count)/historical_transaction_count)*100,2),'%') + else '0%' + end as transaction_delta, + case when historical_customer_count > 0 + then concat(round(((current_customer_count-historical_customer_count)/historical_customer_count)*100,2),'%') + else '0%' + end as customer_delta, + sysdate() as refresh_date +from agg; diff --git a/src/main/resources/reports/payment_provider_errors/README.md b/reports/payment_provider_errors/README.md similarity index 84% rename from src/main/resources/reports/payment_provider_errors/README.md rename to reports/payment_provider_errors/README.md index 06b20b42..2cdec97d 100644 --- a/src/main/resources/reports/payment_provider_errors/README.md +++ b/reports/payment_provider_errors/README.md @@ -1,10 +1,10 @@ -# Payment Provider Errors report +# Payment Provider Errors Report Compute the top errors per provider and currency, per day. The snapshot view is: `v_report_payment_provider_errors` -## Timeline configuration +## Report Creation ``` curl -v \ @@ -21,3 +21,7 @@ curl -v \ "refreshFrequency": "HOURLY"}' \ "http://127.0.0.1:8080/plugins/killbill-analytics/reports" ``` + +## Report UI: + +![payment-provider-errors.png](payment-provider-errors.png) diff --git a/reports/payment_provider_errors/payment-provider-errors.png b/reports/payment_provider_errors/payment-provider-errors.png new file mode 100644 index 00000000..3d3f4b2b Binary files /dev/null and b/reports/payment_provider_errors/payment-provider-errors.png differ diff --git a/reports/payment_provider_errors/report_payment_provider_errors.ddl b/reports/payment_provider_errors/report_payment_provider_errors.ddl new file mode 100644 index 00000000..02eff80c --- /dev/null +++ b/reports/payment_provider_errors/report_payment_provider_errors.ddl @@ -0,0 +1,21 @@ +create table report_payment_provider_errors as select * from v_report_payment_provider_errors limit 0; +drop procedure if exists refresh_report_payment_provider_errors; +DELIMITER // +CREATE PROCEDURE refresh_report_payment_provider_errors() +BEGIN + + DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; + DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + + SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; + + START TRANSACTION; + + delete from report_payment_provider_errors; + insert into report_payment_provider_errors select * from v_report_payment_provider_errors; + + COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/payment_provider_errors/v_report_payment_provider_errors.ddl b/reports/payment_provider_errors/v_report_payment_provider_errors.ddl new file mode 100644 index 00000000..5c19ab43 --- /dev/null +++ b/reports/payment_provider_errors/v_report_payment_provider_errors.ddl @@ -0,0 +1,64 @@ +create or replace view v_report_payment_provider_errors_sub1 as +select + aa.tenant_record_id +, 'AUTH' as op +, date_format(aa.created_date,'%Y-%m-%d') as day +, aa.currency +, aa.plugin_name +, aa.record_id +from analytics_payment_auths aa +where 1=1 + and aa.payment_transaction_status not in ('PENDING', 'SUCCESS') + and aa.report_group = 'default' + and aa.created_date > utc_timestamp() - interval '60' day +union +select + ap.tenant_record_id +, 'PURCHASE' as op +, date_format(ap.created_date,'%Y-%m-%d') as day +, ap.currency +, ap.plugin_name +, ap.record_id +from analytics_payment_purchases ap +where 1=1 + and ap.payment_transaction_status not in ('PENDING', 'SUCCESS') + and ap.report_group = 'default' + and ap.created_date > utc_timestamp() - interval '60' day +; + +create or replace view v_report_payment_provider_errors_sub2 as +select + v1.tenant_record_id +, v1.day +, v1.currency +, v1.plugin_name +, substring_index(ifnull(apa.plugin_gateway_error, app.plugin_gateway_error), ' ', 10) as plugin_gateway_error +, count(1) as count +from v_report_payment_provider_errors_sub1 v1 +left join analytics_payment_auths apa on apa.record_id = v1.record_id and v1.op = 'AUTH' +left join analytics_payment_purchases app on app.record_id = v1.record_id and v1.op = 'PURCHASE' +where 1=1 +and ifnull(apa.plugin_gateway_error, app.plugin_gateway_error) is not null +group by 1,2,3,4,5 +; + +create or replace view v_report_payment_provider_errors as +select + tenant_record_id +, day +, currency +, plugin_name +, plugin_gateway_error +, count +from v_report_payment_provider_errors_sub2 sub2 +where ( + select count(*) from v_report_payment_provider_errors_sub2 as sub21 + where 1=1 + and sub21.tenant_record_id = sub2.tenant_record_id + and sub21.day = sub2.day + and sub21.currency = sub2.currency + and sub21.plugin_name = sub2.plugin_name + and sub21.count >= sub2.count +) <= 3 +; + diff --git a/src/main/resources/reports/payment_provider_monitor/README.md b/reports/payment_provider_monitor/README.md similarity index 76% rename from src/main/resources/reports/payment_provider_monitor/README.md rename to reports/payment_provider_monitor/README.md index 8982c0ce..cfcfbc78 100644 --- a/src/main/resources/reports/payment_provider_monitor/README.md +++ b/reports/payment_provider_monitor/README.md @@ -4,7 +4,7 @@ Compute the number of successful transactions that have occurred in the past hou The snapshot view is: `v_report_payment_provider_monitor` -## History table configuration +## Report Creation ``` curl -v \ @@ -16,8 +16,12 @@ curl -v \ -d '{"reportName": "report_payment_provider_monitor", "reportType": "TABLE", "reportPrettyName": "Payment Provider Monitor", - "sourceTableName": "report_payment_provider_monitor_history", - "refreshProcedureName": "refresh_report_payment_provider_monitor_history", - "refreshFrequency": "DAILY"}' \ + "sourceTableName": "report_payment_provider_monitor", + "refreshProcedureName": "refresh_report_payment_provider_monitor", + "refreshFrequency": "HOURLY"}' \ "http://127.0.0.1:8080/plugins/killbill-analytics/reports" ``` + +## Report UI: + +![payment-provider-monitor.png](payment-provider-monitor.png) \ No newline at end of file diff --git a/reports/payment_provider_monitor/payment-provider-monitor.png b/reports/payment_provider_monitor/payment-provider-monitor.png new file mode 100644 index 00000000..4de7ce08 Binary files /dev/null and b/reports/payment_provider_monitor/payment-provider-monitor.png differ diff --git a/reports/payment_provider_monitor/report_payment_provider_monitor.ddl b/reports/payment_provider_monitor/report_payment_provider_monitor.ddl new file mode 100644 index 00000000..6a5c28fc --- /dev/null +++ b/reports/payment_provider_monitor/report_payment_provider_monitor.ddl @@ -0,0 +1,16 @@ +create table report_payment_provider_monitor as select * from v_report_payment_provider_monitor limit 0; + +drop procedure if exists refresh_report_payment_provider_monitor; +DELIMITER // +CREATE PROCEDURE refresh_report_payment_provider_monitor() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +insert into report_payment_provider_monitor select * from v_report_payment_provider_monitor; + +END; +// +DELIMITER ; diff --git a/reports/payment_provider_monitor/v_report_payment_provider_monitor.ddl b/reports/payment_provider_monitor/v_report_payment_provider_monitor.ddl new file mode 100644 index 00000000..a952a500 --- /dev/null +++ b/reports/payment_provider_monitor/v_report_payment_provider_monitor.ddl @@ -0,0 +1,88 @@ +create or replace view v_report_payment_provider_monitor_sub1 as +SELECT distinct + apa.plugin_name +, ifnull(apa.plugin_property_4,'unknown') as merchant_account +, ifnull(apa.plugin_property_5,'unknown') as payment_method +, apa.tenant_record_id +FROM analytics_payment_auths apa +WHERE 1=1 +AND apa.created_date > utc_timestamp() - interval '7' day +UNION +SELECT distinct + app.plugin_name +, ifnull(app.plugin_property_4,'unknown') as merchant_account +, ifnull(app.plugin_property_5,'unknown') as payment_method +, app.tenant_record_id +FROM analytics_payment_purchases app +WHERE 1=1 +AND app.created_date > utc_timestamp() - interval '7' day +; + +create or replace view v_report_payment_provider_monitor_sub2 as +SELECT + apa.plugin_name +, ifnull(apa.plugin_property_4,'unknown') as merchant_account +, ifnull(apa.plugin_property_5,'unknown') as payment_method +, apa.tenant_record_id +, sum(case when apa.created_date > utc_timestamp() - interval 1 hour then 1 else 0 end) success_count_last_hour +, count(1) success_count_last_12_hours +FROM analytics_payment_auths apa +WHERE 1=1 +AND apa.payment_transaction_status = 'SUCCESS' +AND apa.created_date > utc_timestamp() - interval '12' hour +GROUP BY + apa.plugin_name +, ifnull(apa.plugin_property_4,'unknown') +, ifnull(apa.plugin_property_5,'unknown') +, apa.tenant_record_id +UNION +SELECT + app.plugin_name +, ifnull(app.plugin_property_4,'unknown') as merchant_account +, ifnull(app.plugin_property_5,'unknown') as payment_method +, app.tenant_record_id +, sum(case when app.created_date > utc_timestamp() - interval 1 hour then 1 else 0 end) success_count_last_hour +, count(1) success_count_last_12_hours +FROM analytics_payment_purchases app +WHERE 1=1 +AND app.payment_transaction_status = 'SUCCESS' +AND app.created_date > utc_timestamp() - interval '12' hour +GROUP BY + app.plugin_name +, ifnull(app.plugin_property_4,'unknown') +, ifnull(app.plugin_property_5,'unknown') +, app.tenant_record_id +; + +create or replace view v_report_payment_provider_monitor_sub3 as +SELECT + plugin_name +, merchant_account +, payment_method +, tenant_record_id +, sum(ifnull(success_count_last_hour,0)) as success_count_last_hour +, sum(ifnull(success_count_last_12_hours,0)) as success_count_last_12_hours +FROM v_report_payment_provider_monitor_sub2 t2 +GROUP BY + plugin_name +, merchant_account +, payment_method +, tenant_record_id +; + +create or replace view v_report_payment_provider_monitor as +SELECT + plugin_list.plugin_name +, plugin_list.merchant_account +, plugin_list.payment_method +, plugin_list.tenant_record_id +, ifnull(recent_success_trx.success_count_last_hour,0) as success_count_last_hour +, ifnull(recent_success_trx.success_count_last_12_hours,0) as success_count_last_12_hours +, sysdate() as refresh_date +FROM v_report_payment_provider_monitor_sub1 plugin_list +LEFT OUTER JOIN v_report_payment_provider_monitor_sub3 recent_success_trx on + plugin_list.plugin_name=recent_success_trx.plugin_name +AND plugin_list.merchant_account=recent_success_trx.merchant_account +AND plugin_list.payment_method=recent_success_trx.payment_method +AND plugin_list.tenant_record_id=recent_success_trx.tenant_record_id +; \ No newline at end of file diff --git a/src/main/resources/reports/payments_by_provider/README.md b/reports/payments_by_provider/README.md similarity index 74% rename from src/main/resources/reports/payments_by_provider/README.md rename to reports/payments_by_provider/README.md index ed9000cc..ac74566d 100644 --- a/src/main/resources/reports/payments_by_provider/README.md +++ b/reports/payments_by_provider/README.md @@ -4,7 +4,7 @@ Compute the number of payments by transaction state over different timeframes fo The snapshot view is: `v_report_payments_by_provider` -## History table configuration +## Report Creation (Payment by Provider) ``` curl -v \ @@ -16,13 +16,17 @@ curl -v \ -d '{"reportName": "report_payments_by_provider", "reportType": "TABLE", "reportPrettyName": "Payments By Provider", - "sourceTableName": "report_payments_by_provider_history", - "refreshProcedureName": "refresh_report_payments_by_provider_history", + "sourceTableName": "report_payments_by_provider", + "refreshProcedureName": "refresh_report_payments_by_provider", "refreshFrequency": "HOURLY"}' \ "http://127.0.0.1:8080/plugins/killbill-analytics/reports" ``` -## Summary pie charts configuration +## Payment by Provider Report UI + +![payment-by-provider.png](payment-by-provider.png) + +## Report Creation (Payment by Provider - Last 24 Hours Summary) ``` curl -v \ @@ -36,6 +40,10 @@ curl -v \ "reportPrettyName": "Payments By Provider Summary (last 24hrs)", "sourceTableName": "report_payments_by_provider_last_24h_summary", "refreshProcedureName": "refresh_report_payments_by_provider_last_24h_summary", - "refreshFrequency": "DAILY"}' \ + "refreshFrequency": "HOURLY"}' \ "http://127.0.0.1:8080/plugins/killbill-analytics/reports" ``` + +## Payment by Provider - Last 24 Hours Summary UI + +![payment-by-provider-last-24-hr-summary.png](payment-by-provider-last-24-hr-summary.png) \ No newline at end of file diff --git a/reports/payments_by_provider/payment-by-provider-last-24-hr-summary.png b/reports/payments_by_provider/payment-by-provider-last-24-hr-summary.png new file mode 100644 index 00000000..4dbab796 Binary files /dev/null and b/reports/payments_by_provider/payment-by-provider-last-24-hr-summary.png differ diff --git a/reports/payments_by_provider/payment-by-provider.png b/reports/payments_by_provider/payment-by-provider.png new file mode 100644 index 00000000..efca829e Binary files /dev/null and b/reports/payments_by_provider/payment-by-provider.png differ diff --git a/reports/payments_by_provider/report_payments_by_provider.ddl b/reports/payments_by_provider/report_payments_by_provider.ddl new file mode 100644 index 00000000..89224aed --- /dev/null +++ b/reports/payments_by_provider/report_payments_by_provider.ddl @@ -0,0 +1,16 @@ +create table report_payments_by_provider as select * from v_report_payments_by_provider limit 0; + +drop procedure if exists refresh_report_payments_by_provider; +DELIMITER // +CREATE PROCEDURE refresh_report_payments_by_provider() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +insert into report_payments_by_provider select * from v_report_payments_by_provider; + +END; +// +DELIMITER ; diff --git a/src/main/resources/reports/payments_by_provider/refresh_report_payments_by_provider_last_24h_summary.ddl b/reports/payments_by_provider/report_payments_by_provider_last_24h_summary.ddl similarity index 100% rename from src/main/resources/reports/payments_by_provider/refresh_report_payments_by_provider_last_24h_summary.ddl rename to reports/payments_by_provider/report_payments_by_provider_last_24h_summary.ddl diff --git a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub1.ddl b/reports/payments_by_provider/v_report_payments_by_provider.ddl similarity index 75% rename from src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub1.ddl rename to reports/payments_by_provider/v_report_payments_by_provider.ddl index abe3fc4c..2c075149 100644 --- a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub1.ddl +++ b/reports/payments_by_provider/v_report_payments_by_provider.ddl @@ -15,7 +15,7 @@ SELECT FROM analytics_payment_auths a FORCE INDEX(analytics_payment_auths_created_date) WHERE 1=1 -AND a.created_date>now() - interval '7' day +AND a.created_date>utc_timestamp() - interval '7' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -39,7 +39,7 @@ SELECT FROM analytics_payment_captures a FORCE INDEX(analytics_payment_captures_created_date) WHERE 1=1 -AND a.created_date>now() - interval '7' day +AND a.created_date>utc_timestamp() - interval '7' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -63,7 +63,7 @@ SELECT FROM analytics_payment_chargebacks a FORCE INDEX(analytics_payment_chargebacks_created_date) WHERE 1=1 -AND a.created_date>now() - interval '7' day +AND a.created_date>utc_timestamp() - interval '7' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -87,7 +87,7 @@ SELECT FROM analytics_payment_credits a FORCE INDEX(analytics_payment_credits_created_date) WHERE 1=1 -AND a.created_date>now() - interval '7' day +AND a.created_date>utc_timestamp() - interval '7' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -111,7 +111,7 @@ SELECT FROM analytics_payment_purchases a FORCE INDEX(analytics_payment_purchases_created_date) WHERE 1=1 -AND a.created_date>now() - interval '7' day +AND a.created_date>utc_timestamp() - interval '7' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -135,7 +135,7 @@ SELECT FROM analytics_payment_refunds a FORCE INDEX(analytics_payment_refunds_created_date) WHERE 1=1 -AND a.created_date>now() - interval '7' day +AND a.created_date>utc_timestamp() - interval '7' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -159,7 +159,7 @@ SELECT FROM analytics_payment_voids a FORCE INDEX(analytics_payment_voids_created_date) WHERE 1=1 -AND a.created_date>now() - interval '7' day +AND a.created_date>utc_timestamp() - interval '7' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -183,7 +183,7 @@ SELECT , a.converted_currency FROM analytics_payment_auths a WHERE 1=1 -AND a.created_date>now() - interval '1' day +AND a.created_date>utc_timestamp() - interval '1' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -206,7 +206,7 @@ SELECT , a.converted_currency FROM analytics_payment_captures a WHERE 1=1 -AND a.created_date>now() - interval '1' day +AND a.created_date>utc_timestamp() - interval '1' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -229,7 +229,7 @@ SELECT , a.converted_currency FROM analytics_payment_chargebacks a WHERE 1=1 -AND a.created_date>now() - interval '1' day +AND a.created_date>utc_timestamp() - interval '1' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -252,7 +252,7 @@ SELECT , a.converted_currency FROM analytics_payment_credits a WHERE 1=1 -AND a.created_date>now() - interval '1' day +AND a.created_date>utc_timestamp() - interval '1' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -275,7 +275,7 @@ SELECT , a.converted_currency FROM analytics_payment_purchases a WHERE 1=1 -AND a.created_date>now() - interval '1' day +AND a.created_date>utc_timestamp() - interval '1' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -298,7 +298,7 @@ SELECT , a.converted_currency FROM analytics_payment_refunds a WHERE 1=1 -AND a.created_date>now() - interval '1' day +AND a.created_date>utc_timestamp() - interval '1' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -321,7 +321,7 @@ SELECT , a.converted_currency FROM analytics_payment_voids a WHERE 1=1 -AND a.created_date>now() - interval '1' day +AND a.created_date>utc_timestamp() - interval '1' day GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -345,8 +345,8 @@ SELECT , a.converted_currency FROM analytics_payment_auths a WHERE 1=1 -AND a.created_date>now() - interval '34' minute -AND a.created_date<=now() - interval '4' minute +AND a.created_date>utc_timestamp() - interval '34' minute +AND a.created_date<=utc_timestamp() - interval '4' minute GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -369,8 +369,8 @@ SELECT , a.converted_currency FROM analytics_payment_captures a WHERE 1=1 -AND a.created_date>now() - interval '34' minute -AND a.created_date<=now() - interval '4' minute +AND a.created_date>utc_timestamp() - interval '34' minute +AND a.created_date<=utc_timestamp() - interval '4' minute GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -393,8 +393,8 @@ SELECT , a.converted_currency FROM analytics_payment_chargebacks a WHERE 1=1 -AND a.created_date>now() - interval '34' minute -AND a.created_date<=now() - interval '4' minute +AND a.created_date>utc_timestamp() - interval '34' minute +AND a.created_date<=utc_timestamp() - interval '4' minute GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -417,8 +417,8 @@ SELECT , a.converted_currency FROM analytics_payment_credits a WHERE 1=1 -AND a.created_date>now() - interval '34' minute -AND a.created_date<=now() - interval '4' minute +AND a.created_date>utc_timestamp() - interval '34' minute +AND a.created_date<=utc_timestamp() - interval '4' minute GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -441,8 +441,8 @@ SELECT , a.converted_currency FROM analytics_payment_purchases a WHERE 1=1 -AND a.created_date>now() - interval '34' minute -AND a.created_date<=now() - interval '4' minute +AND a.created_date>utc_timestamp() - interval '34' minute +AND a.created_date<=utc_timestamp() - interval '4' minute GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -465,8 +465,8 @@ SELECT , a.converted_currency FROM analytics_payment_refunds a WHERE 1=1 -AND a.created_date>now() - interval '34' minute -AND a.created_date<=now() - interval '4' minute +AND a.created_date>utc_timestamp() - interval '34' minute +AND a.created_date<=utc_timestamp() - interval '4' minute GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -489,8 +489,8 @@ SELECT , a.converted_currency FROM analytics_payment_voids a WHERE 1=1 -AND a.created_date>now() - interval '34' minute -AND a.created_date<=now() - interval '4' minute +AND a.created_date>utc_timestamp() - interval '34' minute +AND a.created_date<=utc_timestamp() - interval '4' minute GROUP BY a.plugin_name , ifnull(a.plugin_property_4,'unknown') @@ -498,3 +498,99 @@ GROUP BY , a.converted_currency , a.tenant_record_id ; + + +create or replace view v_report_payments_by_provider_sub2 as +select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_auths force index(analytics_payment_auths_date_trid_plugin_name) where created_date > utc_timestamp() - interval '7' day +union +select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_captures force index(analytics_payment_captures_date_trid_plugin_name) where created_date > utc_timestamp() - interval '7' day +union +select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_chargebacks force index(analytics_payment_chargebacks_date_trid_plugin_name) where created_date > utc_timestamp() - interval '7' day +union +select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_credits force index(analytics_payment_credits_date_trid_plugin_name) where created_date > utc_timestamp() - interval '7' day +union +select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_purchases force index(analytics_payment_purchases_date_trid_plugin_name) where created_date > utc_timestamp() - interval '7' day +union +select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_refunds force index(analytics_payment_refunds_date_trid_plugin_name) where created_date > utc_timestamp() - interval '7' day +union +select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_voids force index(analytics_payment_voids_date_trid_plugin_name) where created_date > utc_timestamp() - interval '7' day +; + +create or replace view v_report_payments_by_provider_sub3 as +select 1 as timeframe union +select 2 as timeframe union +select 3 as timeframe union +select 4 as timeframe +; + +create or replace view v_report_payments_by_provider as +SELECT + t1.plugin_name +, t1.merchant_account +, t1.payment_method +, t1.tenant_record_id +, t2.timeframe +, transaction_type +, case when t2.timeframe=1 then 'Last 30 days' + when t2.timeframe=2 then 'Last 7 days' + when t2.timeframe=3 then 'Last 24 hours' + when t2.timeframe=4 then 'Last 30 min' + end as period +, sum(ifnull(total,0)) as total +, sum(ifnull(failed,0)) as failed +, sum(ifnull(pending,0)) as pending +, sum(ifnull(good,0)) as good +, case when sum(failed) is not null and sum(total) is not null + then concat(round(((sum(failed)/sum(total))*100),2),'%') + else '0%' + end as pct_failed +, case when sum(pending) is not null and sum(total) is not null + then concat(round(((sum(pending)/sum(total))*100),2),'%') + else '0%' + end as pct_pending +, case when sum(good) is not null and sum(total) is not null + then concat(round(((sum(good)/sum(total))*100),2),'%') + else '0%' + end as pct_good +, sum(ifnull(v1.converted_amount,0)) as converted_amount +, t1.converted_currency +, CAST(sysdate() AS DATETIME) as refresh_date -- ✅ cast to DATETIME +FROM v_report_payments_by_provider_sub2 t1 +INNER JOIN v_report_payments_by_provider_sub3 t2 +LEFT OUTER JOIN v_report_payments_by_provider_sub1 v1 + on v1.plugin_name = t1.plugin_name + and v1.merchant_account = t1.merchant_account + and v1.payment_method = t1.payment_method + and v1.timeframe = t2.timeframe + and v1.converted_currency = t1.converted_currency + and v1.tenant_record_id = t1.tenant_record_id +GROUP BY + t1.plugin_name +, t1.merchant_account +, t1.payment_method +, t2.timeframe +, transaction_type +, t1.converted_currency +, t1.tenant_record_id +ORDER BY + t1.tenant_record_id +, t1.merchant_account +, t1.payment_method +, t1.plugin_name +, t2.timeframe +, transaction_type +, t1.converted_currency +; + + +create or replace view v_report_payments_by_provider_last_24h_summary as +select + tenant_record_id +, payment_method as label +, sum(total) as count +from v_report_payments_by_provider +where 1 = 1 +and timeframe = 3 +and transaction_type in ('AUTHORIZE', 'PURCHASE') +group by 1,2 +; \ No newline at end of file diff --git a/reports/payments_monthly/README.md b/reports/payments_monthly/README.md new file mode 100644 index 00000000..aa72ab93 --- /dev/null +++ b/reports/payments_monthly/README.md @@ -0,0 +1,31 @@ +# Payments Monthly Report + +Report of all payments from the previous month, showing amounts in both original currency and USD equivalents. + +The snapshot view is: `v_report_payments_monthly` + +## Prerequisites + +This report requires the `analytics_currency_conversion` table to be populated. See [insertMonthlyCurrencyConversionRates.ddl](../utils/insertMonthlyCurrencyConversionRates.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_payments_monthly", + "reportType": "TABLE", + "reportPrettyName": "Payments Monthly Report", + "sourceTableName": "report_payments_monthly", + "refreshProcedureName": "refresh_report_payments_monthly", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Report UI: + +![payments-monthly.png](payments-monthly.png) diff --git a/reports/payments_monthly/payments-monthly.png b/reports/payments_monthly/payments-monthly.png new file mode 100644 index 00000000..3e8fe6af Binary files /dev/null and b/reports/payments_monthly/payments-monthly.png differ diff --git a/reports/payments_monthly/report_payments_monthly.ddl b/reports/payments_monthly/report_payments_monthly.ddl new file mode 100644 index 00000000..b0e39bb6 --- /dev/null +++ b/reports/payments_monthly/report_payments_monthly.ddl @@ -0,0 +1,19 @@ +create table report_payments_monthly as select * from v_report_payments_monthly limit 0; + +drop procedure if exists refresh_report_payments_monthly; +DELIMITER // +CREATE PROCEDURE refresh_report_payments_monthly() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_payments_monthly; + insert into report_payments_monthly select * from v_report_payments_monthly; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/extracts/finance/payments_monthly.sql b/reports/payments_monthly/v_report_payments_monthly.ddl similarity index 74% rename from src/main/resources/extracts/finance/payments_monthly.sql rename to reports/payments_monthly/v_report_payments_monthly.ddl index 7fe66f3a..1540a722 100644 --- a/src/main/resources/extracts/finance/payments_monthly.sql +++ b/reports/payments_monthly/v_report_payments_monthly.ddl @@ -1,3 +1,4 @@ +CREATE OR REPLACE VIEW v_report_payments_monthly AS select pmt.account_name as "Customer Name" , pmt.account_external_key as "Account Number" @@ -5,7 +6,7 @@ select , date_format(pmt.invoice_date, '%m/%d/%Y') as "Invoice Date" , pmt.currency as "Currency" , pmt.invoice_original_amount_charged as "Invoice Amount" -, round(cc.reference_rate * pmt.invoice_original_amount_charged,4) as "Invoice Amount USD" +, case when pmt.currency != 'USD' THEN round(cc.reference_rate * pmt.invoice_original_amount_charged,4) else pmt.invoice_original_amount_charged end as "Invoice Amount USD" , pmt.plugin_name as "Payment Gateway" , pmt.plugin_pm_type as "Payment Method" , pmt.plugin_pm_cc_type as "Payment Card Type" @@ -16,18 +17,16 @@ select , date_format(pmt.created_date, '%m/%d/%Y') as "Payment Date" , pmt.currency as "Payment Currency" , pmt.amount as "Payment Amount" -, round(cc.reference_rate * pmt.amount,4) as "Payment Amount USD" +, case when pmt.currency != 'USD' then round(cc.reference_rate * pmt.amount,4) else pmt.amount end as "Payment Amount USD" , pmt.payment_id +, pmt.tenant_record_id from analytics_payment_purchases pmt - join analytics_currency_conversion cc on pmt.created_date >= cc.start_date and pmt.created_date <= cc.end_date and cc.currency = pmt.currency + left outer join analytics_currency_conversion cc on pmt.created_date >= cc.start_date and pmt.created_date <= cc.end_date and cc.currency = pmt.currency where 1=1 and pmt.created_date >= cast(date_format(date_sub(sysdate(), interval '1' month), '%Y-%m-01') as date) and pmt.created_date < cast(date_format(sysdate(), '%Y-%m-01') as date) and pmt.report_group != 'test' order by account_name -, pmt.invoice_payment_record_id; -- just for well defined ordering - - - +, pmt.invoice_payment_record_id; -- just for well defined ordering \ No newline at end of file diff --git a/reports/payments_summary/README.md b/reports/payments_summary/README.md index 8d02c88c..81e7b9fd 100644 --- a/reports/payments_summary/README.md +++ b/reports/payments_summary/README.md @@ -4,7 +4,7 @@ Provides payment summary. Provides details like payment_id, amount, etc. The snapshot view is: `v_report_payments_summary` -## Pie chart configuration +## Report Creation ``` curl -v \ @@ -15,9 +15,13 @@ curl -v \ -H 'Content-Type: application/json' \ -d '{"reportName": "report_payments_summary", "reportType": "TABLE", - "reportPrettyName": "Payments summary", + "reportPrettyName": "Payments Summary", "sourceTableName": "report_payments_summary", "refreshProcedureName": "refresh_report_payments_summary", "refreshFrequency": "HOURLY"}' \ "http://127.0.0.1:8080/plugins/killbill-analytics/reports" ``` + +## Report UI: + +![payments-summary.png](payments-summary.png) \ No newline at end of file diff --git a/reports/payments_summary/payments-summary.png b/reports/payments_summary/payments-summary.png new file mode 100644 index 00000000..d85d23ff Binary files /dev/null and b/reports/payments_summary/payments-summary.png differ diff --git a/reports/payments_summary/v_payments_summary.ddl b/reports/payments_summary/v_report_payments_summary.ddl similarity index 100% rename from reports/payments_summary/v_payments_summary.ddl rename to reports/payments_summary/v_report_payments_summary.ddl diff --git a/reports/payments_total_daily/README.md b/reports/payments_total_daily/README.md new file mode 100644 index 00000000..44c44a08 --- /dev/null +++ b/reports/payments_total_daily/README.md @@ -0,0 +1,39 @@ +# Daily Payments Report + +Compute the total value (in the reference currency) of payments per day per currency. + +The snapshot view is: `v_report_payments_total_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_payments_total_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Payments Value", + "sourceTableName": "report_payments_total_daily", + "refreshProcedureName": "refresh_report_payments_total_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| tenant_record_id | day | currency | amount | +|------------------|------------|----------|----------| +| 1 | 2025-05-01 | USD | 49.9000 | +| 1 | 2025-05-05 | USD | 26.0900 | +| 1 | 2025-05-05 | EUR | 79.8500 | +| 2 | 2025-07-03 | USD | 229.6000 | +| 2 | 2025-07-20 | EUR | 379.9000 | + +The first row in the above table indicates that on the date `2025-05-01`, the tenant with record id=1 had a total payment value of USD 49.9. + +## Report UI: + +![payments-total-daily.png](payments-total-daily.png) \ No newline at end of file diff --git a/reports/payments_total_daily/payments-total-daily.png b/reports/payments_total_daily/payments-total-daily.png new file mode 100644 index 00000000..4ab265a1 Binary files /dev/null and b/reports/payments_total_daily/payments-total-daily.png differ diff --git a/src/main/resources/reports/payments_total_daily/report_payments_total_daily.ddl b/reports/payments_total_daily/report_payments_total_daily.ddl similarity index 100% rename from src/main/resources/reports/payments_total_daily/report_payments_total_daily.ddl rename to reports/payments_total_daily/report_payments_total_daily.ddl diff --git a/reports/payments_total_daily/v_report_payments_total_daily.ddl b/reports/payments_total_daily/v_report_payments_total_daily.ddl new file mode 100644 index 00000000..1f90fd06 --- /dev/null +++ b/reports/payments_total_daily/v_report_payments_total_daily.ddl @@ -0,0 +1,28 @@ +create or replace view v_report_payments_total_daily as +select + tenant_record_id, + date_format(created_date,'%Y-%m-%d') as day, + currency, + sum(ifnull(amount, 0)) as count +from ( + select + ac.tenant_record_id, + ac.created_date, + ac.currency, + ac.amount + from analytics_payment_captures ac + where ac.payment_transaction_status = 'SUCCESS' + and ac.report_group='default' + + union all + + select + ap.tenant_record_id, + ap.created_date, + ap.currency, + ap.amount + from analytics_payment_purchases ap + where ap.payment_transaction_status = 'SUCCESS' + and ap.report_group='default' +) t +group by 1,2,3; diff --git a/reports/refunds-monthly/README.md b/reports/refunds-monthly/README.md new file mode 100644 index 00000000..19415169 --- /dev/null +++ b/reports/refunds-monthly/README.md @@ -0,0 +1,31 @@ +# Refunds Monthly Report + +Report of all refunds from the previous month, showing amounts in both original currency and USD equivalents. + +The snapshot view is: `v_report_refunds_monthly` + +## Prerequisites + +This report requires the `analytics_currency_conversion` table to be populated. See [insertMonthlyCurrencyConversionRates.ddl](../utils/insertMonthlyCurrencyConversionRates.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_refunds_monthly", + "reportType": "TABLE", + "reportPrettyName": "Refunds Monthly Report", + "sourceTableName": "report_refunds_monthly", + "refreshProcedureName": "refresh_report_refunds_monthly", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Report UI: + +![refunds-monthly.png](refunds-monthly.png) diff --git a/reports/refunds-monthly/refunds-monthly.png b/reports/refunds-monthly/refunds-monthly.png new file mode 100644 index 00000000..65674b2b Binary files /dev/null and b/reports/refunds-monthly/refunds-monthly.png differ diff --git a/reports/refunds-monthly/report_refunds_monthly.ddl b/reports/refunds-monthly/report_refunds_monthly.ddl new file mode 100644 index 00000000..6010ccb0 --- /dev/null +++ b/reports/refunds-monthly/report_refunds_monthly.ddl @@ -0,0 +1,19 @@ +create table report_refunds_monthly as select * from v_report_refunds_monthly limit 0; + +drop procedure if exists refresh_report_refunds_monthly; +DELIMITER // +CREATE PROCEDURE refresh_report_refunds_monthly() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_refunds_monthly; + insert into report_refunds_monthly select * from v_report_refunds_monthly; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/refunds-monthly/v_report_refunds_monthly.ddl b/reports/refunds-monthly/v_report_refunds_monthly.ddl new file mode 100644 index 00000000..90d7bdf7 --- /dev/null +++ b/reports/refunds-monthly/v_report_refunds_monthly.ddl @@ -0,0 +1,34 @@ +CREATE OR REPLACE VIEW v_report_refunds_monthly AS +select + rfnd.account_name as "Customer Name" +, rfnd.account_external_key as "Account Number" +, rfnd.invoice_number as "Invoice Number" +, date_format(rfnd.invoice_date, '%m/%d/%Y') as "Invoice Date" +, rfnd.currency as "Currency" +, rfnd.invoice_original_amount_charged as "Invoice Amount" +, case when rfnd.currency != 'USD' then round(cc.reference_rate * rfnd.invoice_original_amount_charged,4) else rfnd.invoice_original_amount_charged end as "Invoice Amount USD" +, rfnd.plugin_name as "Payment Type" +, rfnd.plugin_pm_type as "Payment Method" +, rfnd.plugin_pm_cc_type as "Payment Card Type" +, date_format(rfnd.created_date, '%m/%d/%Y') as "Payment Date" +, rfnd.currency as "Payment Currency" +, rfnd.amount as "Payment Amount" +, case when rfnd.currency != 'USD' then round(cc.reference_rate * rfnd.amount,4) else rfnd.amount end as "Payment Amount USD" +, rfnd.record_id as "Refund Number" +, date_format(rfnd.created_date, '%m/%d/%Y') as "Refund Date" +, rfnd.currency as "Refund Currency" +, rfnd.amount as "Refund Amount" +, case when rfnd.currency != 'USD' then round(cc.reference_rate * rfnd.amount,4) else rfnd.amount end as "Refund Amount USD" +, rfnd.invoice_amount_charged - rfnd.invoice_original_amount_charged "Total IA, IIA for Invoice" +, case when rfnd.currency != 'USD' then round(cc.reference_rate * (rfnd.invoice_amount_charged - rfnd.invoice_original_amount_charged),4) else rfnd.invoice_amount_charged - rfnd.invoice_original_amount_charged end "Total IA, IIA for Invoice USD" +, rfnd.created_by as "User Responsible for Refund" +, 'REASON' as "Reason for Refund" +, rfnd.tenant_record_id +from + analytics_payment_refunds rfnd + left outer join analytics_currency_conversion cc on rfnd.created_date >= cc.start_date and rfnd.created_date <= cc.end_date and cc.currency = rfnd.currency +where 1=1 + and rfnd.created_date >= cast(date_format(date_sub(sysdate(), interval '1' month), '%Y-%m-01') as date) + and rfnd.created_date < cast(date_format(sysdate(), '%Y-%m-01') as date) + and rfnd.report_group != 'test' +order by 1,rfnd.invoice_payment_record_id; -- just for well defined ordering \ No newline at end of file diff --git a/reports/refunds_total_daily/README.md b/reports/refunds_total_daily/README.md new file mode 100644 index 00000000..19d1a81d --- /dev/null +++ b/reports/refunds_total_daily/README.md @@ -0,0 +1,39 @@ +# Daily Refunds Report + +Compute the total value (in the reference currency) of refunds per day per currency for each tenant. + +The snapshot view is: `v_report_refunds_total_daily` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_refunds_total_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Refunds Value", + "sourceTableName": "report_refunds_total_daily", + "refreshProcedureName": "refresh_report_refunds_total_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| ID | Date | Currency | Amount | +|----|------------|----------|----------| +| 1 | 2025-09-02 | USD | 119.0000 | +| 1 | 2025-08-04 | USD | 29.9500 | +| 1 | 2025-08-04| EUR | 199.4000 | +| 2 | 2025-07-03 | USD | 29.9500 | +| 2 | 2025-09-18 | EUR | 15.0000 | + +The first row in the above table indicates that on the date `2025-09-02`, the tenant with record id=1 had a total refund value of USD 119. + +## Report UI: + +![refunds-total-daily.png](refunds-total-daily.png) diff --git a/reports/refunds_total_daily/refunds-total-daily.png b/reports/refunds_total_daily/refunds-total-daily.png new file mode 100644 index 00000000..a4085c9e Binary files /dev/null and b/reports/refunds_total_daily/refunds-total-daily.png differ diff --git a/src/main/resources/reports/refunds_total_daily/report_refunds_total_daily.ddl b/reports/refunds_total_daily/report_refunds_total_daily.ddl similarity index 100% rename from src/main/resources/reports/refunds_total_daily/report_refunds_total_daily.ddl rename to reports/refunds_total_daily/report_refunds_total_daily.ddl diff --git a/src/main/resources/reports/refunds_total_daily/v_report_refunds_total_daily.ddl b/reports/refunds_total_daily/v_report_refunds_total_daily.ddl similarity index 84% rename from src/main/resources/reports/refunds_total_daily/v_report_refunds_total_daily.ddl rename to reports/refunds_total_daily/v_report_refunds_total_daily.ddl index 3502c176..34965f49 100644 --- a/src/main/resources/reports/refunds_total_daily/v_report_refunds_total_daily.ddl +++ b/reports/refunds_total_daily/v_report_refunds_total_daily.ddl @@ -3,7 +3,7 @@ select ar.tenant_record_id , date_format(ar.created_date,'%Y-%m-%d') as day , ar.currency as currency -, sum(ar.converted_amount) as count +, sum(abs(ar.invoice_amount_refunded)) as count from analytics_payment_refunds ar where 1=1 diff --git a/reports/reports_setup.sh b/reports/reports_setup.sh new file mode 100644 index 00000000..319aff8d --- /dev/null +++ b/reports/reports_setup.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env bash + +# +# Copyright 2020-2025 Equinix, Inc +# Copyright 2014-2025 The Billing Project, LLC +# +# The Billing Project licenses this file to you under the Apache License, version 2.0 +# (the "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +HERE=`cd \`dirname $0\`; pwd` + +KILLBILL_HTTP_PROTOCOL=${KILLBILL_HTTP_PROTOCOL-"http"} +KILLBILL_HOST=${KILLBILL_HOST-"127.0.0.1"} +KILLBILL_PORT=${KILLBILL_PORT-"8080"} + +KILLBILL_USER=${KILLBILL_USER-"admin"} +KILLBILL_PASSWORD=${KILLBILL_PASSWORD-"password"} +KILLBILL_API_KEY=${KILLBILL_API_KEY-"bob"} +KILLBILL_API_SECRET=${KILLBILL_API_SECRET-"lazar"} + +MYSQL_HOST=${MYSQL_HOST-"127.0.0.1"} +MYSQL_USER=${MYSQL_USER-"root"} +MYSQL_PASSWORD=${MYSQL_PASSWORD-"killbill"} +MYSQL_DATABASE=${MYSQL_DATABASE-"killbill"} +INSTALL_DDL=true +DROP_EXISTING_REPORT=false + +REPORTS=$HERE + +function install_ddl() { + local ddl=$1 + mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE -e "source $ddl" +} + +install_all_ddls() { + echo "Installing DDLs from utils directory first..." + ( + cd "$REPORTS" || exit 1 + + # Process utils first if it exists + if [ -d "./utils" ]; then + for ddl_pattern in "v_report_*.ddl" "report_*.ddl" "*.ddl" "*.sql"; do + shopt -s nullglob + for ddl in ./utils/$ddl_pattern; do + [ -f "$ddl" ] && install_ddl "$ddl" + done + shopt -u nullglob + done + fi + + echo "Installing other DDLs..." + # Loop over all directories except utils + find . -type d ! -path "./utils" -print0 | while IFS= read -r -d '' dir; do + for ddl_pattern in "v_report_*.ddl" "report_*.ddl"; do + shopt -s nullglob + for ddl in "$dir"/$ddl_pattern; do + [ -f "$ddl" ] && install_ddl "$ddl" + done + shopt -u nullglob + done + done + ) +} + + + +function create_report() { + local report_name=$1 + local report_pretty_name=$2 + local report_type=$3 + local source_table_name=$4 + local refresh_procedure_name=$5 + +if [[ "$DROP_EXISTING_REPORT" == "true" ]]; then + + curl -v \ + -X DELETE \ + -u "$KILLBILL_USER:$KILLBILL_PASSWORD" \ + -H "X-Killbill-ApiKey:$KILLBILL_API_KEY" \ + -H "X-Killbill-ApiSecret:$KILLBILL_API_SECRET" \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports/$report_name" +fi + + + curl -v \ + -X POST \ + -u $KILLBILL_USER:$KILLBILL_PASSWORD \ + -H "X-Killbill-ApiKey:$KILLBILL_API_KEY" \ + -H "X-Killbill-ApiSecret:$KILLBILL_API_SECRET" \ + -H 'Content-Type: application/json' \ + -d "{\"reportName\": \"$report_name\", + \"reportPrettyName\": \"$report_pretty_name\", + \"reportType\": \"$report_type\", + \"sourceTableName\": \"$source_table_name\", + \"refreshProcedureName\": \"$refresh_procedure_name\", + \"refreshFrequency\": \"HOURLY\" + }" \ + $KILLBILL_HTTP_PROTOCOL://$KILLBILL_HOST:$KILLBILL_PORT/plugins/killbill-analytics/reports +} + +# Create all Killbill reports +create_all_reports() { + declare -a reports=( + "accounts_summary|Account Summary|TABLE|report_accounts_summary|refresh_report_accounts_summary" + "active_by_product_term_monthly|Active Subscriptions|TIMELINE|report_active_by_product_term_monthly|refresh_report_active_by_product_term_monthly" + "bundles_summary|Bundles Summary|TABLE|report_bundles_summary|refresh_report_bundles_summary" + "cancellations_daily|Cancellations Daily|TIMELINE|report_cancellations_daily|refresh_report_cancellations_daily" + "chargebacks_daily|Chargebacks Daily|TIMELINE|report_chargebacks_daily|refresh_report_chargebacks_daily" + "churn_percent|Churn Percent|TIMELINE|report_churn_percent|refresh_report_churn_percent" + "churn_amount|Churn Total USD|TIMELINE|report_churn_total_usd|refresh_report_churn_total_usd" + "conversion-total-dollar-amount|Conversions Daily|TIMELINE|report_conversions_daily|refresh_report_conversions_daily" + "invoice_aging|Invoice Aging|TABLE|report_invoice_aging|refresh_report_invoice_aging" + "invoice_aging_no_payments_monthly|Invoice Aging No Payments|TABLE|report_invoice_aging_no_payment|refresh_report_invoice_aging_no_payment" + "invoice_credits_daily|Invoice Credits Daily|TIMELINE|report_invoice_credits_daily|refresh_report_invoice_credits_daily" + "invoice_credits_monthly|Invoice Credits Monthly|TABLE|report_invoice_credits_monthly|refresh_report_invoice_credits_monthly" + "invoice_item_adjustments_daily|Invoice Item Adjustments Daily|TIMELINE|report_invoice_item_adjustments_daily|refresh_report_invoice_item_adjustments_daily" + "invoice_item_adjustments_monthly|Invoice Item Adjustments Monthly|TABLE|report_invoice_item_adjustments_monthly|refresh_report_invoice_item_adjustments_monthly" + "invoice_items_monthly|Invoice Items Monthly|TABLE|report_invoice_items_monthly|refresh_report_invoice_items_monthly" + "invoices_balance_daily|Invoice Balance|TIMELINE|report_invoices_balance_daily|refresh_report_invoices_balance_daily" + "invoices_daily|Invoices Daily|TIMELINE|report_invoices_daily|refresh_report_invoices_daily" + "invoices_monthly|Invoices Monthly|TABLE|report_invoices_monthly|refresh_report_invoices_monthly" + "mrr_daily|MRR|TIMELINE|report_mrr_daily|refresh_report_mrr_daily" + "new_accounts_daily|New Accounts Daily|TIMELINE|report_new_accounts_daily|refresh_report_new_accounts_daily" + "overdue_states_count_daily|Overdue States Count|TIMELINE|report_overdue_states_count_daily|refresh_report_overdue_states_count_daily" + "payment_provider_conversions|Payment Provider Conversions|TABLE|report_payment_provider_conversions|refresh_payment_provider_conversions" + "payment_provider_errors|Payment Provider Errors|TIMELINE|report_payment_provider_errors|refresh_report_payment_provider_errors" + "payment_provider_monitor|Payment Provider Monitor|TABLE|report_payment_provider_monitor|refresh_payment_provider_monitor" + "payments_by_provider|Payments By Provider|TABLE|report_payments_by_provider|refresh_report_payments_by_provider" + "payments_by_provider_summary_last_24_hr_|Payments By Provider Summary (Last 24hrs)|COUNTERS|report_payments_by_provider_last_24h_summary|refresh_report_payments_by_provider_last_24h_summary" + "payments_monthly|Payments Monthly|TABLE|report_payments_monthly|refresh_report_payments_monthly" + "payments_summary|Payments Summary|TABLE|report_payments_summary|refresh_report_payments_summary" + "payments_total_daily|Payment Total Daily|TIMELINE|report_payments_total_daily|refresh_report_payments_total_daily" + "refunds_monthly|Refunds Monthly|TABLE|report_refunds_summary|refresh_report_refunds_summary" + "refunds_total_daily|Refunds Total Daily|TIMELINE|report_refunds_total_daily|refresh_report_refunds_total_daily" + "subscribers_vs_non_subscribers|Subscribers v/s Non Subscribers|COUNTERS|report_subscribers_vs_non_subscribers|refresh_report_subscribers_vs_non_subscribers" + "trial_starts_count_daily|Trials Start Count|TIMELINE|report_trial_starts_count_daily|refresh_report_trial_starts_count_daily" + "trial_to_no_trial_conversions_daily|Trial to No Trial Conversions Daily|TIMELINE|report_trial_to_no_trial_conversions_daily|refresh_report_trial_to_no_trial_conversions_daily" + ) + + for r in "${reports[@]}"; do + IFS="|" read -r name pretty type source refresh <<< "$r" + create_report "$name" "$pretty" "$type" "$source" "$refresh" + done +} + +# ======================== +# Main Execution +# ======================== + +if [[ "$INSTALL_DDL" == "true" ]]; then + install_all_ddls +else + echo "INSTALL_DDL is not true. Skipping DDL installation." +fi + +create_all_reports + diff --git a/reports/subscribers-vs-non-subscribers/README.md b/reports/subscribers-vs-non-subscribers/README.md new file mode 100644 index 00000000..751fbd3c --- /dev/null +++ b/reports/subscribers-vs-non-subscribers/README.md @@ -0,0 +1,42 @@ +# Subscribers vs Non-subscribers Report + +Compute the total number of active (i.e. with at least one active subscription) and non-active accounts per tenant. + +The snapshot view is: `v_report_subscribers_vs_non_subscribers` + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_subscribers_vs_non_subscribers", + "reportType": "COUNTERS", + "reportPrettyName": "Subscribers vs Non Subscribers", + "sourceTableName": "report_subscribers_vs_non_subscribers", + "refreshProcedureName": "refresh_report_subscribers_vs_non_subscribers", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| ID | Type | Count | +|-----|----------------|-------| +| 1 | Non-subscriber | 74 | +| 1 | Subscriber | 18 | +| 2 | Subscriber | 1 | +| 5 | Non-subscriber | 1 | +| 5 | Subscriber | 3 | + +The first row in the above table indicates that the tenant with record id=1 had 74 non-subscribers (accounts with no active subscriptions). + + +## Report UI: + +![subscribers-vs-non-subscribers.png](subscribers-vs-non-subscribers.png) + + diff --git a/reports/subscribers-vs-non-subscribers/report_subscribers-vs-non-subscribers.ddl b/reports/subscribers-vs-non-subscribers/report_subscribers-vs-non-subscribers.ddl new file mode 100644 index 00000000..eca6176a --- /dev/null +++ b/reports/subscribers-vs-non-subscribers/report_subscribers-vs-non-subscribers.ddl @@ -0,0 +1,19 @@ +create table report_subscribers_vs_non_subscribers as select * from v_report_subscribers_vs_non_subscribers limit 0; + +drop procedure if exists refresh_report_subscribers_vs_non_subscribers; +DELIMITER // +CREATE PROCEDURE refresh_report_subscribers_vs_non_subscribers() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_subscribers_vs_non_subscribers; + insert into report_subscribers_vs_non_subscribers select * from v_report_subscribers_vs_non_subscribers; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/subscribers-vs-non-subscribers/subscribers-vs-non-subscribers.png b/reports/subscribers-vs-non-subscribers/subscribers-vs-non-subscribers.png new file mode 100644 index 00000000..d0f0bd23 Binary files /dev/null and b/reports/subscribers-vs-non-subscribers/subscribers-vs-non-subscribers.png differ diff --git a/src/main/resources/reports/accounts_summary/v_report_accounts_summary.ddl b/reports/subscribers-vs-non-subscribers/v_report_subscribers-vs-non-subscribers.ddl similarity index 79% rename from src/main/resources/reports/accounts_summary/v_report_accounts_summary.ddl rename to reports/subscribers-vs-non-subscribers/v_report_subscribers-vs-non-subscribers.ddl index a9b404ce..f3b8ea68 100644 --- a/src/main/resources/reports/accounts_summary/v_report_accounts_summary.ddl +++ b/reports/subscribers-vs-non-subscribers/v_report_subscribers-vs-non-subscribers.ddl @@ -1,4 +1,4 @@ -create or replace view v_report_accounts_summary as +create or replace view v_report_subscribers_vs_non_subscribers as select a.tenant_record_id , case when nb_active_bundles <= 0 then 'Non-subscriber' else 'Subscriber' end as label diff --git a/reports/trial-starts-count-daily/README.md b/reports/trial-starts-count-daily/README.md new file mode 100644 index 00000000..4b0d2f02 --- /dev/null +++ b/reports/trial-starts-count-daily/README.md @@ -0,0 +1,42 @@ +# Daily Trials Count Report + +Count of new trial subscriptions per tenant, per day and per product. + +The snapshot view is: [v_report_trial_starts_count_daily](v_report_trial_starts_count_daily.md) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_trials_start_count_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Trials Count", + "sourceTableName": "report_trial_starts_count_daily", + "refreshProcedureName": "refresh_report_trial_starts_count_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +### Sample Data + +| Tenant Record Id | Day | Product | Count | +|------------------|------------|---------------|-------| +| 1 | 2025-07-01 | Assault-Rifle | 3 | +| 1 | 2025-05-05 | Pistol | 5 | +| 22 | 2025-09-18 | Pistol | 2 | +| 22 | 2025-07-01 | Pistol | 1 | +| 44 | 2025-07-08 | Blowdart | 8 | +| 44 | 2025-06-12 | Pistol | 6 | + +The first row in the above table indicates that on the date `2025-07-01`, 3 new subscriptions were started in the `TRIAL` phase for the `Assault-Rifle` product for the `tenant_record_id=1`. + +## Report UI: + +![daily-trials-count.png](daily-trials-count.png) + + diff --git a/reports/trial-starts-count-daily/daily-trials-count.png b/reports/trial-starts-count-daily/daily-trials-count.png new file mode 100644 index 00000000..22c02d93 Binary files /dev/null and b/reports/trial-starts-count-daily/daily-trials-count.png differ diff --git a/reports/trial-starts-count-daily/report_trial_starts_count_daily.ddl b/reports/trial-starts-count-daily/report_trial_starts_count_daily.ddl new file mode 100644 index 00000000..0d131301 --- /dev/null +++ b/reports/trial-starts-count-daily/report_trial_starts_count_daily.ddl @@ -0,0 +1,19 @@ +create table report_trial_starts_count_daily as select * from v_report_trial_starts_count_daily limit 0; + +drop procedure if exists refresh_report_trial_starts_count_daily; +DELIMITER // +CREATE PROCEDURE refresh_report_trial_starts_count_daily() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_trial_starts_count_daily; + insert into report_trial_starts_count_daily select * from v_report_trial_starts_count_daily; +COMMIT; + +END; +// +DELIMITER ; diff --git a/src/main/resources/reports/v_report_trial_starts_count_daily.ddl b/reports/trial-starts-count-daily/v_report_trial_starts_count_daily.ddl similarity index 100% rename from src/main/resources/reports/v_report_trial_starts_count_daily.ddl rename to reports/trial-starts-count-daily/v_report_trial_starts_count_daily.ddl diff --git a/reports/trial-to-no-trial-conversions_daily/README.md b/reports/trial-to-no-trial-conversions_daily/README.md new file mode 100644 index 00000000..a11e3225 --- /dev/null +++ b/reports/trial-to-no-trial-conversions_daily/README.md @@ -0,0 +1,44 @@ +# Daily Trials To No Trials Count Report + +Count of subscriptions converting from trial to non-trial per tenant per day. + +The snapshot view is: [v_report_trial_to_no_trial_conversions_daily](v_report_trial_to_no_trial_conversions_daily.ddl) + +## Report Creation + +``` +curl -v \ + -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey:bob" \ + -H "X-Killbill-ApiSecret:lazar" \ + -H 'Content-Type: application/json' \ + -d '{"reportName": "report_trial_to_no_trial_conversions_daily", + "reportType": "TIMELINE", + "reportPrettyName": "Daily Trials to No Trials Count", + "sourceTableName": "report_trial_to_no_trial_conversions_daily", + "refreshProcedureName": "refresh_report_trial_to_no_trial_conversions_daily", + "refreshFrequency": "HOURLY"}' \ + "http://127.0.0.1:8080/plugins/killbill-analytics/reports" +``` + +## Sample Data + +| Tenant Record Id | Day | Count | +|------------------|------------|-------| +| 1 | 2025-10-18 | 3 | +| 22 | 2025-08-07 | 1 | +| 22 | 2025-07-12 | 1 | +| 1 | 2025-01-31 | 1 | +| 45 | 2025-04-04 | 1 | +| 489 | 2025-09-05 | 1 | + + +The first row in the above table indicates that on the date `2025-10-18`, 3 subscriptions transitioned from the `TRIAL` phase to some other phase for the `tenant_record_id=1`. + +## Report UI: + +![trial-to-no-trial-conversions.png](trial-to-no-trial-conversions.png) + + + diff --git a/reports/trial-to-no-trial-conversions_daily/report_trial-to-no-trial-conversions_daily.ddl b/reports/trial-to-no-trial-conversions_daily/report_trial-to-no-trial-conversions_daily.ddl new file mode 100644 index 00000000..4b16adf0 --- /dev/null +++ b/reports/trial-to-no-trial-conversions_daily/report_trial-to-no-trial-conversions_daily.ddl @@ -0,0 +1,19 @@ +create table report_trial_to_no_trial_conversions_daily as select * from v_report_trial_to_no_trial_conversions_daily limit 0; + +drop procedure if exists refresh_report_trial_to_no_trial_conversions_daily; +DELIMITER // +CREATE PROCEDURE refresh_report_trial_to_no_trial_conversions_daily() +BEGIN + +DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; +DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +START TRANSACTION; + delete from report_trial_to_no_trial_conversions_daily; + insert into report_trial_to_no_trial_conversions_daily select * from v_report_trial_to_no_trial_conversions_daily; +COMMIT; + +END; +// +DELIMITER ; diff --git a/reports/trial-to-no-trial-conversions_daily/trial-to-no-trial-conversions.png b/reports/trial-to-no-trial-conversions_daily/trial-to-no-trial-conversions.png new file mode 100644 index 00000000..e0428e70 Binary files /dev/null and b/reports/trial-to-no-trial-conversions_daily/trial-to-no-trial-conversions.png differ diff --git a/src/main/resources/reports/v_report_conversions_daily.ddl b/reports/trial-to-no-trial-conversions_daily/v_report_trial-to-no-trial-conversions_daily.ddl similarity index 70% rename from src/main/resources/reports/v_report_conversions_daily.ddl rename to reports/trial-to-no-trial-conversions_daily/v_report_trial-to-no-trial-conversions_daily.ddl index 2df30a45..db7fa14a 100644 --- a/src/main/resources/reports/v_report_conversions_daily.ddl +++ b/reports/trial-to-no-trial-conversions_daily/v_report_trial-to-no-trial-conversions_daily.ddl @@ -1,4 +1,4 @@ -create or replace view v_report_conversions_daily as +create or replace view v_report_trial_to_no_trial_conversions_daily as select ast.tenant_record_id , date_format(ast.next_start_date,'%Y-%m-%d') as day @@ -8,6 +8,7 @@ from where 1=1 and ast.prev_phase='TRIAL' and ast.next_phase!='TRIAL' + and ast.prev_service='billing-service' and ast.report_group='default' group by 1,2 ; diff --git a/reports/utils/README.md b/reports/utils/README.md new file mode 100644 index 00000000..b5994900 --- /dev/null +++ b/reports/utils/README.md @@ -0,0 +1,75 @@ +# Insert Monthly Currency Conversion Rates + +This procedure inserts the monthly currency conversion rates (AUD, BRL, EUR, GBP, MXN → USD) into the `analytics_currency_conversion` table. +It validates the input month/year, checks for duplicate or missing previous entries, ensures rates don’t deviate beyond a threshold, updates prior rows, and inserts the new month’s rates. + +## Initialization + +Before executing the procedure, the following initialization script needs to be run: + +```` +insert into analytics_currency_conversion values (1, 'AUD', date_sub(str_to_date(concat(cast(year(now()) as char(4)),'-',cast(month(now()) as char(2)),'-01'),'%Y-%m-%d'), interval 1 month), '2020-01-01', 0.77, 'USD'); +insert into analytics_currency_conversion values (2, 'BRL', date_sub(str_to_date(concat(cast(year(now()) as char(4)),'-',cast(month(now()) as char(2)),'-01'),'%Y-%m-%d'), interval 1 month), '2020-01-01', 0.31, 'USD'); +insert into analytics_currency_conversion values (3, 'EUR', date_sub(str_to_date(concat(cast(year(now()) as char(4)),'-',cast(month(now()) as char(2)),'-01'),'%Y-%m-%d'), interval 1 month), '2020-01-01', 1.12, 'USD'); +insert into analytics_currency_conversion values (4, 'GBP', date_sub(str_to_date(concat(cast(year(now()) as char(4)),'-',cast(month(now()) as char(2)),'-01'),'%Y-%m-%d'), interval 1 month), '2020-01-01', 1.29, 'USD'); +insert into analytics_currency_conversion values (5, 'MXN', date_sub(str_to_date(concat(cast(year(now()) as char(4)),'-',cast(month(now()) as char(2)),'-01'),'%Y-%m-%d'), interval 1 month), '2020-01-01', 0.052, 'USD'); +```` + +## Executing The Procedure + +The stored procedure can be executed as follows: + +```` +CALL updateAnalyticsCurrConvMonth( + 9, -- month = September + 2025, -- year + 0.78, -- AUD + 0.30, -- BRL + 1.13, -- EUR + 1.28, -- GBP + 0.051 -- MXN +); +```` + +## How it works + +- The procedure verifies that the previous month has entries for all five currencies. +- It ensures the current month’s entries don’t already exist. +- Rates are validated to be positive and within a 10% deviation from the previous month. +- The end_date of previous month entries is updated to the start of the new month. +- New records are inserted for the current month with `end_date = 2020-01-01`. + +Here is an example. + +Suppose the `analytics_currency_conversion` table has the conversion rates for August 2025: + +| Currency | Start Date | End Date | Reference Rate | Reference Currency | +|----------|------------|------------|----------------|--------------------| +| AUD | 2025-08-01 | 2020-01-01 | 0.77 | USD | +| BRL | 2025-08-01 | 2020-01-01 | 0.31 | USD | +| EUR | 2025-08-01 | 2020-01-01 | 1.12 | USD | +| GBP | 2025-08-01 | 2020-01-01 | 1.29 | USD | +| MXN | 2025-08-01 | 2020-01-01 | 0.052 | USD | + + +Notice how all `end_date = 2020-01-01`. +This is a placeholder meaning “open-ended” (valid until a new record replaces it). + + +After calling the procedure for September 2025 as mentioned above, the table now looks as follows: + +```` +| Currency | Start Date | End Date | Reference Rate | Reference Currency | +|----------|------------|------------|----------------|--------------------| +| AUD | 2025-08-01 | 2025-09-01 | 0.77 | USD | +| BRL | 2025-08-01 | 2025-09-01 | 0.31 | USD | +| EUR | 2025-08-01 | 2025-09-01 | 1.12 | USD | +| GBP | 2025-08-01 | 2025-09-01 | 1.29 | USD | +| MXN | 2025-08-01 | 2025-09-01 | 0.052 | USD | +| AUD | 2025-09-01 | 2020-01-01 | 0.78 | USD | +| BRL | 2025-09-01 | 2020-01-01 | 0.30 | USD | +| EUR | 2025-09-01 | 2020-01-01 | 1.13 | USD | +| GBP | 2025-09-01 | 2020-01-01 | 1.28 | USD | +| MXN | 2025-09-01 | 2020-01-01 | 0.051 | USD | +```` + diff --git a/src/main/resources/reports/calendar.sql b/reports/utils/calendar.sql similarity index 100% rename from src/main/resources/reports/calendar.sql rename to reports/utils/calendar.sql diff --git a/src/main/resources/utils/updateAnalyticsCurrConvMonth.prc b/reports/utils/insertMonthlyCurrencyConversionRates.ddl similarity index 96% rename from src/main/resources/utils/updateAnalyticsCurrConvMonth.prc rename to reports/utils/insertMonthlyCurrencyConversionRates.ddl index 5b731ee7..7cbaeb37 100644 --- a/src/main/resources/utils/updateAnalyticsCurrConvMonth.prc +++ b/reports/utils/insertMonthlyCurrencyConversionRates.ddl @@ -94,19 +94,19 @@ BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Not Allowed: New AUD rate differs from previous rate by more than allowed threshhold'; - elseif abs(v_old_brl_rate-p_brl_rate)/v_old_aud_rate >v_conv_rate_test_threshhold then + elseif abs(v_old_brl_rate-p_brl_rate)/v_old_brl_rate >v_conv_rate_test_threshhold then SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Not Allowed: New BRL rate differs from previous rate by more than allowed threshhold'; - elseif abs(v_old_eur_rate-p_eur_rate)/v_old_aud_rate >v_conv_rate_test_threshhold then + elseif abs(v_old_eur_rate-p_eur_rate)/v_old_eur_rate >v_conv_rate_test_threshhold then SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Not Allowed: New EUR rate differs from previous rate by more than allowed threshhold'; - elseif abs(v_old_gbp_rate-p_gbp_rate)/v_old_aud_rate >v_conv_rate_test_threshhold then + elseif abs(v_old_gbp_rate-p_gbp_rate)/v_old_gbp_rate >v_conv_rate_test_threshhold then SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Not Allowed: New GBP rate differs from previous rate by more than allowed threshhold'; - elseif abs(v_old_mxn_rate-p_mxn_rate)/v_old_aud_rate >v_conv_rate_test_threshhold then + elseif abs(v_old_mxn_rate-p_mxn_rate)/v_old_mxn_rate >v_conv_rate_test_threshhold then SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Not Allowed: New MXN rate differs from previous rate by more than allowed threshhold'; end if; diff --git a/src/main/resources/extracts/finance/invoice_adjustments_monthly.sql b/src/main/resources/extracts/finance/invoice_adjustments_monthly.sql deleted file mode 100644 index 6000e891..00000000 --- a/src/main/resources/extracts/finance/invoice_adjustments_monthly.sql +++ /dev/null @@ -1,29 +0,0 @@ -select - ia.invoice_number as "Invoice Number" -, ia.account_name as "Customer Name" -, ia.account_external_key as "Account Number" -, date_format(ia.created_date, '%m/%d/%Y') as "Adjustment Date" -, date_format(ia.invoice_date, '%m/%d/%Y') as "Invoice Date" -, date_format(ia.invoice_date, '%m/%d/%Y') as "Target Date" -, date_format(ia.created_date, '%m/%d/%Y') as "Creation Date" -- Adjustment date? -, ia.currency as "Currency" -, ia.invoice_original_amount_charged as "Invoice Amount" -, ia.invoice_balance as "Invoice Balance" -, ia.amount as "Invoice Adjustment Amount" -, abs(ia.amount) as "Impact Amount" -, round(cc.reference_rate * ia.invoice_original_amount_charged,4) as "Invoice Amount USD" -, round(cc.reference_rate * ia.invoice_balance,4) as "Invoice Balance USD" -, round(cc.reference_rate * ia.amount,4) as "Invoice Adjustment Amount USD" -, abs(round(cc.reference_rate * ia.amount,4)) as "Impact Amount USD" -, case when ia.amount > 0 then 'CREDIT' else 'CHARGE' end as "Adjustment Type" -, 'PROCESSED' as "Invoice Adjustment Status" -from - analytics_invoice_adjustments ia - join analytics_currency_conversion cc on ia.created_date >= cc.start_date and ia.created_date <= cc.end_date and cc.currency =ia.currency -where 1=1 - and ia.created_date >= cast(date_format(date_sub(sysdate(), interval '1' month), '%Y-%m-01') as date) - and ia.created_date < cast(date_format(sysdate(), '%Y-%m-01') as date) - and ia.report_group != 'test' -order by - invoice_number -, ia.record_id; -- just for well defined ordering diff --git a/src/main/resources/extracts/finance/invoice_aging.sql b/src/main/resources/extracts/finance/invoice_aging.sql deleted file mode 100644 index c4506470..00000000 --- a/src/main/resources/extracts/finance/invoice_aging.sql +++ /dev/null @@ -1,58 +0,0 @@ -select - account_name "Customer Name" -, account_external_key "Account Number" -, a.currency "Currency" -, case when invoice_creation_date > cast('2014-01-01' as date) - interval '30' day then invoice_original_amount_charged else 0 end as "Balance due 0-30 Days" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '60' day and cast('2014-01-01' as date) - interval '30' day then invoice_original_amount_charged else 0 end as "Balance due 30-60 Days" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '90' day and cast('2014-01-01' as date) - interval '60' day then invoice_original_amount_charged else 0 end as "Balance due 60-90 Days" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '120' day and cast('2014-01-01' as date) - interval '90' day then invoice_original_amount_charged else 0 end as "Balance due 90-120 Days" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '150' day and cast('2014-01-01' as date) - interval '120' day then invoice_original_amount_charged else 0 end as "Balance due 120-150 Days" -, case when invoice_creation_date < cast('2014-01-01' as date) - interval '150' day then invoice_original_amount_charged else 0 end as "Balance due 150+ Days" -, invoice_original_amount_charged as "Total Balance Due" -, case when invoice_creation_date > cast('2014-01-01' as date) - interval '30' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 0-30 Days USD" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '60' day and cast('2014-01-01' as date) - interval '30' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 30-60 Days USD" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '90' day and cast('2014-01-01' as date) - interval '60' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 60-90 Days USD" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '120' day and cast('2014-01-01' as date) - interval '90' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 90-120 Days USD" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '150' day and cast('2014-01-01' as date) - interval '120' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 120-150 Days USD" -, case when invoice_creation_date < cast('2014-01-01' as date) - interval '150' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 150+ Days USD" -, cc.reference_rate * invoice_original_amount_charged as "Total Balance Due USD" -, invoice_number "Invoice Number" -, bundle_external_key "Bundle External Key" -, slug "Slug" -, service_start_date "Service Start Date" -, service_end_date "Service End Date" -, invoice_date "Invoice Date" -, invoice_original_amount_charged "Invoice Amount" -, invoice_balance "Invoice Balance" -, round(cc.reference_rate * invoice_original_amount_charged,4) "Invoice Amount USD" -, round(cc.reference_rate * invoice_balance,4) "Invoice Balance USD" -from ( - select - ii.invoice_number - , ii.account_name - , ii.account_external_key - , cast(date_format(ii.created_date, '%m/%d/%Y') as date) invoice_creation_date - , cast(date_format(ii.invoice_date, '%m/%d/%Y') as date) invoice_date - , cast(date_format(ii.invoice_date, '%m/%d/%Y') as date) target_date - , ii.bundle_external_key - , ii.product_name - , ii.slug - , cast(date_format(ii.start_date, '%m/%d/%Y') as date) as service_start_date - , cast(date_format(ii.end_date, '%m/%d/%Y') as date) as service_end_date - , ii.currency - , ii.invoice_original_amount_charged - , ii.invoice_balance - , ii.amount - , ii.created_date - , ii.invoice_item_record_id - from analytics_invoice_items ii - where 1=1 - and ii.invoice_date < cast(date_format(sysdate(), '%Y-%m-01') as date) - and ii.report_group != 'test' - and ii.invoice_balance > 0 -) a -join analytics_currency_conversion cc on a.created_date >= cc.start_date and a.created_date <= cc.end_date and cc.currency = a.currency -order by - account_name -, invoice_number -, a.invoice_item_record_id; diff --git a/src/main/resources/extracts/finance/invoice_aging_no_pmt.sql b/src/main/resources/extracts/finance/invoice_aging_no_pmt.sql deleted file mode 100644 index 34442eb5..00000000 --- a/src/main/resources/extracts/finance/invoice_aging_no_pmt.sql +++ /dev/null @@ -1,60 +0,0 @@ -select - account_name "Customer Name" -, account_external_key "Account Number" -, a.currency "Currency" -, case when invoice_creation_date > cast('2014-01-01' as date) - interval '30' day then invoice_original_amount_charged else 0 end as "Balance due 0-30 Days" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '60' day and cast('2014-01-01' as date) - interval '30' day then invoice_original_amount_charged else 0 end as "Balance due 30-60 Days" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '90' day and cast('2014-01-01' as date) - interval '60' day then invoice_original_amount_charged else 0 end as "Balance due 60-90 Days" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '120' day and cast('2014-01-01' as date) - interval '90' day then invoice_original_amount_charged else 0 end as "Balance due 90-120 Days" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '150' day and cast('2014-01-01' as date) - interval '120' day then invoice_original_amount_charged else 0 end as "Balance due 120-150 Days" -, case when invoice_creation_date < cast('2014-01-01' as date) - interval '150' day then invoice_original_amount_charged else 0 end as "Balance due 150+ Days" -, invoice_original_amount_charged as "Total Balance Due" -, case when invoice_creation_date > cast('2014-01-01' as date) - interval '30' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 0-30 Days USD" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '60' day and cast('2014-01-01' as date) - interval '30' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 30-60 Days USD" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '90' day and cast('2014-01-01' as date) - interval '60' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 60-90 Days USD" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '120' day and cast('2014-01-01' as date) - interval '90' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 90-120 Days USD" -, case when invoice_creation_date between cast('2014-01-01' as date) - interval '150' day and cast('2014-01-01' as date) - interval '120' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 120-150 Days USD" -, case when invoice_creation_date < cast('2014-01-01' as date) - interval '150' day then round(cc.reference_rate * invoice_original_amount_charged,4) else 0 end as "Balance due 150+ Days USD" -, cc.reference_rate * invoice_original_amount_charged as "Total Balance Due USD" -, invoice_number "Invoice Number" -, bundle_external_key "Bundle External Key" -, slug "Slug" -, service_start_date "Service Start Date" -, service_end_date "Service End Date" -, invoice_date "Invoice Date" -, invoice_original_amount_charged "Invoice Amount" -, invoice_balance "Invoice Balance" -, round(cc.reference_rate * invoice_original_amount_charged,4) "Invoice Amount USD" -, round(cc.reference_rate * invoice_balance,4) "Invoice Balance USD" -from ( - select - ii.invoice_number - , ii.account_name - , ii.account_external_key - , cast(date_format(ii.created_date, '%m/%d/%Y') as date) invoice_creation_date - , cast(date_format(ii.invoice_date, '%m/%d/%Y') as date) invoice_date - , cast(date_format(ii.invoice_date, '%m/%d/%Y') as date) target_date - , ii.bundle_external_key - , ii.product_name - , ii.slug - , cast(date_format(ii.start_date, '%m/%d/%Y') as date) as service_start_date - , cast(date_format(ii.end_date, '%m/%d/%Y') as date) as service_end_date - , ii.currency - , ii.invoice_original_amount_charged - , ii.invoice_balance - , ii.amount - , ii.created_date - , ii.invoice_item_record_id - from analytics_invoice_items ii - where 1=1 - and ii.invoice_date < cast(date_format(sysdate(), '%Y-%m-01') as date) - and ii.report_group != 'test' - and ii.amount > 0 - and ii.invoice_amount_paid=0 - and ii.invoice_amount_charged=ii.invoice_original_amount_charged -) a -join analytics_currency_conversion cc on a.created_date >= cc.start_date and a.created_date <= cc.end_date and cc.currency = a.currency -order by - account_name -, invoice_number -, a.invoice_item_record_id; diff --git a/src/main/resources/extracts/finance/refunds_monthly.sql b/src/main/resources/extracts/finance/refunds_monthly.sql deleted file mode 100644 index 9a431f7a..00000000 --- a/src/main/resources/extracts/finance/refunds_monthly.sql +++ /dev/null @@ -1,37 +0,0 @@ -select - rfnd.account_name as "Customer Name" -, rfnd.account_external_key as "Account Number" -, rfnd.invoice_number as "Invoice Number" -, date_format(rfnd.invoice_date, '%m/%d/%Y') as "Invoice Date" -, rfnd.currency as "Currency" -, rfnd.invoice_original_amount_charged as "Invoice Amount" -, round(cc.reference_rate * rfnd.invoice_original_amount_charged,4) as "Invoice Amount USD" -, rfnd.plugin_name as "Payment Type" -, rfnd.plugin_pm_type as "Payment Method" -, rfnd.plugin_pm_cc_type as "Payment Card Type" -, case when plugin_name = 'killbill-litle' and plugin_pm_cc_type != 'AmericanExpress' then upper(lr.params_litleonelineresponse_saleresponse_litle_txn_id) else null end as "Litle Payment Reference ID" -- workaround -, case when plugin_name = 'killbill-litle' and plugin_pm_cc_type = 'AmericanExpress' then upper(lr.params_litleonelineresponse_saleresponse_id) else null end as "AmericanExpress Payment Reference ID" -- workaround -, case when plugin_name = 'killbill-paypal-express' then pp.paypal_express_txn_id else null end as "PayPal Payment Reference ID" -- workaround -, date_format(rfnd.created_date, '%m/%d/%Y') as "Payment Date" -, rfnd.currency as "Payment Currency" -, rfnd.amount as "Payment Amount" -, round(cc.reference_rate * rfnd.amount,4) as "Payment Amount USD" -, rfnd.record_id as "Refund Number" -, date_format(rfnd.created_date, '%m/%d/%Y') as "Refund Date" -, rfnd.currency as "Refund Currency" -, rfnd.amount as "Refund Amount" -, round(cc.reference_rate * rfnd.amount,4) as "Refund Amount USD" -, rfnd.invoice_amount_charged - rfnd.invoice_original_amount_charged "Total IA, IIA for Invoice" -, round(cc.reference_rate * (rfnd.invoice_amount_charged - rfnd.invoice_original_amount_charged),4) "Total IA, IIA for Invoice" -, rfnd.created_by as "User Responsible for Refund" -, 'REASON' as "Reason for Refund" -from - analytics_payment_refunds rfnd - join analytics_currency_conversion cc on rfnd.created_date >= cc.start_date and rfnd.created_date <= cc.end_date and cc.currency = rfnd.currency - left outer join paypal_express_transactions pp on pp.kb_payment_id=rfnd.payment_id and api_call = 'refund' -- workaround paypal # - left outer join litle_responses lr on lr.kb_payment_id=rfnd.payment_id and lr.success=1 and lr.api_call = 'charge' -- workaround litle # -where 1=1 - and rfnd.created_date >= cast(date_format(date_sub(sysdate(), interval '1' month), '%Y-%m-01') as date) - and rfnd.created_date < cast(date_format(sysdate(), '%Y-%m-01') as date) - and rfnd.report_group != 'test' -order by 1,rfnd.invoice_payment_record_id; -- just for well defined ordering diff --git a/src/main/resources/extracts/finance/test_accounts.sql b/src/main/resources/extracts/finance/test_accounts.sql deleted file mode 100644 index 647f071c..00000000 --- a/src/main/resources/extracts/finance/test_accounts.sql +++ /dev/null @@ -1,3 +0,0 @@ -select account_id,account_external_key -from analytics_account_tags -where name in ( 'TEST','PARTNER'); diff --git a/src/main/resources/reports/README.md b/src/main/resources/reports/README.md deleted file mode 100644 index f9058539..00000000 --- a/src/main/resources/reports/README.md +++ /dev/null @@ -1,28 +0,0 @@ -Default set of report queries. - -* [churn](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/churn): procedure to compute monthly churn (as a percentage and in USD) -* [conversion](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/conversion): procedure to compute revenue converted per day and billing period -* [mrr](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/mrr): procedure to compute MRR per day and billing period -* [v_report_accounts_summary.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_accounts_summary.ddl): overall number of Non-subscribers vs subscribers (defined as having at least one active bundle) -* [v_report_active_by_product_term_monthly.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_active_by_product_term_monthly.ddl): number of active subscriptions created per day, product and billing period -* [v_report_cancellations_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_cancellations_daily.ddl): number of cancellations per day and phase -* [v_report_chargebacks_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_chargebacks_daily.ddl): amount of chargebacks reported per day and currency -* [v_report_conversions_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_conversions_daily.ddl): number of conversions (trial to non-trial), per day -* [v_report_invoice_adjustments_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_invoice_adjustments_daily.ddl): sum of invoices adjustments per day and currency -* [v_report_invoice_item_adjustments_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_invoice_item_adjustments_daily.ddl): sum of invoices item adjustments per day and currency -* [v_report_invoice_item_credits_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_invoice_item_credits_daily.ddl): sum of invoices credits per day and currency -* [v_report_invoices_balance_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_invoices_balance_daily.ddl): sum of invoices balance per day and currency -* [v_report_invoices_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_invoices_daily.ddl): sum of invoices original amount per day and currency -* [v_report_mrr_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_mrr_daily.ddl): daily MRR, broken down by product -* [v_report_new_accounts_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_new_accounts_daily.ddl): number of created accounts per day -* [v_report_overdue_states_count_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_overdue_states_count_daily.ddl): number of accounts per overdue state, per day -* [v_report_payments_total_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_payments_total_daily.ddl): sum of payments (purchases) per day and currency -* [v_report_refunds_total_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_refunds_total_daily.ddl): sum of refunds per day and currency -* [v_report_trial_starts_count_daily.ddl](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/v_report_trial_starts_count_daily.ddl): number of new trials, per product and day - - -Utility tables: - -* [calendar.sql](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/reports/calendar.sql): procedure to create a *calendar* table - - diff --git a/src/main/resources/reports/accounts_summary/README.md b/src/main/resources/reports/accounts_summary/README.md deleted file mode 100644 index c61b7c40..00000000 --- a/src/main/resources/reports/accounts_summary/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Accounts summary report - -Compute the total number of active (i.e. with at least one active subscription) and non-active accounts. - -The snapshot view is: `v_report_accounts_summary` - -## Pie chart configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_accounts_summary", - "reportType": "COUNTERS", - "reportPrettyName": "Accounts summary", - "sourceTableName": "report_accounts_summary", - "refreshProcedureName": "refresh_report_accounts_summary", - "refreshFrequency": "HOURLY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/accounts_summary/report_accounts_summary.ddl b/src/main/resources/reports/accounts_summary/report_accounts_summary.ddl deleted file mode 100644 index bb307650..00000000 --- a/src/main/resources/reports/accounts_summary/report_accounts_summary.ddl +++ /dev/null @@ -1,19 +0,0 @@ -create table report_accounts_summary as select * from v_report_accounts_summary limit 0; - -drop procedure if exists refresh_report_accounts_summary; -DELIMITER // -CREATE PROCEDURE refresh_report_accounts_summary() -BEGIN - -DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; -DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; - -SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -START TRANSACTION; - delete from report_accounts_summary; - insert into report_accounts_summary select * from v_report_accounts_summary; -COMMIT; - -END; -// -DELIMITER ; diff --git a/src/main/resources/reports/active_by_product_term_monthly/README.md b/src/main/resources/reports/active_by_product_term_monthly/README.md deleted file mode 100644 index ed75eea9..00000000 --- a/src/main/resources/reports/active_by_product_term_monthly/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Monthly active subscriptions report - -Compute (at the end of each month) the total number of active subscriptions per product and billing period. - -The snapshot view is: `v_report_active_by_product_term_monthly` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_active_by_product_term_monthly", - "reportType": "TIMELINE", - "reportPrettyName": "Monthly active subscriptions", - "sourceTableName": "report_active_by_product_term_monthly", - "refreshProcedureName": "refresh_active_by_product_term_monthly", - "refreshFrequency": "DAILY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/cancellations_daily/README.md b/src/main/resources/reports/cancellations_daily/README.md deleted file mode 100644 index 38086d8d..00000000 --- a/src/main/resources/reports/cancellations_daily/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Daily cancellations report - -Compute the total number of cancellations per day per phase. - -The snapshot view is: `v_report_cancellations_daily` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_cancellations_daily", - "reportType": "TIMELINE", - "reportPrettyName": "Daily cancellations", - "sourceTableName": "report_cancellations_daily", - "refreshProcedureName": "refresh_report_cancellations_daily", - "refreshFrequency": "DAILY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/chargebacks_daily/README.md b/src/main/resources/reports/chargebacks_daily/README.md deleted file mode 100644 index 98dbe96b..00000000 --- a/src/main/resources/reports/chargebacks_daily/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Daily chargebacks report - -Compute the total value (in the reference currency) of chargebacks per day per currency. - -The snapshot view is: `v_report_chargebacks_daily` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_chargebacks_daily", - "reportType": "TIMELINE", - "reportPrettyName": "Daily chargebacks value", - "sourceTableName": "report_chargebacks_daily", - "refreshProcedureName": "refresh_report_chargebacks_daily", - "refreshFrequency": "DAILY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/churn/refresh_report_churn_total_and_pct.prc b/src/main/resources/reports/churn/refresh_report_churn_total_and_pct.prc deleted file mode 100644 index 6cb735f3..00000000 --- a/src/main/resources/reports/churn/refresh_report_churn_total_and_pct.prc +++ /dev/null @@ -1,211 +0,0 @@ -drop procedure if exists refresh_report_churn_total_and_pct; -DELIMITER // -CREATE PROCEDURE refresh_report_churn_total_and_pct() -BEGIN - - -- Refresh Churn Dollars and Churn Percent for MONTHLY subscriptions - create temporary table report_temp_churn_monthly_paid_bundles (index (bundle_id)) as - select distinct - tenant_record_id - , bundle_id - from - analytics_invoice_items - where 1=1 - and invoice_original_amount_charged >0 - and invoice_balance = 0 - ; - - create temporary table report_temp_churn_monthly_paid_bundles2 (index (bundle_id)) as - select distinct - tenant_record_id - , bundle_id - from - analytics_invoice_items - where 1=1 - and invoice_original_amount_charged >0 - and invoice_balance = 0 - ; - - create temporary table report_temp_churn_monthly_dollars_pct_monthly as - select - active_sub_dollar.tenant_record_id - , active_sub_dollar.month - , round(churn_dollar.amount) churn_dollars_monthly - , round(churn_dollar.amount / active_sub_dollar.amount,4) churn_pct_monthly - from ( - select - ast.tenant_record_id - , date_format(next_start_date, '%Y-%m-01') month - , prev_billing_period - , sum(converted_prev_price) amount - from - analytics_subscription_transitions ast - join report_temp_churn_monthly_paid_bundles paid_bundles on ast.bundle_id = paid_bundles.bundle_id and ast.tenant_record_id = paid_bundles.tenant_record_id - where 1=1 - and report_group='default' - and next_service='entitlement-service' - and event like 'STOP_ENTITLEMENT%' - and prev_billing_period in ('MONTHLY') - group by 1,2,3 - ) churn_dollar join ( - select - ast.tenant_record_id - , cal.d month - , next_billing_period - , sum(converted_next_price) amount - from - analytics_subscription_transitions ast - join calendar cal on next_start_date < cal.d and (next_end_date > cal.d or next_end_date is null ) and (cal.d = date_format(cal.d, '%Y-%m-01')) and cal.d>='2013-01-01' and cal.d < sysdate() - join report_temp_churn_monthly_paid_bundles2 paid_bundles on ast.bundle_id = paid_bundles.bundle_id and ast.tenant_record_id = paid_bundles.tenant_record_id - where 1=1 - and report_group='default' - and next_service='entitlement-service' - and event not like 'STOP_ENTITLEMENT%' - and next_billing_period in ('MONTHLY') - group by 1,2,3 - ) active_sub_dollar on churn_dollar.month=active_sub_dollar.month and churn_dollar.prev_billing_period=active_sub_dollar.next_billing_period and churn_dollar.tenant_record_id=active_sub_dollar.tenant_record_id - ; - - DELETE FROM report_churn_total_usd_monthly; - DELETE FROM report_churn_percent_monthly; - - insert into report_churn_total_usd_monthly - select - tenant_record_id - , month day - , 'MONTHLY' - , churn_dollars_monthly count - from - report_temp_churn_monthly_dollars_pct_monthly - ; - - insert into report_churn_percent_monthly - select - tenant_record_id - , month day - , 'MONTHLY' - , churn_pct_monthly count - from - report_temp_churn_monthly_dollars_pct_monthly - ; - - -- Refresh Churn Dollars and Churn Percent for ANNUAL subscriptions - create temporary table report_temp_churn_annual_paid_bundles (index (bundle_id)) as - select distinct - tenant_record_id - , bundle_id - from ( - select - tenant_record_id - , bundle_id - from - analytics_invoice_items - where 1=1 - and invoice_original_amount_charged >0 - and invoice_balance = 0 - union - select - s.tenant_record_id - , s.bundle_id - from - subscription_events se - join subscriptions s on se.subscription_id = s.id and se.tenant_record_id = s.tenant_record_id - where 1=1 - and user_type in ('MIGRATE_ENTITLEMENT') - ) bundles - ; - - - create temporary table report_temp_churn_annual_paid_bundles2 (index (bundle_id)) as - select distinct - tenant_record_id - , bundle_id - , charged_through_date - from ( - select - tenant_record_id - , bundle_id - , end_date charged_through_date - from - analytics_invoice_items - where 1=1 - and invoice_original_amount_charged >0 - and invoice_balance = 0 - union - select - s.tenant_record_id - , s.bundle_id - , effective_date charged_through_date - from - subscription_events se - join subscriptions s on se.subscription_id = s.id and se.tenant_record_id = s.tenant_record_id - where 1=1 - and user_type in ('MIGRATE_ENTITLEMENT') - ) bundles - ; - - create temporary table report_temp_churn_annual_dollars_pct_monthly as - select - churn_dollar.tenant_record_id - , churn_dollar.month - , churn_dollar.amount churn_dollars_annual - , round(churn_dollar.amount /active_sub_dollar.amount,4) churn_pct_annual - from ( - select - ast.tenant_record_id - , date_format(next_start_date, '%Y-%m-01') month - , prev_billing_period - , round(sum(converted_prev_price)) amount - from - analytics_subscription_transitions ast - join report_temp_churn_annual_paid_bundles paid_bundles on ast.bundle_id = paid_bundles.bundle_id and ast.tenant_record_id = paid_bundles.tenant_record_id - where 1=1 - and report_group='default' - and next_service='entitlement-service' - and event like 'STOP_ENTITLEMENT%' - and prev_billing_period in ('ANNUAL') - group by 1,2,3 - ) churn_dollar join ( - select - ast.tenant_record_id - , cal.d month - , next_billing_period - , round(sum(converted_next_price)) amount - from - analytics_subscription_transitions ast - join calendar cal on next_start_date < cal.d and (next_end_date > cal.d or next_end_date is null ) and (cal.d = date_format(cal.d, '%Y-%m-01')) and cal.d>='2013-01-01' and cal.d < sysdate() - join report_temp_churn_annual_paid_bundles2 paid_bundles on ast.bundle_id = paid_bundles.bundle_id and ast.tenant_record_id = paid_bundles.tenant_record_id - where 1=1 - and report_group='default' - and next_service='entitlement-service' - and event not like 'STOP_ENTITLEMENT%' - and next_billing_period in ('ANNUAL') - and extract(month from date_add(charged_through_date,interval 1 day)) = extract(month from cal.d) - group by 1,2,3 - ) active_sub_dollar on churn_dollar.month=active_sub_dollar.month and churn_dollar.prev_billing_period=active_sub_dollar.next_billing_period and churn_dollar.tenant_record_id=active_sub_dollar.tenant_record_id - ; - - insert into report_churn_total_usd_monthly - select - tenant_record_id - , month day - , 'ANNUAL' - , churn_dollars_annual count - from - report_temp_churn_annual_dollars_pct_monthly - ; - - insert into report_churn_percent_monthly - select - tenant_record_id - , month day - , 'ANNUAL' - , churn_pct_annual count - from - report_temp_churn_annual_dollars_pct_monthly - ; - - -END; -// -DELIMITER ; diff --git a/src/main/resources/reports/churn/report_churn_percent_monthly.ddl b/src/main/resources/reports/churn/report_churn_percent_monthly.ddl deleted file mode 100644 index 6b0802cd..00000000 --- a/src/main/resources/reports/churn/report_churn_percent_monthly.ddl +++ /dev/null @@ -1,2 +0,0 @@ -drop table if exists report_churn_percent_monthly; -create table report_churn_percent_monthly (tenant_record_id int(11), day date, term varchar(50), count decimal(5,4)); diff --git a/src/main/resources/reports/churn/report_churn_total_usd_monthly.ddl b/src/main/resources/reports/churn/report_churn_total_usd_monthly.ddl deleted file mode 100644 index 1ba6ccde..00000000 --- a/src/main/resources/reports/churn/report_churn_total_usd_monthly.ddl +++ /dev/null @@ -1,2 +0,0 @@ -drop table if exists report_churn_total_usd_monthly; -create table report_churn_total_usd_monthly (tenant_record_id int(11), day date, term varchar(50), count int(8)); diff --git a/src/main/resources/reports/conversion/refresh_report_conversions_total_dollar_monthly.prc b/src/main/resources/reports/conversion/refresh_report_conversions_total_dollar_monthly.prc deleted file mode 100644 index 9c730f04..00000000 --- a/src/main/resources/reports/conversion/refresh_report_conversions_total_dollar_monthly.prc +++ /dev/null @@ -1,39 +0,0 @@ -drop procedure if exists refresh_report_conversions_total_dollar_monthly; -DELIMITER // -CREATE PROCEDURE refresh_report_conversions_total_dollar_monthly() -BEGIN - - DELETE FROM report_conversions_total_dollar_monthly; - - create temporary table report_temp_paid_bundles (index (bundle_id)) as - select distinct - tenant_record_id - , bundle_id - from - analytics_invoice_items - where 1=1 - and invoice_original_amount_charged > 0 - and invoice_balance = 0 - ; - - insert into report_conversions_total_dollar_monthly - select - ast.tenant_record_id - , date_format(next_start_date, '%Y-%m-01') day - , next_billing_period billing_period - , round(sum(converted_next_price)) count - from - analytics_subscription_transitions ast - join report_temp_paid_bundles paid_bundles on ast.bundle_id = paid_bundles.bundle_id and ast.tenant_record_id = paid_bundles.tenant_record_id - where 1=1 - and report_group='default' - and next_service='entitlement-service' - and prev_phase='TRIAL' - and next_phase!='TRIAL' - and event not like 'STOP_ENTITLEMENT%' - group by 1,2,3 - ; - -END; -// -DELIMITER ; diff --git a/src/main/resources/reports/conversion/report_conversions_total_dollar_monthly.ddl b/src/main/resources/reports/conversion/report_conversions_total_dollar_monthly.ddl deleted file mode 100644 index ebb29301..00000000 --- a/src/main/resources/reports/conversion/report_conversions_total_dollar_monthly.ddl +++ /dev/null @@ -1,2 +0,0 @@ -drop table if exists report_conversions_total_dollar_monthly; -create table report_conversions_total_dollar_monthly (tenant_record_id int(11), day date, term varchar(50), count int(10)); diff --git a/src/main/resources/reports/invoices_balance_daily/README.md b/src/main/resources/reports/invoices_balance_daily/README.md deleted file mode 100644 index 8fa1736f..00000000 --- a/src/main/resources/reports/invoices_balance_daily/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Daily invoices balance report - -Compute the total sum of invoices balance (in the reference currency) per invoice created day. - -The snapshot view is: `v_report_invoices_balance_daily` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_invoices_balance_daily", - "reportType": "TIMELINE", - "reportPrettyName": "Daily invoices balance", - "sourceTableName": "report_invoices_balance_daily", - "refreshProcedureName": "refresh_report_invoices_balance_daily", - "refreshFrequency": "DAILY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/invoices_daily/README.md b/src/main/resources/reports/invoices_daily/README.md deleted file mode 100644 index 6021359c..00000000 --- a/src/main/resources/reports/invoices_daily/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Daily invoices report - -Compute the total invoice amount charged (in the reference currency) per day per currency. - -The snapshot view is: `v_report_invoices_daily` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_invoices_daily", - "reportType": "TIMELINE", - "reportPrettyName": "Daily invoices value", - "sourceTableName": "report_invoices_daily", - "refreshProcedureName": "refresh_report_invoices_daily", - "refreshFrequency": "DAILY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/mrr/README.md b/src/main/resources/reports/mrr/README.md deleted file mode 100644 index 330450c8..00000000 --- a/src/main/resources/reports/mrr/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Daily MRR - -Compute the Monthly Recurring Revenue (MRR) on a daily basis. - -The snapshot view is: `v_report_mrr_daily` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_mrr_daily", - "reportType": "TIMELINE", - "reportPrettyName": "Daily MRR", - "sourceTableName": "report_mrr_daily", - "refreshProcedureName": "refresh_report_mrr_daily", - "refreshFrequency": "DAILY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/new_accounts_daily/README.md b/src/main/resources/reports/new_accounts_daily/README.md deleted file mode 100644 index 924d4b3e..00000000 --- a/src/main/resources/reports/new_accounts_daily/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Daily new accounts report - -Compute the total amount of new accounts created per day. - -The snapshot view is: `v_report_new_accounts_daily` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_new_accounts_daily", - "reportType": "TIMELINE", - "reportPrettyName": "Daily new accounts", - "sourceTableName": "report_new_accounts_daily", - "refreshProcedureName": "refresh_report_new_accounts_daily", - "refreshFrequency": "HOURLY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/payment_provider_conversion/refresh_report_payment_provider_conversion_history.ddl b/src/main/resources/reports/payment_provider_conversion/refresh_report_payment_provider_conversion_history.ddl deleted file mode 100644 index a63fee42..00000000 --- a/src/main/resources/reports/payment_provider_conversion/refresh_report_payment_provider_conversion_history.ddl +++ /dev/null @@ -1,16 +0,0 @@ -create table report_payment_provider_conversion_history as select * from v_report_payment_provider_conversion limit 0; - -drop procedure if exists refresh_report_payment_provider_conversion_history; -DELIMITER // -CREATE PROCEDURE refresh_report_payment_provider_conversion_history() -BEGIN - -DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; -DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; - -SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -insert into report_payment_provider_conversion_history select * from v_report_payment_provider_conversion; - -END; -// -DELIMITER ; diff --git a/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion.ddl b/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion.ddl deleted file mode 100644 index 973cdbb4..00000000 --- a/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion.ddl +++ /dev/null @@ -1,37 +0,0 @@ -create or replace view v_report_payment_provider_conversion as -select - rpccs1.plugin_name -, rpccs1.merchant_account -, rpccs1.payment_method -, rpccs1.tenant_record_id -, ifnull(sum(rpccs1.current_success_count),0) as current_success_count -, ifnull(sum(rpccs1.current_transaction_count),0) as current_transaction_count -, ifnull(sum(rpccs1.current_customer_count),0) as current_customer_count -, ifnull(sum(rpccs2.historical_success_count),0) as historical_success_count -, ifnull(sum(rpccs2.historical_transaction_count),0) as historical_transaction_count -, ifnull(sum(rpccs2.historical_customer_count),0) as historical_customer_count -, case when current_success_count is not null and historical_success_count is not null - then concat(round((((sum(rpccs1.current_success_count)-sum(rpccs2.historical_success_count))/sum(rpccs2.historical_success_count))*100),2),'%') - else '0%' - end success_delta -, case when current_transaction_count is not null and historical_transaction_count is not null - then concat(round((((sum(rpccs1.current_transaction_count)-sum(rpccs2.historical_transaction_count))/sum(rpccs2.historical_transaction_count))*100),2),'%') - else '0%' - end transaction_delta -, case when current_customer_count is not null and historical_customer_count is not null - then concat(round((((sum(rpccs1.current_customer_count)-sum(rpccs2.historical_customer_count))/sum(rpccs2.historical_customer_count))*100),2),'%') - else '0%' - end customer_delta -, sysdate() as refresh_date -from v_report_payment_provider_conversion_sub2 rpccs2 -LEFT OUTER JOIN v_report_payment_provider_conversion_sub1 rpccs1 ON - rpccs1.plugin_name=rpccs2.plugin_name -AND rpccs1.merchant_account=rpccs2.merchant_account -AND rpccs1.payment_method=rpccs2.payment_method -AND rpccs1.tenant_record_id=rpccs2.tenant_record_id -GROUP BY - plugin_name -, merchant_account -, payment_method -, tenant_record_id -; diff --git a/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion_sub1.ddl b/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion_sub1.ddl deleted file mode 100644 index f4d50c56..00000000 --- a/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion_sub1.ddl +++ /dev/null @@ -1,39 +0,0 @@ -create or replace view v_report_payment_provider_conversion_sub1 as -SELECT - apa.plugin_name -, ifnull(apa.plugin_property_4,'unknown') as merchant_account -, ifnull(apa.plugin_property_5,'unknown') as payment_method -, apa.tenant_record_id -, sum(case when apa.payment_transaction_status='SUCCESS' then 1 else 0 end) as current_success_count -, count(1) as current_transaction_count -, count(distinct apa.account_id) as current_customer_count -FROM - analytics_payment_auths apa -WHERE 1=1 -AND apa.created_date >= FROM_UNIXTIME(UNIX_TIMESTAMP(NOW()) - 15*60 - (UNIX_TIMESTAMP(NOW()) - 15*60)%(15*60)) -AND cast(FROM_UNIXTIME(UNIX_TIMESTAMP(apa.created_date) - UNIX_TIMESTAMP(apa.created_date)%(15*60)) as time) = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(NOW()) - 15*60 - (UNIX_TIMESTAMP(NOW()) - 15*60)%(15*60)) as time) -GROUP BY - apa.plugin_name -, ifnull(apa.plugin_property_4,'unknown') -, ifnull(apa.plugin_property_5,'unknown') -, apa.tenant_record_id -UNION -SELECT - app.plugin_name -, ifnull(app.plugin_property_4,'unknown') as merchant_account -, ifnull(app.plugin_property_5,'unknown') as payment_method -, app.tenant_record_id -, sum(case when app.payment_transaction_status='SUCCESS' then 1 else 0 end) as current_success_count -, count(1) as current_transaction_count -, count(distinct app.account_id) as current_customer_count -FROM - analytics_payment_purchases app -WHERE 1=1 -AND app.created_date >= FROM_UNIXTIME(UNIX_TIMESTAMP(NOW()) - 15*60 - (UNIX_TIMESTAMP(NOW()) - 15*60)%(15*60)) -AND cast(FROM_UNIXTIME(UNIX_TIMESTAMP(app.created_date) - UNIX_TIMESTAMP(app.created_date)%(15*60)) as time) = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(NOW()) - 15*60 - (UNIX_TIMESTAMP(NOW()) - 15*60)%(15*60)) as time) -GROUP BY - app.plugin_name -, ifnull(app.plugin_property_4,'unknown') -, ifnull(app.plugin_property_5,'unknown') -, app.tenant_record_id -; diff --git a/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion_sub2.ddl b/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion_sub2.ddl deleted file mode 100644 index 82c1a733..00000000 --- a/src/main/resources/reports/payment_provider_conversion/v_report_payment_provider_conversion_sub2.ddl +++ /dev/null @@ -1,41 +0,0 @@ -create or replace view v_report_payment_provider_conversion_sub2 as -SELECT - apa.plugin_name -, ifnull(apa.plugin_property_4,'unknown') as merchant_account -, ifnull(apa.plugin_property_5,'unknown') as payment_method -, apa.tenant_record_id -, sum(case when apa.payment_transaction_status='SUCCESS' then 1 else 0 end) as historical_success_count -, count(1) as historical_transaction_count -, count(distinct apa.account_id) as historical_customer_count -FROM - analytics_payment_auths apa -WHERE 1=1 -AND apa.created_date < FROM_UNIXTIME(UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60)%(15*60) + 15*60) -AND cast(FROM_UNIXTIME(UNIX_TIMESTAMP(apa.created_date) - UNIX_TIMESTAMP(apa.created_date)%(15*60)) as time) = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60)%(15*60)) as time) -AND apa.created_date >= FROM_UNIXTIME(UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60)%(15*60)) -GROUP BY - apa.plugin_name -, ifnull(apa.plugin_property_4,'unknown') -, ifnull(apa.plugin_property_5,'unknown') -, apa.tenant_record_id -UNION -SELECT - app.plugin_name -, ifnull(app.plugin_property_4,'unknown') as merchant_account -, ifnull(app.plugin_property_5,'unknown') as payment_method -, app.tenant_record_id -, sum(case when app.payment_transaction_status='SUCCESS' then 1 else 0 end) as historical_success_count -, count(1) as historical_transaction_count -, count(distinct app.account_id) as historical_customer_count -FROM - analytics_payment_purchases app -WHERE 1=1 -AND app.created_date < FROM_UNIXTIME(UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60)%(15*60) + 15*60) -AND cast(FROM_UNIXTIME(UNIX_TIMESTAMP(app.created_date) - UNIX_TIMESTAMP(app.created_date)%(15*60)) as time) = cast(FROM_UNIXTIME(UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60)%(15*60)) as time) -AND app.created_date >= FROM_UNIXTIME(UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60 - (UNIX_TIMESTAMP(NOW() - interval '14' day) - 15*60)%(15*60)) -GROUP BY - app.plugin_name -, ifnull(app.plugin_property_4,'unknown') -, ifnull(app.plugin_property_5,'unknown') -, app.tenant_record_id -; diff --git a/src/main/resources/reports/payment_provider_errors/report_payment_provider_errors.ddl b/src/main/resources/reports/payment_provider_errors/report_payment_provider_errors.ddl deleted file mode 100644 index 3b499733..00000000 --- a/src/main/resources/reports/payment_provider_errors/report_payment_provider_errors.ddl +++ /dev/null @@ -1,42 +0,0 @@ -create table report_payment_provider_errors_sub2 as select * from v_report_payment_provider_errors_sub2 limit 0; -create table report_payment_provider_errors as select * from v_report_payment_provider_errors limit 0; - -drop procedure if exists refresh_report_payment_provider_errors; -DELIMITER // -CREATE PROCEDURE refresh_report_payment_provider_errors() -BEGIN - -DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; -DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; - -SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; - -START TRANSACTION; - delete from report_payment_provider_errors_sub2; - insert into report_payment_provider_errors_sub2 select * from v_report_payment_provider_errors_sub2; - - delete from report_payment_provider_errors; - insert into report_payment_provider_errors - select - tenant_record_id - , day - , currency - , plugin_name - , plugin_gateway_error - , count - from report_payment_provider_errors_sub2 sub2 - where ( - select count(*) from report_payment_provider_errors_sub2 as sub21 - where 1=1 - and sub21.tenant_record_id = sub2.tenant_record_id - and sub21.day = sub2.day - and sub21.currency = sub2.currency - and sub21.plugin_name = sub2.plugin_name - and sub21.count >= sub2.count - ) <= 3 - ; -COMMIT; - -END; -// -DELIMITER ; diff --git a/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors.ddl b/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors.ddl deleted file mode 100644 index 0103dd5e..00000000 --- a/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors.ddl +++ /dev/null @@ -1,19 +0,0 @@ -create or replace view v_report_payment_provider_errors as -select - tenant_record_id -, day -, currency -, plugin_name -, plugin_gateway_error -, count -from v_report_payment_provider_errors_sub2 sub2 -where ( - select count(*) from v_report_payment_provider_errors_sub2 as sub21 - where 1=1 - and sub21.tenant_record_id = sub2.tenant_record_id - and sub21.day = sub2.day - and sub21.currency = sub2.currency - and sub21.plugin_name = sub2.plugin_name - and sub21.count >= sub2.count -) <= 3 -; diff --git a/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors_sub1.ddl b/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors_sub1.ddl deleted file mode 100644 index 7b650b13..00000000 --- a/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors_sub1.ddl +++ /dev/null @@ -1,27 +0,0 @@ -create or replace view v_report_payment_provider_errors_sub1 as -select - aa.tenant_record_id -, 'AUTH' as op -, date_format(aa.created_date,'%Y-%m-%d') as day -, aa.currency -, aa.plugin_name -, aa.record_id -from analytics_payment_auths aa -where 1=1 - and aa.payment_transaction_status not in ('PENDING', 'SUCCESS') - and aa.report_group = 'default' - and aa.created_date > now() - interval '60' day -union -select - ap.tenant_record_id -, 'PURCHASE' as op -, date_format(ap.created_date,'%Y-%m-%d') as day -, ap.currency -, ap.plugin_name -, ap.record_id -from analytics_payment_purchases ap -where 1=1 - and ap.payment_transaction_status not in ('PENDING', 'SUCCESS') - and ap.report_group = 'default' - and ap.created_date > now() - interval '60' day -; diff --git a/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors_sub2.ddl b/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors_sub2.ddl deleted file mode 100644 index d887f621..00000000 --- a/src/main/resources/reports/payment_provider_errors/v_report_payment_provider_errors_sub2.ddl +++ /dev/null @@ -1,15 +0,0 @@ -create or replace view v_report_payment_provider_errors_sub2 as -select - v1.tenant_record_id -, v1.day -, v1.currency -, v1.plugin_name -, substring_index(ifnull(apa.plugin_gateway_error, app.plugin_gateway_error), ' ', 10) as plugin_gateway_error -, count(1) as count -from v_report_payment_provider_errors_sub1 v1 -left join analytics_payment_auths apa on apa.record_id = v1.record_id and v1.op = 'AUTH' -left join analytics_payment_purchases app on app.record_id = v1.record_id and v1.op = 'PURCHASE' -where 1=1 -and ifnull(apa.plugin_gateway_error, app.plugin_gateway_error) is not null -group by 1,2,3,4,5 -; diff --git a/src/main/resources/reports/payment_provider_monitor/refresh_report_payment_provider_monitor_history.ddl b/src/main/resources/reports/payment_provider_monitor/refresh_report_payment_provider_monitor_history.ddl deleted file mode 100644 index 20c5a9c0..00000000 --- a/src/main/resources/reports/payment_provider_monitor/refresh_report_payment_provider_monitor_history.ddl +++ /dev/null @@ -1,16 +0,0 @@ -create table report_payment_provider_monitor_history as select * from v_report_payment_provider_monitor limit 0; - -drop procedure if exists refresh_report_payment_provider_monitor_history; -DELIMITER // -CREATE PROCEDURE refresh_report_payment_provider_monitor_history() -BEGIN - -DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; -DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; - -SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -insert into report_payment_provider_monitor_history select * from v_report_payment_provider_monitor; - -END; -// -DELIMITER ; diff --git a/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor.ddl b/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor.ddl deleted file mode 100644 index 983e044f..00000000 --- a/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor.ddl +++ /dev/null @@ -1,16 +0,0 @@ -create or replace view v_report_payment_provider_monitor as -SELECT - plugin_list.plugin_name -, plugin_list.merchant_account -, plugin_list.payment_method -, plugin_list.tenant_record_id -, ifnull(recent_success_trx.success_count_last_hour,0) as success_count_last_hour -, ifnull(recent_success_trx.success_count_last_12_hours,0) as success_count_last_12_hours -, sysdate() as refresh_date -FROM v_report_payment_provider_monitor_sub1 plugin_list -LEFT OUTER JOIN v_report_payment_provider_monitor_sub3 recent_success_trx on - plugin_list.plugin_name=recent_success_trx.plugin_name -AND plugin_list.merchant_account=recent_success_trx.merchant_account -AND plugin_list.payment_method=recent_success_trx.payment_method -AND plugin_list.tenant_record_id=recent_success_trx.tenant_record_id -; diff --git a/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub1.ddl b/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub1.ddl deleted file mode 100644 index 10baac09..00000000 --- a/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub1.ddl +++ /dev/null @@ -1,19 +0,0 @@ -create or replace view v_report_payment_provider_monitor_sub1 as -SELECT distinct - apa.plugin_name -, ifnull(apa.plugin_property_4,'unknown') as merchant_account -, ifnull(apa.plugin_property_5,'unknown') as payment_method -, apa.tenant_record_id -FROM analytics_payment_auths apa -WHERE 1=1 -AND apa.created_date > now() - interval '7' day -UNION -SELECT distinct - app.plugin_name -, ifnull(app.plugin_property_4,'unknown') as merchant_account -, ifnull(app.plugin_property_5,'unknown') as payment_method -, app.tenant_record_id -FROM analytics_payment_purchases app -WHERE 1=1 -AND app.created_date > now() - interval '7' day -; diff --git a/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub2.ddl b/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub2.ddl deleted file mode 100644 index d2306067..00000000 --- a/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub2.ddl +++ /dev/null @@ -1,35 +0,0 @@ -create or replace view v_report_payment_provider_monitor_sub2 as -SELECT - apa.plugin_name -, ifnull(apa.plugin_property_4,'unknown') as merchant_account -, ifnull(apa.plugin_property_5,'unknown') as payment_method -, apa.tenant_record_id -, sum(case when apa.created_date > now() - interval 1 hour then 1 else 0 end) success_count_last_hour -, count(1) success_count_last_12_hours -FROM analytics_payment_auths apa -WHERE 1=1 -AND apa.payment_transaction_status = 'SUCCESS' -AND apa.created_date > now() - interval '12' hour -GROUP BY - apa.plugin_name -, ifnull(apa.plugin_property_4,'unknown') -, ifnull(apa.plugin_property_5,'unknown') -, apa.tenant_record_id -UNION -SELECT - app.plugin_name -, ifnull(app.plugin_property_4,'unknown') as merchant_account -, ifnull(app.plugin_property_5,'unknown') as payment_method -, app.tenant_record_id -, sum(case when app.created_date > now() - interval 1 hour then 1 else 0 end) success_count_last_hour -, count(1) success_count_last_12_hours -FROM analytics_payment_purchases app -WHERE 1=1 -AND app.payment_transaction_status = 'SUCCESS' -AND app.created_date > now() - interval '12' hour -GROUP BY - app.plugin_name -, ifnull(app.plugin_property_4,'unknown') -, ifnull(app.plugin_property_5,'unknown') -, app.tenant_record_id -; diff --git a/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub3.ddl b/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub3.ddl deleted file mode 100644 index 3c4b8697..00000000 --- a/src/main/resources/reports/payment_provider_monitor/v_report_payment_provider_monitor_sub3.ddl +++ /dev/null @@ -1,15 +0,0 @@ -create or replace view v_report_payment_provider_monitor_sub3 as -SELECT - plugin_name -, merchant_account -, payment_method -, tenant_record_id -, sum(ifnull(success_count_last_hour,0)) as success_count_last_hour -, sum(ifnull(success_count_last_12_hours,0)) as success_count_last_12_hours -FROM v_report_payment_provider_monitor_sub2 t2 -GROUP BY - plugin_name -, merchant_account -, payment_method -, tenant_record_id -; diff --git a/src/main/resources/reports/payments_by_provider/refresh_report_payments_by_provider_history.ddl b/src/main/resources/reports/payments_by_provider/refresh_report_payments_by_provider_history.ddl deleted file mode 100644 index cf6b7c89..00000000 --- a/src/main/resources/reports/payments_by_provider/refresh_report_payments_by_provider_history.ddl +++ /dev/null @@ -1,16 +0,0 @@ -create table report_payments_by_provider_history as select * from v_report_payments_by_provider limit 0; - -drop procedure if exists refresh_report_payments_by_provider_history; -DELIMITER // -CREATE PROCEDURE refresh_report_payments_by_provider_history() -BEGIN - -DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; -DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK; - -SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -insert into report_payments_by_provider_history select * from v_report_payments_by_provider; - -END; -// -DELIMITER ; diff --git a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider.ddl b/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider.ddl deleted file mode 100644 index 3184a98a..00000000 --- a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider.ddl +++ /dev/null @@ -1,54 +0,0 @@ -create or replace view v_report_payments_by_provider as -SELECT - t1.plugin_name -, t1.merchant_account -, t1.payment_method -, t1.tenant_record_id -, t2.timeframe -, transaction_type -, case when t2.timeframe=1 then 'Last 30 days' - when t2.timeframe=2 then 'Last 7 days' - when t2.timeframe=3 then 'Last 24 hours' - when t2.timeframe=4 then 'Last 30 min' - end as period -, sum(ifnull(total,0)) as total -, sum(ifnull(failed,0)) as failed -, sum(ifnull(pending,0)) as pending -, sum(ifnull(good,0)) as good -, case when failed is not null and total is not null then concat(round(((sum(failed)/sum(total))*100),2),'%') - else '0%' - end as pct_failed -, case when failed is not null and total is not null then concat(round(((sum(pending)/sum(total))*100),2),'%') - else '0%' - end as pct_pending -, case when failed is not null and total is not null then concat(round(((sum(good)/sum(total))*100),2),'%') - else '0%' - end as pct_good -, converted_amount -, t1.converted_currency -, sysdate() as refresh_date -FROM v_report_payments_by_provider_sub2 t1 -INNER JOIN v_report_payments_by_provider_sub3 t2 -LEFT OUTER JOIN v_report_payments_by_provider_sub1 v1 on v1.plugin_name=t1.plugin_name -AND v1.merchant_account=t1.merchant_account -AND v1.payment_method=t1.payment_method -AND v1.timeframe=t2.timeframe -AND v1.converted_currency=t1.converted_currency -AND v1.tenant_record_id=t1.tenant_record_id -GROUP BY - plugin_name -, merchant_account -, payment_method -, timeframe -, transaction_type -, converted_currency -, tenant_record_id -ORDER BY - tenant_record_id -, merchant_account -, payment_method -, plugin_name -, timeframe -, transaction_type -, converted_currency -; diff --git a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_last_24h_summary.ddl b/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_last_24h_summary.ddl deleted file mode 100644 index 85a234a0..00000000 --- a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_last_24h_summary.ddl +++ /dev/null @@ -1,11 +0,0 @@ -create or replace view v_report_payments_by_provider_last_24h_summary as -select - tenant_record_id -, payment_method as label -, sum(total) as count -from v_report_payments_by_provider -where 1 = 1 -and timeframe = 3 -and transaction_type in ('AUTHORIZE', 'PURCHASE') -group by 1,2 -; diff --git a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub2.ddl b/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub2.ddl deleted file mode 100644 index e21931b7..00000000 --- a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub2.ddl +++ /dev/null @@ -1,15 +0,0 @@ -create or replace view v_report_payments_by_provider_sub2 as -select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_auths force index(analytics_payment_auths_date_trid_plugin_name) where created_date > now() - interval '7' day -union -select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_captures force index(analytics_payment_captures_date_trid_plugin_name) where created_date > now() - interval '7' day -union -select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_chargebacks force index(analytics_payment_chargebacks_date_trid_plugin_name) where created_date > now() - interval '7' day -union -select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_credits force index(analytics_payment_credits_date_trid_plugin_name) where created_date > now() - interval '7' day -union -select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_purchases force index(analytics_payment_purchases_date_trid_plugin_name) where created_date > now() - interval '7' day -union -select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_refunds force index(analytics_payment_refunds_date_trid_plugin_name) where created_date > now() - interval '7' day -union -select distinct plugin_name,ifnull(plugin_property_4,'unknown') as merchant_account,ifnull(plugin_property_5,'unknown') as payment_method,converted_currency,tenant_record_id from analytics_payment_voids force index(analytics_payment_voids_date_trid_plugin_name) where created_date > now() - interval '7' day -; diff --git a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub3.ddl b/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub3.ddl deleted file mode 100644 index f743cd15..00000000 --- a/src/main/resources/reports/payments_by_provider/v_report_payments_by_provider_sub3.ddl +++ /dev/null @@ -1,3 +0,0 @@ -create or replace view v_report_payments_by_provider_sub3 as -select 1 as timeframe union select 2 as timeframe union select 3 as timeframe union select 4 as timeframe -; \ No newline at end of file diff --git a/src/main/resources/reports/payments_total_daily/README.md b/src/main/resources/reports/payments_total_daily/README.md deleted file mode 100644 index be71c95b..00000000 --- a/src/main/resources/reports/payments_total_daily/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Daily payments report - -Compute the total value (in the reference currency) of captured payments per day per currency. - -The snapshot view is: `v_report_payments_total_daily` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_payments_total_daily", - "reportType": "TIMELINE", - "reportPrettyName": "Daily payments value", - "sourceTableName": "report_payments_total_daily", - "refreshProcedureName": "refresh_report_payments_total_daily", - "refreshFrequency": "HOURLY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/payments_total_daily/v_report_payments_total_daily.ddl b/src/main/resources/reports/payments_total_daily/v_report_payments_total_daily.ddl deleted file mode 100644 index e1558f8a..00000000 --- a/src/main/resources/reports/payments_total_daily/v_report_payments_total_daily.ddl +++ /dev/null @@ -1,9 +0,0 @@ -create or replace view v_report_payments_total_daily as -select - tenant_record_id -, day -, currency -, sum(count) as count -from v_report_payments_total_daily_sub1 -group by 1,2,3 -; diff --git a/src/main/resources/reports/payments_total_daily/v_report_payments_total_daily_sub1.ddl b/src/main/resources/reports/payments_total_daily/v_report_payments_total_daily_sub1.ddl deleted file mode 100644 index bbd885e5..00000000 --- a/src/main/resources/reports/payments_total_daily/v_report_payments_total_daily_sub1.ddl +++ /dev/null @@ -1,25 +0,0 @@ -create or replace view v_report_payments_total_daily_sub1 as -select - ac.tenant_record_id -, 'CAPTURE' as op -, date_format(ac.created_date,'%Y-%m-%d') as day -, ac.currency -, sum(ifnull(ac.converted_amount, 0)) as count -from analytics_payment_captures ac -where 1=1 - and ac.payment_transaction_status = 'SUCCESS' - and ac.report_group='default' -group by 1,2,3,4 -union -select - ap.tenant_record_id -, 'PURCHASE' as op -, date_format(ap.created_date,'%Y-%m-%d') as day -, ap.currency -, sum(ifnull(ap.converted_amount, 0)) as count -from analytics_payment_purchases ap -where 1=1 - and ap.payment_transaction_status = 'SUCCESS' - and ap.report_group='default' -group by 1,2,3,4 -; diff --git a/src/main/resources/reports/refunds_total_daily/README.md b/src/main/resources/reports/refunds_total_daily/README.md deleted file mode 100644 index 42fd3c40..00000000 --- a/src/main/resources/reports/refunds_total_daily/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Daily refunds report - -Compute the total value (in the reference currency) of refunds per day per currency. - -The snapshot view is: `v_report_refunds_total_daily` - -## Timeline configuration - -``` -curl -v \ - -X POST \ - -u admin:password \ - -H "X-Killbill-ApiKey:bob" \ - -H "X-Killbill-ApiSecret:lazar" \ - -H 'Content-Type: application/json' \ - -d '{"reportName": "report_refunds_total_daily", - "reportType": "TIMELINE", - "reportPrettyName": "Daily refunds value", - "sourceTableName": "report_refunds_total_daily", - "refreshProcedureName": "refresh_report_refunds_total_daily", - "refreshFrequency": "HOURLY"}' \ - "http://127.0.0.1:8080/plugins/killbill-analytics/reports" -``` diff --git a/src/main/resources/reports/v_report_invoice_adjustments_daily.ddl b/src/main/resources/reports/v_report_invoice_adjustments_daily.ddl deleted file mode 100644 index ad815459..00000000 --- a/src/main/resources/reports/v_report_invoice_adjustments_daily.ddl +++ /dev/null @@ -1,12 +0,0 @@ -create or replace view v_report_invoice_adjustments_daily as -select - aia.tenant_record_id -, aia.currency -, date_format(aia.created_date,'%Y-%m-%d') as day -, sum(aia.converted_amount) as count -from - analytics_invoice_adjustments aia -where 1=1 - and aia.report_group='default' -group by 1,2,3 -; diff --git a/src/main/resources/sanity/README.md b/src/main/resources/sanity/README.md deleted file mode 100644 index 2d47a855..00000000 --- a/src/main/resources/sanity/README.md +++ /dev/null @@ -1,4 +0,0 @@ -These sanity queries verify that the data in the Analytics tables are in sync with the core Kill Bill tables. - -* [account_record_id_sanity.sql](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/sanity/account_record_id_sanity.sql): check the account_id/account_record_id mapping in each table is correct -* [sanity.sql](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/sanity/sanity.sql): check invariants to make sure the Analytics tables are self-consistent diff --git a/src/main/resources/sanity/account_record_id_sanity.sql b/src/main/resources/sanity/account_record_id_sanity.sql deleted file mode 100644 index 690a7ccd..00000000 --- a/src/main/resources/sanity/account_record_id_sanity.sql +++ /dev/null @@ -1,253 +0,0 @@ -select - 'BAC' as table_name -, count(1) count -from analytics_accounts bac -left outer join accounts a on bac.account_id = a.id -where 1 = 1 -and ( - bac.account_record_id != a.record_id - or bac.account_record_id is null -) -union -select - 'BAC_FIELDS' as table_name -, count(1) count -from analytics_account_fields bac -left outer join accounts a on bac.account_id = a.id -where 1 = 1 -and ( - bac.account_record_id != a.record_id - or a.record_id is null -) -union -select - 'BAC_TAGS' as table_name -, count(1) count -from analytics_account_tags bac -left outer join accounts a on bac.account_id = a.id -where 1 = 1 -and ( - bac.account_record_id != a.record_id - or bac.account_record_id is null -) -union -select - 'BBU' as table_name -, count(1) count -from analytics_bundles bbu -left outer join accounts a on bbu.account_id = a.id -where 1 = 1 -and ( - bbu.account_record_id != a.record_id - or bbu.account_record_id is null -) -union -select - 'BBU_FIELDS' as table_name -, count(1) count -from analytics_bundle_fields bbu -left outer join accounts a on bbu.account_id = a.id -where 1 = 1 -and ( - bbu.account_record_id != a.record_id - or bbu.account_record_id is null -) -union -select - 'BBU_TAGS' as table_name -, count(1) count -from analytics_bundle_tags bbu -left outer join accounts a on bbu.account_id = a.id -where 1 = 1 -and ( - bbu.account_record_id != a.record_id - or bbu.account_record_id is null -) -union -select - 'BII' as table_name -, count(1) count -from analytics_invoice_items bii -left outer join accounts a on bii.account_id = a.id -where 1 = 1 -and ( - bii.account_record_id != a.record_id - or bii.account_record_id is null -) -union -select - 'BIA' as table_name -, count(1) count -from analytics_invoice_adjustments bia -left outer join accounts a on bia.account_id = a.id -where 1 = 1 -and ( - bia.account_record_id != a.record_id - or bia.account_record_id is null -) -union -select - 'BIIA' as table_name -, count(1) count -from analytics_invoice_item_adjustments biia -left outer join accounts a on biia.account_id = a.id -where 1 = 1 -and ( - biia.account_record_id != a.record_id - or biia.account_record_id is null -) -union -select - 'BIC' as table_name -, count(1) count -from analytics_invoice_credits bic -left outer join accounts a on bic.account_id = a.id -where 1 = 1 -and ( - bic.account_record_id != a.record_id - or bic.account_record_id is null -) -union -select - 'BIN' as table_name -, count(1) count -from analytics_invoices bin -left outer join accounts a on bin.account_id = a.id -where 1 = 1 -and ( - bin.account_record_id != a.record_id - or bin.account_record_id is null -) -union -select - 'BIN_FIELDS' as table_name -, count(1) count -from analytics_invoice_fields bin -left outer join accounts a on bin.account_id = a.id -where 1 = 1 -and ( - bin.account_record_id != a.record_id - or bin.account_record_id is null -) -union -select - 'BIN_TAGS' as table_name -, count(1) count -from analytics_invoice_tags bin -left outer join accounts a on bin.account_id = a.id -where 1 = 1 -and ( - bin.account_record_id != a.record_id - or bin.account_record_id is null -) -union -select - 'BIP_AUTHS' as table_name -, count(1) count -from analytics_payment_auths bip -left outer join accounts a on bip.account_id = a.id -where 1 = 1 -and ( - bip.account_record_id != a.record_id - or bip.account_record_id is null -) -union -select - 'BIP_CAPTURES' as table_name -, count(1) count -from analytics_payment_captures bip -left outer join accounts a on bip.account_id = a.id -where 1 = 1 -and ( - bip.account_record_id != a.record_id - or bip.account_record_id is null -) -union -select - 'BIP_PURCHASES' as table_name -, count(1) count -from analytics_payment_purchases bip -left outer join accounts a on bip.account_id = a.id -where 1 = 1 -and ( - bip.account_record_id != a.record_id - or bip.account_record_id is null -) -union -select - 'BIP_REFUNDS' as table_name -, count(1) count -from analytics_payment_refunds bip -left outer join accounts a on bip.account_id = a.id -where 1 = 1 -and ( - bip.account_record_id != a.record_id - or bip.account_record_id is null -) -union -select - 'BIP_CREDITS' as table_name -, count(1) count -from analytics_payment_credits bip -left outer join accounts a on bip.account_id = a.id -where 1 = 1 -and ( - bip.account_record_id != a.record_id - or bip.account_record_id is null -) -union -select - 'BIP_CHARGEBACKS' as table_name -, count(1) count -from analytics_payment_chargebacks bip -left outer join accounts a on bip.account_id = a.id -where 1 = 1 -and ( - bip.account_record_id != a.record_id - or bip.account_record_id is null -) -union -select - 'BIP_FIELDS' as table_name -, count(1) count -from analytics_payment_fields bip -left outer join accounts a on bip.account_id = a.id -where 1 = 1 -and ( - bip.account_record_id != a.record_id - or bip.account_record_id is null -) -union -select - 'BIP_TAGS' as table_name -, count(1) count -from analytics_payment_tags bip -left outer join accounts a on bip.account_id = a.id -where 1 = 1 -and ( - bip.account_record_id != a.record_id - or bip.account_record_id is null -) -union -select - 'BOS' as table_name -, count(1) count -from analytics_account_transitions bos -left outer join accounts a on bos.account_id = a.id -where 1 = 1 -and ( - bos.account_record_id != a.record_id - or bos.account_record_id is null -) -union -select - 'BST' as table_name -, count(1) count -from analytics_subscription_transitions bst -left outer join accounts a on bst.account_id = a.id -where 1 = 1 -and ( - bst.account_record_id != a.record_id - or bst.account_record_id is null -) -; diff --git a/src/main/resources/sanity/plugin_sanity/README.md b/src/main/resources/sanity/plugin_sanity/README.md deleted file mode 100644 index 792fffc9..00000000 --- a/src/main/resources/sanity/plugin_sanity/README.md +++ /dev/null @@ -1 +0,0 @@ -Any sanity queries that are plugin specific should go here. \ No newline at end of file diff --git a/src/main/resources/sanity/plugin_sanity/cybersource/sanity.sql b/src/main/resources/sanity/plugin_sanity/cybersource/sanity.sql deleted file mode 100644 index d82dd3e9..00000000 --- a/src/main/resources/sanity/plugin_sanity/cybersource/sanity.sql +++ /dev/null @@ -1,78 +0,0 @@ -select 'CyberSource sanity' as sanity_query_name; -select - 'payment_transactions in cybersource_transactions' as description - ,sum(case when b.kb_payment_transaction_id is null then 1 else 0 end) as row_missing - ,sum(case when b.kb_payment_transaction_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.transaction_type!='VOID' then - case when a.id=b.kb_payment_transaction_id and a.payment_id=b.kb_payment_id and a.transaction_type=b.transaction_type and a.amount*100=b.amount_in_cents and a.currency=b.currency and a.payment_id=b.kb_payment_id and a.transaction_type=b.transaction_type and p.account_id=b.kb_account_id - then 1 else 0 end - when a.transaction_type='VOID' then - case when a.id=b.kb_payment_transaction_id and a.payment_id=b.kb_payment_id and a.transaction_type=b.transaction_type and p.account_id=b.kb_account_id - then 1 else 0 end - end) as matches - ,count(1) total -from - payment_transactions a - inner join payments p on - a.payment_id=p.id - inner join payment_methods pm on - p.payment_method_id=pm.id - and pm.plugin_name='killbill-cybersource' - left outer join cybersource_transactions b on - a.id=b.kb_payment_transaction_id -where 1=1 - and a.transaction_status='SUCCESS' -UNION -select - 'cybersource_transactions in payment_transactions' - ,sum(case when b.id is null then 1 else 0 end) as row_missing - ,sum(case when b.id is not null then 1 else 0 end) as row_exists - ,count(1) matches - ,count(1) total -from - cybersource_transactions a - left outer join payment_transactions b on - a.kb_payment_transaction_id=b.id -UNION -select - 'payment_responses in cybersource_transactions' as description - ,sum(case when b.kb_payment_transaction_id is null then 1 else 0 end) as row_missing - ,sum(case when b.kb_payment_transaction_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.kb_payment_transaction_id and a.payment_id=b.kb_payment_id and a.transaction_type=b.transaction_type and p.account_id=b.kb_account_id then 1 else 0 end) as matches - ,count(1) total -from - payment_transactions a - inner join payments p on - a.payment_id=p.id - inner join payment_methods pm on - p.payment_method_id=pm.id - and pm.plugin_name='killbill-cybersource' - left outer join cybersource_responses b on - a.id=b.kb_payment_transaction_id -where 1=1 - and a.transaction_status='SUCCESS' -UNION -select - 'cybersource_responses in payment_transactions' - ,sum(case when b.id is null then 1 else 0 end) as row_missing - ,sum(case when b.id is not null then 1 else 0 end) as row_exists - ,count(1) matches - ,count(1) total -from - cybersource_responses a - left outer join payment_transactions b on - a.kb_payment_transaction_id=b.id -where 1=1 - and a.api_call !='add_payment_method' -UNION -select - 'cybersource_responses in cybersource_transactions' - ,sum(case when b.id is null then 1 else 0 end) as row_missing - ,sum(case when b.id is not null then 1 else 0 end) as row_exists - ,count(1) matches - ,count(1) total -from - cybersource_transactions a - left outer join cybersource_responses b on - a.cybersource_response_id = b.id -; \ No newline at end of file diff --git a/src/main/resources/sanity/plugin_sanity/payU/sanity.sql b/src/main/resources/sanity/plugin_sanity/payU/sanity.sql deleted file mode 100644 index 593fe319..00000000 --- a/src/main/resources/sanity/plugin_sanity/payU/sanity.sql +++ /dev/null @@ -1,78 +0,0 @@ -select 'payU sanity' as sanity_query_name; -select - 'payment_transactions in payu_latam_transactions' as description - ,sum(case when b.kb_payment_transaction_id is null then 1 else 0 end) as row_missing - ,sum(case when b.kb_payment_transaction_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.transaction_type!='VOID' then - case when a.id=b.kb_payment_transaction_id and a.payment_id=b.kb_payment_id and a.transaction_type=b.transaction_type and a.amount*100=b.amount_in_cents and a.currency=b.currency and a.payment_id=b.kb_payment_id and a.transaction_type=b.transaction_type and p.account_id=b.kb_account_id - then 1 else 0 end - when a.transaction_type='VOID' then - case when a.id=b.kb_payment_transaction_id and a.payment_id=b.kb_payment_id and a.transaction_type=b.transaction_type and p.account_id=b.kb_account_id - then 1 else 0 end - end) as matches - ,count(1) total -from - payment_transactions a - inner join payments p on - a.payment_id=p.id - inner join payment_methods pm on - p.payment_method_id=pm.id - and pm.plugin_name='killbill-payu-latam' - left outer join payu_latam_transactions b on - a.id=b.kb_payment_transaction_id -where 1=1 - and a.transaction_status='SUCCESS' -UNION -select - 'payu_latam_transactions in payment_transactions' - ,sum(case when b.id is null then 1 else 0 end) as row_missing - ,sum(case when b.id is not null then 1 else 0 end) as row_exists - ,count(1) matches - ,count(1) total -from - payu_latam_transactions a - left outer join payment_transactions b on - a.kb_payment_transaction_id=b.id -UNION -select - 'payment_responses in payu_latam_transactions' as description - ,sum(case when b.kb_payment_transaction_id is null then 1 else 0 end) as row_missing - ,sum(case when b.kb_payment_transaction_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.kb_payment_transaction_id and a.payment_id=b.kb_payment_id and a.transaction_type=b.transaction_type and p.account_id=b.kb_account_id then 1 else 0 end) as matches - ,count(1) total -from - payment_transactions a - inner join payments p on - a.payment_id=p.id - inner join payment_methods pm on - p.payment_method_id=pm.id - and pm.plugin_name='killbill-payu-latam' - left outer join payu_latam_responses b on - a.id=b.kb_payment_transaction_id -where 1=1 - and a.transaction_status='SUCCESS' -UNION -select - 'payu_latam_responses in payment_transactions' - ,sum(case when b.id is null then 1 else 0 end) as row_missing - ,sum(case when b.id is not null then 1 else 0 end) as row_exists - ,count(1) matches - ,count(1) total -from - payu_latam_responses a - left outer join payment_transactions b on - a.kb_payment_transaction_id=b.id -where 1=1 - and a.api_call !='add_payment_method' -UNION -select - 'payu_latam_responses in payu_latam_transactions' - ,sum(case when b.id is null then 1 else 0 end) as row_missing - ,sum(case when b.id is not null then 1 else 0 end) as row_exists - ,count(1) matches - ,count(1) total -from - payu_latam_transactions a - left outer join payu_latam_responses b on - a.payu_latam_response_id = b.id -; \ No newline at end of file diff --git a/src/main/resources/sanity/plugin_sanity/zuora/sanity.sql b/src/main/resources/sanity/plugin_sanity/zuora/sanity.sql deleted file mode 100644 index 6e4a5b01..00000000 --- a/src/main/resources/sanity/plugin_sanity/zuora/sanity.sql +++ /dev/null @@ -1,35 +0,0 @@ -select 'G6ai' as sanity_query_name; -select * -from bip -left outer join invoice_payments ip on bip.invoice_payment_id = ip.id -left outer join _zuora_payments pp on ip.payment_id = pp.kb_p_id -where (coalesce(pp.z_created_date, 'NULL') != coalesce(bip.plugin_created_date, 'NULL') -or coalesce(pp.z_effective_date, 'NULL') != coalesce(bip.plugin_effective_date, 'NULL') -or coalesce(pp.z_status, 'NULL') != coalesce(bip.plugin_status, 'NULL') -or coalesce(pp.z_gateway_error, 'NULL') != coalesce(bip.plugin_gateway_error, 'NULL') -or coalesce(pp.z_gateway_error_code, 'NULL') != coalesce(bip.plugin_gateway_error_code, 'NULL') -or coalesce(pp.z_reference_id, 'NULL') != coalesce(bip.plugin_first_reference_id, 'NULL') -or coalesce(pp.z_snd_reference_id, 'NULL') != coalesce(bip.plugin_second_reference_id, 'NULL') ) and pp.kb_p_id is not null -- workaround until we get plugin name, query will miss missing rows -; - -select 'G6bi' as sanity_query_name; -select * -from _zuora_payments pp -left outer join invoice_payments ip on ip.payment_id = pp.kb_p_id -left outer join analytics_payments bip on bip.invoice_payment_id = ip.id -where (coalesce(pp.z_created_date, 'NULL') != coalesce(bip.plugin_created_date, 'NULL') -or coalesce(pp.z_effective_date, 'NULL') != coalesce(bip.plugin_effective_date, 'NULL') -or coalesce(pp.z_status, 'NULL') != coalesce(bip.plugin_status, 'NULL') -or coalesce(pp.z_gateway_error, 'NULL') != coalesce(bip.plugin_gateway_error, 'NULL') -or coalesce(pp.z_gateway_error_code, 'NULL') != coalesce(bip.plugin_gateway_error_code, 'NULL') -or coalesce(pp.z_reference_id, 'NULL') != coalesce(bip.plugin_first_reference_id, 'NULL') -or coalesce(pp.z_snd_reference_id, 'NULL') != coalesce(bip.plugin_second_reference_id, 'NULL')) and z_status != 'Error' -; - -select 'G7i' as sanity_query_name; -select * -from analytics_payments bip -left outer join _zuora_payment_methods ppm on bip.plugin_pm_id = ppm.z_pm_id -where (coalesce(ppm.z_pm_id, 'NULL') != coalesce(bip.plugin_pm_id, 'NULL') -or coalesce(ppm.z_default, 'NULL') != coalesce(bip.plugin_pm_is_default, 'NULL')) and ppm.z_pm_id is not null -- workaround until we get plugin name, query will miss missing rows -; \ No newline at end of file diff --git a/src/main/resources/sanity/sanity.sql b/src/main/resources/sanity/sanity.sql deleted file mode 100644 index 940e8c16..00000000 --- a/src/main/resources/sanity/sanity.sql +++ /dev/null @@ -1,2045 +0,0 @@ --- ACCOUNTS - -select 'A1a' as sanity_query_name; -select distinct a.record_id -from accounts a -left outer join analytics_accounts bac on a.id = bac.account_id -where a.record_id != bac.account_record_id -or (coalesce(a.id, '') != coalesce(bac.account_id, '')) -or a.external_key != bac.account_external_key -or (coalesce(a.email, '') != coalesce(bac.email, '')) -or (coalesce(a.name, '') != coalesce(bac.account_name, '')) -or (coalesce(a.first_name_length, -1) != coalesce(bac.first_name_length, -1)) -or (coalesce(a.currency, '') != coalesce(bac.currency, '')) -or (coalesce(a.billing_cycle_day_local, -1) != coalesce(bac.billing_cycle_day_local, -1)) -or (coalesce(a.payment_method_id, '') != coalesce(bac.payment_method_id, '')) -or (coalesce(a.time_zone, '') != coalesce(bac.time_zone, '')) -or (coalesce(a.locale, '') != coalesce(bac.locale, '')) -or (coalesce(a.address1, '') != coalesce(bac.address1, '')) -or (coalesce(a.address2, '') != coalesce(bac.address2, '')) -or (coalesce(a.company_name, '') != coalesce(bac.company_name, '')) -or (coalesce(a.city, '') != coalesce(bac.city, '')) -or (coalesce(a.state_or_province, '') != coalesce(bac.state_or_province, '')) -or (coalesce(a.country, '') != coalesce(bac.country, '')) -or (coalesce(a.postal_code, '') != coalesce(bac.postal_code, '')) -or (coalesce(a.phone, '') != coalesce(bac.phone, '')) -or (coalesce(a.migrated, false) != coalesce(bac.migrated, false)) -or a.created_date != bac.created_date -or a.updated_date != bac.updated_date -or a.tenant_record_id != bac.tenant_record_id -; - -select 'A1b' as sanity_query_name; -select distinct bac.account_record_id -from analytics_accounts bac -left outer join accounts a on a.id = bac.account_id -where a.record_id != bac.account_record_id -or (coalesce(a.id, '') != coalesce(bac.account_id, '')) -or a.external_key != bac.account_external_key -or (coalesce(a.email, '') != coalesce(bac.email, '')) -or (coalesce(a.name, '') != coalesce(bac.account_name, '')) -or (coalesce(a.first_name_length, -1) != coalesce(bac.first_name_length, -1)) -or (coalesce(a.currency, '') != coalesce(bac.currency, '')) -or (coalesce(a.billing_cycle_day_local, -1) != coalesce(bac.billing_cycle_day_local, -1)) -or (coalesce(a.payment_method_id, '') != coalesce(bac.payment_method_id, '')) -or (coalesce(a.time_zone, '') != coalesce(bac.time_zone, '')) -or (coalesce(a.locale, '') != coalesce(bac.locale, '')) -or (coalesce(a.address1, '') != coalesce(bac.address1, '')) -or (coalesce(a.address2, '') != coalesce(bac.address2, '')) -or (coalesce(a.company_name, '') != coalesce(bac.company_name, '')) -or (coalesce(a.city, '') != coalesce(bac.city, '')) -or (coalesce(a.state_or_province, '') != coalesce(bac.state_or_province, '')) -or (coalesce(a.country, '') != coalesce(bac.country, '')) -or (coalesce(a.postal_code, '') != coalesce(bac.postal_code, '')) -or (coalesce(a.phone, '') != coalesce(bac.phone, '')) -or (coalesce(a.migrated, false) != coalesce(bac.migrated, false)) -or (coalesce(a.created_date, cast('1970-01-01' as date)) != coalesce(bac.created_date, cast('1970-01-01' as date))) -or (coalesce(a.updated_date, cast('1970-01-01' as date)) != coalesce(bac.updated_date, cast('1970-01-01' as date))) -or (coalesce(a.tenant_record_id, -1) != coalesce(bac.tenant_record_id, -1)) -; - -select 'A2' as sanity_query_name; -select distinct b.account_record_id -from analytics_accounts b -join account_history ah on b.account_record_id = ah.target_record_id -join audit_log al on ah.record_id = al.target_record_id and al.change_type = 'INSERT' and al.table_name = 'ACCOUNT_HISTORY' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- ACCOUNT FIELDS - -select 'K1a' as sanity_query_name; -select distinct cf.account_record_id -from custom_fields cf -left outer join analytics_account_fields b on cf.record_id = b.custom_field_record_id and cf.object_id = b.account_id /* To use the index */ -where 1 = 1 -and ( - coalesce(b.name, 'NULL') != coalesce(cf.field_name, 'NULL') - or coalesce(b.value, 'NULL') != coalesce(cf.field_value, 'NULL') - or coalesce(b.created_date, cast('1970-01-01' as date)) != coalesce(cf.created_date, cast('1970-01-01' as date)) - or coalesce(b.account_record_id, -1) != coalesce(cf.account_record_id, -1) - or coalesce(b.tenant_record_id, -1) != coalesce(cf.tenant_record_id, -1) -) -and cf.object_type = 'ACCOUNT' -; - -select 'K1b' as sanity_query_name; -select distinct b.account_record_id -from analytics_account_fields b -left outer join custom_fields cf on cf.record_id = b.custom_field_record_id and cf.object_id = b.account_id /* To use the index */ -where 1 = 1 -and ( - coalesce(b.name, 'NULL') != coalesce(cf.field_name, 'NULL') - or coalesce(b.value, 'NULL') != coalesce(cf.field_value, 'NULL') - or coalesce(b.created_date, cast('1970-01-01' as date)) != coalesce(cf.created_date, cast('1970-01-01' as date)) - or coalesce(b.account_record_id, -1) != coalesce(cf.account_record_id, -1) - or coalesce(b.tenant_record_id, -1) != coalesce(cf.tenant_record_id, -1) - or cf.object_type != 'ACCOUNT' -) -; - -select 'K2' as sanity_query_name; -select distinct b.account_record_id -from analytics_account_fields b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.id, '') != coalesce(b.account_id, '') -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'K3' as sanity_query_name; -select distinct b.account_record_id -from analytics_account_fields b -join custom_field_history cfh on b.custom_field_record_id = cfh.target_record_id -join audit_log al on cfh.record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'CUSTOM_FIELD_HISTORY' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- ACCOUNT TAGS - -select 'L1a' as sanity_query_name; -select distinct t.account_record_id -from tags t -join tag_definitions td on t.tag_definition_id = td.id -left outer join analytics_account_tags b on t.record_id = b.tag_record_id and t.object_id = b.account_id /* To use the index */ -where 1 = 1 -and ( - coalesce(b.tag_record_id, -1) != coalesce(t.record_id, -1) - or coalesce(b.name, 'NULL') != coalesce(td.name, 'NULL') - or coalesce(b.created_date, cast('1970-01-01' as date)) != coalesce(t.created_date, cast('1970-01-01' as date)) - or coalesce(b.account_record_id, -1) != coalesce(t.account_record_id, -1) - or coalesce(b.tenant_record_id, -1) != coalesce(t.tenant_record_id, -1) -) -and t.object_type = 'ACCOUNT' -; - -select 'L1b' as sanity_query_name; -select distinct b.account_record_id -from analytics_account_tags b -left outer join tags t on t.record_id = b.tag_record_id -left outer join tag_definitions td on t.tag_definition_id = td.id -where (coalesce(b.tag_record_id, -1) != coalesce(t.record_id, -1) -or coalesce(b.name, 'NULL') != coalesce(td.name, 'NULL') -or coalesce(b.created_date, cast('1970-01-01' as date)) != coalesce(t.created_date, cast('1970-01-01' as date)) -or coalesce(b.account_record_id, -1) != coalesce(t.account_record_id, -1) -or coalesce(b.tenant_record_id, -1) != coalesce(t.tenant_record_id, -1)) -and t.object_type = 'ACCOUNT' --- Ignore system tags -and t.tag_definition_id not in ('00000000-0000-0000-0000-000000000001', - '00000000-0000-0000-0000-000000000002', - '00000000-0000-0000-0000-000000000003', - '00000000-0000-0000-0000-000000000004', - '00000000-0000-0000-0000-000000000005', - '00000000-0000-0000-0000-000000000006', - '00000000-0000-0000-0000-000000000007') -; - -select 'L2' as sanity_query_name; -select distinct b.account_record_id -from analytics_account_tags b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.id, '') != coalesce(b.account_id, '') -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'L3' as sanity_query_name; -select distinct b.account_record_id -from analytics_account_tags b -join tag_history th on b.tag_record_id = th.target_record_id -join audit_log al on th.record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'TAG_HISTORY' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- INVOICE ADJUSTMENTS - -select 'B1a' as sanity_query_name; --- this will find things it thinks should be in bia but it's correct that they're not there -select distinct b.account_record_id -from invoice_items ii -left outer join analytics_invoice_adjustments b on ii.id = b.item_id -where ii.type in ('CREDIT_ADJ','REFUND_ADJ') -and (coalesce(ii.record_id, -1) != coalesce(b.invoice_item_record_id, -1) -or (coalesce(ii.id, '') != coalesce(b.item_id, '')) -or (coalesce(ii.type, '') != coalesce(b.item_type, '')) -or (coalesce(ii.invoice_id, '') != coalesce(b.invoice_id, '')) -or (coalesce(ii.account_id, '')!= coalesce(b.account_id, '')) -or (coalesce(ii.phase_name, '') != coalesce(b.slug, '')) -or (coalesce(ii.start_date, cast('1970-01-01' as date)) != coalesce(b.start_date, cast('1970-01-01' as date))) -or (coalesce(ii.amount, -1) != coalesce(b.amount, -1)) -or (coalesce(ii.currency, '') != coalesce(b.currency, '')) -or (coalesce(ii.linked_item_id, '') != coalesce(b.linked_item_id, '')) -or (coalesce(ii.created_date, cast('1970-01-01' as date)) != coalesce(b.created_date, cast('1970-01-01' as date))) -or (coalesce(ii.account_record_id, -1) != coalesce(b.account_record_id, -1)) -or (coalesce(ii.tenant_record_id, -1) != coalesce(b.tenant_record_id, -1))) -; - -select 'B1b' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_adjustments b -left outer join invoice_items ii on ii.id = b.item_id -where (coalesce(ii.record_id, -1) != coalesce(b.invoice_item_record_id, -1)) -or (coalesce(ii.id, '') != coalesce(b.item_id, '')) -or (coalesce(ii.type, '') != coalesce(b.item_type, '')) -or (coalesce(ii.invoice_id, '') != coalesce(b.invoice_id, '')) -or (coalesce(ii.account_id, '')!= coalesce(b.account_id, '')) -or (coalesce(ii.phase_name, '') != coalesce(b.slug, '')) -or (coalesce(ii.start_date, cast('1970-01-01' as date)) != coalesce(b.start_date, cast('1970-01-01' as date))) -or (coalesce(ii.amount, -1) != coalesce(b.amount, -1)) -or (coalesce(ii.currency, '') != coalesce(b.currency, '')) -or (coalesce(ii.linked_item_id, '') != coalesce(b.linked_item_id, '')) -or (coalesce(ii.created_date, cast('1970-01-01' as date)) != coalesce(b.created_date, cast('1970-01-01' as date))) -or (coalesce(ii.account_record_id, -1) != coalesce(b.account_record_id, -1)) -or (coalesce(ii.tenant_record_id, -1) != coalesce(b.tenant_record_id, -1)) -or ii.type not in ('CREDIT_ADJ','REFUND_ADJ') -; - -select 'B2' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_adjustments b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.id, '') != coalesce(b.account_id, '') -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'B3' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_adjustments b -left outer join invoices i on i.id = b.invoice_id -where coalesce(i.record_id, -1) != coalesce(b.invoice_number, -1) -or coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce(b.invoice_created_date, cast('1970-01-01' as date)) -or coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(b.invoice_date, cast('1970-01-01' as date)) -or coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(b.invoice_target_date, cast('1970-01-01' as date)) -or coalesce(i.currency, 'NULL') != coalesce(b.invoice_currency, 'NULL') -; - -select 'B4' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_adjustments b -left outer join bundles bndl on b.bundle_id = bndl.id -where coalesce(bndl.external_key, 'NULL') != coalesce(b.bundle_external_key, 'NULL') -; - -select 'B5' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_adjustments b -left outer join analytics_invoices bin on b.invoice_id = bin.invoice_id -where b.invoice_balance != bin.balance -or b.invoice_amount_paid != bin.amount_paid -or b.invoice_amount_charged != bin.amount_charged -or b.invoice_original_amount_charged != bin.original_amount_charged -or b.invoice_amount_credited != bin.amount_credited -; - -select 'B6' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_adjustments b -join audit_log al on b.invoice_item_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'INVOICE_ITEMS' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- INVOICE ITEMS - -select 'C1a' as sanity_query_name; -select distinct ii.account_record_id -from invoice_items ii -left outer join analytics_invoice_items bii on ii.id = bii.item_id -where ii.type in ('FIXED','RECURRING','EXTERNAL_CHARGE') -and (coalesce(ii.record_id, -1) != coalesce(bii.invoice_item_record_id, -1) -or (coalesce(ii.id, '') != coalesce(bii.item_id, '')) -or (coalesce(ii.type, '') != coalesce(bii.item_type, '')) -or (coalesce(ii.invoice_id, '') != coalesce(bii.invoice_id, '')) -or (coalesce(ii.account_id, '') != coalesce(bii.account_id, '')) -or (coalesce(ii.phase_name, '') != coalesce(bii.slug, '')) -or (coalesce(ii.start_date, cast('1970-01-01' as date)) != coalesce(bii.start_date, cast('1970-01-01' as date))) -or (coalesce(ii.amount, -1) != coalesce(bii.amount, -1)) -or (coalesce(ii.currency, '') != coalesce(bii.currency, '')) -or (coalesce(ii.linked_item_id, '') != coalesce(bii.linked_item_id, '')) -or (coalesce(ii.created_date, cast('1970-01-01' as date)) != coalesce(bii.created_date, cast('1970-01-01' as date))) -or (coalesce(ii.account_record_id, -1) != coalesce(bii.account_record_id, -1)) -or (coalesce(ii.tenant_record_id, -1) != coalesce(bii.tenant_record_id, -1))) -; - -select 'C1b' as sanity_query_name; -select distinct bii.account_record_id -from analytics_invoice_items bii -left outer join invoice_items ii on ii.id = bii.item_id -where (coalesce(ii.record_id, -1) != coalesce(bii.invoice_item_record_id, -1)) -or (coalesce(ii.id, '') != coalesce(bii.item_id, '')) -or (coalesce(ii.type, '') != coalesce(bii.item_type, '')) -or (coalesce(ii.invoice_id, '') != coalesce(bii.invoice_id, '')) -or (coalesce(ii.account_id, '')!= coalesce(bii.account_id, '')) -or (coalesce(ii.phase_name, '') != coalesce(bii.slug, '')) -or (coalesce(ii.start_date, cast('1970-01-01' as date)) != coalesce(bii.start_date, cast('1970-01-01' as date))) -or (coalesce(ii.amount, -1) != coalesce(bii.amount, -1)) -or (coalesce(ii.currency, '') != coalesce(bii.currency, '')) -or (coalesce(ii.linked_item_id, '') != coalesce(bii.linked_item_id, '')) -or (coalesce(ii.created_date, cast('1970-01-01' as date)) != coalesce(bii.created_date, cast('1970-01-01' as date))) -or (coalesce(ii.account_record_id, -1) != coalesce(bii.account_record_id, -1)) -or (coalesce(ii.tenant_record_id, -1) != coalesce(bii.tenant_record_id, -1)) -or ii.type not in ('FIXED','RECURRING','EXTERNAL_CHARGE') -; - -select 'C2' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_items b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.id, '') != coalesce(b.account_id, '') -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'C3' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_items b -left outer join invoices i on i.id = b.invoice_id -where coalesce(i.record_id, -1) != coalesce(b.invoice_number, -1) -or coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce(b.invoice_created_date, cast('1970-01-01' as date)) -or coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(b.invoice_date, cast('1970-01-01' as date)) -or coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(b.invoice_target_date, cast('1970-01-01' as date)) -or coalesce(i.currency, 'NULL') != coalesce(b.invoice_currency, 'NULL') -; - -select 'C4' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_items b -left outer join bundles bndl on b.bundle_id = bndl.id -where coalesce(bndl.external_key, 'NULL') != coalesce(b.bundle_external_key, 'NULL') -; - -select 'C5' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_items b -left outer join analytics_invoices bin on b.invoice_id = bin.invoice_id -where b.invoice_balance != bin.balance -or b.invoice_amount_paid != bin.amount_paid -or b.invoice_amount_charged != bin.amount_charged -or b.invoice_original_amount_charged != bin.original_amount_charged -or b.invoice_amount_credited != bin.amount_credited -; - -select 'C6' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_items b -join audit_log al on b.invoice_item_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'INVOICE_ITEMS' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- INVOICE ITEM ADJUSTMENTS - -select 'D1a' as sanity_query_name; -select distinct ii.account_record_id -from invoice_items ii -left outer join analytics_invoice_item_adjustments b on ii.id = b.item_id -where ii.type in ('ITEM_ADJ', 'REPAIR_ADJ') -and (coalesce(ii.record_id, -1) != coalesce(b.invoice_item_record_id, -1) -or (coalesce(ii.id, '') != coalesce(b.item_id, '')) -or (coalesce(ii.type, '') != coalesce(b.item_type, '')) -or (coalesce(ii.invoice_id, '') != coalesce(b.invoice_id, '')) -or (coalesce(ii.account_id, '')!= coalesce(b.account_id, '')) -or ((coalesce(ii.phase_name, '') != coalesce(b.slug,'')) and ii.phase_name is not null) -or (coalesce(ii.start_date, cast('1970-01-01' as date)) != coalesce(b.start_date, cast('1970-01-01' as date))) -or (coalesce(ii.amount, -1) != coalesce(b.amount, -1)) -or (coalesce(ii.currency, '') != coalesce(b.currency, '')) -or (coalesce(ii.linked_item_id, '') != coalesce(b.linked_item_id, '')) -or (coalesce(ii.created_date, cast('1970-01-01' as date)) != coalesce(b.created_date, cast('1970-01-01' as date))) -or (coalesce(ii.account_record_id, -1) != coalesce(b.account_record_id, -1)) -or (coalesce(ii.tenant_record_id, -1) != coalesce(b.tenant_record_id, -1))) -; - -select 'D1b' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_item_adjustments b -left outer join invoice_items ii on ii.id = b.item_id -where coalesce(ii.record_id, -1) != coalesce(b.invoice_item_record_id, -1) -or (coalesce(ii.id, '') != coalesce(b.item_id, '')) -or (coalesce(ii.type, '') != coalesce(b.item_type, '')) -or (coalesce(ii.invoice_id, '') != coalesce(b.invoice_id, '')) -or (coalesce(ii.account_id, '')!= coalesce(b.account_id, '')) -/* The code is smart and will populate NULL columns from the linked item id */ -or (ii.phase_name is not null and ii.phase_name != b.slug) -or (coalesce(ii.start_date, cast('1970-01-01' as date)) != coalesce(b.start_date, cast('1970-01-01' as date))) -or ( (coalesce(ii.amount, -1) != coalesce(b.amount, -1)) and ii.type != 'REPAIR_ADJ' ) -- need to calc correct amount in case of REPAIR_ADJ case -or (coalesce(ii.currency, '') != coalesce(b.currency, '')) -or (coalesce(ii.linked_item_id, '') != coalesce(b.linked_item_id, '')) -or (coalesce(ii.created_date, cast('1970-01-01' as date)) != coalesce(b.created_date, cast('1970-01-01' as date))) -or (coalesce(ii.account_record_id, -1) != coalesce(b.account_record_id, -1)) -or (coalesce(ii.tenant_record_id, -1) != coalesce(b.tenant_record_id, -1)) -or ii.type not in ('ITEM_ADJ','REPAIR_ADJ') -; - -select 'D2' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_item_adjustments b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.id, '') != coalesce(b.account_id, '') -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'D3' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_item_adjustments b -left outer join invoices i on i.id = b.invoice_id -where coalesce(i.record_id, -1) != coalesce(b.invoice_number, -1) -or coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce(b.invoice_created_date, cast('1970-01-01' as date)) -or coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(b.invoice_date, cast('1970-01-01' as date)) -or coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(b.invoice_target_date, cast('1970-01-01' as date)) -or coalesce(i.currency, 'NULL') != coalesce(b.invoice_currency, 'NULL') -; - -select 'D4' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_item_adjustments b -left outer join bundles bndl on b.bundle_id = bndl.id -where coalesce(bndl.external_key, 'NULL') != coalesce(b.bundle_external_key, 'NULL') -and b.bundle_id is not null -; - -select 'D5' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_item_adjustments b -left outer join analytics_invoices bin on b.invoice_id = bin.invoice_id -where b.invoice_balance != bin.balance -or b.invoice_amount_paid != bin.amount_paid -or b.invoice_amount_charged != bin.amount_charged -or b.invoice_original_amount_charged != bin.original_amount_charged -or b.invoice_amount_credited != bin.amount_credited -; - -select 'D6' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_item_adjustments b -join audit_log al on b.invoice_item_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'INVOICE_ITEMS' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- INVOICE CREDITS - -select 'E1a' as sanity_query_name; -select distinct ii.account_record_id -from invoice_items ii -left outer join analytics_invoice_credits b on ii.id = b.item_id -where ii.type in ('CBA_ADJ') -and (coalesce(ii.record_id, -1) != coalesce(b.invoice_item_record_id, -1) -or (coalesce(ii.id, '') != coalesce(b.item_id, '')) -or (coalesce(ii.type, '') != coalesce(b.item_type, '')) -or (coalesce(ii.invoice_id, '') != coalesce(b.invoice_id, '')) -or (coalesce(ii.account_id, '')!= coalesce(b.account_id, '')) -or (coalesce(ii.phase_name, '') != coalesce(b.slug, '')) -or (coalesce(ii.start_date, cast('1970-01-01' as date)) != coalesce(b.start_date, cast('1970-01-01' as date))) -or (coalesce(ii.amount, -1) != coalesce(b.amount, -1)) -or (coalesce(ii.currency, '') != coalesce(b.currency, '')) -or (coalesce(ii.linked_item_id, '') != coalesce(b.linked_item_id, '')) -or (coalesce(ii.created_date, cast('1970-01-01' as date)) != coalesce(b.created_date, cast('1970-01-01' as date))) -or (coalesce(ii.account_record_id, -1) != coalesce(b.account_record_id, -1)) -or (coalesce(ii.tenant_record_id, -1) != coalesce(b.tenant_record_id, -1))) -; - -select 'E1b' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_credits b -left outer join invoice_items ii on ii.id = b.item_id -where (coalesce(ii.record_id, -1) != coalesce(b.invoice_item_record_id, -1)) -or (coalesce(ii.id, '') != coalesce(b.item_id, '')) -or (coalesce(ii.type, '') != coalesce(b.item_type, '')) -or (coalesce(ii.invoice_id, '') != coalesce(b.invoice_id, '')) -or (coalesce(ii.account_id, '')!= coalesce(b.account_id, '')) -or (coalesce(ii.phase_name, '') != coalesce(b.slug, '')) -or (coalesce(ii.start_date, cast('1970-01-01' as date)) != coalesce(b.start_date, cast('1970-01-01' as date))) -or (coalesce(ii.amount, -1) != coalesce(b.amount, -1)) -or (coalesce(ii.currency, '') != coalesce(b.currency, '')) -or (coalesce(ii.linked_item_id, '') != coalesce(b.linked_item_id, '')) -or (coalesce(ii.created_date, cast('1970-01-01' as date)) != coalesce(b.created_date, cast('1970-01-01' as date))) -or (coalesce(ii.account_record_id, -1) != coalesce(b.account_record_id, -1)) -or (coalesce(ii.tenant_record_id, -1) != coalesce(b.tenant_record_id, -1)) -or ii.type not in ('CBA_ADJ') -; - -select 'E2' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_credits b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.id, '') != coalesce(b.account_id, '') -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'E3' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_credits b -left outer join invoices i on i.id = b.invoice_id -where coalesce(i.record_id, -1) != coalesce(b.invoice_number, -1) -or coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce(b.invoice_created_date, cast('1970-01-01' as date)) -or coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(b.invoice_date, cast('1970-01-01' as date)) -or coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(b.invoice_target_date, cast('1970-01-01' as date)) -or coalesce(i.currency, 'NULL') != coalesce(b.invoice_currency, 'NULL') -; - -select 'E4' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_credits b -left outer join bundles bndl on b.bundle_id = bndl.id -where coalesce(bndl.external_key, 'NULL') != coalesce(b.bundle_external_key, 'NULL') -; - -select 'E5' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_credits b -left outer join analytics_invoices bin on b.invoice_id = bin.invoice_id -where b.invoice_balance != bin.balance -or b.invoice_amount_paid != bin.amount_paid -or b.invoice_amount_charged != bin.amount_charged -or b.invoice_original_amount_charged != bin.original_amount_charged -or b.invoice_amount_credited != bin.amount_credited -; - -select 'E6' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoice_credits b -join audit_log al on b.invoice_item_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'INVOICE_ITEMS' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- INVOICE FIELDS -/* table not currently used */ - - --- INVOICE TAGS -/* table not currently used */ - - --- INVOICES - -select 'F1a' as sanity_query_name; -select distinct i.account_record_id -from invoices i -left outer join analytics_invoices bin on i.id = bin.invoice_id -where coalesce(i.record_id, -1) != coalesce(bin.invoice_record_id, -1) -or coalesce(i.record_id, -1) != coalesce(bin.invoice_number, -1) -or coalesce(i.id, '') != coalesce(bin.invoice_id, '') -or (coalesce(i.account_id, '') != coalesce(bin.account_id, '')) -or (coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(bin.invoice_date, cast('1970-01-01' as date))) -or (coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(bin.target_date, cast('1970-01-01' as date))) -or (coalesce(i.currency, '') != coalesce(bin.currency, '')) -or (coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce( bin.created_date, cast('1970-01-01' as date))) -or (coalesce(i.account_record_id, -1) != coalesce(bin.account_record_id, -1)) -or (coalesce(i.tenant_record_id, -1) != coalesce(bin.tenant_record_id, -1)) -; - -select 'F1b' as sanity_query_name; -select distinct bin.account_record_id -from analytics_invoices bin -left outer join invoices i on i.id = bin.invoice_id -where (coalesce(i.record_id, -1) != coalesce(bin.invoice_record_id, -1)) -or (coalesce(i.id, '') != coalesce(bin.invoice_id, '')) -or (coalesce(i.account_id, '') != coalesce(bin.account_id, '')) -or (coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(bin.invoice_date, cast('1970-01-01' as date))) -or (coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(bin.target_date, cast('1970-01-01' as date))) -or (coalesce(i.currency, '') != coalesce(bin.currency, '')) -or (coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce(bin.created_date, cast('1970-01-01' as date))) -or (coalesce(i.account_record_id, -1) != coalesce(bin.account_record_id, -1)) -; - -select 'F2' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoices b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.id, '') != coalesce(b.account_id, '') -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'F3a' as sanity_query_name; -select * -from ( - select - invoice_id - , invoice_amount_charged - , sum(coalesce(amount,0)) bii_sum - from analytics_invoice_items - group by invoice_id, invoice_amount_charged, invoice_original_amount_charged -) bii_sum -left outer join ( - select - invoice_id - , sum(coalesce(amount,0)) bia_sum - from analytics_invoice_adjustments - group by invoice_id -) bia_sum using (invoice_id) -left outer join ( - select - invoice_id - , sum(coalesce(amount,0)) biia_sum - from analytics_invoice_item_adjustments - group by invoice_id -) biia_sum using (invoice_id) -where bii_sum + coalesce(bia_sum,0) + coalesce(biia_sum,0) != bii_sum.invoice_amount_charged -; - -select 'F3b' as sanity_query_name; -select - bin.invoice_id -, bin.original_amount_charged -, sum(bii.amount) -from analytics_invoice_items bii -join analytics_invoices bin on bii.invoice_id = bin.invoice_id and bii.created_date = bin.created_date -group by bin.invoice_id, bin.original_amount_charged -having sum(bii.amount) != bin.original_amount_charged -; - -select 'F3c' as sanity_query_name; -select * -from ( - select - invoice_id - , invoice_amount_credited - , sum(coalesce(amount,0)) biic_sum - from analytics_invoice_credits biic - group by invoice_id,invoice_amount_credited -) biic_sum -where biic_sum != biic_sum.invoice_amount_credited -; - -select 'F3d' as sanity_query_name; -select - * -, bip_sum invoice_amount_paid -from ( - select - invoice_id - , invoice_amount_paid - , sum(coalesce(amount,0)) bip_sum - from analytics_payment_purchases bip - group by invoice_id, invoice_amount_paid -) bip_sum -where bip_sum != bip_sum.invoice_amount_paid -; - -select 'F3e' as sanity_query_name; -select * -from ( - select - invoice_id - , balance - , amount_charged - , amount_credited - , amount_paid - , amount_refunded - , original_amount_charged - from analytics_invoices -) bin -left outer join ( - select - invoice_id - , sum(coalesce(amount,0)) bipc_sum - from analytics_payment_chargebacks bipc - group by invoice_id -) bipc_sum using (invoice_id) -left outer join ( - select - invoice_id - , sum(coalesce(amount,0)) bipr_sum - from analytics_payment_refunds bipr - group by invoice_id -) bipr_sum using (invoice_id) -where bipc_sum + bipr_sum != bin.amount_refunded -; - -select 'F3f' as sanity_query_name; -select distinct bin.account_record_id -from analytics_invoices bin -where bin.amount_charged + bin.amount_credited - bin.amount_paid - bin.amount_refunded != bin.balance -and (bin.amount_charged != 0 and bin.amount_paid != 0 and bin.amount_refunded != 0 and bin.balance != 0) -- deal w / acct credit -; - - -select 'F6' as sanity_query_name; -select distinct b.account_record_id -from analytics_invoices b -join audit_log al on b.invoice_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'INVOICES' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- PAYMENTS - -select 'G1a' as sanity_query_name; -select distinct ip.account_record_id -from invoice_payments ip -left outer join analytics_payment_purchases bip on ip.id = bip.invoice_payment_id -where (coalesce(ip.record_id, -1) != coalesce(bip.invoice_payment_record_id, -1) -or coalesce(ip.ID, 'NULL') != coalesce(bip.invoice_payment_id, 'NULL') -or coalesce(ip.invoice_id, 'NULL') != coalesce(bip.invoice_id, 'NULL') -or coalesce(ip.type, 'NULL') != coalesce(bip.invoice_payment_type, 'NULL') -or coalesce(ip.linked_invoice_payment_id, 'NULL') != coalesce(bip.linked_invoice_payment_id, 'NULL') -or coalesce(ip.amount, -1) != coalesce(bip.amount, -1) -or coalesce(ip.currency, 'NULL') != coalesce(bip.currency, 'NULL') -or coalesce(ip.created_date, cast('1970-01-01' as date)) != coalesce(bip.created_date, cast('1970-01-01' as date)) -or coalesce(ip.account_record_id, -1) != coalesce(bip.account_record_id, -1) -or coalesce(ip.tenant_record_id, -1) != coalesce(bip.tenant_record_id, -1)) -and ip.type = 'ATTEMPT' -; - -select 'G1b' as sanity_query_name; -select distinct bip.account_record_id -from analytics_payment_purchases bip -left outer join invoice_payments ip on ip.id = bip.invoice_payment_id -where (coalesce(ip.record_id, -1) != coalesce(bip.invoice_payment_record_id, -1) -or coalesce(ip.ID, 'NULL') != coalesce(bip.invoice_payment_id, 'NULL') -or coalesce(ip.invoice_id, 'NULL') != coalesce(bip.invoice_id, 'NULL') -or coalesce(ip.type, 'NULL') != coalesce(bip.invoice_payment_type, 'NULL') -or coalesce(ip.linked_invoice_payment_id, 'NULL') != coalesce(bip.linked_invoice_payment_id, 'NULL') -or coalesce(ip.amount, -1) != coalesce(bip.amount, -1) -or coalesce(ip.currency, 'NULL') != coalesce(bip.currency, 'NULL') -or coalesce(ip.created_date, cast('1970-01-01' as date)) != coalesce(bip.created_date, cast('1970-01-01' as date)) -or coalesce(ip.account_record_id, -1) != coalesce(bip.account_record_id, -1) -or coalesce(ip.tenant_record_id, -1) != coalesce(bip.tenant_record_id, -1) -or bip.invoice_payment_type != 'ATTEMPT') -and bip.invoice_payment_record_id !=0 -; - -select 'G2' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_purchases b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'G3' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_purchases b -left outer join invoices i on i.id = b.invoice_id -where coalesce(i.record_id, -1) != coalesce(b.invoice_number, -1) -or coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce(b.invoice_created_date, cast('1970-01-01' as date)) -or coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(b.invoice_date, cast('1970-01-01' as date)) -or coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(b.invoice_target_date, cast('1970-01-01' as date)) -or coalesce(i.currency, 'NULL') != coalesce(b.invoice_currency, 'NULL') -; - -select 'G4' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_purchases b -left outer join analytics_invoices bin on b.invoice_id = bin.invoice_id -where b.invoice_balance != bin.balance -or b.invoice_amount_paid != bin.amount_paid -or b.invoice_amount_charged != bin.amount_charged -or b.invoice_original_amount_charged != bin.original_amount_charged -or b.invoice_amount_credited != bin.amount_credited -; - -select 'G5' as sanity_query_name; -select distinct bip.account_record_id -from analytics_payment_purchases bip -left outer join invoice_payments ip on bip.invoice_payment_id = ip.id -left outer join payments p on ip.payment_id = p.id -where coalesce(p.record_id, -1) != coalesce(bip.payment_number, -1) -and bip.invoice_payment_record_id!=0 -; - -select 'G8' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_purchases b -join audit_log al on b.invoice_payment_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'INVOICE_PAYMENTS' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- PAYMENT FIELDS -/* table not currently used */ - - --- PAYMENT TAGS -/* table not currently used */ - - --- CHARGEBACKS - -select 'H1a' as sanity_query_name; -select distinct ip.account_record_id -from invoice_payments ip -left outer join analytics_payment_chargebacks bipc on ip.id = bipc.invoice_payment_id -where (coalesce(ip.record_id, -1) != coalesce(bipc.invoice_payment_record_id, -1) -or coalesce(ip.ID, 'NULL') != coalesce(bipc.invoice_payment_id, 'NULL') -or coalesce(ip.invoice_id, 'NULL') != coalesce(bipc.invoice_id, 'NULL') -or coalesce(ip.type, 'NULL') != coalesce(bipc.invoice_payment_type, 'NULL') -or coalesce(ip.linked_invoice_payment_id, 'NULL') != coalesce(bipc.linked_invoice_payment_id, 'NULL') -or coalesce(ip.amount, -1) != coalesce(bipc.amount, -1) -or coalesce(ip.currency, 'NULL') != coalesce(bipc.currency, 'NULL') -or coalesce(ip.created_date, cast('1970-01-01' as date)) != coalesce(bipc.created_date, cast('1970-01-01' as date)) -or coalesce(ip.account_record_id, -1) != coalesce(bipc.account_record_id, -1) -or coalesce(ip.tenant_record_id, -1) != coalesce(bipc.tenant_record_id, -1)) -and ip.type = 'CHARGED_BACK' -; - -select 'H1b' as sanity_query_name; -select distinct bipc.account_record_id -from analytics_payment_chargebacks bipc -left outer join invoice_payments ip on ip.id = bipc.invoice_payment_id -where (coalesce(ip.record_id, -1) != coalesce(bipc.invoice_payment_record_id, -1) -or coalesce(ip.ID, 'NULL') != coalesce(bipc.invoice_payment_id, 'NULL') -or coalesce(ip.invoice_id, 'NULL') != coalesce(bipc.invoice_id, 'NULL') -or coalesce(ip.type, 'NULL') != coalesce(bipc.invoice_payment_type, 'NULL') -or coalesce(ip.linked_invoice_payment_id, 'NULL') != coalesce(bipc.linked_invoice_payment_id, 'NULL') -or coalesce(ip.amount, -1) != coalesce(bipc.amount, -1) -or coalesce(ip.currency, 'NULL') != coalesce(bipc.currency, 'NULL') -or coalesce(ip.created_date, cast('1970-01-01' as date)) != coalesce(bipc.created_date, cast('1970-01-01' as date)) -or coalesce(ip.account_record_id, -1) != coalesce(bipc.account_record_id, -1) -or coalesce(ip.tenant_record_id, -1) != coalesce(bipc.tenant_record_id, -1) -or bipc.invoice_payment_type != 'CHARGED_BACK') -and bipc.invoice_payment_record_id!=0 -; - -select 'H2' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_chargebacks b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'H3' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_chargebacks b -left outer join invoices i on i.id = b.invoice_id -where coalesce(i.record_id, -1) != coalesce(b.invoice_number, -1) -or coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce(b.invoice_created_date, cast('1970-01-01' as date)) -or coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(b.invoice_date, cast('1970-01-01' as date)) -or coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(b.invoice_target_date, cast('1970-01-01' as date)) -or coalesce(i.currency, 'NULL') != coalesce(b.invoice_currency, 'NULL') -; - -select 'H4' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_chargebacks b -left outer join analytics_invoices bin on b.invoice_id = bin.invoice_id -where b.invoice_balance != bin.balance -or b.invoice_amount_paid != bin.amount_paid -or b.invoice_amount_charged != bin.amount_charged -or b.invoice_original_amount_charged != bin.original_amount_charged -or b.invoice_amount_credited != bin.amount_credited -; - -select 'H5' as sanity_query_name; -select distinct bipc.account_record_id -from analytics_payment_chargebacks bipc -left outer join invoice_payments ip on bipc.invoice_payment_id = ip.id -left outer join payments p on ip.payment_id = p.id -where coalesce(p.record_id, -1) != coalesce(bipc.payment_number, -1) -and bipc.invoice_payment_record_id!=0 -; - -select 'H8' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_chargebacks b -join audit_log al on b.invoice_payment_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'INVOICE_PAYMENTS' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- REFUNDS - -select 'H1a' as sanity_query_name; -select distinct ip.account_record_id -from invoice_payments ip -left outer join analytics_payment_refunds bipr on ip.id = bipr.invoice_payment_id -where (coalesce(ip.record_id, -1) != coalesce(bipr.invoice_payment_record_id, -1) -or coalesce(ip.ID, 'NULL') != coalesce(bipr.invoice_payment_id, 'NULL') -or coalesce(ip.invoice_id, 'NULL') != coalesce(bipr.invoice_id, 'NULL') -or coalesce(ip.type, 'NULL') != coalesce(bipr.invoice_payment_type, 'NULL') -or coalesce(ip.linked_invoice_payment_id, 'NULL') != coalesce(bipr.linked_invoice_payment_id, 'NULL') -or coalesce(ip.amount, -1) != coalesce(bipr.amount, -1) -or coalesce(ip.currency, 'NULL') != coalesce(bipr.currency, 'NULL') -or coalesce(ip.created_date, cast('1970-01-01' as date)) != coalesce(bipr.created_date, cast('1970-01-01' as date)) -or coalesce(ip.account_record_id, -1) != coalesce(bipr.account_record_id, -1) -or coalesce(ip.tenant_record_id, -1) != coalesce(bipr.tenant_record_id, -1)) -and ip.type = 'REFUND' -; - -select 'H1b' as sanity_query_name; -select distinct bipr.account_record_id -from analytics_payment_refunds bipr -left outer join invoice_payments ip on ip.id = bipr.invoice_payment_id -where (coalesce(ip.record_id, -1) != coalesce(bipr.invoice_payment_record_id, -1) -or coalesce(ip.id, 'NULL') != coalesce(bipr.invoice_payment_id, 'NULL') -or coalesce(ip.invoice_id, 'NULL') != coalesce(bipr.invoice_id, 'NULL') -or coalesce(ip.type, 'NULL') != coalesce(bipr.invoice_payment_type, 'NULL') -or coalesce(ip.linked_invoice_payment_id, 'NULL') != coalesce(bipr.linked_invoice_payment_id, 'NULL') -or coalesce(ip.amount, -1) != coalesce(bipr.amount, -1) -or coalesce(ip.currency, 'NULL') != coalesce(bipr.currency, 'NULL') -or coalesce(ip.created_date, cast('1970-01-01' as date)) != coalesce(bipr.created_date, cast('1970-01-01' as date)) -or coalesce(ip.account_record_id, -1) != coalesce(bipr.account_record_id, -1) -or coalesce(ip.tenant_record_id, -1) != coalesce(bipr.tenant_record_id, -1) -or bipr.invoice_payment_type != 'REFUND') -and bipr.invoice_payment_record_id!=0 -; - -select 'H2' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_refunds b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'H3' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_refunds b -left outer join invoices i on i.id = b.invoice_id -where coalesce(i.record_id, -1) != coalesce(b.invoice_number, -1) -or coalesce(i.created_date, cast('1970-01-01' as date)) != coalesce(b.invoice_created_date, cast('1970-01-01' as date)) -or coalesce(i.invoice_date, cast('1970-01-01' as date)) != coalesce(b.invoice_date, cast('1970-01-01' as date)) -or coalesce(i.target_date, cast('1970-01-01' as date)) != coalesce(b.invoice_target_date, cast('1970-01-01' as date)) -or coalesce(i.currency, 'NULL') != coalesce(b.invoice_currency, 'NULL') -; - -select 'H4' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_refunds b -left outer join analytics_invoices bin on b.invoice_id = bin.invoice_id -where b.invoice_balance != bin.balance -or b.invoice_amount_paid != bin.amount_paid -or b.invoice_amount_charged != bin.amount_charged -or b.invoice_original_amount_charged != bin.original_amount_charged -or b.invoice_amount_credited != bin.amount_credited -; - -select 'H5' as sanity_query_name; -select distinct bipr.account_record_id -from analytics_payment_refunds bipr -left outer join invoice_payments ip on bipr.invoice_payment_id = ip.id -left outer join payments p on ip.payment_id = p.id -where coalesce(p.record_id, -1) != coalesce(bipr.payment_number, -1) -and bipr.invoice_payment_record_id!=0 -; - -select 'H8' as sanity_query_name; -select distinct b.account_record_id -from analytics_payment_refunds b -join audit_log al on b.invoice_payment_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'INVOICE_PAYMENTS' -where coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '') -; - - --- ACCOUNT TRANSITIONS - -select 'I1a' as sanity_query_name; -select distinct bs.account_record_id -from blocking_states bs -join analytics_account_transitions bos on bs.record_id = bos.blocking_state_record_id -where 1 = 1 -and bs.is_active = true -and ( - coalesce(bs.record_id, -1) != coalesce(bos.blocking_state_record_id, -1) - or coalesce(bs.state, 'NULL') != coalesce(bos.state, 'NULL') - /* TODO SubscriptionEvent is not an entity, we don't have that info yet - or coalesce(bs.created_date, cast('1970-01-01' as date)) != coalesce(bos.created_date, cast('1970-01-01' as date)) */ - or ( - /* Tricky... Need to look at the account timezone */ - coalesce(date(bs.effective_date), cast('1970-01-01' as date)) != coalesce(date(bos.start_date), cast('1970-01-01' as date)) - and coalesce(date(bs.effective_date), cast('1970-01-01' as date)) != coalesce(date_add(date(bos.start_date), INTERVAL '1' DAY), cast('1970-01-01' as date)) - and coalesce(date(bs.effective_date), cast('1970-01-01' as date)) != coalesce(date_sub(date(bos.start_date), INTERVAL '1' DAY), cast('1970-01-01' as date)) - ) - or coalesce(bs.account_record_id, -1) != coalesce(bos.account_record_id, -1) - or coalesce(bs.tenant_record_id, -1) != coalesce(bos.tenant_record_id, -1) -) -; - -select 'I1b' as sanity_query_name; -select distinct bos.account_record_id -from analytics_account_transitions bos -join blocking_states bs on bs.record_id = bos.blocking_state_record_id -where 1 = 1 -and bs.is_active = true -and ( - coalesce(bs.record_id, -1) != coalesce(bos.blocking_state_record_id, -1) - or coalesce(bs.state, 'NULL') != coalesce(bos.state, 'NULL') - /* TODO SubscriptionEvent is not an entity, we don't have that info yet - or coalesce(bs.created_date, cast('1970-01-01' as date)) != coalesce(bos.created_date, cast('1970-01-01' as date)) */ - /* Tricky... Need to look at the account timezone */ - or (coalesce(date(bs.effective_date), cast('1970-01-01' as date)) != coalesce(date(bos.start_date), cast('1970-01-01' as date)) - and coalesce(date(bs.effective_date), cast('1970-01-01' as date)) != coalesce(date_add(date(bos.start_date), INTERVAL '1' DAY), cast('1970-01-01' as date)) - and coalesce(date(bs.effective_date), cast('1970-01-01' as date)) != coalesce(date_sub(date(bos.start_date), INTERVAL '1' DAY), cast('1970-01-01' as date))) - or coalesce(bs.account_record_id, -1) != coalesce(bos.account_record_id, -1) - or coalesce(bs.tenant_record_id, -1) != coalesce(bos.tenant_record_id, -1) -) -; - -select 'I2' as sanity_query_name; -select distinct b.account_record_id -from analytics_account_transitions b -left outer join accounts a on a.id = b.account_id -where 1 = 1 -and ( - coalesce(a.record_id) != coalesce(b.account_record_id, -1) - or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') - or coalesce(a.name, '') != coalesce(b.account_name, '') -) -; - -select 'I3' as sanity_query_name; -select distinct b.account_record_id -from analytics_account_transitions b -join audit_log al on b.blocking_state_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'BLOCKING_STATES' -where 1 = 1 -and ( - coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') - or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') - or coalesce(b.created_by, '') != coalesce(al.created_by, '') -) -; - - --- SUBSCRIPTION TRANSITIONS - -select 'J1' as sanity_query_name; -select distinct bst.account_record_id -from analytics_subscription_transitions bst -left outer join subscription_events se on bst.subscription_event_record_id = se.record_id -/* TODO Look at entilement rows only, ignore in-memory events and blocking states */ -where (coalesce(bst.prev_service, 'entitlement-service') = 'entitlement-service' -and coalesce(bst.next_service, 'entitlement-service') = 'entitlement-service' -and bst.subscription_event_record_id is not null) -/* Tricky... Need to look at the account timezone */ -and ((coalesce(date(se.requested_date), cast('1970-01-01' as date)) != coalesce(date(bst.requested_timestamp), cast('1970-01-01' as date)) -and coalesce(date(se.requested_date), cast('1970-01-01' as date)) != coalesce(date_add(date(bst.requested_timestamp), INTERVAL '1' DAY), cast('1970-01-01' as date)) -and coalesce(date(se.requested_date), cast('1970-01-01' as date)) != coalesce(date_sub(date(bst.requested_timestamp), INTERVAL '1' DAY), cast('1970-01-01' as date))) -or (coalesce(date(se.effective_date), cast('1970-01-01' as date)) != coalesce(date(bst.next_start_date), cast('1970-01-01' as date)) -and coalesce(date(se.effective_date), cast('1970-01-01' as date)) != coalesce(date_add(date(bst.next_start_date), INTERVAL '1' DAY), cast('1970-01-01' as date)) -and coalesce(date(se.effective_date), cast('1970-01-01' as date)) != coalesce(date_sub(date(bst.next_start_date), INTERVAL '1' DAY), cast('1970-01-01' as date))) -or coalesce(se.subscription_id, '') != coalesce(bst.subscription_id, '') -or coalesce(se.phase_name, '') != coalesce(bst.next_slug, '') -/* See https://github.com/killbill/killbill/issues/65: subscription_events won't have the pricelist but the SubscriptionEvent object will at runtime */ -or (se.price_list_name is not null and se.price_list_name != coalesce(bst.next_price_list, '')) -/* TODO SubscriptionEvent is not an entity, we don't have that info yet (we currently look at audit logs but this doesn't work for in-memory events) -or coalesce(se.created_date, cast('1970-01-01' as date)) != coalesce(bst.created_date, cast('1970-01-01' as date)) */ -or coalesce(se.account_record_id, -1) != coalesce(bst.account_record_id, -1) -or coalesce(se.tenant_record_id, -1) != coalesce(bst.tenant_record_id, -1)) -; - -select 'J2' as sanity_query_name; -select distinct b.account_record_id -from analytics_subscription_transitions b -left outer join accounts a on a.id = b.account_id -where coalesce(a.record_id) != coalesce(b.account_record_id, -1) -or coalesce(a.id, '') != coalesce(b.account_id, '') -or coalesce(a.external_key, '') != coalesce(b.account_external_key, '') -or coalesce(a.name, '') != coalesce(b.account_name, '') -; - -select 'J4' as sanity_query_name; -select distinct b.account_record_id -from analytics_subscription_transitions b -join audit_log al on b.subscription_event_record_id = al.target_record_id and al.change_type = 'INSERT' and table_name = 'SUBSCRIPTION_EVENTS' -/* TODO Look at entilement rows only, ignore in-memory events and blocking states */ -where (coalesce(b.prev_service, 'entitlement-service') = 'entitlement-service' -and coalesce(b.next_service, 'entitlement-service') = 'entitlement-service' -and b.subscription_event_record_id is not null) -and (coalesce(b.created_reason_code, 'NULL') != coalesce(al.reason_code, 'NULL') -or coalesce(b.created_comments, 'NULL') != coalesce(al.comments, 'NULL') -or coalesce(b.created_by, '') != coalesce(al.created_by, '')) -; - -select 'J5a' as sanity_query_name; -select distinct account_record_id -from analytics_subscription_transitions -where 1 = 1 -and event like 'START%' -and prev_product_name is not null -; - -select 'J5b' as sanity_query_name; -select distinct account_record_id -from analytics_subscription_transitions -where 1 = 1 -and event like 'START%' -and next_product_name is null -; - -select 'J6' as sanity_query_name; -select distinct account_record_id -from analytics_subscription_transitions -where 1 = 1 -and event like 'STOP%' -and prev_product_name is null -; - -select 'J7' as sanity_query_name; -select distinct account_record_id -from analytics_subscription_transitions -where 1 = 1 -and event like 'ERROR%' -; - -select 'J8' as sanity_query_name; -select distinct account_record_id -from analytics_subscription_transitions -where 1 = 1 -and prev_service != next_service -; - -select 'J9' as sanity_query_name; -select distinct account_record_id -from analytics_subscription_transitions -where 1 = 1 -and coalesce(prev_product_category, next_product_category) != coalesce(next_product_category, prev_product_category) -; - -select 'J10' as sanity_query_name; -select distinct account_record_id -from analytics_subscription_transitions ast -join ( - select - subscription_id - , event - , count(*) - from analytics_subscription_transitions - where 1 = 1 - and (event like 'START_%' or event like 'STOP_%') - group by 1, 2 - having(count(*)) > 1 -) duplicates using(subscription_id) -; - -select 'J11' as sanity_query_name; -select distinct account_record_id -from analytics_subscription_transitions ast -join ( - select - account_record_id - , event - , next_service - from analytics_subscription_transitions - where 1 = 1 - and ( - (event like '%ENTITLEMENT%' and next_service = 'billing-service') - or (event like '%BILLING%' and next_service = 'entitlement-service') - ) -) wrong_service using(account_record_id) -; - --- BUNDLE FIELDS -/* table not currently used */ - - --- BUNDLE TAGS -/* table not currently used */ - -select 'K1: Validate consistency of states between payments and payment_transactions' as sanity_query_name; -select * from ( -select - p.id - ,case when first_failure_record_id is not null then - case when first_failure_ptrx.transaction_status in ('UNKNOWN','PLUGIN_FAILURE') then - case when first_failure_ptrx.transaction_type='AUTHORIZE' then 'AUTH_ERRORED' - when first_failure_ptrx.transaction_type='CAPTURE' then 'CAPTURE_ERRORED' - when first_failure_ptrx.transaction_type='CHARGEBACK' then'CHARGEBACK_ERRORED' - when first_failure_ptrx.transaction_type='CREDIT' then 'CREDIT_ERRORED' - when first_failure_ptrx.transaction_type='PURCHASE' then 'PURCHASE_ERRORED' - when first_failure_ptrx.transaction_type='REFUND' then 'REFUND_ERRORED' - when first_failure_ptrx.transaction_type='VOID' then 'VOID_ERRORED' - end - when first_failure_ptrx.transaction_status='PAYMENT_FAILURE' then - case when first_failure_ptrx.transaction_type='AUTHORIZE' then 'AUTH_FAILED' - when first_failure_ptrx.transaction_type='CAPTURE' then 'CAPTURE_FAILED' - when first_failure_ptrx.transaction_type='CHARGEBACK' then'CHARGEBACK_FAILED' - when first_failure_ptrx.transaction_type='CREDIT' then 'CREDIT_FAILED' - when first_failure_ptrx.transaction_type='PURCHASE' then 'PURCHASE_FAILED' - when first_failure_ptrx.transaction_type='REFUND' then 'REFUND_FAILED' - when first_failure_ptrx.transaction_type='VOID' then 'VOID_FAILED' - end - end - when first_failure_record_id is null and last_success_record_id is not null then - case when last_success_ptrx.transaction_type='AUTHORIZE' then 'AUTH_SUCCESS' - when last_success_ptrx.transaction_type='CAPTURE' then 'CAPTURE_SUCCESS' - when last_success_ptrx.transaction_type='CHARGEBACK' then'CHARGEBACK_SUCCESS' - when last_success_ptrx.transaction_type='CREDIT' then 'CREDIT_SUCCESS' - when last_success_ptrx.transaction_type='PURCHASE' then 'PURCHASE_SUCCESS' - when last_success_ptrx.transaction_type='REFUND' then 'REFUND_SUCCESS' - when last_success_ptrx.transaction_type='VOID' then 'VOID_SUCCESS' - end - when first_failure_record_id is null and last_success_record_id is null and first_record_id is not null then - case when first_ptrx.transaction_status in ('UNKNOWN','PLUGIN_FAILURE') then - case when first_ptrx.transaction_type='AUTHORIZE' then 'AUTH_ERRORED' - when first_ptrx.transaction_type='CAPTURE' then 'CAPTURE_ERRORED' - when first_ptrx.transaction_type='CHARGEBACK' then'CHARGEBACK_ERRORED' - when first_ptrx.transaction_type='CREDIT' then 'CREDIT_ERRORED' - when first_ptrx.transaction_type='PURCHASE' then 'PURCHASE_ERRORED' - when first_ptrx.transaction_type='REFUND' then 'REFUND_ERRORED' - when first_ptrx.transaction_type='VOID' then 'VOID_ERRORED' - end - when first_ptrx.transaction_status='PAYMENT_FAILURE' then - case when first_ptrx.transaction_type='AUTHORIZE' then 'AUTH_FAILED' - when first_ptrx.transaction_type='CAPTURE' then 'CAPTURE_FAILED' - when first_ptrx.transaction_type='CHARGEBACK' then'CHARGEBACK_FAILED' - when first_ptrx.transaction_type='CREDIT' then 'CREDIT_FAILED' - when first_ptrx.transaction_type='PURCHASE' then 'PURCHASE_FAILED' - when first_ptrx.transaction_type='REFUND' then 'REFUND_FAILED' - when first_ptrx.transaction_type='VOID' then 'VOID_FAILED' - end - end - end as expected_value - ,p.state_name -from ( - select - first_ptrx.payment_id - ,first_ptrx.record_id first_record_id - ,latest_success.record_id as last_success_record_id - ,min(pt.record_id) as first_failure_record_id - from ( - select - pt.payment_id - ,min(pt.record_id) record_id - from payment_transactions pt - group by 1 - ) first_ptrx - left outer join ( - select - pt.payment_id - ,max(pt.record_id) record_id - from payment_transactions pt - where 1=1 - and pt.transaction_status = 'SUCCESS' - group by 1 - ) latest_success on first_ptrx.payment_id=latest_success.payment_id - left outer join payment_transactions pt on - pt.payment_id=latest_success.payment_id - and pt.record_id>latest_success.record_id - and pt.transaction_status != 'SUCCESS' - group by 1,2 - ) ptrx - inner join payments p on - ptrx.payment_id = p.id - left outer join payment_transactions first_ptrx on - ptrx.first_record_id=first_ptrx.record_id - left outer join payment_transactions last_success_ptrx on - ptrx.last_success_record_id=last_success_ptrx.record_id - left outer join payment_transactions first_failure_ptrx on - ptrx.first_failure_record_id=first_failure_ptrx.record_id -) t1 -where 1=1 - and expected_value != state_name -; - -select 'K2: payments.last_success_state_name and payment_transactions.transaction_state and payment_transactions.transaction_status' as sanity_query_name; -select - * -from ( -select - pt.transaction_type - ,pt.transaction_status - ,case when pt.transaction_type='AUTHORIZE' then 'AUTH_SUCCESS' - when pt.transaction_type='CAPTURE' then 'CAPTURE_SUCCESS' - when pt.transaction_type='CHARGEBACK' then'CHARGEBACK_SUCCESS' - when pt.transaction_type='CREDIT' then 'CREDIT_SUCCESS' - when pt.transaction_type='PURCHASE' then 'PURCHASE_SUCCESS' - when pt.transaction_type='REFUND' then 'REFUND_SUCCESS' - when pt.transaction_type='VOID' then 'VOID_SUCCESS' - end as expected_value - ,p.last_success_state_name - ,count(1) -from ( -select - pt.payment_id - ,max(pt.record_id) latest_record_id -from payment_transactions pt -where 1=1 - and pt.transaction_status = 'SUCCESS' -group by 1 -) latest_pt - inner join payment_transactions pt on - latest_pt.payment_id=pt.payment_id - and latest_pt.latest_record_id=pt.record_id - inner join payments p on - latest_pt.payment_id=p.id -group by 1,2,3,4 -) t1 -where 1=1 - and expected_value != last_success_state_name -; - -select 'K3: At least on payment trx for each payment' as sanity_query_name; -select - * -from - payments p - left outer join payment_transactions pt on - p.id=pt.payment_id -where 1=1 - and pt.payment_id is null -; - -select 'L1: Validate consistency between base payments tables and anlytics_payment_* tables' as sanity_query_name; -select - a.tenant_record_id - ,'analytics_payment_auths' as table_name - ,sum(case when b.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when b.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - and p.id=b.payment_id and p.external_key=b.payment_external_key and p.record_id=b.payment_number and p.account_id=b.account_id - then 1 else 0 end) matches - ,count(1) total -from - payments p - inner join payment_transactions a on - p.id=a.payment_id - left outer join analytics_payment_auths b on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='AUTHORIZE' -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_captures' as table_name - ,sum(case when b.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when b.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - and p.id=b.payment_id and p.external_key=b.payment_external_key and p.record_id=b.payment_number and p.account_id=b.account_id - then 1 else 0 end) matches - ,count(1) total -from - payments p - inner join payment_transactions a on - p.id=a.payment_id - left outer join analytics_payment_captures b on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='CAPTURE' -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_credits' as table_name - ,sum(case when b.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when b.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - and p.id=b.payment_id and p.external_key=b.payment_external_key and p.record_id=b.payment_number and p.account_id=b.account_id - then 1 else 0 end) matches - ,count(1) total -from - payments p - inner join payment_transactions a on - p.id=a.payment_id - left outer join analytics_payment_credits b on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='CREDIT' -- ?? -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_chargebacks' as table_name - ,sum(case when b.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when b.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - and p.id=b.payment_id and p.external_key=b.payment_external_key and p.record_id=b.payment_number and p.account_id=b.account_id - then 1 else 0 end) matches - ,count(1) total -from - payments p - inner join payment_transactions a on - p.id=a.payment_id - left outer join analytics_payment_chargebacks b on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='CHARGEBACK' -- ?? -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_purchases' as table_name - ,sum(case when b.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when b.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - and p.id=b.payment_id and p.external_key=b.payment_external_key and p.record_id=b.payment_number and p.account_id=b.account_id - then 1 else 0 end) matches - ,count(1) total -from - payments p - inner join payment_transactions a on - p.id=a.payment_id - left outer join analytics_payment_purchases b on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='PURCHASE' -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_refunds' as table_name - ,sum(case when b.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when b.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - and p.id=b.payment_id and p.external_key=b.payment_external_key and p.record_id=b.payment_number and p.account_id=b.account_id - then 1 else 0 end) matches - ,count(1) total -from - payments p - inner join payment_transactions a on - p.id=a.payment_id - left outer join analytics_payment_refunds b on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='REFUND' -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_voids' as table_name - ,sum(case when b.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when b.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - and p.id=b.payment_id and p.external_key=b.payment_external_key and p.record_id=b.payment_number and p.account_id=b.account_id - then 1 else 0 end) matches - ,count(1) total -from - payments p - inner join payment_transactions a on - p.id=a.payment_id - left outer join analytics_payment_voids b on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='VOID' -group by 1,2 -; - -select 'L2: Validate consistency between base payment_transactions tables and anlytics_payment_* tables' as sanity_query_name; -select - a.tenant_record_id - ,'analytics_payment_auths' as table_name - ,sum(case when a.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when a.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_auths b - left outer join payment_transactions a on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='AUTHORIZE' -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_captures' as table_name - ,sum(case when a.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when a.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_captures b - left outer join payment_transactions a on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='CAPTURE' -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_credits' as table_name - ,sum(case when a.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when a.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_credits b - left outer join payment_transactions a on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='CREDIT' -- ?? -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_chargebacks' as table_name - ,sum(case when a.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when a.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_chargebacks b - left outer join payment_transactions a on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='CHARGEBACK' -- ?? -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_purchases' as table_name - ,sum(case when a.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when a.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_purchases b - left outer join payment_transactions a on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='PURCHASE' -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_refunds' as table_name - ,sum(case when a.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when a.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_refunds b - left outer join payment_transactions a on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='REFUND' -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_voids' as table_name - ,sum(case when a.payment_id is null then 1 else 0 end) as row_missing - ,sum(case when a.payment_id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_transaction_id and a.transaction_external_key=b.payment_transaction_external_key - and a.transaction_status=b.payment_transaction_status -- and a.amount=b.amount and a.currency=b.currency and a.tenant_record_id=b.tenant_record_id - then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_voids b - left outer join payment_transactions a on - a.id=b.payment_transaction_id -where 1=1 - and a.transaction_type='VOID' -group by 1,2 -; - -select 'L3: Validate consistency between anlytics_payment_* tables and base payments tables' as sanity_query_name; -select - a.tenant_record_id - ,'analytics_payment_auths' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_id and a.record_id=b.payment_number and a.external_key=b.payment_external_key then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_auths b - left outer join payments a on - a.id=b.payment_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_captures' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_id and a.record_id=b.payment_number and a.external_key=b.payment_external_key then 1 else 0 end) matches ,count(1) total -from - analytics_payment_captures b - left outer join payments a on - a.id=b.payment_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_credits' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_id and a.record_id=b.payment_number and a.external_key=b.payment_external_key then 1 else 0 end) matches ,count(1) total -from - analytics_payment_credits b - left outer join payments a on - a.id=b.payment_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_chargebacks' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_id and a.record_id=b.payment_number and a.external_key=b.payment_external_key then 1 else 0 end) matches ,count(1) total -from - analytics_payment_chargebacks b - left outer join payments a on - a.id=b.payment_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_purchases' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_id and a.record_id=b.payment_number and a.external_key=b.payment_external_key then 1 else 0 end) matches ,count(1) total -from - analytics_payment_purchases b - left outer join payments a on - a.id=b.payment_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_refunds' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_id and a.record_id=b.payment_number and a.external_key=b.payment_external_key then 1 else 0 end) matches ,count(1) total -from - analytics_payment_refunds b - left outer join payments a on - a.id=b.payment_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_voids' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.payment_id and a.record_id=b.payment_number and a.external_key=b.payment_external_key then 1 else 0 end) matches ,count(1) total -from - analytics_payment_voids b - left outer join payments a on - a.id=b.payment_id -group by 1,2 -; - -select 'L4: Validate consistency between anlytics_payment_* tables and base accounts tables' as sanity_query_name; -select - a.tenant_record_id - ,'analytics_payment_auths' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.account_id and a.record_id=b.account_record_id and a.external_key=b.account_external_key then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_auths b - left outer join accounts a on - a.id=b.account_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_captures' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.account_id and a.record_id=b.account_record_id and a.external_key=b.account_external_key then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_captures b - left outer join accounts a on - a.id=b.account_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_credits' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.account_id and a.record_id=b.account_record_id and a.external_key=b.account_external_key then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_credits b - left outer join accounts a on - a.id=b.account_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_chargebacks' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.account_id and a.record_id=b.account_record_id and a.external_key=b.account_external_key then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_chargebacks b - left outer join accounts a on - a.id=b.account_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_purchases' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.account_id and a.record_id=b.account_record_id and a.external_key=b.account_external_key then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_purchases b - left outer join accounts a on - a.id=b.account_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_refunds' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.account_id and a.record_id=b.account_record_id and a.external_key=b.account_external_key then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_refunds b - left outer join accounts a on - a.id=b.account_id -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_voids' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id=b.account_id and a.record_id=b.account_record_id and a.external_key=b.account_external_key then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_voids b - left outer join accounts a on - a.id=b.account_id -group by 1,2 -; - -select 'L5: Validate consistency between anlytics_payment_* tables and base invoice_payments tables' as sanity_query_name; -select - a.tenant_record_id - ,'analytics_payment_auths' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_payment_id and a.record_id = b.invoice_payment_record_id and a.type = b.invoice_payment_type then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_auths b - left outer join invoice_payments a on - a.id=b.invoice_payment_id -where 1=1 - and b.invoice_payment_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_captures' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_payment_id and a.record_id = b.invoice_payment_record_id and a.type = b.invoice_payment_type then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_captures b - left outer join invoice_payments a on - a.id=b.invoice_payment_id -where 1=1 - and b.invoice_payment_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_credits' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_payment_id and a.record_id = b.invoice_payment_record_id and a.type = b.invoice_payment_type then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_credits b - left outer join invoice_payments a on - a.id=b.invoice_payment_id -where 1=1 - and b.invoice_payment_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_chargebacks' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_payment_id and a.record_id = b.invoice_payment_record_id and a.type = b.invoice_payment_type then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_chargebacks b - left outer join invoice_payments a on - a.id=b.invoice_payment_id -where 1=1 - and b.invoice_payment_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_purchases' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_payment_id and a.record_id = b.invoice_payment_record_id and a.type = b.invoice_payment_type then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_purchases b - left outer join invoice_payments a on - a.id=b.invoice_payment_id -where 1=1 - and b.invoice_payment_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_refunds' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_payment_id and a.record_id = b.invoice_payment_record_id and a.type = b.invoice_payment_type then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_refunds b - left outer join invoice_payments a on - a.id=b.invoice_payment_id -where 1=1 - and b.invoice_payment_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_voids' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_payment_id and a.record_id = b.invoice_payment_record_id and a.type = b.invoice_payment_type then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_voids b - left outer join invoice_payments a on - a.id=b.invoice_payment_id -where 1=1 - and b.invoice_payment_id is not null -group by 1,2 -; - -select 'L6: Validate consistency between anlytics_payment_* tables and base invoices tables' as sanity_query_name; -select - a.tenant_record_id - ,'analytics_payment_auths' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_id and a.currency = b.invoice_currency and a.record_id = b.invoice_number - and a.invoice_date = b.invoice_date and a.target_date = b.invoice_target_date then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_auths b - left outer join invoices a on - a.id=b.invoice_id -where 1=1 - and b.invoice_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_captures' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_id and a.currency = b.invoice_currency and a.record_id = b.invoice_number - and a.invoice_date = b.invoice_date and a.target_date = b.invoice_target_date then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_captures b - left outer join invoices a on - a.id=b.invoice_id -where 1=1 - and b.invoice_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_credits' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_id and a.currency = b.invoice_currency and a.record_id = b.invoice_number - and a.invoice_date = b.invoice_date and a.target_date = b.invoice_target_date then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_credits b - left outer join invoices a on - a.id=b.invoice_id -where 1=1 - and b.invoice_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_chargebacks' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_id and a.currency = b.invoice_currency and a.record_id = b.invoice_number - and a.invoice_date = b.invoice_date and a.target_date = b.invoice_target_date then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_chargebacks b - left outer join invoices a on - a.id=b.invoice_id -where 1=1 - and b.invoice_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_purchases' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_id and a.currency = b.invoice_currency and a.record_id = b.invoice_number - and a.invoice_date = b.invoice_date and a.target_date = b.invoice_target_date then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_purchases b - left outer join invoices a on - a.id=b.invoice_id -where 1=1 - and b.invoice_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_refunds' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_id and a.currency = b.invoice_currency and a.record_id = b.invoice_number - and a.invoice_date = b.invoice_date and a.target_date = b.invoice_target_date then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_refunds b - left outer join invoices a on - a.id=b.invoice_id -where 1=1 - and b.invoice_id is not null -group by 1,2 -UNION -select - a.tenant_record_id - ,'analytics_payment_voids' as table_name - ,sum(case when a.id is null then 1 else 0 end) as row_missing - ,sum(case when a.id is not null then 1 else 0 end) as row_exists - ,sum(case when a.id = b.invoice_id and a.currency = b.invoice_currency and a.record_id = b.invoice_number - and a.invoice_date = b.invoice_date and a.target_date = b.invoice_target_date then 1 else 0 end) matches - ,count(1) total -from - analytics_payment_voids b - left outer join invoices a on - a.id=b.invoice_id -where 1=1 - and b.invoice_id is not null -group by 1,2 -; - -select 'L7: Validate no duplicate rows in analytics_payment_*' as sanity_query_name; -select - table_name as "Table Name" - ,repeat_count as "Repeated Row Count" -from ( -select 'analytics_accounts' as table_name, ifnull(count(1),0) as repeat_count from ( - select account_record_id from analytics_accounts a group by 1 having count(1)>1 ) b -UNION -select 'analytics_bundles' as table_name, count(1) from ( - select bundle_record_id from analytics_bundles a group by 1 having count(1)>1 ) b -UNION -select 'analytics_invoice_adjustments' as table_name, ifnull(count(1),0) as repeat_count from ( - select invoice_item_record_id from analytics_invoice_adjustments a group by 1 having count(1)>1 ) b -UNION -select 'analytics_invoice_credits' as table_name, ifnull(count(1),0) as repeat_count from ( - select invoice_item_record_id from analytics_invoice_credits a group by 1 having count(1)>1 ) b -UNION -select 'analytics_invoice_item_adjustments' as table_name, ifnull(count(1),0) as repeat_count from ( - select invoice_item_record_id from analytics_invoice_item_adjustments a group by 1 having count(1)>1 ) b -UNION -select 'analytics_invoice_items' as table_name, ifnull(count(1),0) as repeat_count from ( - select invoice_item_record_id from analytics_invoice_items a group by 1 having count(1)>1 ) b -UNION -select 'analytics_invoices' as table_name, ifnull(count(1),0) as repeat_count from ( - select invoice_record_id from analytics_invoices a group by 1 having count(1)>1 ) b -UNION -select 'analytics_payment_auths' as table_name, ifnull(count(1),0) as repeat_count from ( - select payment_transaction_id from analytics_payment_auths a group by 1 having count(1)>1 ) b -UNION -select 'analytics_payment_captures' as table_name, count(1) from ( - select payment_transaction_id from analytics_payment_captures a group by 1 having count(1)>1 ) b -UNION -select 'analytics_payment_credits' as table_name, count(1) from ( - select payment_transaction_id from analytics_payment_credits a group by 1 having count(1)>1 ) b -UNION -select 'analytics_payment_chargebacks' as table_name, count(1) from ( - select payment_transaction_id from analytics_payment_chargebacks a group by 1 having count(1)>1 ) b -UNION -select 'analytics_payment_purchases' as table_name, count(1) from ( - select payment_transaction_id from analytics_payment_purchases a group by 1 having count(1)>1 ) b -UNION -select 'analytics_payment_refunds' as table_name, count(1) from ( - select payment_transaction_id from analytics_payment_refunds a group by 1 having count(1)>1 ) b -UNION -select 'analytics_payment_voids' as table_name, count(1) from ( - select payment_transaction_id from analytics_payment_voids a group by 1 having count(1)>1 ) b -) c -order by 2 desc, 1 -; diff --git a/src/main/resources/seed_reports.sh b/src/main/resources/seed_reports.sh deleted file mode 100644 index 1584501e..00000000 --- a/src/main/resources/seed_reports.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env bash - -# -# Copyright 2010-2014 Ning, Inc. -# Copyright 2014-2020 Groupon, Inc -# Copyright 2020-2020 Equinix, Inc -# Copyright 2014-2020 The Billing Project, LLC -# -# The Billing Project licenses this file to you under the Apache License, version 2.0 -# (the "License"); you may not use this file except in compliance with the -# License. You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -HERE=`cd \`dirname $0\`; pwd` - -KILLBILL_HTTP_PROTOCOL=${KILLBILL_HTTP_PROTOCOL-"http"} -KILLBILL_HOST=${KILLBILL_HOST-"127.0.0.1"} -KILLBILL_PORT=${KILLBILL_PORT-"8080"} - -KILLBILL_USER=${KILLBILL_USER-"admin"} -KILLBILL_PASSWORD=${KILLBILL_PASSWORD-"password"} -KILLBILL_API_KEY=${KILLBILL_API_KEY-"bob"} -KILLBILL_API_SECRET=${KILLBILL_API_SECRET-"lazar"} - -MYSQL_HOST=${MYSQL_HOST-"127.0.0.1"} -MYSQL_USER=${MYSQL_USER-"root"} -MYSQL_PASSWORD=${MYSQL_PASSWORD-"root"} -MYSQL_DATABASE=${MYSQL_DATABASE-"killbill"} - -REPORTS=$HERE/reports -SYSTEM=$HERE/system - -function install_ddl() { - local ddl=$1 - mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE -e "source $ddl" -} - -function create_report() { - local report_name=$1 - local report_pretty_name=$2 - local report_type=$3 - local source_table_name=$4 - - curl -v \ - -X POST \ - -u $KILLBILL_USER:$KILLBILL_PASSWORD \ - -H "X-Killbill-ApiKey:$KILLBILL_API_KEY" \ - -H "X-Killbill-ApiSecret:$KILLBILL_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d "{\"reportName\": \"$report_name\", - \"reportPrettyName\": \"$report_pretty_name\", - \"reportType\": \"$report_type\", - \"sourceTableName\": \"$source_table_name\"}" \ - $KILLBILL_HTTP_PROTOCOL://$KILLBILL_HOST:$KILLBILL_PORT/plugins/killbill-analytics/reports -} - -# Install the DDL - the calendar table needs to be first -install_ddl $REPORTS/calendar.sql -for r in `find $REPORTS -type f -name '*.sql' -o -name '*.ddl' -maxdepth 1`; do install_ddl $r; done -for r in `find $SYSTEM -type f -name '*.sql' -o -name '*.ddl' -maxdepth 1`; do install_ddl $r; done - -# Dashboard views -create_report 'accounts_summary' 'Account summary' 'COUNTERS' 'v_report_accounts_summary' -create_report 'active_by_product_term_monthly' 'Active subscriptions' 'TIMELINE' 'v_report_active_by_product_term_monthly' -create_report 'cancellations_count_daily' 'Cancellations' 'TIMELINE' 'v_report_cancellations_daily' -create_report 'chargebacks_daily' 'Chargebacks' 'TIMELINE' 'v_report_chargebacks_daily' -create_report 'conversions_daily' 'Conversions' 'TIMELINE' 'v_report_conversions_daily' -create_report 'invoice_adjustments_daily' 'Invoice adjustments' 'TIMELINE' 'v_report_invoice_adjustments_daily' -create_report 'invoice_item_adjustments_daily' 'Invoice item adjustments' 'TIMELINE' 'v_report_invoice_item_adjustments_daily' -create_report 'invoice_item_credits_daily' 'Invoice credits' 'TIMELINE' 'v_report_invoice_item_credits_daily' -create_report 'invoices_balance_daily' 'Invoice balance' 'TIMELINE' 'v_report_invoices_balance_daily' -create_report 'invoices_daily' 'Invoices' 'TIMELINE' 'v_report_invoices_daily' -create_report 'mrr_daily' 'MRR' 'TIMELINE' 'v_report_mrr_daily' -create_report 'new_accounts_daily' 'New accounts' 'TIMELINE' 'v_report_new_accounts_daily' -create_report 'overdue_states_count_daily' 'Overdue states' 'TIMELINE' 'v_report_overdue_states_count_daily' -create_report 'payments_total_daily' 'Payment ($ amount)' 'TIMELINE' 'v_report_payments_total_daily' -create_report 'refunds_total_daily' 'Refunds' 'TIMELINE' 'v_report_refunds_total_daily' -create_report 'trial_starts_count_daily' 'Trials' 'TIMELINE' 'v_report_trial_starts_count_daily' - -# System views -create_report 'system_report_control_tag_no_test' 'Control tags' 'COUNTERS' 'v_system_report_control_tag_no_test' -create_report 'system_report_notifications_per_queue_name' 'Notification queues' 'TIMELINE' 'v_system_report_notifications_per_queue_name' -create_report 'system_report_notifications_per_queue_name_late' 'Late notifications' 'COUNTERS' 'v_system_report_notifications_per_queue_name_late' -create_report 'system_report_payments' 'Payments status' 'COUNTERS' 'v_system_report_payments' -create_report 'system_report_payments_per_day' 'Payments' 'TIMELINE' 'v_system_report_payments_per_day' diff --git a/src/main/resources/system/README.md b/src/main/resources/system/README.md deleted file mode 100644 index e8d17f78..00000000 --- a/src/main/resources/system/README.md +++ /dev/null @@ -1,7 +0,0 @@ -These system queries help verify the health of Kill Bill. - -* [system_report_control_tag_no_test.sql](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/system/system_report_control_tag_no_test.sql): for each system tag, count the number of non-test accounts -* [system_report_notifications_per_queue_name.sql](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/system/system_report_notifications_per_queue_name.sql): for each notification queue and date, list the number of *AVAILABLE* notifications -* [system_report_notifications_per_queue_name_late.sql](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/system/system_report_notifications_per_queue_name_late.sql): for each notification queue, count the number of late notifications -* [system_report_payments.sql](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/system/system_report_payments.sql): count the number of payments in each state -* [system_report_payments_per_day.sql](https://github.com/killbill/killbill-analytics-plugin/blob/master/src/main/resources/system/system_report_payments_per_day.sql): count the number of payments in each state per day diff --git a/src/main/resources/system/system_report_control_tag_no_test.sql b/src/main/resources/system/system_report_control_tag_no_test.sql deleted file mode 100644 index 371f47dc..00000000 --- a/src/main/resources/system/system_report_control_tag_no_test.sql +++ /dev/null @@ -1,13 +0,0 @@ -create or replace view v_system_report_control_tag_no_test as -select - a1.tenant_record_id -, a1.name as tag_name -, count(distinct(a1.account_id)) as count -from analytics_account_tags a1 -left outer join analytics_account_tags a2 -on a1.account_id = a2.account_id and a2.name = 'TEST' -where 1=1 -and a2.record_id IS NULL -and a1.name IN ('OVERDUE_ENFORCEMENT_OFF', 'AUTO_PAY_OFF', 'AUTO_INVOICING_OFF', 'MANUAL_PAY', 'PARTNER') -group by 1, 2 -; diff --git a/src/main/resources/system/system_report_notifications_per_queue_name.sql b/src/main/resources/system/system_report_notifications_per_queue_name.sql deleted file mode 100644 index 88971e9a..00000000 --- a/src/main/resources/system/system_report_notifications_per_queue_name.sql +++ /dev/null @@ -1,11 +0,0 @@ -create or replace view v_system_report_notifications_per_queue_name as -select - search_key2 as tenant_record_id -, queue_name -, date_format(effective_date, '%Y-%m-%d') as day -, count(*) as count -from notifications -where processing_state = 'AVAILABLE' -group by 1, 2, 3 -order by 1, 2, 3 asc -; diff --git a/src/main/resources/system/system_report_notifications_per_queue_name_late.sql b/src/main/resources/system/system_report_notifications_per_queue_name_late.sql deleted file mode 100644 index 33599f6b..00000000 --- a/src/main/resources/system/system_report_notifications_per_queue_name_late.sql +++ /dev/null @@ -1,13 +0,0 @@ -create or replace view v_system_report_notifications_per_queue_name_late as -select - search_key2 as tenant_record_id -, queue_name as label -, count(*) as count -from notifications -where 1=1 -and processing_state = 'AVAILABLE' -and effective_date < NOW() --- and (processing_owner IS NULL OR processing_available_date <= NOW()) -group by 1, 2 -order by 1, 2 asc -; diff --git a/src/main/resources/system/system_report_payments.sql b/src/main/resources/system/system_report_payments.sql deleted file mode 100644 index 36152c14..00000000 --- a/src/main/resources/system/system_report_payments.sql +++ /dev/null @@ -1,8 +0,0 @@ -create or replace view v_system_report_payments as -select - tenant_record_id -, state_name as label -, count(*) as count -from payments -group by 1, 2 -; diff --git a/src/main/resources/system/system_report_payments_per_day.sql b/src/main/resources/system/system_report_payments_per_day.sql deleted file mode 100644 index e25f3adc..00000000 --- a/src/main/resources/system/system_report_payments_per_day.sql +++ /dev/null @@ -1,16 +0,0 @@ -create or replace view v_system_report_payments_per_day as -select - tenant_record_id -, date_format(greatest(created_date, updated_date), '%Y-%m-%d') as day -, case - when state_name IN ('AUTH_ERRORED', 'CAPTURE_ERRORED', 'CHARGEBACK_ERRORED', 'CREDIT_ERRORED', 'PURCHASE_ERRORED', 'REFUND_ERRORED', 'VOID_ERRORED') then 'ERRORED' - when state_name IN ('AUTH_FAILED', 'CAPTURE_FAILED', 'CHARGEBACK_FAILED', 'CREDIT_FAILED', 'PURCHASE_FAILED', 'REFUND_FAILED', 'VOID_FAILED') then 'FAILED' - when state_name IN ('AUTH_PENDING', 'CAPTURE_PENDING', 'CHARGEBACK_PENDING', 'CREDIT_PENDING', 'PURCHASE_PENDING', 'REFUND_PENDING', 'VOID_PENDING') then 'PENDING' - when state_name IN ('AUTH_SUCCESS', 'CAPTURE_SUCCESS', 'CHARGEBACK_SUCCESS', 'CREDIT_SUCCESS', 'PURCHASE_SUCCESS', 'REFUND_SUCCESS', 'VOID_SUCCESS') then 'SUCCESS' - else 'OTHER' - end as payment_status -, count(*) as count -from payments -group by 1, 2, 3 -order by 1, 2, 3 asc -;