-- Migration: Add kandang management -- Date: 2026-02-26 -- Description: Adds kandangs table, kandang_cycles junction table, and kandang_id to mortality_records -- Also auto-assigns existing mortality records to a default "Kandang 1" -- 1. Create kandangs table CREATE TABLE IF NOT EXISTS kandangs ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- 2. Create kandang_cycles junction table (doc_in_count per kandang per cycle) CREATE TABLE IF NOT EXISTS kandang_cycles ( id SERIAL PRIMARY KEY, kandang_id INTEGER NOT NULL, cycle_id VARCHAR(255) NOT NULL, doc_in_count INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (kandang_id) REFERENCES kandangs(id) ON DELETE CASCADE, FOREIGN KEY (cycle_id) REFERENCES cycles(id) ON DELETE CASCADE, UNIQUE(kandang_id, cycle_id) ); -- 3. Add kandang_id column to mortality_records (nullable for backward compat) ALTER TABLE mortality_records ADD COLUMN IF NOT EXISTS kandang_id INTEGER REFERENCES kandangs(id) ON DELETE SET NULL; -- 4. Add indexes CREATE INDEX IF NOT EXISTS idx_mortality_kandang_id ON mortality_records(kandang_id); CREATE INDEX IF NOT EXISTS idx_kandang_cycles_kandang ON kandang_cycles(kandang_id); CREATE INDEX IF NOT EXISTS idx_kandang_cycles_cycle ON kandang_cycles(cycle_id); -- 5. Add updated_at triggers DROP TRIGGER IF EXISTS update_kandangs_updated_at ON kandangs; CREATE TRIGGER update_kandangs_updated_at BEFORE UPDATE ON kandangs FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); DROP TRIGGER IF EXISTS update_kandang_cycles_updated_at ON kandang_cycles; CREATE TRIGGER update_kandang_cycles_updated_at BEFORE UPDATE ON kandang_cycles FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- 6. Auto-assign existing data: Create default "Kandang 1" if not exists and assign all unassigned mortality records -- Insert default kandang (only if no kandangs exist yet) INSERT INTO kandangs (name) SELECT 'Kandang 1' WHERE NOT EXISTS (SELECT 1 FROM kandangs LIMIT 1); -- Assign existing mortality records to the first kandang (only records with NULL kandang_id) UPDATE mortality_records SET kandang_id = (SELECT id FROM kandangs ORDER BY id LIMIT 1) WHERE kandang_id IS NULL AND EXISTS (SELECT 1 FROM kandangs LIMIT 1); -- Create kandang_cycles entries for the default kandang for all cycles that have doc_in_count INSERT INTO kandang_cycles (kandang_id, cycle_id, doc_in_count) SELECT k.id, c.id, COALESCE(c.doc_in_count, 0) FROM kandangs k, cycles c WHERE k.id = (SELECT id FROM kandangs ORDER BY id LIMIT 1) AND NOT EXISTS ( SELECT 1 FROM kandang_cycles kc WHERE kc.kandang_id = k.id AND kc.cycle_id = c.id ); -- 7. Drop old unique constraint and add new one that includes kandang_id -- Note: The constraint name follows PostgreSQL naming convention ALTER TABLE mortality_records DROP CONSTRAINT IF EXISTS mortality_records_cycle_id_day_key; -- Create new unique index that handles kandang_id (using COALESCE for NULL safety) DROP INDEX IF EXISTS idx_mortality_cycle_day_kandang; CREATE UNIQUE INDEX idx_mortality_cycle_day_kandang ON mortality_records(cycle_id, day, COALESCE(kandang_id, -1));