diff --git a/apps/web/supabase/migrations/20221215192558_schema.sql b/apps/web/supabase/migrations/20221215192558_schema.sql index 90f66127d..211f0c8cf 100644 --- a/apps/web/supabase/migrations/20221215192558_schema.sql +++ b/apps/web/supabase/migrations/20221215192558_schema.sql @@ -1595,7 +1595,8 @@ on conflict ( trial_ends_at = excluded.trial_ends_at returning * into new_subscription; - -- Upsert subscription items + + -- Upsert subscription items and delete ones that are not in the line_items array with item_data as ( select (line_item ->> 'id')::varchar as line_item_id, @@ -1607,7 +1608,19 @@ on conflict ( (line_item ->> 'interval')::varchar as intv, (line_item ->> 'interval_count')::integer as intv_count from - jsonb_array_elements(line_items) as line_item) + jsonb_array_elements(line_items) as line_item + ), + line_item_ids as ( + select line_item_id from item_data + ), + deleted_items as ( + delete from + public.subscription_items + where + public.subscription_items.subscription_id = new_subscription.id + and public.subscription_items.id not in (select line_item_id from line_item_ids) + returning * + ) insert into public.subscription_items( id, subscription_id, @@ -1964,6 +1977,28 @@ on conflict ( returning * into new_order; + -- Upsert order items and delete ones that are not in the line_items array + with item_data as ( + select + (line_item ->> 'id')::varchar as line_item_id, + (line_item ->> 'product_id')::varchar as prod_id, + (line_item ->> 'variant_id')::varchar as var_id, + (line_item ->> 'price_amount')::numeric as price_amt, + (line_item ->> 'quantity')::integer as qty + from + jsonb_array_elements(line_items) as line_item + ), + line_item_ids as ( + select line_item_id from item_data + ), + deleted_items as ( + delete from + public.order_items + where + public.order_items.order_id = new_order.id + and public.order_items.id not in (select line_item_id from line_item_ids) + returning * + ) insert into public.order_items( id, order_id, @@ -1972,18 +2007,18 @@ on conflict ( price_amount, quantity) select - (line_item ->> 'id')::varchar, + line_item_id, target_order_id, -(line_item ->> 'product_id')::varchar, -(line_item ->> 'variant_id')::varchar, -(line_item ->> 'price_amount')::numeric, -(line_item ->> 'quantity')::integer + prod_id, + var_id, + price_amt, + qty from - jsonb_array_elements(line_items) as line_item -on conflict (id) - do update set - price_amount = excluded.price_amount, - quantity = excluded.quantity; + item_data + on conflict (id) + do update set + price_amount = excluded.price_amount, + quantity = excluded.quantity; return new_order; diff --git a/apps/web/supabase/tests/database/personal-billing-orders.test.sql b/apps/web/supabase/tests/database/personal-billing-orders.test.sql index c7ad32bfa..f1ec6c4ba 100644 --- a/apps/web/supabase/tests/database/personal-billing-orders.test.sql +++ b/apps/web/supabase/tests/database/personal-billing-orders.test.sql @@ -12,7 +12,7 @@ INSERT INTO public.billing_customers(account_id, provider, customer_id) VALUES (tests.get_supabase_uid('primary_owner'), 'stripe', 'cus_test'); -- Call the upsert_order function -SELECT public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1}]'); +SELECT public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1}, {"id":"order_item_2", "product_id": "prod_test", "variant_id": "var_test_2", "price_amount": 100, "quantity": 1}]'); -- Verify that the order was created correctly SELECT is( @@ -24,37 +24,42 @@ SELECT is( -- Verify that the subscription items were created correctly SELECT row_eq( $$ select count(*) from order_items where order_id = 'order_test' $$, - row(1::bigint), + row(2::bigint), 'The order items should be created' ); -- Call the upsert_order function again to update the order -SELECT public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 10}]'); +select public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 10}]'); -- Verify that the order was updated correctly -SELECT is( - (SELECT status FROM public.orders WHERE id = 'order_test'), +select is( + (select status FROM public.orders WHERE id = 'order_test'), 'succeeded', 'The order status should be succeeded' ); -SELECT row_eq( +select row_eq( $$ select quantity from order_items where variant_id = 'var_test' $$, row(10::int), 'The subscription items should be updated' ); +select is_empty( + $$ select * from order_items where id = 'order_item_2' $$, + 'The order item should be deleted when the order is updated' +); + select tests.authenticate_as('primary_owner'); -- account can read their own subscription -SELECT isnt_empty( +select isnt_empty( $$ select 1 from orders where id = 'order_test' $$, 'The account can read their own order' ); -SELECT isnt_empty( +select isnt_empty( $$ select * from order_items where order_id = 'order_test' $$, - 'The account can read their own order' + 'The account can read their own orders items' ); -- foreigners @@ -62,17 +67,18 @@ select tests.create_supabase_user('foreigner'); select tests.authenticate_as('foreigner'); -- account cannot read other's subscription -SELECT is_empty( +select is_empty( $$ select 1 from orders where id = 'order_test' $$, 'The account cannot read the other account orders' ); -SELECT is_empty( +select is_empty( $$ select 1 from order_items where order_id = 'order_test' $$, 'The account cannot read the other account order items' ); -- Finish the tests and clean up -SELECT * FROM finish(); -ROLLBACK; +select * from finish(); + +rollback; diff --git a/apps/web/supabase/tests/database/personal-billing-subscriptions.test.sql b/apps/web/supabase/tests/database/personal-billing-subscriptions.test.sql index 542cc1746..00007af91 100644 --- a/apps/web/supabase/tests/database/personal-billing-subscriptions.test.sql +++ b/apps/web/supabase/tests/database/personal-billing-subscriptions.test.sql @@ -33,13 +33,23 @@ SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_ "quantity": 2, "interval": "month", "interval_count": 1 + }, + { + "id": "sub_789", + "product_id": "prod_test_3", + "variant_id": "var_test_3", + "type": "flat", + "price_amount": 2000, + "quantity": 2, + "interval": "month", + "interval_count": 1 } ]'); -- Verify that the subscription items were created correctly SELECT row_eq( $$ select count(*) from subscription_items where subscription_id = 'sub_test' $$, - row(2::bigint), + row(3::bigint), 'The subscription items should be created' ); @@ -95,24 +105,34 @@ SELECT row_eq( ); -- Verify that the subscription was updated correctly -SELECT is( - (SELECT active FROM public.subscriptions WHERE id = 'sub_test'), +select is( + (select active FROM public.subscriptions WHERE id = 'sub_test'), false, 'The subscription should be inactive' ); -SELECT is( - (SELECT status FROM public.subscriptions WHERE id = 'sub_test'), +select is( + (select status FROM public.subscriptions WHERE id = 'sub_test'), 'past_due', 'The subscription status should be past_due' ); +select isnt_empty( + $$ select * from public.subscription_items where subscription_id = 'sub_test' $$, + 'The account can read their own subscription items' +); + +select is_empty( + $$ select * from public.subscription_items where subscription_id = 'sub_test' and variant_id = 'var_test_3' $$, + 'The subscription items should be deleted when the subscription is updated and the item is missing' +); + -- Call the upsert_subscription function again to update the subscription -SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]'); +select public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]'); -- Verify that the subscription was updated correctly -SELECT is( - (SELECT active FROM public.subscriptions WHERE id = 'sub_test'), +select is( + (select active FROM public.subscriptions WHERE id = 'sub_test'), true, 'The subscription should be active' ); @@ -120,14 +140,14 @@ SELECT is( select tests.authenticate_as('primary_owner'); -- account can read their own subscription -SELECT isnt_empty( +select isnt_empty( $$ select 1 from subscriptions where id = 'sub_test' $$, 'The account can read their own subscription' ); -SELECT isnt_empty( +select is_empty( $$ select * from subscription_items where subscription_id = 'sub_test' $$, - 'The account can read their own subscription items' + 'No subscription items should be returned when the subscription is empty' ); -- users cannot manually update subscriptions diff --git a/apps/web/supabase/tests/database/team-billing-orders.test.sql b/apps/web/supabase/tests/database/team-billing-orders.test.sql index d7c58a761..bfb578ad5 100644 --- a/apps/web/supabase/tests/database/team-billing-orders.test.sql +++ b/apps/web/supabase/tests/database/team-billing-orders.test.sql @@ -12,7 +12,12 @@ INSERT INTO public.billing_customers(account_id, provider, customer_id) VALUES (makerkit.get_account_id_by_slug('makerkit'), 'stripe', 'cus_test'); -- Call the upsert_order function -SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1}]'); +SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[ + {"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1}, + {"id":"order_item_2", "product_id": "prod_test", "variant_id": "var_test_2", "price_amount": 100, "quantity": 1}, + {"id":"order_item_3", "product_id": "prod_test", "variant_id": "var_test_3", "price_amount": 100, "quantity": 1}, + {"id":"order_item_4", "product_id": "prod_test", "variant_id": "var_test_4", "price_amount": 100, "quantity": 1} +]'); -- Verify that the order was created correctly SELECT is( @@ -24,12 +29,22 @@ SELECT is( -- Verify that the subscription items were created correctly SELECT row_eq( $$ select count(*) from order_items where order_id = 'order_test' $$, - row(1::bigint), + row(4::bigint), 'The order items should be created' ); -- Call the upsert_order function again to update the order -SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 10}]'); +SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[ + {"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 10}, + {"id":"order_item_2", "product_id": "prod_test", "variant_id": "var_test_2", "price_amount": 200, "quantity": 1} +]'); + +-- Verify that the subscription items were created correctly +SELECT row_eq( + $$ select count(*) from order_items where order_id = 'order_test' $$, + row(2::bigint), + 'The order items should be updated' +); -- Verify that the order was updated correctly SELECT is( @@ -44,6 +59,12 @@ SELECT row_eq( 'The subscription items should be updated' ); +SELECT row_eq( + $$ select price_amount from order_items where variant_id = 'var_test_2' $$, + row(200::numeric), + 'The subscription items should be updated' +); + select tests.authenticate_as('member'); -- account can read their own subscription diff --git a/apps/web/supabase/tests/database/team-billing-subscriptions.test.sql b/apps/web/supabase/tests/database/team-billing-subscriptions.test.sql index 2aa6b13e7..b69e59fcf 100644 --- a/apps/web/supabase/tests/database/team-billing-subscriptions.test.sql +++ b/apps/web/supabase/tests/database/team-billing-subscriptions.test.sql @@ -33,13 +33,23 @@ SELECT public.upsert_subscription(makerkit.get_account_id_by_slug('makerkit'), ' "quantity": 2, "interval": "month", "interval_count": 1 + }, + { + "id": "sub_789", + "product_id": "prod_test_3", + "variant_id": "var_test_3", + "type": "flat", + "price_amount": 2000, + "quantity": 2, + "interval": "month", + "interval_count": 1 } ]'); -- Verify that the subscription items were created correctly SELECT row_eq( $$ select count(*) from subscription_items where subscription_id = 'sub_test' $$, - row(2::bigint), + row(3::bigint), 'The subscription items should be created' ); @@ -80,6 +90,12 @@ SELECT public.upsert_subscription(makerkit.get_account_id_by_slug('makerkit'), ' } ]'); +SELECT row_eq( + $$ select count(*) from subscription_items where subscription_id = 'sub_test' $$, + row(2::bigint), + 'The subscription items should be updated' +); + -- Verify that the subscription items were updated correctly SELECT row_eq( $$ select price_amount from subscription_items where variant_id = 'var_test' $$, @@ -107,6 +123,16 @@ SELECT is( 'The subscription status should be past_due' ); +select tests.authenticate_as('member'); + +SELECT row_eq( + $$ select count(*) from subscription_items where subscription_id = 'sub_test' $$, + row(2::bigint), + 'The member can also read the subscription items' +); + +set role service_role; + -- Call the upsert_subscription function again to update the subscription SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]'); @@ -125,9 +151,9 @@ select isnt_empty( 'The account can read their own subscription' ); -select isnt_empty( +select is_empty( $$ select * from subscription_items where subscription_id = 'sub_test' $$, - 'The account can read their own subscription items' + 'The subscription items are now empty' ); select is(