CREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, password TEXT NOT NULL, created TIMESTAMPTZ, email VARCHAR(255), last_login TIMESTAMPTZ, owner INTEGER ); CREATE TABLE service_categories ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT NOT NULL, clicks_total INTEGER DEFAULT 0, total_companies INTEGER DEFAULT 0 ); CREATE TABLE company ( id SERIAL PRIMARY KEY, active BOOLEAN DEFAULT true, created DATE NOT NULL DEFAULT CURRENT_DATE, name VARCHAR(255) NOT NULL, address VARCHAR(255), town VARCHAR(255), state VARCHAR(2), phone VARCHAR(20), owner_name VARCHAR(255), owner_phone_number VARCHAR(20), email VARCHAR(255), user_id INTEGER ); -- Counties (populated by scripts/add_county_to_db.py) CREATE TABLE county ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, state VARCHAR(2) NOT NULL, UNIQUE(name, state) ); CREATE TABLE listings ( id SERIAL PRIMARY KEY, company_name VARCHAR(255) NOT NULL, is_active BOOLEAN DEFAULT true, price_per_gallon DOUBLE PRECISION NOT NULL, price_per_gallon_cash DOUBLE PRECISION, note TEXT, minimum_order INTEGER, service BOOLEAN DEFAULT false, bio_percent INTEGER NOT NULL, phone VARCHAR(20), online_ordering VARCHAR(20) NOT NULL DEFAULT 'none', county_id INTEGER NOT NULL, town VARCHAR(100), url VARCHAR(255), logo_url VARCHAR(255), banner_url VARCHAR(255), facebook_url VARCHAR(255), instagram_url VARCHAR(255), google_business_url VARCHAR(255), user_id INTEGER NOT NULL, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, last_edited TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); -- Oil prices (scraped from NewEnglandOil.com / MaineOil.com) CREATE TABLE oil_prices ( id SERIAL PRIMARY KEY, state VARCHAR(100), zone INTEGER, name VARCHAR(255), price DOUBLE PRECISION, date VARCHAR(20), scrapetimestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, company_id INTEGER, county_id INTEGER ); -- If the table already exists, add county_id -- ALTER TABLE oil_prices ADD COLUMN county_id INTEGER; -- If the table already exists, add the new columns -- ALTER TABLE listings ADD COLUMN price_per_gallon_cash DOUBLE PRECISION; -- ALTER TABLE listings ADD COLUMN note TEXT; -- ALTER TABLE listings ADD COLUMN minimum_order INTEGER; -- ALTER TABLE listings ADD COLUMN last_edited TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP; -- If the table already exists, drop the company_id column -- ALTER TABLE listings DROP COLUMN company_id; CREATE TABLE stats_prices ( id SERIAL PRIMARY KEY, state VARCHAR(2) NOT NULL, price DOUBLE PRECISION NOT NULL, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); -- Boiler/HVAC service companies (separate from fuel price listings) CREATE TABLE service_listings ( id SERIAL PRIMARY KEY, company_name VARCHAR(255) NOT NULL, is_active BOOLEAN DEFAULT true, twenty_four_hour BOOLEAN DEFAULT false, -- 24/7 emergency coverage emergency_service BOOLEAN DEFAULT false, -- emergency same-day response town VARCHAR(100), county_id INTEGER NOT NULL, phone VARCHAR(20), website VARCHAR(255), email VARCHAR(255), description TEXT, -- short description of services offered licensed_insured BOOLEAN DEFAULT false, -- licensed and insured service_area VARCHAR(255), -- e.g. "All of Middlesex County", "Greater Boston" years_experience INTEGER, -- years in business logo_url VARCHAR(255), banner_url VARCHAR(255), facebook_url VARCHAR(255), instagram_url VARCHAR(255), google_business_url VARCHAR(255), user_id INTEGER NOT NULL, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, last_edited TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); -- Run this to create the table if not exists (after initial schema is deployed): -- CREATE TABLE IF NOT EXISTS service_listings ( ... same as above ... ); -- Subscription tracking (one per company, auto-created with 1-year trial) CREATE TABLE subscriptions ( id SERIAL PRIMARY KEY, company_id INTEGER NOT NULL UNIQUE, trial_start DATE NOT NULL DEFAULT CURRENT_DATE, trial_end DATE NOT NULL DEFAULT (CURRENT_DATE + INTERVAL '1 year'), status VARCHAR(20) NOT NULL DEFAULT 'trial', plan VARCHAR(50), created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); -- Admin-managed site-wide banners CREATE TABLE banners ( id SERIAL PRIMARY KEY, message TEXT NOT NULL, is_active BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE listing_towns ( id SERIAL PRIMARY KEY, listing_id INTEGER NOT NULL REFERENCES listings(id) ON DELETE CASCADE, town VARCHAR(100) NOT NULL, UNIQUE(listing_id, town) ); CREATE TABLE service_listing_towns ( id SERIAL PRIMARY KEY, service_listing_id INTEGER NOT NULL REFERENCES service_listings(id) ON DELETE CASCADE, town VARCHAR(100) NOT NULL, UNIQUE(service_listing_id, town) );