Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

target/
target-base/
dbt_packages/
dbt_modules/
logs/
Expand Down
8 changes: 7 additions & 1 deletion models/customer_segments.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ SELECT
customer_id,
number_of_orders,
customer_lifetime_value,
net_customer_lifetime_value,
CASE
WHEN number_of_orders > 10 THEN 'Frequent Buyer'
WHEN number_of_orders BETWEEN 5 AND 10 THEN 'Occasional Buyer'
Expand All @@ -12,5 +13,10 @@ SELECT
WHEN customer_lifetime_value > 4000 THEN 'High Value'
WHEN customer_lifetime_value BETWEEN 1500 AND 4000 THEN 'Medium Value'
ELSE 'Low Value'
END AS value_segment
END AS value_segment,
CASE
WHEN net_customer_lifetime_value > 4000 THEN 'High Value'
WHEN net_customer_lifetime_value BETWEEN 1500 AND 4000 THEN 'Medium Value'
ELSE 'Low Value'
END AS net_value_segment
FROM {{ ref('customers') }}
10 changes: 8 additions & 2 deletions models/customers.sql
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,17 @@ customer_payments as (

select
orders.customer_id,
sum(amount)::bigint as total_amount
sum(amount)::bigint as gross_amount, -- Includes coupon amount
sum(amount - coupon_amount)::bigint as net_amount, -- Excludes coupon amount

from payments

left join orders on
payments.order_id = orders.order_id
and orders.status = 'completed'

where payments.amount is not null -- Exclude incomplete payments
and payments.amount > 0 -- Exclude negative amounts

group by orders.customer_id

Expand All @@ -54,7 +59,8 @@ final as (
customer_orders.first_order,
customer_orders.most_recent_order,
customer_orders.number_of_orders,
customer_payments.total_amount as customer_lifetime_value
customer_payments.gross_amount as customer_lifetime_value, -- Gross CLV
customer_payments.net_amount as net_customer_lifetime_value -- Net CLV

from customers

Expand Down
31 changes: 31 additions & 0 deletions models/finance_revenue.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
with payments as (
select * from {{ ref('stg_payments') }}
),

payments_revenue as (
select
order_id,
sum(amount) as gross_revenue,
sum(amount - coupon_amount) as net_revenue
from payments
group by order_id
),

orders as (
select * from {{ ref('stg_orders') }}
),

final as (
select
orders.order_id,
orders.customer_id,
orders.order_date,
orders.status,
payments_revenue.gross_revenue,
payments_revenue.net_revenue
from orders
left join payments_revenue
on orders.order_id = payments_revenue.order_id
)

select * from final
77 changes: 72 additions & 5 deletions models/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2

models:
- name: customers
description: This table has basic information about a customer, as well as some derived facts based on a customer's orders
description: This table has basic information about a customer, as well as some derived facts based on a customer's orders and payments, including both gross and profit-based customer lifetime value metrics

columns:
- name: customer_id
Expand All @@ -26,33 +26,57 @@ models:
- name: number_of_orders
description: Count of the number of orders a customer has placed

- name: customer_lifetime_value
description: Total value of a customer's orders including coupon amounts

- name: net_customer_lifetime_value
description: Total value of a customer's orders excluding coupon amounts

- name: total_order_amount
description: Total value (AUD) of a customer's orders

- name: customer_segments
description: This table categorizes customers based on their ordering behavior and value to the company, using derived metrics from their order history.
description: This table categorizes customers based on their ordering behavior and value to the company, using derived metrics from their order history and payment information.

columns:
- name: customer_id
description: This is a unique identifier for a customer.
tests:
- unique
- not_null
- relationships:
to: ref('customers')
field: customer_id

- name: number_of_orders
description: Count of the number of orders a customer has placed.
tests:
- not_null

- name: customer_lifetime_value
description: Total value (in currency) of all orders placed by a customer over their lifetime.
description: Total value of all orders including coupon amounts.

- name: net_customer_lifetime_value
description: Total value of all orders excluding coupon amounts.

- name: order_frequency_segment
description: Categorization of customers based on how frequently they place orders.
tests:
- not_null
- accepted_values:
values: ['Frequent Buyer', 'Occasional Buyer', 'Rare Buyer']

- name: value_segment
description: Categorization of customers based on the monetary value they bring to the company.
description: Categorization of customers based on the gross monetary value they bring to the company.
tests:
- accepted_values:
values: ['High Value', 'Medium Value', 'Low Value']
values: ['High Value', 'Medium Value', 'Low Value']

- name: net_value_segment
description: Categorization of customers based on the profit-based monetary value they bring to the company.
tests:
- accepted_values:
values: ['High Value', 'Medium Value', 'Low Value']

- name: customer_order_pattern
description: This table provides detailed insights into the ordering patterns of customers, including the frequency and recency of their orders.
Expand Down Expand Up @@ -130,3 +154,46 @@ models:
description: Amount of the order (AUD) paid for by gift card
tests:
- not_null

- name: finance_revenue
description: This table provides financial metrics for each order, including both gross revenue (including coupons) and profit-based revenue (excluding coupons).

columns:
- name: order_id
description: This is a unique identifier for an order
tests:
- unique
- not_null
- relationships:
to: ref('stg_orders')
field: order_id

- name: customer_id
description: Foreign key to the customers table
tests:
- not_null
- relationships:
to: ref('customers')
field: customer_id

- name: order_date
description: Date (UTC) that the order was placed
tests:
- not_null

- name: status
description: Current status of the order
tests:
- not_null
- accepted_values:
values: ['placed', 'shipped', 'completed', 'return_pending', 'returned']

- name: gross_revenue
description: Total revenue including coupon amounts
tests:
- not_null

- name: net_revenue
description: Total revenue excluding coupon amounts
tests:
- not_null
8 changes: 8 additions & 0 deletions models/staging/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,11 @@ models:
tests:
- accepted_values:
values: ['credit_card', 'coupon', 'bank_transfer', 'gift_card']
- name: amount
description: Amount in dollars (converted from cents)
tests:
- not_null
- name: coupon_amount
description: Amount of the payment that was paid using a coupon (in dollars)
tests:
- not_null
3 changes: 2 additions & 1 deletion models/staging/stg_payments.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ renamed as (
payment_method,

-- `amount` is currently stored in cents, so we convert it to dollars
amount / 100 as amount
amount / 100 as amount,
(payment_method = 'coupon')::int * (amount / 100) as coupon_amount

from source

Expand Down
Loading