### Setup and Run Development Server Source: https://github.com/forsbergplustwo/partner-metrics/blob/main/README.md Execute these commands to set up dependencies, environment, and the database, then start the web server and sidekiq workers. ```bash bin/setup bin/dev ``` -------------------------------- ### Setup and Local Development Commands Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Commands for initial setup, running the development server with Sidekiq, and executing tests. Includes specific commands for upgrading from older versions. ```bash # First-time setup bin/setup ``` ```bash # Start web server (port 4000) + Sidekiq workers bin/dev ``` ```bash # Run standard tests bin/rails test ``` ```bash # Run all tests including system tests bin/rails test:all ``` ```bash # Manually trigger a full import from the Partner API (requires credentials saved in UI) bin/rails import_all_from_partner_api ``` ```bash # Upgrading from the pre-Rails 7 version bin/rails db:migrate bin/rails db:encryption:init bin/rails create_initial_imports bin/rails migrate_partner_api_credentials ``` -------------------------------- ### Smiirl Public Endpoint Example Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt The public JSON endpoint for Smiirl counters. It accepts a token in the URL and returns a count if the integration is enabled, otherwise a 404. ```http GET /public/smiirl/:token # => { "count": 155 } when integration.enabled? is true # => HTTP 404 when disabled or token not found ``` -------------------------------- ### Deploy to Production Source: https://github.com/forsbergplustwo/partner-metrics/blob/main/README.md Steps for deploying the application to production, including managing credentials and setting up cron jobs. ```bash bin/rails credentials:edit -e production heroku config:set RAILS_MASTER_KEY=[key] ``` -------------------------------- ### Initialize Metric Calculator Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Instantiate Metric::Calculator with user, date, charge type, app title, and yearly revenue flag. Use this to compute various business metrics for a specific combination. ```ruby calculator = Metric::Calculator.new( user: current_user, date: Date.parse("2024-03-15"), charge_type: "recurring_revenue", app_title: "My App", is_yearly_revenue: false ) ``` -------------------------------- ### Upgrade Database Migrations Source: https://github.com/forsbergplustwo/partner-metrics/blob/main/README.md Run these commands to migrate the database and encryption keys when upgrading the application. ```bash bin/rails db:migrate bin/rails db:encryption:init bin/rails create_initial_imports bin/rails migrate_partner_api_credentials ``` -------------------------------- ### Import Data Manually Source: https://github.com/forsbergplustwo/partner-metrics/blob/main/README.md Run this command to manually import data from the Partner API after adding credentials in the app UI. ```bash bin/rails import_all_from_partner_api ``` -------------------------------- ### Import Model - Pipeline Orchestration Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Ruby code demonstrating how to create and manage import records, check their status, and manually trigger pipeline stages. Useful for understanding the import lifecycle and state transitions. ```ruby # Create a CSV-based import for the current user import = current_user.imports.create!( source: Import.sources[:csv_file] # payouts_file attached separately via Active Storage ) # => Import transitions: draft -> scheduled, ImportPaymentsJob enqueued automatically ``` ```ruby # Create an API-based import import = current_user.imports.create!( source: Import.sources[:shopify_payments_api] ) ``` ```ruby # Check state import.scheduled? # => true import.importing? # => false import.completed? # => false import.failed? # => false ``` ```ruby # Retry a failed API import (only when no other import is in progress) if import.retriable? import.retry # resets to :draft and re-enqueues ImportPaymentsJob end ``` ```ruby # Manually run the full pipeline (normally done by background jobs) import.import # runs PaymentsProcessor, then enqueues CalculateMetricsJob import.calculate # runs MetricsProcessor, marks completed! ``` ```ruby # Query in-progress imports current_user.imports.in_progress # => ActiveRecord::Relation with status in [:scheduled, :importing, :calculating] ``` ```ruby # Determine how far back to fetch payments import.import_payments_after_date # => 3.months.ago (API) or 5.years.ago (CSV) import.import_metrics_after_date # => day after newest existing metric import.import_metrics_before_date # => yesterday (avoids incomplete current day) ``` -------------------------------- ### Initialize Dashboard Filter Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Create a Metric::TilesFilter instance by providing user and URL parameters. This filter manages the state for dashboard metrics. ```ruby filter = Metric::TilesFilter.new( user: current_user, params: { date: "2024-03-15", charge_type: "recurring_revenue", period: "30", app: "My App", chart: "recurring_revenue" } ) ``` -------------------------------- ### Create Smiirl Integration Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Create a Smiirl integration for a user, which auto-generates a unique token. This allows Smiirl physical counters to display real-time metrics like 'paying_users_30d' or 'total_revenue_30d'. ```ruby integration = user.create_smiirl_integration!(metric_type: "paying_users_30d") ``` ```ruby integration = user.create_smiirl_integration!(metric_type: "total_revenue_30d") ``` -------------------------------- ### Revenue Forecasting with Prophet Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Projects future revenue for up to 6 months using historical monthly chart data. Requires at least 10 data points. ```ruby historical_data = { "2023-10-01" => 3200.0, "2023-11-01" => 3500.0, "2023-12-01" => 3800.0, "2024-01-01" => 4000.0, "2024-02-01" => 4200.0, "2024-03-01" => 4500.0, # ... at least 10 months needed } forecaster = Metric::ForecastCharter.new(chart_data: historical_data) forecaster.chart_data # => [ # [#, 4680.0], # [#, 4820.0], # [#, 4950.0], # [#, 5100.0], # [#, 5230.0], # [#, 5380.0] # ] Metric::ForecastCharter::FORECAST_PERIODS # => 6 ``` -------------------------------- ### Manual Full Import via Rake Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Execute a full import of all data from the partner API using this Rake task. Useful for cron jobs or ad-hoc imports. ```ruby # bin/rails import_all_from_partner_api ``` -------------------------------- ### User Revenue Aggregation Methods Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Provides high-level methods for aggregating user revenue and shop data. Useful for dashboard KPIs and breakdowns. ```ruby user = User.find(42) # Quick KPIs user.total_revenue_30d # => 4500.00 (sum of all metrics last 30 days) user.paying_users_30d # => 155 (distinct shops with payments last 30 days) user.newest_metric_date # => # user.oldest_metric_date # => # # Revenue breakdowns (last 12 months from a given date) user.yearly_revenue_per_product(date: Date.today) # => { "My App" => 52000.0, "My Theme" => 8400.0 } user.yearly_revenue_per_country(date: Date.today) # => { "US" => 28000.0, "DE" => 12000.0, "GB" => 8000.0, ... } user.yearly_revenue_per_charge_type(date: Date.today) # => { "recurring_revenue" => 48000.0, "onetime_revenue" => 9600.0, "affiliate_revenue" => 2800.0 } # All-time revenue user.total_revenue_per_app(date: Date.today) # => { "My App" => 142000.0, "My Theme" => 24000.0 } user.total_revenue_per_charge_type(date: Date.today) # => { "recurring_revenue" => 138000.0, "onetime_revenue" => 24000.0 } # Plan distribution (count of payments per price point) user.total_revenue_per_plan(date: Date.today, charge_type: "recurring_revenue", app_title: "My App", period: 30) # => { 29.99 => 95, 49.99 => 40, 99.99 => 20 } # App title management user.app_titles # => ["My App", "My Theme"] user.app_titles("recurring_revenue") # => ["My App"] # Clear stale payments before re-import user.clear_old_payments!(after: 3.months.ago) ``` -------------------------------- ### Run Tests Source: https://github.com/forsbergplustwo/partner-metrics/blob/main/README.md Commands to run standard Rails tests and all tests including system tests. ```bash bin/rails test # including system tests bin/rails test:all ``` -------------------------------- ### Import::PaymentsProcessor - Raw Payment Ingestion Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Ruby code for initializing and running the `Import::PaymentsProcessor`. This processor fetches, filters, and bulk-inserts payment data into the database, handling different data sources and payment types. ```ruby processor = Import::PaymentsProcessor.new(import: import) processor.import! # Internally: # 1. Clears payments newer than import_payments_after_date # 2. Streams transactions from adaptor in batches (1000 for CSV, 100 for API) # 3. Filters out: payment_date <= cutoff, nil charge_type, nil shop, zero revenue # 4. Resolves "usage_revenue" to "recurring_revenue" or "onetime_revenue" # based on user.count_usage_charges_as_recurring # 5. Bulk-inserts Payment hashes (avoids AR object memory bloat) ``` ```ruby # Payment hash structure inserted by processor: { user_id: 42, import_id: 7, payment_date: Date.parse("2024-03-15"), charge_type: "recurring_revenue", # or "onetime_revenue", "affiliate_revenue", "refund", "usage_revenue" revenue: 29.99, is_yearly_revenue: false, app_title: "My App", shop: "example.myshopify.com", shop_country: "US" # nil when sourced from API (not provided by Shopify) } ``` -------------------------------- ### Monthly Revenue Summary Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Generates a 36-month historical summary of payments, revenue, and churn, filterable by app title. ```ruby summary = Summary::Monthly.new(user: current_user, selected_app: "My App") result = summary.summarize # => { # # => { # payments: 160, # revenue: 4500.0, # revenue_per_payment: 28.13, # revenue_churn: 2.5, # user_churn: 1.8 # }, # # => { ... }, # ... # up to 36 months # } Summary::Monthly::MONTHS_TO_SUMMARIZE # => 36 ``` -------------------------------- ### Calculate Derived Metrics from Partner Data Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Iterates through a date range, calculating derived metrics for each (charge_type × is_yearly_revenue × app_title) combination. Results are bulk-inserted. Processes specific charge types and supports yearly interval split only for recurring revenue. ```ruby processor = Import::MetricsProcessor.new(import: import) processor.calculate! # For each date in range, for each charge type and app title, it creates: # { # user_id: 42, # import_id: 7, # metric_date: Date.parse("2024-03-15"), # charge_type: "recurring_revenue", # app_title: "My App", # is_yearly_revenue: false, # revenue: 4500.00, # number_of_charges: 160, # number_of_shops: 155, # average_revenue_per_shop: 29.03, # average_revenue_per_charge: 28.13, # revenue_churn: 2.5, # % (only for recurring/affiliate) # shop_churn: 1.8, # % (only for recurring/affiliate) # lifetime_value: 1612.22, # (only for recurring/affiliate) # repeat_customers: 12, # count (only for onetime) # repeat_vs_new_customers: 7.74 # % (only for onetime) # } # Charge types processed: recurring_revenue, onetime_revenue, affiliate_revenue, refund Metric::CHARGE_TYPES # => ["recurring_revenue", "onetime_revenue", "affiliate_revenue", "refund"] # Only recurring_revenue supports the yearly interval split Metric::CHARGE_TYPE_CAN_HAVE_YEARLY_INTERVAL # => { "recurring_revenue" => true, "onetime_revenue" => false, # "affiliate_revenue" => false, "refund" => false } ``` -------------------------------- ### Calculate Metric Values Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Access computed metrics like revenue, number of charges, churn rates, and lifetime value from a Metric::Calculator instance. Churn calculations are billing-frequency-aware. ```ruby calculator.has_metrics? # => true (payments exist for this combination) calculator.revenue # => 4500.00 (sum of payment revenue) calculator.number_of_charges # => 160 calculator.number_of_shops # => 155 (unique myshopify domains) calculator.average_revenue_per_shop # => 29.03 calculator.average_revenue_per_charge # => 28.13 # Churn (only meaningful for recurring_revenue / affiliate_revenue) # Monthly: looks back 30 days + 15-day window; Yearly: 365 days + 30-day window calculator.revenue_churn # => 2.5 (%) calculator.shop_churn # => 1.8 (%) calculator.lifetime_value # => 1612.22 # Repeat customers (only meaningful for onetime_revenue) calculator.repeat_customers # => 12 (shops with >1 onetime purchase) calculator.repeat_vs_new_customers # => 7.74 (%) ``` -------------------------------- ### Query Metrics by Date and Period Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Retrieve metrics for a specified date and a given number of days prior. This scope is useful for time-series analysis. ```ruby user.metrics.by_date_and_period(date: Date.today, period: 30) ``` -------------------------------- ### Available Metric Periods and Types Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Constants defining the available periods in days for metric queries and the types of charges that can be displayed on the dashboard. ```ruby Metric::PERIODS # => [1, 7, 28, 29, 30, 31, 90, 180, 365] Metric::PERIODS_AGO # => [1, 2, 3, 6, 12] # Displayable charge types (excludes :refund from dashboard tiles) Metric::DISPLAYABLE_TYPES # => [:recurring_revenue, :onetime_revenue, :affiliate_revenue] ``` -------------------------------- ### Enqueue CalculateMetricsJob Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt This job is enqueued automatically after payments are successfully imported. It calculates the relevant metrics for the import. On error, it calls `import.fail` and re-raises. ```ruby CalculateMetricsJob.perform_later(import: import) ``` -------------------------------- ### Churn Window Constants Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Constants defining the billing frequency and churn window durations for monthly and yearly billing cycles. ```ruby Metric::Calculator::MONTHLY_BILLING_FREQUENCY # => 30.days Metric::Calculator::MONTHLY_BILLING_CHURN_WINDOW # => 15.days Metric::Calculator::YEARLY_BILLING_FREQUENCY # => 1.year Metric::Calculator::YEARLY_BILLING_CHURN_WINDOW # => 30.days ``` -------------------------------- ### Smiirl Integration Details and Token Rotation Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Access the integration's token and metric type. Use `rotate_token!` to invalidate the current token and generate a new one, affecting the Smiirl device URL. `SmiirlIntegration::METRIC_TYPES` lists available metric types. ```ruby integration.token # => "a3f8c2d1e9b4..." integration.metric_type # => "paying_users_30d" ``` ```ruby integration.rotate_token! ``` ```ruby SmiirlIntegration::METRIC_TYPES # => ["paying_users_30d", "total_revenue_30d"] ``` -------------------------------- ### Access Tile Presenter Properties Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Obtain computed values, period-over-period changes, and chart data from a Metric::TilePresenter object. This includes current/previous values, percentage change, and average values. ```ruby # Tiles are built by TilesFilter; each tile exposes: tile = filter.selected_tile tile.handle # => :recurring_revenue tile.display # => :currency (or :number, :percentage) tile.positive_change_is_good # => true tile.current_value # => 4500.00 (sum/average over current period) tile.previous_value # => 4200.00 (same period, one period back) tile.change # => 7.14 (% change) tile.average_value # => 150.0 (current_value / period days) # Period-ago comparisons (period_ago = 1 means 1 period ago, 2 = 2 periods ago, etc.) tile.period_ago_value(3) # => 3800.0 (value 3 periods ago) tile.period_ago_change(3) # => 18.42 (% change vs 3 periods ago) # Chart data for Chartkick — includes forecast if user.show_forecasts? and period == 30 tile.chart_data # => [ ``` -------------------------------- ### Rename App Across Payments and Metrics Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Use AppRenamer to update an app's name across all associated payments and metrics. Returns true on success, false if the 'from' and 'to' names are identical or blank. ```ruby renamer = AppRenamer.new(user: current_user, from: "Old App Name", to: "New App Name") renamer.rename ``` -------------------------------- ### Enqueue ImportPaymentsJob Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt This job is enqueued automatically after an Import record is created. It processes payments for the import. On error, it calls `import.fail` and re-raises. ```ruby ImportPaymentsJob.perform_later(import: import) ``` -------------------------------- ### Generate Chartkick Data Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Build datasets compatible with Chartkick from Metric records. Specify charge type, date, period, aggregation method, and metric to chart. ```ruby user.metrics .by_optional_charge_type("recurring_revenue") .chart_data(Date.today, 30, :sum, :revenue) # => [["2024-02-15", 4200.0], ["2024-03-15", 4500.0], ...] ``` -------------------------------- ### Top Shops Aggregation Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Aggregates lifetime payment data for the top 100 shops by revenue, filterable by app title. ```ruby summary = Summary::Shop.new(user: current_user, selected_app: nil) result = summary.summarize # => { # "top-store.myshopify.com" => { # payments: 48, # revenue: 1440.0, # last_payment: "2024-03-14" # }, # "another-store.myshopify.com" => { ... }, # ... # up to 100 shops # } Summary::Shop::TOP_SHOPS_LIMIT # => 100 ``` -------------------------------- ### Background Jobs Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Details the Sidekiq background jobs used for the asynchronous import pipeline, including payment and metric calculations. ```APIDOC ## Enqueued automatically by Import#schedule (after_create_commit) ImportPaymentsJob.perform_later(import: import) # Calls import.import => Import::PaymentsProcessor#import! # On error: calls import.fail, re-raises ## Enqueued automatically by Import#imported after payments are done CalculateMetricsJob.perform_later(import: import) # Calls import.calculate => Import::MetricsProcessor#calculate! # On error: calls import.fail, re-raises ## Manual full import via Rake (for cron or ad-hoc use) # bin/rails import_all_from_partner_api ``` -------------------------------- ### Filter Metrics by Charge Type and App Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Use ActiveRecord scopes on the Metric model to filter metrics by charge type, app title, and yearly revenue flag. Requires a user object with associated metrics. ```ruby user.metrics .by_optional_charge_type("recurring_revenue") .by_optional_app_title("My App") .by_optional_is_yearly_revenue(false) ``` -------------------------------- ### App Data Management (Rename/Delete) Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Service objects for renaming or deleting all metrics and payments associated with a specific app title for a user. ```ruby # No specific code example provided in the source for AppRenamer/AppDeleter. ``` -------------------------------- ### Fix Linters and Formatters Source: https://github.com/forsbergplustwo/partner-metrics/blob/main/README.md Use this command to automatically fix linter and formatter issues in the codebase. ```bash standardrb --fix ``` -------------------------------- ### SmiirlIntegration — Physical Counter API Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Exposes a public JSON endpoint for Smiirl physical counters to display real-time metrics like paying users or total revenue. ```APIDOC ## Create an integration (token auto-generated) integration = user.create_smiirl_integration!(metric_type: "paying_users_30d") # or integration = user.create_smiirl_integration!(metric_type: "total_revenue_30d") integration.token # => "a3f8c2d1e9b4..." (32-char hex, unique) integration.metric_type # => "paying_users_30d" ## Rotate the token (invalidates old Smiirl device URL) integration.rotate_token! SmiirlIntegration::METRIC_TYPES # => ["paying_users_30d", "total_revenue_30d"] ## Public endpoint (no auth, token in URL): # GET /public/smiirl/:token # => { "count": 155 } when integration.enabled? is true # => HTTP 404 when disabled or token not found ``` -------------------------------- ### Fetch Shopify Payments API Transactions Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Fetches paginated transactions from the Shopify Partner GraphQL API using cursor-based pagination. Includes throttling and automatic credential invalidation on 401/403 errors. Supports up to 3 months of history. ```ruby # Used internally by PaymentsProcessor; direct usage example: credential = user.partner_api_credential # credential.context => { access_token: "...", organization_id: "12345" } adaptor = Import::Adaptor::ShopifyPaymentsApi.new( import: import, import_payments_after_date: 3.months.ago ) # Returns an Enumerator — lazily streams all paginated results adaptor.fetch_payments.each do |transaction| puts transaction # => { # charge_type: "recurring_revenue", # payment_date: #, # revenue: 29.99, # is_yearly_revenue: false, # true for ANNUAL billing interval # app_title: "My Shopify App", # shop: "customer-store.myshopify.com", # shop_country: nil # not available from API # } end # API revenue type mapping: # "recurring_revenue" <- AppSubscriptionSale # "onetime_revenue" <- AppOneTimeSale, ServiceSale, ThemeSale # "affiliate_revenue" <- ReferralTransaction # "refund" <- AppCredit, AppSaleAdjustment, ReferralAdjustment, etc. # "usage_revenue" <- AppUsageSale, AppUsageRecord # Skipped: TaxTransaction, LegacyTransaction ``` -------------------------------- ### Shopify Partner GraphQL Query for Transactions Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt This GraphQL query is used to fetch transaction data from the Shopify Partner API. It supports cursor-based pagination and includes inline fragments for various transaction types. ```graphql query($createdAtMin: DateTime, $cursor: String, $first: Int) { transactions(createdAtMin: $createdAtMin, after: $cursor, first: $first) { edges { cursor node { id createdAt ... on AppSubscriptionSale { billingInterval, netAmount { amount }, app { name }, shop { myshopifyDomain } } ... on AppOneTimeSale { netAmount { amount }, app { name }, shop { myshopifyDomain } } ... on AppUsageSale { netAmount { amount }, app { name }, shop { myshopifyDomain } } ... on ReferralTransaction { amount { amount }, shopNonNullable: shop { myshopifyDomain } } ... on ThemeSale { netAmount { amount }, theme { name }, shop { myshopifyDomain } } # ... and AppSaleAdjustment, AppSaleCredit, ReferralAdjustment, ServiceSale, ThemeSaleAdjustment } } pageInfo { hasNextPage } } } ``` -------------------------------- ### Shopify Partner GraphQL Query Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt The GraphQL query used for importing transactions from the Shopify Partner API, supporting cursor-based pagination and various transaction types. ```APIDOC ## Used as: ShopifyPartnerAPI.client.query( # Graphql::TransactionsQuery, # variables: { cursor: "", createdAtMin: "2024-01-01T00:00:00.000+0000", first: 100 }, # context: { access_token: "prtapi_...", organization_id: "12345" } # ) query($createdAtMin: DateTime, $cursor: String, $first: Int) { transactions(createdAtMin: $createdAtMin, after: $cursor, first: $first) { edges { cursor node { id createdAt ... on AppSubscriptionSale { billingInterval, netAmount { amount }, app { name }, shop { myshopifyDomain } } ... on AppOneTimeSale { netAmount { amount }, app { name }, shop { myshopifyDomain } } ... on AppUsageSale { netAmount { amount }, app { name }, shop { myshopifyDomain } } ... on ReferralTransaction { amount { amount }, shopNonNullable: shop { myshopifyDomain } } ... on ThemeSale { netAmount { amount }, theme { name }, shop { myshopifyDomain } } # ... and AppSaleAdjustment, AppSaleCredit, ReferralAdjustment, ServiceSale, ThemeSaleAdjustment } } pageInfo { hasNextPage } } } ``` -------------------------------- ### App Renaming and Deletion Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Provides methods to rename an app across all associated payments and metrics, or to delete all data for a specific app. ```APIDOC ## Rename an app across all payments and metrics renamer = AppRenamer.new(user: current_user, from: "Old App Name", to: "New App Name") renamer.rename # => true (updates app_title on all metrics and payments) # => false (if from == to, or either is blank) ## Delete all data for an app deleter = AppDeleter.new(user: current_user, app_title: "Discontinued App") deleter.delete # => true (deletes all matching metrics and payments for this user) ``` -------------------------------- ### Parse Shopify Payout CSV/ZIP Files Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Reads Shopify Partner payout CSV exports or ZIP archives containing a CSV. Supports up to 5 years of history and handles historical column naming conventions. Assumes `import.payouts_file` is an Active Storage attachment. ```ruby # Typical CSV columns used: charge_creation_time, charge_type, partner_share, # app_title, shop, shop_country adaptor = Import::Adaptor::CsvFile.new( import: import, # import.payouts_file is an Active Storage attachment import_payments_after_date: 5.years.ago ) adaptor.fetch_payments.each do |row| puts row # => { # charge_type: "recurring_revenue", # payment_date: #, # revenue: 19.99, # is_yearly_revenue: false, # true if charge_type string includes "yearly" # app_title: "My Theme", # shop: "example.myshopify.com", # shop_country: "DE" # available in CSV exports # } end # Accepted file types Import::ACCEPTED_FILE_TYPES # => ["text/csv", "application/zip"] # CSV charge type string examples that map to normalized keys: # "recurring_revenue" <- "RecurringApplicationFee", "App sale – recurring", "App sale – yearly subscription" # "onetime_revenue" <- "OneTimeApplicationFee", "ThemePurchaseFee", "App sale – one-time" # "affiliate_revenue" <- "AffiliateFee", "Development store referral commission" # "refund" <- "ApplicationCredit", "App refund", "Payout correction" # "usage_revenue" <- "App sale – usage", "AppUsageSale" ``` -------------------------------- ### Shopify Partner API Credential Storage Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Stores Shopify Partner API credentials securely using encrypted attributes. Validates credentials on creation/update. ```ruby # Create credentials (triggers live API validation) credential = user.create_partner_api_credential!( organization_id: "12345", access_token: "prtapi_abc123..." ) credential.valid_status? # => true (API test passed) credential.invalid_status? # => false credential.draft_status? # => false # Context hash passed to GraphQL client credential.context # => { access_token: "prtapi_abc123...", organization_id: "12345" } # Automatically called when API returns 401/403 credential.invalidate_with_message!("Unauthorized: missing permissions") # Updates status to :invalid and stores the error message ``` -------------------------------- ### Delete App Data Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Use AppDeleter to remove all payment and metric data associated with a specific app title for the current user. Returns true on successful deletion. ```ruby deleter = AppDeleter.new(user: current_user, app_title: "Discontinued App") deleter.delete ``` -------------------------------- ### Access Filtered Metric Data Source: https://context7.com/forsbergplustwo/partner-metrics/llms.txt Retrieve parsed filter attributes, current and previous period metrics, and related tiles from a Metric::TilesFilter object. The filter state can be serialized back to parameters. ```ruby filter.date # => # filter.charge_type # => "recurring_revenue" filter.period # => 30 filter.app # => "My App" filter.app_titles # => ["My App", "Another App"] filter.has_metrics? # => true filter.current_period_metrics # => ActiveRecord::Relation (last 30 days) filter.previous_period_metrics # => ActiveRecord::Relation (30 days before that) filter.tiles # => Array of Metric::TilePresenter filter.selected_tile # => Metric::TilePresenter for "recurring_revenue" filter.to_param # => { date: "2024-03-15", charge_type: "recurring_revenue", # chart: "recurring_revenue", period: 30, app: "My App" } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.