more test fixin
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 12m40s
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 12m40s
This commit is contained in:
@@ -2115,6 +2115,61 @@ AS $$
|
|||||||
ORDER BY potential_savings_cents DESC;
|
ORDER BY potential_savings_cents DESC;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
-- Function to get a user's spending breakdown by category for a given date range.
|
||||||
|
DROP FUNCTION IF EXISTS public.get_spending_by_category(UUID, DATE, DATE);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.get_spending_by_category(p_user_id UUID, p_start_date DATE, p_end_date DATE)
|
||||||
|
RETURNS TABLE (
|
||||||
|
category_id BIGINT,
|
||||||
|
category_name TEXT,
|
||||||
|
total_spent_cents BIGINT
|
||||||
|
)
|
||||||
|
LANGUAGE sql
|
||||||
|
STABLE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $$
|
||||||
|
WITH all_purchases AS (
|
||||||
|
-- CTE 1: Combine purchases from completed shopping trips.
|
||||||
|
-- We only consider items that have a price paid.
|
||||||
|
SELECT
|
||||||
|
sti.master_item_id,
|
||||||
|
sti.price_paid_cents
|
||||||
|
FROM public.shopping_trip_items sti
|
||||||
|
JOIN public.shopping_trips st ON sti.shopping_trip_id = st.shopping_trip_id
|
||||||
|
WHERE st.user_id = p_user_id
|
||||||
|
AND st.completed_at::date BETWEEN p_start_date AND p_end_date
|
||||||
|
AND sti.price_paid_cents IS NOT NULL
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
-- CTE 2: Combine purchases from processed receipts.
|
||||||
|
SELECT
|
||||||
|
ri.master_item_id,
|
||||||
|
ri.price_paid_cents
|
||||||
|
FROM public.receipt_items ri
|
||||||
|
JOIN public.receipts r ON ri.receipt_id = r.receipt_id
|
||||||
|
WHERE r.user_id = p_user_id
|
||||||
|
AND r.transaction_date::date BETWEEN p_start_date AND p_end_date
|
||||||
|
AND ri.master_item_id IS NOT NULL -- Only include items matched to a master item
|
||||||
|
)
|
||||||
|
-- Final Aggregation: Group all combined purchases by category and sum the spending.
|
||||||
|
SELECT
|
||||||
|
c.category_id,
|
||||||
|
c.name AS category_name,
|
||||||
|
SUM(ap.price_paid_cents)::BIGINT AS total_spent_cents
|
||||||
|
FROM all_purchases ap
|
||||||
|
-- Join with master_grocery_items to get the category_id for each purchase.
|
||||||
|
JOIN public.master_grocery_items mgi ON ap.master_item_id = mgi.master_grocery_item_id
|
||||||
|
-- Join with categories to get the category name for display.
|
||||||
|
JOIN public.categories c ON mgi.category_id = c.category_id
|
||||||
|
GROUP BY
|
||||||
|
c.category_id, c.name
|
||||||
|
HAVING
|
||||||
|
SUM(ap.price_paid_cents) > 0
|
||||||
|
ORDER BY
|
||||||
|
total_spent_cents DESC;
|
||||||
|
$$;
|
||||||
|
|
||||||
-- Function to approve a suggested correction and apply it.
|
-- Function to approve a suggested correction and apply it.
|
||||||
DROP FUNCTION IF EXISTS public.approve_correction(BIGINT);
|
DROP FUNCTION IF EXISTS public.approve_correction(BIGINT);
|
||||||
|
|
||||||
@@ -2669,6 +2724,58 @@ CREATE TRIGGER on_new_recipe_collection_share
|
|||||||
AFTER INSERT ON public.shared_recipe_collections
|
AFTER INSERT ON public.shared_recipe_collections
|
||||||
FOR EACH ROW EXECUTE FUNCTION public.log_new_recipe_collection_share();
|
FOR EACH ROW EXECUTE FUNCTION public.log_new_recipe_collection_share();
|
||||||
|
|
||||||
|
-- 10. Trigger function to geocode a store location's address.
|
||||||
|
-- This function is designed to be extensible for external geocoding services.
|
||||||
|
DROP FUNCTION IF EXISTS public.geocode_store_location();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.geocode_store_location()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
full_address TEXT;
|
||||||
|
BEGIN
|
||||||
|
-- Only proceed if the address has actually changed.
|
||||||
|
-- Note: We check against the linked address fields via the NEW.address_id in a real scenario,
|
||||||
|
-- but for this trigger to work effectively, it usually requires a direct update on the address table
|
||||||
|
-- or this trigger should be moved to the 'addresses' table.
|
||||||
|
-- However, based on the provided logic, we are keeping the placeholder structure.
|
||||||
|
|
||||||
|
-- Placeholder logic:
|
||||||
|
IF TG_OP = 'INSERT' THEN
|
||||||
|
-- Logic to fetch address string based on NEW.address_id and geocode
|
||||||
|
NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Trigger to call the geocoding function.
|
||||||
|
DROP TRIGGER IF EXISTS on_store_location_address_change ON public.store_locations;
|
||||||
|
CREATE TRIGGER on_store_location_address_change
|
||||||
|
BEFORE INSERT OR UPDATE ON public.store_locations
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION public.geocode_store_location();
|
||||||
|
|
||||||
|
-- 11. Trigger function to increment the fork_count on the original recipe.
|
||||||
|
DROP FUNCTION IF EXISTS public.increment_recipe_fork_count();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.increment_recipe_fork_count()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
-- Only run if the recipe is a fork (original_recipe_id is not null).
|
||||||
|
IF NEW.original_recipe_id IS NOT NULL THEN
|
||||||
|
UPDATE public.recipes SET fork_count = fork_count + 1 WHERE recipe_id = NEW.original_recipe_id;
|
||||||
|
-- Award 'First Fork' achievement.
|
||||||
|
PERFORM public.award_achievement(NEW.user_id, 'First Fork');
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS on_recipe_fork ON public.recipes;
|
||||||
|
CREATE TRIGGER on_recipe_fork
|
||||||
|
AFTER INSERT ON public.recipes
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION public.increment_recipe_fork_count();
|
||||||
|
|
||||||
-- =================================================================
|
-- =================================================================
|
||||||
-- Function: get_best_sale_prices_for_all_users()
|
-- Function: get_best_sale_prices_for_all_users()
|
||||||
-- Description: Retrieves the best sale price for every item on every user's watchlist.
|
-- Description: Retrieves the best sale price for every item on every user's watchlist.
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ describe('FlyerDataTransformer', () => {
|
|||||||
mockLogger,
|
mockLogger,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const baseUrl = `http://localhost:${process.env.PORT || 3000}`;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// 0. Check logging
|
// 0. Check logging
|
||||||
expect(mockLogger.info).toHaveBeenCalledWith(
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
||||||
@@ -83,8 +85,8 @@ describe('FlyerDataTransformer', () => {
|
|||||||
// 1. Check flyer data
|
// 1. Check flyer data
|
||||||
expect(flyerData).toEqual({
|
expect(flyerData).toEqual({
|
||||||
file_name: originalFileName,
|
file_name: originalFileName,
|
||||||
image_url: '/flyer-images/flyer-page-1.jpg',
|
image_url: `${baseUrl}/flyer-images/flyer-page-1.jpg`,
|
||||||
icon_url: '/flyer-images/icons/icon-flyer-page-1.webp',
|
icon_url: `${baseUrl}/flyer-images/icons/icon-flyer-page-1.webp`,
|
||||||
checksum,
|
checksum,
|
||||||
store_name: 'Test Store',
|
store_name: 'Test Store',
|
||||||
valid_from: '2024-01-01',
|
valid_from: '2024-01-01',
|
||||||
@@ -151,6 +153,8 @@ describe('FlyerDataTransformer', () => {
|
|||||||
mockLogger,
|
mockLogger,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const baseUrl = `http://localhost:${process.env.PORT || 3000}`;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// 0. Check logging
|
// 0. Check logging
|
||||||
expect(mockLogger.info).toHaveBeenCalledWith(
|
expect(mockLogger.info).toHaveBeenCalledWith(
|
||||||
@@ -167,8 +171,8 @@ describe('FlyerDataTransformer', () => {
|
|||||||
expect(itemsForDb).toHaveLength(0);
|
expect(itemsForDb).toHaveLength(0);
|
||||||
expect(flyerData).toEqual({
|
expect(flyerData).toEqual({
|
||||||
file_name: originalFileName,
|
file_name: originalFileName,
|
||||||
image_url: '/flyer-images/another.png',
|
image_url: `${baseUrl}/flyer-images/another.png`,
|
||||||
icon_url: '/flyer-images/icons/icon-another.webp',
|
icon_url: `${baseUrl}/flyer-images/icons/icon-another.webp`,
|
||||||
checksum,
|
checksum,
|
||||||
store_name: 'Unknown Store (auto)', // Should use fallback
|
store_name: 'Unknown Store (auto)', // Should use fallback
|
||||||
valid_from: null,
|
valid_from: null,
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ export class FlyerDataTransformer {
|
|||||||
|
|
||||||
const flyerData: FlyerInsert = {
|
const flyerData: FlyerInsert = {
|
||||||
file_name: originalFileName,
|
file_name: originalFileName,
|
||||||
image_url: `${baseUrl}/flyer-images/${path.basename(firstImage)}`,
|
image_url: new URL(`/flyer-images/${path.basename(firstImage)}`, baseUrl).href,
|
||||||
icon_url: `${baseUrl}/flyer-images/icons/${iconFileName}`,
|
icon_url: new URL(`/flyer-images/icons/${iconFileName}`, baseUrl).href,
|
||||||
checksum,
|
checksum,
|
||||||
store_name: storeName,
|
store_name: storeName,
|
||||||
valid_from: extractedData.valid_from,
|
valid_from: extractedData.valid_from,
|
||||||
|
|||||||
Reference in New Issue
Block a user