Compare commits
1 Commits
1c54415a29
...
feature/is
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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