Compare commits
2 Commits
1c54415a29
...
4eec4923af
| Author | SHA1 | Date | |
|---|---|---|---|
| 4eec4923af | |||
|
|
f70b90748a |
251
supabase/migrations/003_helper_functions.sql
Normal file
251
supabase/migrations/003_helper_functions.sql
Normal file
@@ -0,0 +1,251 @@
|
||||
-- Migration: Additional SQL Functions for Inventory Management
|
||||
-- Week 2: Helper functions for common queries
|
||||
|
||||
-- Function: Get inventory items with full details (tags, product info, unit conversion)
|
||||
CREATE OR REPLACE FUNCTION get_inventory_details()
|
||||
RETURNS TABLE (
|
||||
item_id UUID,
|
||||
item_name TEXT,
|
||||
quantity DECIMAL,
|
||||
unit_abbreviation TEXT,
|
||||
unit_name TEXT,
|
||||
expiry_date DATE,
|
||||
days_until_expiry INTEGER,
|
||||
tags TEXT[],
|
||||
product_brand TEXT,
|
||||
product_image_url TEXT,
|
||||
product_barcode TEXT,
|
||||
created_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
i.id AS item_id,
|
||||
i.name AS item_name,
|
||||
i.quantity,
|
||||
u.abbreviation AS unit_abbreviation,
|
||||
u.name AS unit_name,
|
||||
i.expiry_date,
|
||||
(i.expiry_date - CURRENT_DATE) AS days_until_expiry,
|
||||
COALESCE(ARRAY_AGG(DISTINCT t.name) FILTER (WHERE t.name IS NOT NULL), '{}') AS tags,
|
||||
p.brand AS product_brand,
|
||||
p.image_url AS product_image_url,
|
||||
p.barcode AS product_barcode,
|
||||
i.created_at,
|
||||
i.updated_at
|
||||
FROM inventory_items i
|
||||
JOIN units u ON i.unit_id = u.id
|
||||
LEFT JOIN products p ON i.product_id = p.id
|
||||
LEFT JOIN item_tags it ON i.id = it.item_id
|
||||
LEFT JOIN tags t ON it.tag_id = t.id
|
||||
GROUP BY
|
||||
i.id, i.name, i.quantity, u.abbreviation, u.name,
|
||||
i.expiry_date, p.brand, p.image_url, p.barcode,
|
||||
i.created_at, i.updated_at
|
||||
ORDER BY i.created_at DESC;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION get_inventory_details() IS 'Returns all inventory items with denormalized data for display';
|
||||
|
||||
-- Function: Get items expiring soon
|
||||
CREATE OR REPLACE FUNCTION get_expiring_items(days_ahead INTEGER DEFAULT 7)
|
||||
RETURNS TABLE (
|
||||
item_id UUID,
|
||||
item_name TEXT,
|
||||
quantity DECIMAL,
|
||||
unit_abbreviation TEXT,
|
||||
expiry_date DATE,
|
||||
days_until_expiry INTEGER,
|
||||
tags TEXT[]
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
i.id AS item_id,
|
||||
i.name AS item_name,
|
||||
i.quantity,
|
||||
u.abbreviation AS unit_abbreviation,
|
||||
i.expiry_date,
|
||||
(i.expiry_date - CURRENT_DATE) AS days_until_expiry,
|
||||
COALESCE(ARRAY_AGG(DISTINCT t.name) FILTER (WHERE t.name IS NOT NULL), '{}') AS tags
|
||||
FROM inventory_items i
|
||||
JOIN units u ON i.unit_id = u.id
|
||||
LEFT JOIN item_tags it ON i.id = it.item_id
|
||||
LEFT JOIN tags t ON it.tag_id = t.id
|
||||
WHERE
|
||||
i.expiry_date IS NOT NULL
|
||||
AND i.expiry_date <= CURRENT_DATE + MAKE_INTERVAL(days => days_ahead)
|
||||
GROUP BY i.id, i.name, i.quantity, u.abbreviation, i.expiry_date
|
||||
ORDER BY i.expiry_date ASC;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION get_expiring_items(INTEGER) IS 'Returns items expiring within specified days (default 7)';
|
||||
|
||||
-- Function: Get items by tag
|
||||
CREATE OR REPLACE FUNCTION get_items_by_tag(tag_name TEXT)
|
||||
RETURNS TABLE (
|
||||
item_id UUID,
|
||||
item_name TEXT,
|
||||
quantity DECIMAL,
|
||||
unit_abbreviation TEXT,
|
||||
expiry_date DATE,
|
||||
created_at TIMESTAMPTZ
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
i.id AS item_id,
|
||||
i.name AS item_name,
|
||||
i.quantity,
|
||||
u.abbreviation AS unit_abbreviation,
|
||||
i.expiry_date,
|
||||
i.created_at
|
||||
FROM inventory_items i
|
||||
JOIN units u ON i.unit_id = u.id
|
||||
JOIN item_tags it ON i.id = it.item_id
|
||||
JOIN tags t ON it.tag_id = t.id
|
||||
WHERE t.name ILIKE tag_name
|
||||
ORDER BY i.created_at DESC;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION get_items_by_tag(TEXT) IS 'Returns all items with specified tag (case-insensitive)';
|
||||
|
||||
-- Function: Get low stock items (quantity <= threshold)
|
||||
CREATE OR REPLACE FUNCTION get_low_stock_items(threshold DECIMAL DEFAULT 1.0)
|
||||
RETURNS TABLE (
|
||||
item_id UUID,
|
||||
item_name TEXT,
|
||||
quantity DECIMAL,
|
||||
unit_abbreviation TEXT,
|
||||
tags TEXT[]
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
i.id AS item_id,
|
||||
i.name AS item_name,
|
||||
i.quantity,
|
||||
u.abbreviation AS unit_abbreviation,
|
||||
COALESCE(ARRAY_AGG(DISTINCT t.name) FILTER (WHERE t.name IS NOT NULL), '{}') AS tags
|
||||
FROM inventory_items i
|
||||
JOIN units u ON i.unit_id = u.id
|
||||
LEFT JOIN item_tags it ON i.id = it.item_id
|
||||
LEFT JOIN tags t ON it.tag_id = t.id
|
||||
WHERE i.quantity <= threshold
|
||||
GROUP BY i.id, i.name, i.quantity, u.abbreviation
|
||||
ORDER BY i.quantity ASC, i.name ASC;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION get_low_stock_items(DECIMAL) IS 'Returns items with quantity at or below threshold';
|
||||
|
||||
-- Function: Update item quantity (consume or restock)
|
||||
CREATE OR REPLACE FUNCTION update_item_quantity(
|
||||
item_uuid UUID,
|
||||
quantity_change DECIMAL,
|
||||
delete_if_zero BOOLEAN DEFAULT TRUE
|
||||
)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
new_quantity DECIMAL;
|
||||
BEGIN
|
||||
-- Calculate new quantity
|
||||
SELECT quantity + quantity_change INTO new_quantity
|
||||
FROM inventory_items
|
||||
WHERE id = item_uuid;
|
||||
|
||||
IF new_quantity IS NULL THEN
|
||||
RETURN FALSE; -- Item not found
|
||||
END IF;
|
||||
|
||||
-- Delete if zero and flag is set
|
||||
IF new_quantity <= 0 AND delete_if_zero THEN
|
||||
DELETE FROM inventory_items WHERE id = item_uuid;
|
||||
RETURN TRUE;
|
||||
END IF;
|
||||
|
||||
-- Update quantity (ensure non-negative)
|
||||
UPDATE inventory_items
|
||||
SET quantity = GREATEST(new_quantity, 0),
|
||||
updated_at = NOW()
|
||||
WHERE id = item_uuid;
|
||||
|
||||
RETURN TRUE;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION update_item_quantity(UUID, DECIMAL, BOOLEAN) IS 'Updates item quantity (positive for restock, negative for consume). Optionally deletes if zero.';
|
||||
|
||||
-- Function: Get inventory statistics
|
||||
CREATE OR REPLACE FUNCTION get_inventory_stats()
|
||||
RETURNS TABLE (
|
||||
total_items BIGINT,
|
||||
total_unique_products BIGINT,
|
||||
items_expiring_week BIGINT,
|
||||
items_expired BIGINT,
|
||||
total_tags_used BIGINT
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
COUNT(DISTINCT i.id) AS total_items,
|
||||
COUNT(DISTINCT i.product_id) FILTER (WHERE i.product_id IS NOT NULL) AS total_unique_products,
|
||||
COUNT(i.id) FILTER (
|
||||
WHERE i.expiry_date IS NOT NULL
|
||||
AND i.expiry_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '7 days'
|
||||
) AS items_expiring_week,
|
||||
COUNT(i.id) FILTER (
|
||||
WHERE i.expiry_date IS NOT NULL
|
||||
AND i.expiry_date < CURRENT_DATE
|
||||
) AS items_expired,
|
||||
COUNT(DISTINCT it.tag_id) AS total_tags_used
|
||||
FROM inventory_items i
|
||||
LEFT JOIN item_tags it ON i.id = it.item_id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION get_inventory_stats() IS 'Returns summary statistics for the entire inventory';
|
||||
|
||||
-- Function: Search inventory (full-text search on items and products)
|
||||
CREATE OR REPLACE FUNCTION search_inventory(search_query TEXT)
|
||||
RETURNS TABLE (
|
||||
item_id UUID,
|
||||
item_name TEXT,
|
||||
quantity DECIMAL,
|
||||
unit_abbreviation TEXT,
|
||||
product_brand TEXT,
|
||||
tags TEXT[],
|
||||
relevance REAL
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
i.id AS item_id,
|
||||
i.name AS item_name,
|
||||
i.quantity,
|
||||
u.abbreviation AS unit_abbreviation,
|
||||
p.brand AS product_brand,
|
||||
COALESCE(ARRAY_AGG(DISTINCT t.name) FILTER (WHERE t.name IS NOT NULL), '{}') AS tags,
|
||||
ts_rank(
|
||||
to_tsvector('english', i.name || ' ' || COALESCE(p.brand, '') || ' ' || COALESCE(p.name, '')),
|
||||
plainto_tsquery('english', search_query)
|
||||
) AS relevance
|
||||
FROM inventory_items i
|
||||
JOIN units u ON i.unit_id = u.id
|
||||
LEFT JOIN products p ON i.product_id = p.id
|
||||
LEFT JOIN item_tags it ON i.id = it.item_id
|
||||
LEFT JOIN tags t ON it.tag_id = t.id
|
||||
WHERE
|
||||
to_tsvector('english', i.name || ' ' || COALESCE(p.brand, '') || ' ' || COALESCE(p.name, ''))
|
||||
@@ plainto_tsquery('english', search_query)
|
||||
GROUP BY i.id, i.name, i.quantity, u.abbreviation, p.brand, p.name
|
||||
ORDER BY relevance DESC, i.created_at DESC
|
||||
LIMIT 50;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION search_inventory(TEXT) IS 'Full-text search across inventory items and products';
|
||||
Reference in New Issue
Block a user