+ <% If showAll = "1" Then %>
+
+ Show Active Only
+
+ <% Else %>
+
+ Show All History
+
+ <% End If %>
Calendar View
@@ -73,11 +92,17 @@
"END as is_complete " & _
"FROM notifications n " & _
"LEFT JOIN notificationtypes nt ON n.notificationtypeid = nt.notificationtypeid " & _
- "LEFT JOIN businessunits bu ON n.businessunitid = bu.businessunitid " & _
- "WHERE n.isactive = 1 OR " & _
- " (n.isactive = 0 AND n.endtime IS NOT NULL AND " & _
- " DATE_ADD(n.endtime, INTERVAL 30 MINUTE) >= NOW()) " & _
- "ORDER BY n.notificationid DESC"
+ "LEFT JOIN businessunits bu ON n.businessunitid = bu.businessunitid "
+
+ ' Add WHERE clause based on showall parameter
+ If showAll <> "1" Then
+ strSQL = strSQL & _
+ "WHERE n.isactive = 1 OR " & _
+ " (n.isactive = 0 AND n.endtime IS NOT NULL AND " & _
+ " DATE_ADD(n.endtime, INTERVAL 30 MINUTE) >= NOW()) "
+ End If
+
+ strSQL = strSQL & "ORDER BY n.notificationid DESC"
Set rs = objconn.Execute(strSQL)
If rs.EOF Then
diff --git a/install_printer.asp b/install_printer.asp
index f900b2d..5347992 100644
--- a/install_printer.asp
+++ b/install_printer.asp
@@ -4,7 +4,7 @@
' install_printer.asp
' Generates a batch file to install printer(s)
' - If printer has installpath: downloads and runs specific .exe
-' - If no installpath: uses PowerShell to install with universal driver
+' - If no installpath: downloads universal PrinterInstaller.exe
' Usage: install_printer.asp?printer=GuardDesk-HIDDTC
Dim printerNames, printerIds, printerArray, i, fileName
@@ -30,10 +30,11 @@ If printerIds <> "" Then
strSQL = "SELECT p.printerid, p.printerwindowsname, p.printercsfname, " & _
"p.fqdn, p.ipaddress, p.installpath, " & _
- "v.vendor, m.modelnumber " & _
+ "v.vendor, m.modelnumber, ma.alias, ma.machinenumber " & _
"FROM printers p " & _
"LEFT JOIN models m ON p.modelid = m.modelnumberid " & _
"LEFT JOIN vendors v ON m.vendorid = v.vendorid " & _
+ "LEFT JOIN machines ma ON p.machineid = ma.machineid " & _
"WHERE p.printerid IN ("
For i = 0 To UBound(printerArray)
@@ -48,10 +49,11 @@ ElseIf printerNames <> "" Then
strSQL = "SELECT p.printerid, p.printerwindowsname, p.printercsfname, " & _
"p.fqdn, p.ipaddress, p.installpath, " & _
- "v.vendor, m.modelnumber " & _
+ "v.vendor, m.modelnumber, ma.alias, ma.machinenumber " & _
"FROM printers p " & _
"LEFT JOIN models m ON p.modelid = m.modelnumberid " & _
"LEFT JOIN vendors v ON m.vendorid = v.vendorid " & _
+ "LEFT JOIN machines ma ON p.machineid = ma.machineid " & _
"WHERE p.printerwindowsname IN ("
For i = 0 To UBound(printerArray)
@@ -76,6 +78,77 @@ If printerIds <> "" Or printerNames <> "" Then
printerInfo("vendor") = rs("vendor") & ""
printerInfo("model") = rs("modelnumber") & ""
+ ' Determine printer name to use
+ ' Prefer Windows Name from database if it's already in standardized format (contains dashes)
+ ' Otherwise generate standardized name (same logic as api_printers.asp)
+ Dim machineAlias, machineNumber, machineName, cleanMachine, cleanModel, shortDescription, standardName
+
+ ' Check if printerwindowsname is already standardized (contains dashes, not just spaces)
+ If InStr(printerInfo("name"), "-") > 0 Then
+ ' Use database Windows Name as-is (user manually set it)
+ standardName = printerInfo("name")
+ Else
+ ' Generate standardized name
+ machineAlias = rs("alias") & ""
+ machineNumber = rs("machinenumber") & ""
+ If machineAlias <> "" Then
+ machineName = machineAlias
+ Else
+ machineName = machineNumber
+ End If
+
+ cleanMachine = Replace(machineName, " ", "")
+ cleanMachine = Replace(cleanMachine, "Machine", "")
+ cleanModel = Replace(printerInfo("model"), " ", "")
+
+ ' Extract short description from model number
+ If InStr(cleanModel, "ColorLaserJet") > 0 Then
+ shortDescription = "ColorLaserJet"
+ ElseIf InStr(cleanModel, "LaserJetPro") > 0 Then
+ shortDescription = "LaserJetPro"
+ ElseIf InStr(cleanModel, "LaserJet") > 0 Then
+ shortDescription = "LaserJet"
+ ElseIf InStr(cleanModel, "Altalink") > 0 Then
+ shortDescription = "Altalink"
+ ElseIf InStr(cleanModel, "Versalink") > 0 Then
+ shortDescription = "Versalink"
+ ElseIf InStr(cleanModel, "DesignJet") > 0 Then
+ shortDescription = "DesignJet"
+ ElseIf InStr(cleanModel, "DTC") > 0 Then
+ shortDescription = "DTC"
+ Else
+ Dim j, char2
+ shortDescription = ""
+ For j = 1 To Len(cleanModel)
+ char2 = Mid(cleanModel, j, 1)
+ If char2 >= "0" And char2 <= "9" Then
+ Exit For
+ End If
+ shortDescription = shortDescription & char2
+ Next
+ If shortDescription = "" Then
+ shortDescription = cleanModel
+ End If
+ End If
+
+ ' Build standard name: CSFName-Location-VendorModel
+ If printerInfo("csfname") <> "" And printerInfo("csfname") <> "NONE" And printerInfo("csfname") <> "gage lab " Then
+ If cleanMachine <> "" And LCase(printerInfo("csfname")) <> LCase(cleanMachine) Then
+ standardName = printerInfo("csfname") & "-" & cleanMachine & "-" & printerInfo("vendor") & shortDescription
+ Else
+ standardName = printerInfo("csfname") & "-" & printerInfo("vendor") & shortDescription
+ End If
+ Else
+ If cleanMachine <> "" Then
+ standardName = cleanMachine & "-" & printerInfo("vendor") & shortDescription
+ Else
+ standardName = "Printer" & rs("printerid") & "-" & printerInfo("vendor") & shortDescription
+ End If
+ End If
+ End If
+
+ printerInfo("standardname") = standardName
+
' Determine preferred address
If printerInfo("fqdn") <> "" And printerInfo("fqdn") <> "USB" Then
printerInfo("address") = printerInfo("fqdn")
@@ -156,75 +229,21 @@ Else
Response.Write(" echo ERROR: Could not download installer" & vbCrLf)
Response.Write(")" & vbCrLf)
Else
- ' No installer - use universal driver
- Dim driverName, driverInf
-
- Select Case UCase(printer("vendor"))
- Case "HP"
- driverName = "HP Universal Printing PCL 6"
- driverInf = "hpcu255u.inf"
- Case "XEROX"
- driverName = "Xerox Global Print Driver PCL6"
- driverInf = "xeroxgpd.inf"
- Case "HID"
- driverName = "HP Universal Printing PCL 6"
- driverInf = "hpcu255u.inf"
- Case Else
- driverName = "Generic / Text Only"
- driverInf = ""
- End Select
-
- Response.Write("echo Using universal driver: " & driverName & vbCrLf)
+ ' No specific installer - use universal PrinterInstaller.exe
+ Response.Write("echo Using universal printer installer..." & vbCrLf)
Response.Write("echo." & vbCrLf)
-
- ' Generate PowerShell script to install printer
- Response.Write("powershell -NoProfile -ExecutionPolicy Bypass -Command """ & vbCrLf)
- Response.Write(" Write-Host 'Installing printer with universal driver...' -ForegroundColor Cyan;" & vbCrLf)
- Response.Write(" " & vbCrLf)
- Response.Write(" $printerName = '" & Replace(printer("name"), "'", "''") & "';" & vbCrLf)
- Response.Write(" $address = '" & Replace(printer("address"), "'", "''") & "';" & vbCrLf)
- Response.Write(" $driverName = '" & Replace(driverName, "'", "''") & "';" & vbCrLf)
- Response.Write(" $portName = 'IP_' + $address;" & vbCrLf)
- Response.Write(" " & vbCrLf)
-
- ' Check if driver is installed
- If driverInf <> "" Then
- Response.Write(" # Check if driver exists" & vbCrLf)
- Response.Write(" $driver = Get-PrinterDriver -Name $driverName -ErrorAction SilentlyContinue;" & vbCrLf)
- Response.Write(" if (-not $driver) {" & vbCrLf)
- Response.Write(" Write-Host 'ERROR: Universal driver not found!' -ForegroundColor Red;" & vbCrLf)
- Response.Write(" Write-Host 'Please install ' $driverName ' from Windows Update or driver package' -ForegroundColor Yellow;" & vbCrLf)
- Response.Write(" exit 1;" & vbCrLf)
- Response.Write(" }" & vbCrLf)
- Response.Write(" " & vbCrLf)
- End If
-
- ' Create port
- Response.Write(" # Create TCP/IP port" & vbCrLf)
- Response.Write(" $port = Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue;" & vbCrLf)
- Response.Write(" if (-not $port) {" & vbCrLf)
- Response.Write(" Write-Host 'Creating printer port...' -ForegroundColor Yellow;" & vbCrLf)
- Response.Write(" Add-PrinterPort -Name $portName -PrinterHostAddress $address -ErrorAction Stop;" & vbCrLf)
- Response.Write(" Write-Host 'Port created successfully' -ForegroundColor Green;" & vbCrLf)
- Response.Write(" } else {" & vbCrLf)
- Response.Write(" Write-Host 'Port already exists' -ForegroundColor Green;" & vbCrLf)
- Response.Write(" }" & vbCrLf)
- Response.Write(" " & vbCrLf)
-
- ' Add printer
- Response.Write(" # Add printer" & vbCrLf)
- Response.Write(" $existingPrinter = Get-Printer -Name $printerName -ErrorAction SilentlyContinue;" & vbCrLf)
- Response.Write(" if (-not $existingPrinter) {" & vbCrLf)
- Response.Write(" Write-Host 'Adding printer...' -ForegroundColor Yellow;" & vbCrLf)
- Response.Write(" Add-Printer -Name $printerName -DriverName $driverName -PortName $portName -ErrorAction Stop;" & vbCrLf)
- Response.Write(" Write-Host 'Printer installed successfully!' -ForegroundColor Green;" & vbCrLf)
- Response.Write(" } else {" & vbCrLf)
- Response.Write(" Write-Host 'Printer already exists' -ForegroundColor Green;" & vbCrLf)
- Response.Write(" }" & vbCrLf)
- Response.Write("""" & vbCrLf)
- Response.Write("" & vbCrLf)
- Response.Write("if %ERRORLEVEL% neq 0 (" & vbCrLf)
- Response.Write(" echo ERROR: Failed to install printer" & vbCrLf)
+ Response.Write("echo Downloading PrinterInstaller.exe..." & vbCrLf)
+ Response.Write("powershell -NoProfile -Command """ & _
+ "$ProgressPreference = 'SilentlyContinue'; " & _
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; " & _
+ "Invoke-WebRequest -Uri 'https://tsgwp00525.rd.ds.ge.com/shopdb/installers/PrinterInstaller.exe' " & _
+ "-OutFile '%TEMP%\PrinterInstaller.exe' -UseBasicParsing -UseDefaultCredentials""" & vbCrLf)
+ Response.Write("if exist ""%TEMP%\PrinterInstaller.exe"" (" & vbCrLf)
+ Response.Write(" echo Running installer..." & vbCrLf)
+ Response.Write(" ""%TEMP%\PrinterInstaller.exe"" ""/PRINTER=" & printer("standardname") & """" & vbCrLf)
+ Response.Write(" del ""%TEMP%\PrinterInstaller.exe"" 2>nul" & vbCrLf)
+ Response.Write(") else (" & vbCrLf)
+ Response.Write(" echo ERROR: Could not download PrinterInstaller.exe" & vbCrLf)
Response.Write(")" & vbCrLf)
End If
diff --git a/sql/CREATE_vw_network_devices_with_fqdn.sql b/sql/CREATE_vw_network_devices_with_fqdn.sql
new file mode 100644
index 0000000..009e4bf
--- /dev/null
+++ b/sql/CREATE_vw_network_devices_with_fqdn.sql
@@ -0,0 +1,156 @@
+-- =============================================================================
+-- Create/Update vw_network_devices View - Phase 2 Compatible
+-- =============================================================================
+-- Date: 2025-11-21
+-- Purpose: Update vw_network_devices to query machines table for infrastructure
+-- Printers remain in printers table (has fqdn column)
+-- Other network devices (IDF, Server, Switch, Camera, Access Point) are in machines table
+-- Machine Type IDs: 15=IDF, 16=Server, 17=Switch, 18=Camera, 19=Access Point, 20=Printer
+-- =============================================================================
+
+USE shopdb;
+
+-- Drop existing view
+DROP VIEW IF EXISTS vw_network_devices;
+
+-- Create view with machines table for infrastructure + printers table for printers
+CREATE VIEW vw_network_devices AS
+-- Network infrastructure devices from machines table (IDF, Server, Switch, Camera, Access Point)
+SELECT
+ mt.machinetype AS device_type,
+ m.machineid AS device_id,
+ COALESCE(m.alias, m.machinenumber) AS device_name,
+ m.modelnumberid AS modelid,
+ mo.modelnumber,
+ v.vendor,
+ m.serialnumber,
+ c.address AS ipaddress,
+ NULL AS fqdn, -- Infrastructure devices don't have FQDN
+ m.machinenotes AS description,
+ m.maptop,
+ m.mapleft,
+ m.isactive,
+ -- Camera-specific: which IDF closet (from machinerelationships)
+ (SELECT mr.related_machineid
+ FROM machinerelationships mr
+ JOIN relationshiptypes rt ON mr.relationshiptypeid = rt.relationshiptypeid
+ WHERE mr.machineid = m.machineid
+ AND rt.relationshiptype = 'Located In'
+ AND mr.isactive = 1
+ LIMIT 1) AS idfid,
+ -- Camera-specific: IDF name
+ (SELECT m2.machinenumber
+ FROM machinerelationships mr
+ JOIN relationshiptypes rt ON mr.relationshiptypeid = rt.relationshiptypeid
+ JOIN machines m2 ON mr.related_machineid = m2.machineid
+ WHERE mr.machineid = m.machineid
+ AND rt.relationshiptype = 'Located In'
+ AND mr.isactive = 1
+ LIMIT 1) AS idfname,
+ -- Camera-specific: MAC address (from communications table)
+ (SELECT c2.macaddress
+ FROM communications c2
+ WHERE c2.machineid = m.machineid
+ AND c2.macaddress IS NOT NULL
+ LIMIT 1) AS macaddress
+FROM machines m
+JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid
+LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid
+LEFT JOIN vendors v ON mo.vendorid = v.vendorid
+LEFT JOIN communications c ON m.machineid = c.machineid
+ AND c.isprimary = 1
+ AND c.isactive = 1
+WHERE m.machinetypeid IN (15, 16, 17, 18, 19) -- IDF, Server, Switch, Camera, Access Point
+ AND m.pctypeid IS NULL -- Exclude PCs
+
+UNION ALL
+
+-- Printers from printers table (has fqdn column)
+SELECT
+ 'Printer' AS device_type,
+ p.printerid AS device_id,
+ p.printerwindowsname AS device_name,
+ p.modelid,
+ m.modelnumber,
+ v.vendor,
+ p.serialnumber,
+ p.ipaddress,
+ p.fqdn, -- Printers have FQDN
+ NULL AS description,
+ p.maptop,
+ p.mapleft,
+ p.isactive,
+ NULL AS idfid,
+ NULL AS idfname,
+ NULL AS macaddress
+FROM printers p
+LEFT JOIN models m ON p.modelid = m.modelnumberid
+LEFT JOIN vendors v ON m.vendorid = v.vendorid;
+
+-- =============================================================================
+-- Verification
+-- =============================================================================
+
+SELECT '✓ View created successfully (Phase 2)' AS status;
+
+SELECT 'View columns:' AS '';
+SELECT COLUMN_NAME, ORDINAL_POSITION
+FROM INFORMATION_SCHEMA.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE()
+ AND TABLE_NAME = 'vw_network_devices'
+ORDER BY ORDINAL_POSITION;
+
+SELECT '' AS '';
+SELECT 'Device counts by type:' AS '';
+SELECT device_type, COUNT(*) AS count
+FROM vw_network_devices
+GROUP BY device_type
+ORDER BY device_type;
+
+SELECT '' AS '';
+SELECT 'Sample devices (first 10):' AS '';
+SELECT device_type, device_name, vendor, modelnumber, ipaddress, fqdn
+FROM vw_network_devices
+LIMIT 10;
+
+-- =============================================================================
+-- NOTES
+-- =============================================================================
+--
+-- View Columns (16 total):
+-- 1. device_type - Type: IDF, Server, Switch, Camera, Access Point (from machines), Printer (from printers)
+-- 2. device_id - machineid or printerid
+-- 3. device_name - Device name/alias
+-- 4. modelid - Foreign key to models table
+-- 5. modelnumber - Model number from models table
+-- 6. vendor - Vendor name from vendors table
+-- 7. serialnumber - Serial number
+-- 8. ipaddress - IP address
+-- 9. fqdn - FQDN (Printers only)
+-- 10. description - Description
+-- 11. maptop - Y coordinate for map display
+-- 12. mapleft - X coordinate for map display
+-- 13. isactive - Active flag
+-- 14. idfid - IDF ID (Camera only, from machinerelationships)
+-- 15. idfname - IDF name (Camera only, from machinerelationships)
+-- 16. macaddress - MAC address (Camera only, from communications)
+--
+-- Machine Type IDs:
+-- 15 = IDF
+-- 16 = Server
+-- 17 = Switch
+-- 18 = Camera
+-- 19 = Access Point
+-- 20 = Printer (still in printers table, not machines)
+--
+-- Data Sources:
+-- - IDFs, Servers, Switches, Cameras, Access Points: machines table
+-- - Printers: printers table (has fqdn column)
+--
+-- Used by: network_devices.asp
+--
+-- To add a new IDF:
+-- INSERT INTO machines (machinetypeid, machinenumber, alias, mapleft, maptop, isactive)
+-- VALUES (15, 'IDF-NAME', 'Description', 100, 100, 1);
+--
+-- =============================================================================
diff --git a/sql/DATA_MIGRATION_EXPLAINED.md b/sql/DATA_MIGRATION_EXPLAINED.md
new file mode 100644
index 0000000..1b0aed6
--- /dev/null
+++ b/sql/DATA_MIGRATION_EXPLAINED.md
@@ -0,0 +1,610 @@
+# Data Migration Scripts - What They Actually Do
+
+**Date:** 2025-11-20
+**Purpose:** Explain exactly what each data migration script does to production data
+**Critical:** This preserves ALL production data while restructuring for Phase 2
+
+---
+
+## Overview: What We're Migrating
+
+Your production database has data split across OLD tables that needs to be consolidated into NEW Phase 2 tables:
+
+### Source Data (OLD Tables - Production)
+- **pc table:** 286 PCs with hostnames, serial numbers, OS, etc.
+- **machines table:** 275 equipment/machines (CNCs, lathes, mills, etc.)
+- **pc_network_interfaces:** 705 network interfaces (IP addresses, MAC addresses)
+- **pc_dualpath_assignments:** PC relationships for redundancy
+- **machine_pc_relationships:** Which PCs control which equipment
+- **machines.ipaddress1/ipaddress2:** IP addresses stored in machine records
+
+### Destination (NEW Phase 2 Structure)
+- **machines table:** Will contain BOTH 275 equipment + 286 PCs = 561 total records
+- **communications table:** Will contain ALL network interfaces (from machines + PCs)
+- **machinerelationships table:** Will contain ALL relationships (dualpath + control)
+- **machinestatus table:** Replaces pcstatus (renamed for broader scope)
+
+---
+
+## Migration Script Breakdown
+
+### Script 1: Backup Machine IP Addresses
+**File:** `01_backup_machine_ips.sql`
+
+**What It Does:**
+```sql
+-- Create temporary backup table
+CREATE TABLE _backup_machine_ips AS
+SELECT
+ machineid,
+ machinenumber,
+ ipaddress1,
+ ipaddress2
+FROM machines
+WHERE ipaddress1 IS NOT NULL OR ipaddress2 IS NOT NULL;
+```
+
+**Why:**
+- Machines table currently has `ipaddress1` and `ipaddress2` columns
+- We're about to DROP these columns (ALTER TABLE)
+- But we need this data to move to the `communications` table
+- This backup preserves ~275 machine IP addresses before columns are dropped
+
+**Data Example:**
+```
+machineid | machinenumber | ipaddress1 | ipaddress2
+----------|---------------|-----------------|----------------
+1 | 0600 | 10.80.11.100 | NULL
+2 | 0612 | 10.80.11.101 | 10.80.11.102
+3 | 3001 | 10.80.12.50 | NULL
+```
+
+**Result:** Temporary backup table with all machine IPs preserved
+
+---
+
+### Script 2: ALTER Machines Table (Add Phase 2 Columns)
+**File:** `02_alter_machines.sql`
+
+**What It Does:**
+```sql
+ALTER TABLE machines
+ -- Add new Phase 2 columns for PCs
+ ADD COLUMN hostname varchar(100) DEFAULT NULL,
+ ADD COLUMN serialnumber varchar(50) DEFAULT NULL,
+ ADD COLUMN loggedinuser varchar(100) DEFAULT NULL,
+ ADD COLUMN pctypeid int(11) DEFAULT NULL,
+ ADD COLUMN osid int(11) DEFAULT NULL,
+ ADD COLUMN controllertypeid int(11) DEFAULT NULL,
+ ADD COLUMN controllerosid int(11) DEFAULT NULL,
+ ADD COLUMN controllermodelid int(11) DEFAULT NULL,
+ ADD COLUMN machinestatusid int(11) DEFAULT NULL,
+ ADD COLUMN lastupdated datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ ADD COLUMN requires_manual_machine_config tinyint(1) DEFAULT '0',
+
+ -- Remove old IP columns (data already backed up)
+ DROP COLUMN ipaddress1,
+ DROP COLUMN ipaddress2;
+```
+
+**Why:**
+- Makes machines table able to store BOTH equipment AND PCs
+- PCs identified by `pctypeid IS NOT NULL`
+- Equipment identified by `pctypeid IS NULL`
+- Removes IP columns because they'll go in communications table
+
+**Impact on Existing Data:**
+- ✅ All 275 existing machines preserved (no data loss)
+- ✅ New columns added with NULL defaults (safe)
+- ✅ ipaddress1/ipaddress2 dropped (but backed up in Script 1)
+
+**After This Script:**
+```
+machines table now has:
+- 275 existing equipment records (pctypeid = NULL)
+- Ready to receive 286 PC records (pctypeid = NOT NULL)
+```
+
+---
+
+### Script 3: Create New Phase 2 Tables
+**File:** `05_create_phase2_tables.sql`
+
+**What It Does:**
+```sql
+-- Create communications table
+CREATE TABLE communications (
+ comid int(11) NOT NULL AUTO_INCREMENT,
+ machineid int(11) NOT NULL,
+ comstypeid int(11) NOT NULL DEFAULT 1,
+ address varchar(100) DEFAULT NULL, -- IP address or COM port
+ macaddress varchar(17) DEFAULT NULL,
+ interfacename varchar(50) DEFAULT NULL,
+ isprimary tinyint(1) DEFAULT 0,
+ isactive tinyint(1) DEFAULT 1,
+ settings text,
+ PRIMARY KEY (comid)
+);
+
+-- Create machinerelationships table
+CREATE TABLE machinerelationships (
+ relationshipid int(11) NOT NULL AUTO_INCREMENT,
+ machineid int(11) NOT NULL,
+ related_machineid int(11) NOT NULL,
+ relationshiptypeid int(11) NOT NULL,
+ isactive tinyint(1) DEFAULT 1,
+ notes text,
+ PRIMARY KEY (relationshipid)
+);
+
+-- Create machinestatus table (copy from pcstatus)
+CREATE TABLE machinestatus (
+ machinestatusid int(11) NOT NULL AUTO_INCREMENT,
+ machinestatus varchar(50) NOT NULL,
+ PRIMARY KEY (machinestatusid)
+);
+
+INSERT INTO machinestatus (machinestatusid, machinestatus)
+SELECT pcstatusid, pcstatus FROM pcstatus;
+
+-- Create other Phase 2 tables (compliance, warranties, etc.)
+```
+
+**Why:**
+- Creates infrastructure for unified data storage
+- No impact on existing data (these are new, empty tables)
+
+**After This Script:**
+```
+New empty tables created:
+- communications (ready for network interface data)
+- machinerelationships (ready for PC relationships)
+- machinestatus (populated with pcstatus data)
+- compliance, warranties, etc. (empty, ready for future data)
+```
+
+---
+
+### Script 4: Migrate PCs from pc → machines
+**File:** `06_migrate_pcs.sql`
+
+**What It Does:**
+```sql
+INSERT INTO machines (
+ hostname, -- PC hostname
+ serialnumber, -- PC serial number
+ loggedinuser, -- Currently logged in user
+ pctypeid, -- PC type (Engineer, Shopfloor, Standard, etc.)
+ osid, -- Operating system
+ modelnumberid, -- PC model (Dell, HP, etc.)
+ businessunitid, -- Business unit assignment
+ machinestatusid, -- Status (In Use, Spare, etc.)
+ machinenumber, -- Machine this PC is assigned to
+ alias, -- PC alias/friendly name
+ machinenotes, -- Notes (was 'notes' in pc table)
+ printerid, -- Assigned printer
+ isactive, -- Active flag
+ islocationonly -- Always 0 for PCs
+)
+SELECT
+ p.hostname,
+ p.serialnumber,
+ p.loggedinuser,
+ p.pctypeid, -- This makes it a PC (NOT NULL = PC)
+ p.osid,
+ p.modelnumberid,
+ p.businessunitid,
+ p.pcstatusid AS machinestatusid, -- Map pcstatusid to machinestatusid
+ p.machinenumber,
+ p.alias,
+ p.notes AS machinenotes, -- Column rename
+ p.printerid,
+ p.isactive,
+ 0 AS islocationonly
+FROM pc p
+WHERE p.isactive = 1;
+```
+
+**Why:**
+- Copies all 286 PCs from separate `pc` table into `machines` table
+- Sets `pctypeid` to NOT NULL (this identifies them as PCs, not equipment)
+- Renames `notes` → `machinenotes` to match Phase 2 schema
+- Maps `pcstatusid` → `machinestatusid`
+
+**Before:**
+```
+machines table: 275 equipment records
+pc table: 286 PC records
+```
+
+**After:**
+```
+machines table: 561 total records
+ - 275 equipment (pctypeid = NULL)
+ - 286 PCs (pctypeid = 1, 2, 3, 4, 5, etc.)
+pc table: 286 records (unchanged - kept as backup)
+```
+
+**Data Example:**
+```
+OLD (pc table):
+pcid | hostname | serialnumber | pctypeid | machinenumber
+-----|---------------|--------------|----------|---------------
+1 | SHOP-PC-01 | ABC123 | 3 | 0600
+2 | ENG-WS-05 | XYZ789 | 2 | 3001
+
+NEW (machines table after migration):
+machineid | hostname | serialnumber | pctypeid | machinenumber
+----------|---------------|--------------|----------|---------------
+276 | SHOP-PC-01 | ABC123 | 3 | 0600
+277 | ENG-WS-05 | XYZ789 | 2 | 3001
+```
+
+**Important:**
+- PC table is NOT dropped (kept as backup for 30 days)
+- Duplicate check prevents re-running script
+
+---
+
+### Script 5: Migrate Machine IPs → communications
+**File:** `07_migrate_machine_ips.sql`
+
+**What It Does:**
+```sql
+-- Migrate ipaddress1 (primary interface)
+INSERT INTO communications (
+ machineid,
+ comstypeid, -- 1 = Network/TCP-IP
+ address, -- The IP address
+ isprimary, -- 1 = primary interface
+ interfacename, -- "Interface 1"
+ isactive
+)
+SELECT
+ machineid,
+ 1 AS comstypeid,
+ ipaddress1,
+ 1 AS isprimary,
+ 'Interface 1' AS interfacename,
+ 1 AS isactive
+FROM _backup_machine_ips
+WHERE ipaddress1 IS NOT NULL AND ipaddress1 != '';
+
+-- Migrate ipaddress2 (secondary interface)
+INSERT INTO communications (
+ machineid,
+ comstypeid,
+ address,
+ isprimary, -- 0 = secondary interface
+ interfacename, -- "Interface 2"
+ isactive
+)
+SELECT
+ machineid,
+ 1 AS comstypeid,
+ ipaddress2,
+ 0 AS isprimary,
+ 'Interface 2' AS interfacename,
+ 1 AS isactive
+FROM _backup_machine_ips
+WHERE ipaddress2 IS NOT NULL AND ipaddress2 != '';
+```
+
+**Why:**
+- Recovers IP addresses that were in machines.ipaddress1 and ipaddress2
+- Moves them to unified communications table
+- Marks ipaddress1 as primary, ipaddress2 as secondary
+
+**Data Example:**
+```
+OLD (machines table - columns dropped):
+machineid | ipaddress1 | ipaddress2
+----------|---------------|-------------
+1 | 10.80.11.100 | NULL
+2 | 10.80.11.101 | 10.80.11.102
+
+NEW (communications table):
+comid | machineid | address | isprimary | interfacename
+------|-----------|---------------|-----------|---------------
+1 | 1 | 10.80.11.100 | 1 | Interface 1
+2 | 2 | 10.80.11.101 | 1 | Interface 1
+3 | 2 | 10.80.11.102 | 0 | Interface 2
+```
+
+**Result:** ~275 machine IP addresses preserved in communications table
+
+---
+
+### Script 6: Migrate PC Network Interfaces → communications
+**File:** `08_migrate_pc_interfaces.sql`
+
+**What It Does:**
+```sql
+INSERT INTO communications (
+ machineid, -- PC's new machineid (from machines table)
+ comstypeid, -- 1 = Network
+ address, -- IP address
+ macaddress, -- MAC address
+ isprimary, -- Primary interface flag
+ interfacename, -- "Interface 1", "Interface 2", etc.
+ isactive
+)
+SELECT
+ m.machineid, -- Find PC's new machineid by matching hostname
+ 1 AS comstypeid,
+ pni.ipaddress,
+ pni.macaddress,
+ pni.isprimary,
+ CONCAT('Interface ',
+ ROW_NUMBER() OVER (PARTITION BY m.machineid ORDER BY pni.isprimary DESC)
+ ) AS interfacename,
+ pni.isactive
+FROM pc_network_interfaces pni
+JOIN pc p ON pni.pcid = p.pcid
+JOIN machines m ON m.hostname = p.hostname AND m.pctypeid IS NOT NULL
+WHERE pni.isactive = 1;
+```
+
+**Why:**
+- Copies all 705 network interfaces from `pc_network_interfaces` table
+- Joins to find PC's NEW machineid in machines table (by hostname match)
+- Preserves IP addresses, MAC addresses, primary flag
+- Generates interface names (Interface 1, Interface 2, Interface 3)
+
+**Data Example:**
+```
+OLD (pc_network_interfaces):
+interfaceid | pcid | ipaddress | macaddress | isprimary
+------------|------|---------------|-------------------|----------
+1 | 1 | 10.80.50.10 | 00:11:22:33:44:55 | 1
+2 | 1 | 10.80.50.11 | 00:11:22:33:44:56 | 0
+3 | 2 | 10.80.50.20 | 00:11:22:33:44:57 | 1
+
+PC table:
+pcid | hostname
+-----|------------
+1 | SHOP-PC-01
+2 | ENG-WS-05
+
+Machines table (after PC migration):
+machineid | hostname | pctypeid
+----------|---------------|----------
+276 | SHOP-PC-01 | 3
+277 | ENG-WS-05 | 2
+
+NEW (communications table):
+comid | machineid | address | macaddress | isprimary | interfacename
+------|-----------|---------------|-------------------|-----------|---------------
+4 | 276 | 10.80.50.10 | 00:11:22:33:44:55 | 1 | Interface 1
+5 | 276 | 10.80.50.11 | 00:11:22:33:44:56 | 0 | Interface 2
+6 | 277 | 10.80.50.20 | 00:11:22:33:44:57 | 1 | Interface 1
+```
+
+**Result:** All 705 PC network interfaces migrated to communications table
+
+---
+
+### Script 7: Migrate PC Dualpath Relationships → machinerelationships
+**File:** `09_migrate_relationships.sql`
+
+**What It Does:**
+```sql
+-- Part 1: Migrate Dualpath (PC ↔ PC redundancy)
+INSERT INTO machinerelationships (
+ machineid, -- Primary PC
+ related_machineid, -- Dualpath PC
+ relationshiptypeid, -- 2 = Dualpath
+ isactive
+)
+SELECT
+ m1.machineid, -- Primary PC's new machineid
+ m2.machineid, -- Dualpath PC's new machineid
+ 2 AS relationshiptypeid, -- Dualpath relationship type
+ 1 AS isactive
+FROM pc_dualpath_assignments pda
+JOIN pc p1 ON pda.pcid = p1.pcid
+JOIN pc p2 ON pda.dualpath_pcid = p2.pcid
+JOIN machines m1 ON m1.hostname = p1.hostname AND m1.pctypeid IS NOT NULL
+JOIN machines m2 ON m2.hostname = p2.hostname AND m2.pctypeid IS NOT NULL;
+
+-- Part 2: Migrate Controls (PC → Equipment)
+INSERT INTO machinerelationships (
+ machineid, -- PC that controls equipment
+ related_machineid, -- Equipment being controlled
+ relationshiptypeid, -- 1 = Controls
+ isactive
+)
+SELECT
+ mpc.pcid AS machineid, -- PC's machineid (from machines)
+ mpc.machineid AS related_machineid, -- Equipment's machineid
+ 1 AS relationshiptypeid, -- Controls relationship
+ mpc.isactive
+FROM machine_pc_relationships mpc;
+```
+
+**Why:**
+- Preserves PC redundancy relationships (dualpath)
+- Preserves which PCs control which equipment
+- Uses new unified machinerelationships table
+
+**Data Example:**
+
+**Dualpath (PC redundancy):**
+```
+OLD (pc_dualpath_assignments):
+assignmentid | pcid | dualpath_pcid
+-------------|------|---------------
+1 | 5 | 12
+
+PC table:
+pcid | hostname
+-----|-------------
+5 | CNC-PC-01
+12 | CNC-PC-02
+
+Machines (after migration):
+machineid | hostname | pctypeid
+----------|-------------|----------
+280 | CNC-PC-01 | 3
+291 | CNC-PC-02 | 3
+
+NEW (machinerelationships):
+relationshipid | machineid | related_machineid | relationshiptypeid
+---------------|-----------|-------------------|-------------------
+1 | 280 | 291 | 2 (Dualpath)
+```
+
+**Controls (PC → Equipment):**
+```
+OLD (machine_pc_relationships):
+relationshipid | pcid | machineid
+---------------|------|----------
+1 | 280 | 50
+
+NEW (machinerelationships):
+relationshipid | machineid | related_machineid | relationshiptypeid
+---------------|-----------|-------------------|-------------------
+2 | 280 | 50 | 1 (Controls)
+```
+
+**Meaning:**
+- PC with machineid 280 controls equipment with machineid 50
+- PC with machineid 280 has dualpath redundancy with PC machineid 291
+
+**Result:** All PC relationships preserved in unified table
+
+---
+
+## Summary: What Happens to Your Data
+
+### Before Migration:
+```
+pc table: 286 PCs
+machines table: 275 equipment
+pc_network_interfaces: 705 network interfaces
+machine IPs (in machines): ~275 IP addresses
+pc_dualpath_assignments: ~50 relationships
+machine_pc_relationships: ~100 relationships
+```
+
+### After Migration:
+```
+machines table: 561 records (275 equipment + 286 PCs)
+communications table: ~980 records (275 machine IPs + 705 PC interfaces)
+machinerelationships table: ~150 records (dualpath + controls)
+machinestatus table: Copied from pcstatus
+
+OLD TABLES KEPT AS BACKUP (for 30 days):
+pc table: 286 records (unchanged)
+pc_network_interfaces: 705 records (unchanged)
+pc_dualpath_assignments: ~50 records (unchanged)
+machine_pc_relationships: ~100 records (unchanged)
+```
+
+---
+
+## Critical Safety Features
+
+### 1. No Data Loss
+- ✅ Old tables are NOT dropped (kept for 30 days)
+- ✅ Can roll back if issues found
+- ✅ Data copied, not moved
+
+### 2. Duplicate Prevention
+```sql
+-- Example: Only insert PCs that don't already exist
+INSERT INTO machines (...)
+SELECT ...
+FROM pc p
+WHERE NOT EXISTS (
+ SELECT 1 FROM machines m
+ WHERE m.hostname = p.hostname AND m.pctypeid IS NOT NULL
+);
+```
+
+### 3. Data Validation Queries
+After each script runs, verification queries check:
+- Row counts match (286 PCs in old table = 286 PCs in new table)
+- No duplicate hostnames
+- All relationships have valid machine IDs
+- No orphaned records
+
+---
+
+## What Gets Modified vs Copied
+
+### MODIFIED (Structure Only):
+- **machines table** - Columns added/removed, but existing 275 equipment preserved
+- **businessunits table** - Columns added, existing data unchanged
+- **controllertypes table** - Columns added, existing data unchanged
+
+### COPIED (Data Duplicated):
+- **pc → machines** - 286 PCs copied, pc table kept as backup
+- **pc_network_interfaces → communications** - 705 interfaces copied, old table kept
+- **machine IPs → communications** - IPs extracted before columns dropped
+- **Relationships → machinerelationships** - Copied, old tables kept
+
+### CREATED (New):
+- **communications table** - Brand new, receives copied data
+- **machinerelationships table** - Brand new, receives copied data
+- **machinestatus table** - Created, populated from pcstatus
+- **compliance, warranties, etc.** - Created empty
+
+---
+
+## Rollback Strategy
+
+If something goes wrong:
+
+1. **Before ASP deployment:**
+ - Drop new tables
+ - Restore machines columns (ADD ipaddress1, ipaddress2)
+ - Restore machine IPs from backup table
+ - Delete migrated PCs from machines table
+
+2. **After ASP deployment:**
+ - Revert ASP code to Phase 1
+ - Keep data in both old and new tables
+ - Fix issues and retry
+
+---
+
+## Timeline
+
+**Estimated execution time:**
+- Script 1 (Backup): 1 second
+- Script 2 (ALTER machines): 10 seconds
+- Script 3 (CREATE tables): 30 seconds
+- Script 4 (Migrate PCs): 30 seconds (286 inserts)
+- Script 5 (Migrate machine IPs): 10 seconds (~275 records)
+- Script 6 (Migrate PC interfaces): 1 minute (705 inserts)
+- Script 7 (Migrate relationships): 30 seconds (~150 inserts)
+
+**Total: ~3-4 minutes for data migration**
+
+---
+
+## Questions These Scripts Answer
+
+**Q: Will I lose production data?**
+A: No. Old tables are kept as backup. Data is copied, not moved.
+
+**Q: What if the same PC exists in both tables?**
+A: Duplicate check prevents re-inserting. Script can be re-run safely.
+
+**Q: Can I roll back?**
+A: Yes. Rollback scripts reverse all changes.
+
+**Q: What happens to machine IPs when columns are dropped?**
+A: Backed up to temp table first, then migrated to communications.
+
+**Q: Will production be down during migration?**
+A: Tables will be locked during ALTER and INSERT operations. Estimated 5-10 minutes.
+
+**Q: What about data added after the backup was taken?**
+A: Scripts work on live production database, not the backup file. All current data migrated.
+
+---
+
+**Status:** Explanation Complete
+**Next Step:** Create actual SQL scripts based on this plan
diff --git a/sql/PRODUCTION_MIGRATION_PLAN.md b/sql/PRODUCTION_MIGRATION_PLAN.md
new file mode 100644
index 0000000..7bd765e
--- /dev/null
+++ b/sql/PRODUCTION_MIGRATION_PLAN.md
@@ -0,0 +1,468 @@
+# Production Migration to Phase 2 Schema - CRITICAL PLAN
+
+**Date:** 2025-11-20
+**Source:** Production Database (database-backup-11-20-25-eod-with-drop.sql)
+**Target:** Phase 2 Schema (as in current dev)
+**Goal:** Migrate production to Phase 2 schema WITHOUT losing production data
+
+---
+
+## ⚠️ CRITICAL DATA PRESERVATION
+
+**Production has MORE data than dev:**
+- Production PCs: **286** (in `pc` table)
+- Dev PCs: **238** (in `machines` table)
+- Production network interfaces: **705**
+- Dev communications: **746**
+
+**Production also has more current data in:**
+- applications (59 in prod)
+- knowledgebase (219 in prod)
+- notifications (64 in prod)
+- machines (275 in prod)
+- printers (45 in prod)
+
+**We MUST preserve ALL production data during migration!**
+
+---
+
+## Migration Strategy: PHASE-BY-PHASE
+
+### Phase 1: Extend Existing Tables (Non-Destructive)
+- ALTER TABLE to add new columns
+- No data loss
+- Reversible
+
+### Phase 2: Create New Tables (Safe)
+- CREATE new Phase 2 tables (communications, compliance, etc.)
+- No impact on existing data
+- Reversible
+
+### Phase 3: Migrate PC Data (Complex)
+- Copy data from `pc` → `machines` (with pctypeid NOT NULL)
+- Copy data from `pc_network_interfaces` → `communications`
+- Keep old tables as backup for 30 days
+- Reversible with rollback scripts
+
+### Phase 4: Update ASP Pages (Application Layer)
+- Deploy Phase 2 ASP code
+- Test thoroughly
+- Rollback ASP code if issues
+
+---
+
+## Data Comparison: Production vs Dev
+
+| Item | Production | Dev | Difference |
+|------|-----------|-----|------------|
+| PCs (in pc table) | 286 | 0 | Prod +286 |
+| PCs (in machines) | 0 | 238 | Dev +238 |
+| Machines (equipment) | 275 | 355 | Dev +80 |
+| Network Interfaces | 705 | 0 | Prod +705 |
+| Communications | 0 | 746 | Dev +746 |
+| Applications | 59 | ? | Prod data |
+| Knowledge Base | 219 | ? | Prod data |
+| Notifications | 64 | ? | Prod data |
+
+**Conclusion:** Production has significant data added since dev started. We must preserve it!
+
+---
+
+## Tables Requiring Changes
+
+### 1. ALTER TABLE (4 tables - preserve data)
+
+#### `machines` - Add 11 Phase 2 columns
+```sql
+ALTER TABLE machines
+ ADD COLUMN hostname varchar(100) DEFAULT NULL AFTER machinenumber,
+ ADD COLUMN serialnumber varchar(50) DEFAULT NULL COMMENT 'Equipment serial number',
+ ADD COLUMN loggedinuser varchar(100) DEFAULT NULL,
+ ADD COLUMN pctypeid int(11) DEFAULT NULL,
+ ADD COLUMN osid int(11) DEFAULT NULL COMMENT 'Foreign key to operatingsystems table',
+ ADD COLUMN controllertypeid int(11) DEFAULT NULL,
+ ADD COLUMN controllerosid int(11) DEFAULT NULL,
+ ADD COLUMN controllermodelid int(11) DEFAULT NULL,
+ ADD COLUMN machinestatusid int(11) DEFAULT NULL,
+ ADD COLUMN lastupdated datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ ADD COLUMN requires_manual_machine_config tinyint(1) DEFAULT '0',
+ DROP COLUMN ipaddress1,
+ DROP COLUMN ipaddress2;
+```
+
+**Impact:**
+- ✅ Preserves all 275 production machines
+- ⚠️ Removes ipaddress1/ipaddress2 (will be migrated to communications)
+- ✅ Makes room for 286 PCs to be added
+
+#### `businessunits` - Add 5 columns
+```sql
+ALTER TABLE businessunits
+ ADD COLUMN liaisonname varchar(100) DEFAULT NULL,
+ ADD COLUMN liaisonsso varchar(50) DEFAULT NULL,
+ ADD COLUMN facility_id varchar(50) DEFAULT NULL COMMENT 'Facility ID (e.g., 212788513)',
+ ADD COLUMN dt_lead varchar(100) DEFAULT NULL COMMENT 'DT Lead name (e.g., Patrick Lipinski)',
+ ADD COLUMN dt_lead_sso varchar(50) DEFAULT NULL COMMENT 'DT Lead SSO';
+```
+
+**Impact:** ✅ No data loss, adds metadata fields
+
+#### `controllertypes` - Add 4 columns
+```sql
+ALTER TABLE controllertypes
+ ADD COLUMN vendorid int(11) DEFAULT NULL,
+ ADD COLUMN controllermodel varchar(100) DEFAULT NULL,
+ ADD COLUMN controller_os varchar(100) DEFAULT NULL COMMENT 'Controller OS (e.g., FANUC OS)',
+ ADD COLUMN controllernotes text;
+```
+
+**Impact:** ✅ No data loss, adds metadata fields
+
+#### `machine_overrides` - Definition changes
+**Action:** Need to investigate exact differences
+
+---
+
+### 2. CREATE NEW TABLES (8 tables - safe)
+
+#### `communications` - Replaces pc_network_interfaces
+```sql
+CREATE TABLE communications (
+ comid int(11) NOT NULL AUTO_INCREMENT,
+ machineid int(11) NOT NULL,
+ comstypeid int(11) NOT NULL DEFAULT 1,
+ address varchar(100) DEFAULT NULL,
+ macaddress varchar(17) DEFAULT NULL,
+ interfacename varchar(50) DEFAULT NULL,
+ isprimary tinyint(1) DEFAULT 0,
+ isactive tinyint(1) DEFAULT 1,
+ settings text,
+ PRIMARY KEY (comid),
+ KEY idx_machineid (machineid),
+ KEY idx_isprimary (isprimary)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+```
+
+#### `comstypes` - Communication types
+```sql
+CREATE TABLE comstypes (
+ comstypeid int(11) NOT NULL AUTO_INCREMENT,
+ comstype varchar(50) NOT NULL,
+ description text,
+ isactive tinyint(1) DEFAULT 1,
+ PRIMARY KEY (comstypeid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+INSERT INTO comstypes (comstypeid, comstype, description) VALUES
+(1, 'Network', 'TCP/IP Network Connection'),
+(2, 'Serial', 'Serial Port (RS-232, RS-485)'),
+(3, 'USB', 'USB Connection'),
+(4, 'Bluetooth', 'Bluetooth Connection'),
+(5, 'WiFi', 'Wireless Network'),
+(6, 'Other', 'Other Communication Type');
+```
+
+#### `compliance` - Compliance tracking
+```sql
+CREATE TABLE compliance (
+ complianceid int(11) NOT NULL AUTO_INCREMENT,
+ machineid int(11) NOT NULL,
+ is_third_party_managed enum('Yes','No','NA') DEFAULT 'NA',
+ third_party_vendorid int(11) DEFAULT NULL,
+ ot_asset_system varchar(100) DEFAULT NULL,
+ ot_asset_device_type varchar(100) DEFAULT NULL,
+ mft varchar(100) DEFAULT NULL,
+ last_scan_date datetime DEFAULT NULL,
+ compliance_status varchar(50) DEFAULT NULL,
+ compliance_notes text,
+ isactive tinyint(1) DEFAULT 1,
+ lastupdated datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (complianceid),
+ KEY idx_machineid (machineid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+```
+
+#### `compliancescans` - Scan history
+```sql
+CREATE TABLE compliancescans (
+ scanid int(11) NOT NULL AUTO_INCREMENT,
+ machineid int(11) NOT NULL,
+ scan_date datetime DEFAULT CURRENT_TIMESTAMP,
+ scan_result text,
+ scan_status varchar(50) DEFAULT NULL,
+ PRIMARY KEY (scanid),
+ KEY idx_machineid (machineid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+```
+
+#### `machinerelationships` - Machine relationships
+```sql
+CREATE TABLE machinerelationships (
+ relationshipid int(11) NOT NULL AUTO_INCREMENT,
+ machineid int(11) NOT NULL,
+ related_machineid int(11) NOT NULL,
+ relationshiptypeid int(11) NOT NULL,
+ isactive tinyint(1) DEFAULT 1,
+ notes text,
+ PRIMARY KEY (relationshipid),
+ KEY idx_machineid (machineid),
+ KEY idx_related_machineid (related_machineid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+```
+
+#### `relationshiptypes` - Relationship types
+```sql
+CREATE TABLE relationshiptypes (
+ relationshiptypeid int(11) NOT NULL AUTO_INCREMENT,
+ relationshiptype varchar(50) NOT NULL,
+ description text,
+ isdirectional tinyint(1) DEFAULT 0,
+ isactive tinyint(1) DEFAULT 1,
+ PRIMARY KEY (relationshiptypeid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+INSERT INTO relationshiptypes (relationshiptypeid, relationshiptype, description, isdirectional) VALUES
+(1, 'Controls', 'PC controls this equipment', 1),
+(2, 'Dualpath', 'Machines share redundant connection', 0);
+```
+
+#### `machinestatus` - Replaces pcstatus
+```sql
+CREATE TABLE machinestatus (
+ machinestatusid int(11) NOT NULL AUTO_INCREMENT,
+ machinestatus varchar(50) NOT NULL,
+ description text,
+ isactive tinyint(1) DEFAULT 1,
+ PRIMARY KEY (machinestatusid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- Copy data from pcstatus
+INSERT INTO machinestatus (machinestatusid, machinestatus, description)
+SELECT pcstatusid, pcstatus, NULL FROM pcstatus;
+```
+
+#### `warranties` - Warranty tracking
+```sql
+CREATE TABLE warranties (
+ warrantyid int(11) NOT NULL AUTO_INCREMENT,
+ machineid int(11) NOT NULL,
+ warrantyname varchar(100) DEFAULT NULL,
+ warrantyenddate date DEFAULT NULL,
+ isactive tinyint(1) DEFAULT 1,
+ PRIMARY KEY (warrantyid),
+ KEY idx_machineid (machineid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+```
+
+---
+
+### 3. MIGRATE DATA (Critical - Most Complex)
+
+#### Step 1: Backup Production Machine IPs
+```sql
+-- Save machine IPs before dropping columns
+CREATE TABLE _backup_machine_ips AS
+SELECT machineid, machinenumber, ipaddress1, ipaddress2
+FROM machines
+WHERE ipaddress1 IS NOT NULL OR ipaddress2 IS NOT NULL;
+```
+
+#### Step 2: Migrate PCs from `pc` to `machines`
+```sql
+-- Insert PCs into machines table
+INSERT INTO machines (
+ hostname, serialnumber, loggedinuser, pctypeid, osid,
+ modelnumberid, businessunitid, machinestatusid,
+ machinenumber, alias, machinenotes, printerid,
+ mapleft, maptop, isactive, islocationonly
+)
+SELECT
+ p.hostname,
+ p.serialnumber,
+ p.loggedinuser,
+ p.pctypeid,
+ p.osid,
+ p.modelnumberid,
+ p.businessunitid,
+ p.pcstatusid AS machinestatusid,
+ p.machinenumber,
+ p.alias,
+ p.notes AS machinenotes,
+ p.printerid,
+ NULL AS mapleft,
+ NULL AS maptop,
+ p.isactive,
+ 0 AS islocationonly
+FROM pc p
+WHERE NOT EXISTS (
+ SELECT 1 FROM machines m WHERE m.hostname = p.hostname
+);
+```
+
+#### Step 3: Migrate Machine IPs to Communications
+```sql
+-- Migrate machine IP addresses (from backup)
+INSERT INTO communications (machineid, comstypeid, address, isprimary, interfacename, isactive)
+SELECT
+ machineid,
+ 1 AS comstypeid, -- Network type
+ ipaddress1,
+ 1 AS isprimary,
+ 'Interface 1' AS interfacename,
+ 1 AS isactive
+FROM _backup_machine_ips
+WHERE ipaddress1 IS NOT NULL AND ipaddress1 != '';
+
+INSERT INTO communications (machineid, comstypeid, address, isprimary, interfacename, isactive)
+SELECT
+ machineid,
+ 1 AS comstypeid,
+ ipaddress2,
+ 0 AS isprimary,
+ 'Interface 2' AS interfacename,
+ 1 AS isactive
+FROM _backup_machine_ips
+WHERE ipaddress2 IS NOT NULL AND ipaddress2 != '';
+```
+
+#### Step 4: Migrate PC Network Interfaces to Communications
+```sql
+-- Migrate pc_network_interfaces to communications
+INSERT INTO communications (machineid, comstypeid, address, macaddress, isprimary, interfacename, isactive)
+SELECT
+ m.machineid,
+ 1 AS comstypeid,
+ pni.ipaddress,
+ pni.macaddress,
+ pni.isprimary,
+ CONCAT('Interface ', ROW_NUMBER() OVER (PARTITION BY m.machineid ORDER BY pni.isprimary DESC)) AS interfacename,
+ pni.isactive
+FROM pc_network_interfaces pni
+JOIN pc p ON pni.pcid = p.pcid
+JOIN machines m ON m.hostname = p.hostname AND m.pctypeid IS NOT NULL;
+```
+
+#### Step 5: Migrate PC Relationships
+```sql
+-- Migrate pc_dualpath_assignments to machinerelationships
+INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive)
+SELECT
+ m1.machineid,
+ m2.machineid,
+ 2 AS relationshiptypeid, -- Dualpath
+ 1 AS isactive
+FROM pc_dualpath_assignments pda
+JOIN pc p1 ON pda.pcid = p1.pcid
+JOIN pc p2 ON pda.dualpath_pcid = p2.pcid
+JOIN machines m1 ON m1.hostname = p1.hostname AND m1.pctypeid IS NOT NULL
+JOIN machines m2 ON m2.hostname = p2.hostname AND m2.pctypeid IS NOT NULL;
+
+-- Migrate machine_pc_relationships (PC controls equipment)
+INSERT INTO machinerelationships (machineid, related_machineid, relationshiptypeid, isactive)
+SELECT
+ mpc.pcid AS machineid,
+ mpc.machineid AS related_machineid,
+ 1 AS relationshiptypeid, -- Controls
+ mpc.isactive
+FROM machine_pc_relationships mpc;
+```
+
+---
+
+## Migration Execution Order
+
+### PRE-MIGRATION (Do First!)
+1. ✅ **BACKUP PRODUCTION DATABASE** (already done: database-backup-11-20-25-eod-with-drop.sql)
+2. ✅ Test all scripts on dev database first
+3. ✅ Schedule maintenance window
+4. ✅ Notify users
+
+### MIGRATION EXECUTION (In Order)
+1. **Phase 1a:** Create backup tables for IPs (5 seconds)
+2. **Phase 1b:** ALTER TABLE machines, businessunits, controllertypes (30 seconds)
+3. **Phase 2:** CREATE new Phase 2 tables (comstypes, communications, etc.) (1 minute)
+4. **Phase 3a:** Migrate PCs from `pc` → `machines` (2 minutes)
+5. **Phase 3b:** Migrate machine IPs to communications (1 minute)
+6. **Phase 3c:** Migrate pc_network_interfaces → communications (2 minutes)
+7. **Phase 3d:** Migrate relationships (1 minute)
+8. **Phase 4:** Verify data integrity (5 minutes)
+9. **Phase 5:** Deploy Phase 2 ASP code (10 minutes)
+10. **Phase 6:** Test all critical pages (30 minutes)
+
+**Total Estimated Time:** 45-60 minutes
+
+---
+
+## Rollback Plan
+
+### If Issues Occur Before ASP Deployment:
+1. Run rollback SQL scripts (reverse ALTERs)
+2. Drop new tables
+3. Restore from backup if needed
+
+### If Issues Occur After ASP Deployment:
+1. Deploy Phase 1 ASP code (revert)
+2. Users can continue working with old schema
+3. Fix issues in dev
+4. Retry migration later
+
+---
+
+## Data Verification Queries
+
+After migration, run these to verify:
+
+```sql
+-- Check PC count matches
+SELECT 'Production PCs' as source, COUNT(*) FROM pc;
+SELECT 'Migrated PCs' as source, COUNT(*) FROM machines WHERE pctypeid IS NOT NULL;
+
+-- Check network interfaces migrated
+SELECT 'Production Interfaces' as source, COUNT(*) FROM pc_network_interfaces;
+SELECT 'Migrated Communications' as source, COUNT(*) FROM communications WHERE machineid IN (SELECT machineid FROM machines WHERE pctypeid IS NOT NULL);
+
+-- Check machines preserved
+SELECT 'Production Machines' as source, COUNT(*) FROM machines; -- Should match before/after
+
+-- Check relationships migrated
+SELECT 'Production Dualpath' as source, COUNT(*) FROM pc_dualpath_assignments;
+SELECT 'Migrated Dualpath' as source, COUNT(*) FROM machinerelationships WHERE relationshiptypeid = 2;
+
+SELECT 'Production PC-Machine' as source, COUNT(*) FROM machine_pc_relationships;
+SELECT 'Migrated Controls' as source, COUNT(*) FROM machinerelationships WHERE relationshiptypeid = 1;
+```
+
+---
+
+## Next Steps
+
+1. **Review this plan** carefully
+2. **Create migration SQL scripts** in proper order
+3. **Test on dev database** first (with production data copy)
+4. **Create rollback scripts** for each phase
+5. **Schedule maintenance window** (2-3 hours)
+6. **Execute migration** with full team monitoring
+7. **Verify data** thoroughly before marking complete
+
+---
+
+## Files to Create
+
+1. `/sql/production_migration/01_backup_machine_ips.sql`
+2. `/sql/production_migration/02_alter_machines.sql`
+3. `/sql/production_migration/03_alter_businessunits.sql`
+4. `/sql/production_migration/04_alter_controllertypes.sql`
+5. `/sql/production_migration/05_create_phase2_tables.sql`
+6. `/sql/production_migration/06_migrate_pcs.sql`
+7. `/sql/production_migration/07_migrate_machine_ips.sql`
+8. `/sql/production_migration/08_migrate_pc_interfaces.sql`
+9. `/sql/production_migration/09_migrate_relationships.sql`
+10. `/sql/production_migration/10_verify_data.sql`
+11. `/sql/production_migration/ROLLBACK_*.sql` (for each phase)
+
+---
+
+**Status:** Plan Complete - Ready for Script Creation
+**Risk Level:** HIGH (Production data migration)
+**Estimated Downtime:** 1-2 hours
+**Reversibility:** HIGH (with rollback scripts and backup)
diff --git a/sql/VIEWS_MIGRATION_ANALYSIS.md b/sql/VIEWS_MIGRATION_ANALYSIS.md
new file mode 100644
index 0000000..3c30085
--- /dev/null
+++ b/sql/VIEWS_MIGRATION_ANALYSIS.md
@@ -0,0 +1,559 @@
+# Views Migration Analysis - Phase 2 Schema
+
+**Date:** 2025-11-20
+**Critical Finding:** 24 out of 27 views require updates for Phase 2 schema
+**Impact:** HIGH - Views will break after PC migration if not updated
+
+---
+
+## Executive Summary
+
+Your production database has **27 views** that provide critical reporting and data aggregation. After analyzing these views:
+
+- ⚠️ **24 views MUST be updated** - They reference deprecated tables (pc, pc_network_interfaces, etc.)
+- ✅ **3 views OK** - Already compatible with Phase 2 schema
+- 🚨 **CRITICAL:** These views must be updated BEFORE or DURING migration, or pages using them will break
+
+---
+
+## Views Status Breakdown
+
+### ✅ Views Compatible with Phase 2 (3 views)
+
+These views don't reference deprecated tables and will continue working after migration:
+
+1. **vw_machinetype_comparison** - Compares machine types between machines and models
+2. **vw_unmapped_machines** - Finds machines without map coordinates
+3. **vw_network_devices** - Already updated to query machines table (from our Nov 13 work!)
+
+---
+
+### ⚠️ Views Requiring Updates (24 views)
+
+All of these reference tables that will be deprecated after Phase 2 migration:
+
+#### Category 1: PC-Focused Views (12 views)
+Views that query the `pc` and `pctype` tables directly:
+
+1. **vw_active_pcs** - Active PCs updated in last 30 days
+ - References: `pc`, `pctype`
+ - Used by: displaypcs.asp (probably)
+ - **Fix:** Query `machines WHERE pctypeid IS NOT NULL`
+
+2. **vw_engineer_pcs** - Engineer workstations
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines WHERE pctypeid IN (SELECT pctypeid FROM pctype WHERE typename='Engineer')`
+
+3. **vw_shopfloor_pcs** - Shop floor PCs
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines WHERE pctypeid IS NOT NULL` and filter by type
+
+4. **vw_standard_pcs** - Standard workstations
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines WHERE pctypeid IN (SELECT pctypeid FROM pctype WHERE typename='Standard')`
+
+5. **vw_pc_summary** - PC inventory summary
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines WHERE pctypeid IS NOT NULL`
+
+6. **vw_pcs_by_hardware** - PC counts by hardware configuration
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines WHERE pctypeid IS NOT NULL` group by vendor/model
+
+7. **vw_vendor_summary** - PC counts by manufacturer
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines WHERE pctypeid IS NOT NULL` group by vendor
+
+8. **vw_recent_updates** - Recently updated PCs
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines WHERE pctypeid IS NOT NULL ORDER BY lastupdated`
+
+9. **vw_pc_resolved_machines** - PCs mapped to machines
+ - References: `pc`
+ - **Fix:** Query `machines WHERE pctypeid IS NOT NULL`
+
+10. **vw_machine_type_stats** - Machine type statistics
+ - References: `pc`
+ - **Fix:** Include PCs from machines table
+
+11. **vw_shopfloor_applications_summary** - Application deployment summary
+ - References: `pc`
+ - **Fix:** Join to `machines WHERE pctypeid IS NOT NULL`
+
+12. **vw_ge_machines** - Machines with assigned PCs
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines WHERE pctypeid IS NOT NULL` and join to machines (equipment)
+
+#### Category 2: Network Interface Views (2 views)
+Views that query `pc_network_interfaces`:
+
+13. **vw_pc_network_summary** - PC network configuration summary
+ - References: `pc`, `pc_network_interfaces`, `pctype`
+ - **Fix:** Query `machines` and `communications` tables
+ - **Example Change:**
+ ```sql
+ -- OLD:
+ FROM pc p
+ JOIN pc_network_interfaces pni ON p.pcid = pni.pcid
+
+ -- NEW:
+ FROM machines m
+ JOIN communications c ON m.machineid = c.machineid
+ WHERE m.pctypeid IS NOT NULL
+ ```
+
+14. **vw_pctype_config** - PC type configuration summary
+ - References: `pctype`, references pc_network_interfaces in subquery
+ - **Fix:** Query machines grouped by pctypeid, join to communications
+
+#### Category 3: DNC Configuration Views (2 views)
+Views that query `pc_dnc_config`:
+
+15. **vw_dnc_config** - DNC configuration for all PCs
+ - References: `pc`, `pc_dnc_config`
+ - **Fix:** This data may need to be migrated to a new table or embedded in machine relationships
+ - **Status:** NEEDS DESIGN DECISION - Where does DNC config go in Phase 2?
+
+16. **vw_shopfloor_comm_config** - Shop floor communication configuration
+ - References: `pc`, `pc_comm_config`, `pctype`
+ - **Fix:** Query `communications` table with comstypeid for different comm types
+
+#### Category 4: Dualpath Views (3 views)
+Views that query `pc_dualpath_assignments`:
+
+17. **vw_dualpath_management** - Dualpath configuration management
+ - References: `pc`, `pctype`, `pc_dnc_config`, `pc_dualpath_assignments`
+ - **Fix:** Query `machinerelationships WHERE relationshiptypeid = 2` (Dualpath)
+ - **Example Change:**
+ ```sql
+ -- OLD:
+ FROM pc p
+ JOIN pc_dualpath_assignments dpa ON p.pcid = dpa.pcid
+
+ -- NEW:
+ FROM machines m
+ JOIN machinerelationships mr ON m.machineid = mr.machineid
+ JOIN relationshiptypes rt ON mr.relationshiptypeid = rt.relationshiptypeid
+ WHERE m.pctypeid IS NOT NULL AND rt.relationshiptype = 'Dualpath'
+ ```
+
+18. **vw_machine_assignments** - Machine to PC assignments
+ - References: `pc`, `pc_dualpath_assignments`
+ - **Fix:** Query `machinerelationships` for both primary and dualpath assignments
+
+19. **vw_multi_pc_machines** - Machines with multiple PC assignments
+ - References: `pc`
+ - **Fix:** Query `machines` and `machinerelationships` where multiple PCs control same equipment
+
+#### Category 5: Machine Assignment Views (1 view)
+20. **vw_machine_assignment_status** - Machine assignment status
+ - References: `pc`, `pctype`, `pc_dnc_config`
+ - **Fix:** Complex view needing updates to join machines table with pctypeid filter
+
+#### Category 6: Warranty Views (2 views)
+Views that track PC warranties:
+
+21. **vw_warranties_expiring** - Warranties expiring soon
+ - References: `pc`, `pctype`
+ - **Fix:** Query `machines` and `warranties` tables where pctypeid IS NOT NULL
+
+22. **vw_warranty_status** - Overall warranty status
+ - References: `pc`, `pctype` (based on pattern)
+ - **Fix:** Query `machines` and `warranties` tables
+
+#### Category 7: Infrastructure Views (2 views)
+Views that query network device tables:
+
+23. **vw_idf_inventory** - IDF inventory with camera counts
+ - References: `idfs`, `cameras`
+ - **Fix:** Query `machines WHERE machinetypeid IN (17, 18)` for IDFs and Cameras
+ - **Note:** IDFs = machinetypeid 17, Cameras = machinetypeid 18
+
+24. **vw_infrastructure_summary** - Infrastructure device counts
+ - References: `switches`, `accesspoints`, `servers`, `cameras`, `idfs`
+ - **Fix:** Query `machines WHERE machinetypeid IN (16,17,18,19,20)`
+ - **Example Change:**
+ ```sql
+ -- OLD:
+ SELECT 'Switches' AS device_type, COUNT(*) FROM switches
+ UNION ALL
+ SELECT 'Access Points', COUNT(*) FROM accesspoints
+
+ -- NEW:
+ SELECT mt.machinetype AS device_type, COUNT(*) AS total_count
+ FROM machines m
+ JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid
+ WHERE mt.machinetypeid IN (16,17,18,19,20)
+ GROUP BY mt.machinetype
+ ```
+
+---
+
+## Migration Strategy for Views
+
+### Option 1: Drop and Recreate All Views (RECOMMENDED)
+
+**Approach:**
+1. Extract all current view definitions from production
+2. Update each view definition for Phase 2 schema
+3. During migration:
+ - DROP all 24 views that need updates
+ - CREATE updated views with Phase 2 schema
+ - Keep 3 compatible views as-is
+
+**Pros:**
+- Clean migration
+- All views updated at once
+- Easy to test before migration
+
+**Cons:**
+- Brief moment where views don't exist (during migration window)
+- Requires updating all 24 views
+
+---
+
+### Option 2: Create Views During Migration
+
+**Approach:**
+1. Keep old tables as legacy (pc, pc_network_interfaces, etc.)
+2. Keep old views working during transition
+3. Create NEW views with "_v2" suffix for Phase 2 schema
+4. Update ASP pages to use new views
+5. After 30 days, drop old tables and old views
+
+**Pros:**
+- Zero downtime for views
+- Gradual transition
+- Can compare old vs new view results
+
+**Cons:**
+- Duplicate views (_v2 versions)
+- Old tables must be kept (taking up space)
+- More complex migration
+
+---
+
+### Option 3: Hybrid Views (Query Both Old and New)
+
+**Approach:**
+Create views that UNION data from both old and new tables during transition period.
+
+**Example:**
+```sql
+CREATE VIEW vw_active_pcs AS
+-- Get PCs from machines table (new Phase 2)
+SELECT
+ m.machineid AS pcid,
+ m.hostname,
+ m.serialnumber,
+ ...
+FROM machines m
+WHERE m.pctypeid IS NOT NULL
+ AND m.lastupdated > DATE_SUB(NOW(), INTERVAL 30 DAY)
+
+UNION ALL
+
+-- Get PCs from legacy pc table (if any remain)
+SELECT
+ p.pcid,
+ p.hostname,
+ p.serialnumber,
+ ...
+FROM pc p
+WHERE p.lastupdated > DATE_SUB(NOW(), INTERVAL 30 DAY)
+ AND NOT EXISTS (
+ SELECT 1 FROM machines m
+ WHERE m.hostname = p.hostname AND m.pctypeid IS NOT NULL
+ );
+```
+
+**Pros:**
+- Works during transition
+- No duplicate view names
+- Handles mixed data scenarios
+
+**Cons:**
+- More complex queries
+- Slower performance (UNION)
+- Must remove legacy portions after migration complete
+
+---
+
+## Recommended Approach
+
+**Use Option 1: Drop and Recreate**
+
+**Reasoning:**
+1. You're doing a full Phase 2 migration - commit to it fully
+2. Migration window is already scheduled for table changes
+3. Cleaner long-term solution
+4. Easier to test and validate
+5. All pages will use consistent schema
+
+**Migration Steps:**
+1. Before migration: Test all updated views on dev database
+2. During migration:
+ - After data migration completes
+ - DROP all 24 old views
+ - CREATE all 24 updated views
+3. After migration: Test all pages that use views
+
+---
+
+## Views Migration Checklist
+
+### Pre-Migration (Dev Testing)
+- [ ] Extract all 27 view definitions from production
+- [ ] Update 24 view definitions for Phase 2 schema
+- [ ] Test updated views on dev database
+- [ ] Identify which ASP pages use which views
+- [ ] Test all pages that use views
+
+### During Migration
+- [ ] Backup all view definitions (already in backup file)
+- [ ] After PC data migration completes
+- [ ] DROP 24 views requiring updates
+- [ ] CREATE 24 updated views
+- [ ] Run verification queries
+- [ ] Test critical pages
+
+### Post-Migration
+- [ ] Monitor view performance
+- [ ] Check all pages using views
+- [ ] Update documentation
+- [ ] Remove old table references from any remaining code
+
+---
+
+## Impact on ASP Pages
+
+Views are typically used in these types of pages:
+
+1. **Display/List Pages** - displaypcs.asp, displaymachines.asp
+2. **Dashboard Pages** - default.asp, reports.asp
+3. **Search Pages** - search.asp
+4. **API Endpoints** - api_*.asp pages
+
+**Action Required:**
+1. Identify all ASP pages that query views
+2. Test each page after view migration
+3. Update any pages that break
+
+---
+
+## DNC Configuration Migration (Special Case)
+
+**Problem:** Views reference `pc_dnc_config` and `pc_comm_config` tables
+
+**Question:** Where does this data go in Phase 2?
+
+**Options:**
+1. **Create new tables:** `machine_dnc_config` (similar structure)
+2. **Use communications table:** Store DNC settings in `settings` JSON field
+3. **Use compliance table:** If DNC is compliance-related
+4. **Keep legacy tables:** Don't migrate DNC config yet (Phase 3?)
+
+**Recommendation:** Keep `pc_dnc_config` and `pc_comm_config` tables for now (Phase 3 migration). Update views to join machines table instead of pc table.
+
+---
+
+## Sample View Conversions
+
+### Example 1: vw_active_pcs
+
+**OLD (Production):**
+```sql
+CREATE VIEW vw_active_pcs AS
+SELECT
+ p.pcid,
+ p.hostname,
+ p.serialnumber,
+ COALESCE(v.vendor, 'Unknown') AS manufacturer,
+ m.modelnumber AS model,
+ p.loggedinuser,
+ p.machinenumber,
+ COALESCE(os.operatingsystem, 'Unknown') AS operatingsystem,
+ COALESCE(pt.typename, 'Unknown') AS pctype,
+ p.lastupdated
+FROM pc p
+LEFT JOIN models m ON p.modelnumberid = m.modelnumberid
+LEFT JOIN vendors v ON m.vendorid = v.vendorid
+LEFT JOIN pctype pt ON p.pctypeid = pt.pctypeid
+LEFT JOIN operatingsystems os ON p.osid = os.osid
+WHERE p.lastupdated > DATE_SUB(NOW(), INTERVAL 30 DAY);
+```
+
+**NEW (Phase 2):**
+```sql
+CREATE VIEW vw_active_pcs AS
+SELECT
+ m.machineid AS pcid,
+ m.hostname,
+ m.serialnumber,
+ COALESCE(v.vendor, 'Unknown') AS manufacturer,
+ mo.modelnumber AS model,
+ m.loggedinuser,
+ m.machinenumber,
+ COALESCE(os.operatingsystem, 'Unknown') AS operatingsystem,
+ COALESCE(pt.typename, 'Unknown') AS pctype,
+ m.lastupdated
+FROM machines m
+LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid
+LEFT JOIN vendors v ON mo.vendorid = v.vendorid
+LEFT JOIN pctype pt ON m.pctypeid = pt.pctypeid
+LEFT JOIN operatingsystems os ON m.osid = os.osid
+WHERE m.pctypeid IS NOT NULL -- Only PCs
+ AND m.lastupdated > DATE_SUB(NOW(), INTERVAL 30 DAY);
+```
+
+**Changes:**
+- `FROM pc p` → `FROM machines m`
+- Added `WHERE m.pctypeid IS NOT NULL` to filter PCs only
+- `m.modelnumberid` alias changed to `mo` to avoid conflict
+
+---
+
+### Example 2: vw_dualpath_management
+
+**OLD (Production):**
+```sql
+CREATE VIEW vw_dualpath_management AS
+SELECT
+ p.hostname AS pc_hostname,
+ p.pcid,
+ pt.typename AS pc_type,
+ p.machinenumber AS primary_machine,
+ dc.dualpath_enabled,
+ dpa.secondary_machine
+FROM pc p
+JOIN pctype pt ON p.pctypeid = pt.pctypeid
+LEFT JOIN pc_dnc_config dc ON p.pcid = dc.pcid
+LEFT JOIN pc_dualpath_assignments dpa ON p.pcid = dpa.pcid
+WHERE p.isactive = 1;
+```
+
+**NEW (Phase 2):**
+```sql
+CREATE VIEW vw_dualpath_management AS
+SELECT
+ m.hostname AS pc_hostname,
+ m.machineid AS pcid,
+ pt.typename AS pc_type,
+ m.machinenumber AS primary_machine,
+ dc.dualpath_enabled,
+ m2.machinenumber AS secondary_machine
+FROM machines m
+JOIN pctype pt ON m.pctypeid = pt.pctypeid
+LEFT JOIN pc_dnc_config dc ON m.machineid = dc.pcid -- Note: keep legacy table for now
+LEFT JOIN machinerelationships mr ON m.machineid = mr.machineid
+ AND mr.relationshiptypeid = 2 -- Dualpath relationship
+LEFT JOIN machines m2 ON mr.related_machineid = m2.machineid
+WHERE m.pctypeid IS NOT NULL
+ AND m.isactive = 1;
+```
+
+**Changes:**
+- `FROM pc p` → `FROM machines m WHERE pctypeid IS NOT NULL`
+- `pc_dualpath_assignments` → `machinerelationships` with relationshiptype filter
+- Join to second machine using machinerelationships
+
+---
+
+### Example 3: vw_infrastructure_summary
+
+**OLD (Production):**
+```sql
+CREATE VIEW vw_infrastructure_summary AS
+SELECT 'Switches' AS device_type, COUNT(*) AS total_count FROM switches
+UNION ALL
+SELECT 'Access Points', COUNT(*) FROM accesspoints
+UNION ALL
+SELECT 'Servers', COUNT(*) FROM servers
+UNION ALL
+SELECT 'Cameras', COUNT(*) FROM cameras
+UNION ALL
+SELECT 'IDFs', COUNT(*) FROM idfs;
+```
+
+**NEW (Phase 2):**
+```sql
+CREATE VIEW vw_infrastructure_summary AS
+-- Network devices from machines table (Phase 2)
+SELECT
+ mt.machinetype AS device_type,
+ COUNT(*) AS total_count,
+ SUM(CASE WHEN m.isactive = 1 THEN 1 ELSE 0 END) AS active_count
+FROM machines m
+JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid
+WHERE mt.machinetypeid IN (16, 17, 18, 19, 20) -- Network device types
+GROUP BY mt.machinetype
+
+UNION ALL
+
+-- Legacy devices from separate tables (if any remain)
+SELECT 'Switches' AS device_type, COUNT(*) AS total_count,
+ SUM(CASE WHEN isactive = 1 THEN 1 ELSE 0 END) AS active_count
+FROM switches
+UNION ALL
+SELECT 'Access Points', COUNT(*), SUM(CASE WHEN isactive = 1 THEN 1 ELSE 0 END) FROM accesspoints
+UNION ALL
+SELECT 'Servers', COUNT(*), SUM(CASE WHEN isactive = 1 THEN 1 ELSE 0 END) FROM servers
+UNION ALL
+SELECT 'Cameras', COUNT(*), SUM(CASE WHEN isactive = 1 THEN 1 ELSE 0 END) FROM cameras
+UNION ALL
+SELECT 'IDFs', COUNT(*), SUM(CASE WHEN isactive = 1 THEN 1 ELSE 0 END) FROM idfs;
+```
+
+**Changes:**
+- Query machines table where machinetypeid IN (16,17,18,19,20)
+- Keep legacy table queries in UNION for transition period
+- Group by machinetype from machinetypes table
+
+---
+
+## Critical Decision Points
+
+### 1. DNC Configuration Tables
+**Decision Needed:** Migrate pc_dnc_config now or later?
+- **Option A:** Keep for Phase 3, update views to join machines instead of pc
+- **Option B:** Migrate now to new table structure
+
+### 2. View Migration Timing
+**Decision Needed:** When to update views?
+- **During migration:** As part of Phase 2 deployment
+- **Before migration:** Create _v2 versions for testing
+- **After migration:** Update after verifying data migrated correctly
+
+### 3. Legacy Network Device Tables
+**Decision Needed:** Keep empty legacy tables?
+- **Keep:** For backward compatibility (until Phase 3)
+- **Drop:** Clean up unused tables now
+
+---
+
+## Next Steps
+
+1. **Review this analysis** with team
+2. **Make decisions** on DNC config and migration timing
+3. **Create updated view definitions** for all 24 views
+4. **Test views on dev** database with Phase 2 schema
+5. **Add view migration** to production deployment plan
+6. **Update PRODUCTION_MIGRATION_PLAN.md** to include views
+
+---
+
+## Files to Create
+
+1. `/sql/production_migration/views/01_drop_old_views.sql` - DROP old views
+2. `/sql/production_migration/views/02_create_pc_views.sql` - Create updated PC views
+3. `/sql/production_migration/views/03_create_network_views.sql` - Create updated network views
+4. `/sql/production_migration/views/04_create_relationship_views.sql` - Create updated relationship views
+5. `/sql/production_migration/views/05_create_infrastructure_views.sql` - Create updated infrastructure views
+6. `/sql/production_migration/views/06_verify_views.sql` - Verify all views work
+
+---
+
+**Status:** Analysis Complete - Awaiting Decisions
+**Priority:** HIGH - Views must be updated for Phase 2 to work
+**Risk:** MEDIUM - Views can be recreated if issues occur
+**Estimated Time:** 4-6 hours to update all 24 views + testing
diff --git a/sql/migration_phase1_0_ensure_all_machinetypes.sql b/sql/migration_phase1_0_ensure_all_machinetypes.sql
new file mode 100644
index 0000000..00756a0
--- /dev/null
+++ b/sql/migration_phase1_0_ensure_all_machinetypes.sql
@@ -0,0 +1,456 @@
+-- =============================================================================
+-- PHASE 1.0: Ensure All Machine Types Match Dev EXACTLY
+-- =============================================================================
+-- Date: 2025-11-21
+-- Purpose: Ensure production has EXACT same machine types as dev database
+-- This is CRITICAL because website code uses specific machinetypeid values
+--
+-- IMPORTANT: This script uses INSERT IGNORE with specific IDs to match dev
+--
+-- Dev Machine Types:
+-- 1-14: Equipment (existing in production)
+-- 15-20: Infrastructure (MUST ADD with exact IDs and colors)
+-- 21-25: Equipment (existing in production)
+-- 33-35: PC types (MUST ADD with exact IDs)
+--
+-- Run this as the FIRST step in Phase 1 migration
+-- Estimated Time: 30 seconds
+-- =============================================================================
+
+USE shopdb;
+SET SQL_SAFE_UPDATES = 0;
+
+SELECT '============================================================' AS '';
+SELECT 'PHASE 1.0: ENSURE MACHINE TYPES MATCH DEV EXACTLY' AS '';
+SELECT '============================================================' AS '';
+SELECT CONCAT('Start time: ', NOW()) AS '';
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 1: Check current machine types
+-- =============================================================================
+
+SELECT 'Current production machine types:' AS '';
+SELECT COUNT(*) AS total_machine_types FROM machinetypes;
+SELECT '' AS '';
+
+SELECT 'Existing machine types in production:' AS '';
+SELECT machinetypeid, machinetype, bgcolor, machinedescription
+FROM machinetypes
+ORDER BY machinetypeid;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 2: Check for missing machine types
+-- =============================================================================
+
+SELECT 'Checking for missing machine types...' AS '';
+
+SET @missing_15 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 15);
+SET @missing_16 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 16);
+SET @missing_17 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 17);
+SET @missing_18 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 18);
+SET @missing_19 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 19);
+SET @missing_20 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 20);
+SET @missing_33 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 33);
+SET @missing_34 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 34);
+SET @missing_35 = NOT EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 35);
+
+SELECT
+ CASE WHEN @missing_15 THEN '⚠️ Missing ID 15 (Printer)' ELSE '✓ Has ID 15 (Printer)' END AS check_15,
+ CASE WHEN @missing_16 THEN '⚠️ Missing ID 16 (Access Point)' ELSE '✓ Has ID 16 (Access Point)' END AS check_16,
+ CASE WHEN @missing_17 THEN '⚠️ Missing ID 17 (IDF)' ELSE '✓ Has ID 17 (IDF)' END AS check_17,
+ CASE WHEN @missing_18 THEN '⚠️ Missing ID 18 (Camera)' ELSE '✓ Has ID 18 (Camera)' END AS check_18,
+ CASE WHEN @missing_19 THEN '⚠️ Missing ID 19 (Switch)' ELSE '✓ Has ID 19 (Switch)' END AS check_19,
+ CASE WHEN @missing_20 THEN '⚠️ Missing ID 20 (Server)' ELSE '✓ Has ID 20 (Server)' END AS check_20,
+ CASE WHEN @missing_33 THEN '⚠️ Missing ID 33 (Standard PC)' ELSE '✓ Has ID 33 (Standard PC)' END AS check_33,
+ CASE WHEN @missing_34 THEN '⚠️ Missing ID 34 (Engineering PC)' ELSE '✓ Has ID 34 (Engineering PC)' END AS check_34,
+ CASE WHEN @missing_35 THEN '⚠️ Missing ID 35 (Shopfloor PC)' ELSE '✓ Has ID 35 (Shopfloor PC)' END AS check_35;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 3: Add infrastructure machine types (EXACT match to dev)
+-- =============================================================================
+
+SELECT 'Adding infrastructure machine types to match dev...' AS '';
+SELECT '' AS '';
+
+-- ID 15: Printer (with bgcolor #4CAF50 - green)
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 15,
+ 'Printer',
+ b'1',
+ 1,
+ '#4CAF50',
+ 'Network printer - HP, Xerox, or other print devices',
+ NULL
+);
+
+-- ID 16: Access Point (with bgcolor #2196F3 - blue)
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 16,
+ 'Access Point',
+ b'1',
+ 1,
+ '#2196F3',
+ 'Wireless access point for network connectivity',
+ NULL
+);
+
+-- ID 17: IDF (with bgcolor #FF9800 - orange)
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 17,
+ 'IDF',
+ b'1',
+ 1,
+ '#FF9800',
+ 'Intermediate Distribution Frame - network equipment closet',
+ NULL
+);
+
+-- ID 18: Camera (with bgcolor #F44336 - red)
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 18,
+ 'Camera',
+ b'1',
+ 1,
+ '#F44336',
+ 'Security camera for facility monitoring',
+ NULL
+);
+
+-- ID 19: Switch (with bgcolor #9C27B0 - purple)
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 19,
+ 'Switch',
+ b'1',
+ 1,
+ '#9C27B0',
+ 'Network switch for connectivity',
+ NULL
+);
+
+-- ID 20: Server (with bgcolor #607D8B - blue-gray)
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 20,
+ 'Server',
+ b'1',
+ 1,
+ '#607D8B',
+ 'Physical or virtual server',
+ NULL
+);
+
+SELECT '✓ Infrastructure machine types added (IDs 15-20)' AS status;
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 4: Deactivate old PC machine types if they exist
+-- =============================================================================
+
+SELECT 'Checking for old PC machine types (36-38)...' AS '';
+SELECT '' AS '';
+
+-- Deactivate old machine types to prevent confusion
+UPDATE machinetypes
+SET isactive = b'0'
+WHERE machinetypeid IN (36, 37, 38);
+
+SELECT CONCAT('✓ Deactivated ', ROW_COUNT(), ' old PC machine types (if they existed)') AS status;
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 5: Add PC machine types (EXACT match to dev)
+-- =============================================================================
+
+SELECT 'Adding PC machine types to match dev...' AS '';
+SELECT '' AS '';
+
+-- ID 33: Standard PC
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 33,
+ 'Standard PC',
+ b'1',
+ 1,
+ NULL,
+ 'Standard user workstation computer',
+ NULL
+);
+
+-- ID 34: Engineering PC
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 34,
+ 'Engineering PC',
+ b'1',
+ 1,
+ NULL,
+ 'Engineering workstation with specialized software',
+ NULL
+);
+
+-- ID 35: Shopfloor PC
+INSERT IGNORE INTO machinetypes (
+ machinetypeid,
+ machinetype,
+ isactive,
+ functionalaccountid,
+ bgcolor,
+ machinedescription,
+ builddocurl
+)
+VALUES (
+ 35,
+ 'Shopfloor PC',
+ b'1',
+ 1,
+ NULL,
+ 'Shop floor computer for machine monitoring and control',
+ NULL
+);
+
+SELECT '✓ PC machine types added (IDs 33-35)' AS status;
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 6: Verification - Compare with Dev
+-- =============================================================================
+
+SELECT 'Verifying production matches dev...' AS '';
+SELECT '' AS '';
+
+-- Check all required types exist
+SELECT 'Required machine types verification:' AS '';
+SELECT
+ 'ID' AS id,
+ 'Type' AS type,
+ 'Status' AS status,
+ 'Bgcolor' AS bgcolor
+UNION ALL
+SELECT
+ '15',
+ 'Printer',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 15) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 15)
+UNION ALL
+SELECT
+ '16',
+ 'Access Point',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 16) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 16)
+UNION ALL
+SELECT
+ '17',
+ 'IDF',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 17) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 17)
+UNION ALL
+SELECT
+ '18',
+ 'Camera',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 18) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 18)
+UNION ALL
+SELECT
+ '19',
+ 'Switch',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 19) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 19)
+UNION ALL
+SELECT
+ '20',
+ 'Server',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 20) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 20)
+UNION ALL
+SELECT
+ '33',
+ 'Standard PC',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 33) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 33)
+UNION ALL
+SELECT
+ '34',
+ 'Engineering PC',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 34) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 34)
+UNION ALL
+SELECT
+ '35',
+ 'Shopfloor PC',
+ CASE WHEN EXISTS (SELECT 1 FROM machinetypes WHERE machinetypeid = 35) THEN '✓' ELSE '✗' END,
+ (SELECT bgcolor FROM machinetypes WHERE machinetypeid = 35);
+
+SELECT '' AS '';
+
+-- Show all machine types after additions
+SELECT 'All machine types in production (after sync):' AS '';
+SELECT machinetypeid, machinetype, isactive, bgcolor, machinedescription
+FROM machinetypes
+ORDER BY machinetypeid;
+
+SELECT '' AS '';
+
+-- Count by category
+SELECT 'Machine types by category:' AS '';
+SELECT
+ CASE
+ WHEN machinetypeid IN (33, 34, 35) THEN 'PC Types'
+ WHEN machinetypeid IN (15, 16, 17, 18, 19, 20) THEN 'Infrastructure'
+ ELSE 'Equipment'
+ END AS category,
+ COUNT(*) AS count,
+ GROUP_CONCAT(CONCAT(machinetypeid, ':', machinetype) ORDER BY machinetypeid SEPARATOR ', ') AS types
+FROM machinetypes
+WHERE isactive = b'1'
+GROUP BY category;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 7: Final summary
+-- =============================================================================
+
+SELECT '============================================================' AS '';
+SELECT '✓✓✓ MACHINE TYPES NOW MATCH DEV EXACTLY ✓✓✓' AS '';
+SELECT '============================================================' AS '';
+SELECT '' AS '';
+
+SELECT 'Machine types summary:' AS '';
+SELECT CONCAT('Total machine types: ', COUNT(*)) AS summary FROM machinetypes;
+SELECT CONCAT('Active machine types: ', COUNT(*)) AS summary FROM machinetypes WHERE isactive = b'1';
+SELECT CONCAT('Infrastructure types (15-20): ', COUNT(*)) AS summary FROM machinetypes WHERE machinetypeid BETWEEN 15 AND 20;
+SELECT CONCAT('PC types (33-35): ', COUNT(*)) AS summary FROM machinetypes WHERE machinetypeid BETWEEN 33 AND 35;
+
+SELECT '' AS '';
+
+SELECT 'Website color coding preserved:' AS '';
+SELECT ' - Printer (15): Green #4CAF50' AS '';
+SELECT ' - Access Point (16): Blue #2196F3' AS '';
+SELECT ' - IDF (17): Orange #FF9800' AS '';
+SELECT ' - Camera (18): Red #F44336' AS '';
+SELECT ' - Switch (19): Purple #9C27B0' AS '';
+SELECT ' - Server (20): Blue-Gray #607D8B' AS '';
+
+SELECT '' AS '';
+
+SELECT 'Differentiation now works:' AS '';
+SELECT ' - PCs: WHERE pctypeid IS NOT NULL' AS '';
+SELECT ' - Equipment: WHERE pctypeid IS NULL AND machinetypeid NOT IN (15-20)' AS '';
+SELECT ' - Infrastructure: WHERE pctypeid IS NULL AND machinetypeid IN (15-20)' AS '';
+
+SELECT '' AS '';
+SELECT CONCAT('Completed at: ', NOW()) AS '';
+SELECT '============================================================' AS '';
+
+SET SQL_SAFE_UPDATES = 1;
+
+-- =============================================================================
+-- EXPECTED DEV MACHINE TYPES (for reference)
+-- =============================================================================
+--
+-- machinetypeid | machinetype | bgcolor | Description
+-- --------------|---------------------------|-----------|---------------------------
+-- 1 | LocationOnly | #ffffff | NULL
+-- 2 | Vertical Lathe | #ffffff | NULL
+-- 3 | CMM | #ffffff | Coordinate-measuring machine
+-- 4 | Lathe | #ffffff | Okuma & Howa 2SPV80...
+-- 5 | Wax Trace | #ffffff | NULL
+-- 6 | Mill Turn | #ffffff |
+-- 7 | Intertia Welder | #ffffff | NULL
+-- 8 | Eddy Current | #ffffff | Wild Stallions...
+-- 9 | Shotpeen | #ffffff | Shot peening...
+-- 10 | Part Washer | #ffffff | NULL
+-- 11 | Grinder | NULL | NULL
+-- 12 | Broach | NULL | NULL
+-- 13 | 5-axis Mill | NULL |
+-- 14 | Furnace | NULL |
+-- 15 | Printer | #4CAF50 | Network printer... ✨ ADDED
+-- 16 | Access Point | #2196F3 | Wireless access... ✨ ADDED
+-- 17 | IDF | #FF9800 | Intermediate... ✨ ADDED
+-- 18 | Camera | #F44336 | Security camera... ✨ ADDED
+-- 19 | Switch | #9C27B0 | Network switch... ✨ ADDED
+-- 20 | Server | #607D8B | Physical or virtual... ✨ ADDED
+-- 21 | Hobbing Machine | NULL | NULL
+-- 22 | Robotic Deburring | NULL |
+-- 23 | Measuring Machine | NULL | NULL
+-- 24 | Vertical Turning Center | NULL | NULL
+-- 25 | Horizontal Machining Ctr | NULL | NULL
+-- 33 | Standard PC | NULL | Standard user... ✨ ADDED
+-- 34 | Engineering PC | NULL | Engineering... ✨ ADDED
+-- 35 | Shopfloor PC | NULL | Shop floor computer... ✨ ADDED
+--
+-- =============================================================================
diff --git a/sql/migration_phase1_5_migrate_equipment_ips_to_communications.sql b/sql/migration_phase1_5_migrate_equipment_ips_to_communications.sql
new file mode 100644
index 0000000..a0fc366
--- /dev/null
+++ b/sql/migration_phase1_5_migrate_equipment_ips_to_communications.sql
@@ -0,0 +1,334 @@
+-- =============================================================================
+-- PHASE 1.5: Migrate Equipment IP Addresses to Communications Table
+-- =============================================================================
+-- Date: 2025-11-21
+-- Purpose: Migrate equipment (CNC, mills, lathes, etc.) IP addresses from
+-- machines.ipaddress1/ipaddress2 to communications table
+--
+-- CRITICAL: Run this AFTER Phase 1 (infrastructure created) and
+-- BEFORE removing ipaddress columns from machines table
+--
+-- Estimated Time: 1-2 minutes
+-- Records: ~275 equipment IP addresses
+-- =============================================================================
+
+USE shopdb;
+SET SQL_SAFE_UPDATES = 0;
+
+SELECT '============================================================' AS '';
+SELECT 'PHASE 1.5: MIGRATE EQUIPMENT IP ADDRESSES' AS '';
+SELECT '============================================================' AS '';
+SELECT CONCAT('Start time: ', NOW()) AS '';
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 1: Pre-migration checks
+-- =============================================================================
+
+SELECT 'Pre-migration checks:' AS '';
+SELECT '' AS '';
+
+-- Check communications table exists
+SET @comms_exists = (
+ SELECT COUNT(*) FROM information_schema.tables
+ WHERE table_schema = 'shopdb' AND table_name = 'communications'
+);
+
+SELECT CASE
+ WHEN @comms_exists = 1 THEN '✓ Communications table exists'
+ ELSE '⚠️ ERROR: Communications table not found - run Phase 1 first!'
+END AS status;
+
+-- Check comstypes populated
+SET @comstypes_count = (SELECT COUNT(*) FROM comstypes WHERE typename = 'Network' OR typename = 'Network_Interface');
+
+SELECT CASE
+ WHEN @comstypes_count >= 1 THEN CONCAT('✓ Network comstypes exist (', @comstypes_count, ')')
+ ELSE '⚠️ ERROR: Network comstypes not found - run Phase 1 first!'
+END AS status;
+
+-- Check machines table has ipaddress columns
+SET @has_ip_columns = (
+ SELECT COUNT(*) FROM information_schema.columns
+ WHERE table_schema = 'shopdb'
+ AND table_name = 'machines'
+ AND column_name IN ('ipaddress1', 'ipaddress2')
+);
+
+SELECT CASE
+ WHEN @has_ip_columns = 2 THEN '✓ Machines table has ipaddress1/ipaddress2 columns'
+ WHEN @has_ip_columns = 0 THEN '⚠️ WARNING: IP columns already removed - migration may have already run'
+ ELSE '⚠️ WARNING: Only some IP columns exist'
+END AS status;
+
+SELECT '' AS '';
+
+-- Count equipment with IP addresses
+SELECT 'Equipment IP address statistics:' AS '';
+SELECT
+ 'Equipment with ipaddress1' AS category,
+ COUNT(*) AS count
+FROM machines
+WHERE (pctypeid IS NULL OR pctypeid = 0) -- Equipment only (not PCs)
+ AND ipaddress1 IS NOT NULL
+ AND ipaddress1 != ''
+ AND ipaddress1 NOT IN ('X', 'TBD', 'N/A', 'x')
+UNION ALL
+SELECT
+ 'Equipment with ipaddress2',
+ COUNT(*)
+FROM machines
+WHERE (pctypeid IS NULL OR pctypeid = 0)
+ AND ipaddress2 IS NOT NULL
+ AND ipaddress2 != ''
+ AND ipaddress2 NOT IN ('X', 'TBD', 'N/A', 'x')
+UNION ALL
+SELECT
+ 'Existing equipment communications',
+ COUNT(DISTINCT c.machineid)
+FROM communications c
+JOIN machines m ON c.machineid = m.machineid
+WHERE (m.pctypeid IS NULL OR m.pctypeid = 0);
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 2: Create backup of machine IP data
+-- =============================================================================
+
+SELECT 'Creating backup table...' AS '';
+
+DROP TABLE IF EXISTS _backup_equipment_ips_phase1_5;
+CREATE TABLE _backup_equipment_ips_phase1_5 AS
+SELECT
+ machineid,
+ machinenumber,
+ alias,
+ machinetypeid,
+ ipaddress1,
+ ipaddress2,
+ NOW() AS backup_timestamp
+FROM machines
+WHERE (pctypeid IS NULL OR pctypeid = 0) -- Equipment only
+ AND (
+ (ipaddress1 IS NOT NULL AND ipaddress1 != '' AND ipaddress1 NOT IN ('X', 'TBD', 'N/A', 'x'))
+ OR
+ (ipaddress2 IS NOT NULL AND ipaddress2 != '' AND ipaddress2 NOT IN ('X', 'TBD', 'N/A', 'x'))
+ );
+
+SELECT CONCAT('✓ Backup created: _backup_equipment_ips_phase1_5 (', COUNT(*), ' equipment records)') AS status
+FROM _backup_equipment_ips_phase1_5;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 3: Migrate ipaddress1 (primary interface)
+-- =============================================================================
+
+SELECT 'Migrating ipaddress1 (primary interface)...' AS '';
+
+-- Get comstypeid for Network type
+SET @network_comstypeid = (
+ SELECT comstypeid FROM comstypes
+ WHERE typename IN ('Network', 'Network_Interface')
+ LIMIT 1
+);
+
+INSERT INTO communications (
+ machineid,
+ comstypeid,
+ address,
+ isprimary,
+ interfacename,
+ isactive,
+ lastupdated
+)
+SELECT
+ m.machineid,
+ @network_comstypeid AS comstypeid,
+ m.ipaddress1 AS address,
+ 1 AS isprimary, -- Primary interface
+ 'Equipment Interface 1' AS interfacename,
+ 1 AS isactive,
+ NOW() AS lastupdated
+FROM machines m
+WHERE (m.pctypeid IS NULL OR m.pctypeid = 0) -- Equipment only (not PCs)
+ AND m.ipaddress1 IS NOT NULL
+ AND m.ipaddress1 != ''
+ AND m.ipaddress1 NOT IN ('X', 'TBD', 'N/A', 'x') -- Exclude placeholder values
+ AND NOT EXISTS (
+ -- Don't insert if equipment already has a primary communication record
+ SELECT 1 FROM communications c
+ WHERE c.machineid = m.machineid
+ AND c.isprimary = 1
+ AND c.address = m.ipaddress1
+ );
+
+SELECT CONCAT('✓ Migrated ', ROW_COUNT(), ' primary IP addresses (ipaddress1)') AS status;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 4: Migrate ipaddress2 (secondary interface)
+-- =============================================================================
+
+SELECT 'Migrating ipaddress2 (secondary interface)...' AS '';
+
+INSERT INTO communications (
+ machineid,
+ comstypeid,
+ address,
+ isprimary,
+ interfacename,
+ isactive,
+ lastupdated
+)
+SELECT
+ m.machineid,
+ @network_comstypeid AS comstypeid,
+ m.ipaddress2 AS address,
+ 0 AS isprimary, -- Secondary interface
+ 'Equipment Interface 2' AS interfacename,
+ 1 AS isactive,
+ NOW() AS lastupdated
+FROM machines m
+WHERE (m.pctypeid IS NULL OR m.pctypeid = 0) -- Equipment only (not PCs)
+ AND m.ipaddress2 IS NOT NULL
+ AND m.ipaddress2 != ''
+ AND m.ipaddress2 NOT IN ('X', 'TBD', 'N/A', 'x') -- Exclude placeholder values
+ AND NOT EXISTS (
+ -- Don't insert if equipment already has this communication record
+ SELECT 1 FROM communications c
+ WHERE c.machineid = m.machineid
+ AND c.address = m.ipaddress2
+ );
+
+SELECT CONCAT('✓ Migrated ', ROW_COUNT(), ' secondary IP addresses (ipaddress2)') AS status;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 5: Verification
+-- =============================================================================
+
+SELECT 'Verifying migration...' AS '';
+SELECT '' AS '';
+
+-- Show summary
+SELECT 'Migration summary:' AS '';
+SELECT
+ 'Equipment in backup table' AS category,
+ COUNT(*) AS count
+FROM _backup_equipment_ips_phase1_5
+UNION ALL
+SELECT
+ 'Equipment with ipaddress1 (before migration)',
+ COUNT(*)
+FROM _backup_equipment_ips_phase1_5
+WHERE ipaddress1 IS NOT NULL AND ipaddress1 != '' AND ipaddress1 NOT IN ('X', 'TBD', 'N/A', 'x')
+UNION ALL
+SELECT
+ 'Equipment with ipaddress2 (before migration)',
+ COUNT(*)
+FROM _backup_equipment_ips_phase1_5
+WHERE ipaddress2 IS NOT NULL AND ipaddress2 != '' AND ipaddress2 NOT IN ('X', 'TBD', 'N/A', 'x')
+UNION ALL
+SELECT
+ 'Equipment communications (after migration)',
+ COUNT(DISTINCT c.machineid)
+FROM communications c
+JOIN machines m ON c.machineid = m.machineid
+WHERE (m.pctypeid IS NULL OR m.pctypeid = 0);
+
+SELECT '' AS '';
+
+-- Show sample of migrated equipment
+SELECT 'Sample of migrated equipment IPs:' AS '';
+SELECT
+ m.machineid,
+ m.machinenumber,
+ m.alias,
+ COUNT(c.comid) AS interface_count,
+ GROUP_CONCAT(
+ CONCAT(c.address, ' (', IF(c.isprimary=1, 'Primary', 'Secondary'), ')')
+ ORDER BY c.isprimary DESC
+ SEPARATOR ', '
+ ) AS ip_addresses
+FROM machines m
+JOIN communications c ON m.machineid = c.machineid
+JOIN _backup_equipment_ips_phase1_5 b ON m.machineid = b.machineid
+WHERE (m.pctypeid IS NULL OR m.pctypeid = 0)
+GROUP BY m.machineid, m.machinenumber, m.alias
+ORDER BY m.machinenumber
+LIMIT 10;
+
+SELECT '' AS '';
+
+-- Check for missing migrations
+SET @missing_migrations = (
+ SELECT COUNT(*)
+ FROM _backup_equipment_ips_phase1_5 b
+ WHERE (b.ipaddress1 IS NOT NULL AND b.ipaddress1 != '' AND b.ipaddress1 NOT IN ('X', 'TBD', 'N/A', 'x'))
+ AND NOT EXISTS (
+ SELECT 1 FROM communications c
+ WHERE c.machineid = b.machineid
+ )
+);
+
+SELECT CASE
+ WHEN @missing_migrations = 0 THEN '✓ All equipment IPs successfully migrated'
+ ELSE CONCAT('⚠️ WARNING: ', @missing_migrations, ' equipment records not migrated - review logs')
+END AS status;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 6: Final summary
+-- =============================================================================
+
+SELECT '============================================================' AS '';
+SELECT '✓✓✓ EQUIPMENT IP MIGRATION COMPLETE ✓✓✓' AS '';
+SELECT '============================================================' AS '';
+SELECT '' AS '';
+
+SELECT 'Backup table created:' AS '';
+SELECT ' _backup_equipment_ips_phase1_5' AS '';
+SELECT '' AS '';
+
+SELECT 'Next steps:' AS '';
+SELECT ' 1. Verify equipment IPs in communications table' AS '';
+SELECT ' 2. Test application pages using equipment IPs' AS '';
+SELECT ' 3. Once verified, run remove_legacy_ip_columns.sql to drop ipaddress1/2 columns' AS '';
+SELECT '' AS '';
+
+SELECT 'IMPORTANT: Keep backup table for 30 days for rollback if needed' AS '';
+
+SELECT '' AS '';
+SELECT CONCAT('Completed at: ', NOW()) AS '';
+SELECT '============================================================' AS '';
+
+SET SQL_SAFE_UPDATES = 1;
+
+-- =============================================================================
+-- ROLLBACK PROCEDURE (if needed)
+-- =============================================================================
+--
+-- If migration needs to be rolled back:
+--
+-- 1. Delete migrated communications:
+-- DELETE c FROM communications c
+-- JOIN _backup_equipment_ips_phase1_5 b ON c.machineid = b.machineid
+-- WHERE c.interfacename LIKE 'Equipment Interface%';
+--
+-- 2. Restore ipaddress columns (if dropped):
+-- ALTER TABLE machines
+-- ADD COLUMN ipaddress1 CHAR(50) DEFAULT NULL,
+-- ADD COLUMN ipaddress2 CHAR(50) DEFAULT NULL;
+--
+-- 3. Restore IP addresses:
+-- UPDATE machines m
+-- JOIN _backup_equipment_ips_phase1_5 b ON m.machineid = b.machineid
+-- SET m.ipaddress1 = b.ipaddress1,
+-- m.ipaddress2 = b.ipaddress2;
+--
+-- =============================================================================
diff --git a/sql/migration_phase2/01_migrate_pcs_to_machines.sql b/sql/migration_phase2/01_migrate_pcs_to_machines.sql
index 6c9f897..4325c17 100644
--- a/sql/migration_phase2/01_migrate_pcs_to_machines.sql
+++ b/sql/migration_phase2/01_migrate_pcs_to_machines.sql
@@ -52,13 +52,16 @@ SELECT
pt.pctypeid,
pt.typename,
CASE pt.typename
- WHEN 'Standard' THEN (SELECT machinetypeid FROM machinetypes WHERE machinetype = 'PC - Standard' LIMIT 1)
- WHEN 'Shopfloor' THEN (SELECT machinetypeid FROM machinetypes WHERE machinetype = 'PC - Shopfloor' LIMIT 1)
- WHEN 'Engineer' THEN (SELECT machinetypeid FROM machinetypes WHERE machinetype = 'PC - Engineer' LIMIT 1)
- WHEN 'Server' THEN (SELECT machinetypeid FROM machinetypes WHERE machinetype = 'PC - Server' LIMIT 1)
- WHEN 'VM' THEN (SELECT machinetypeid FROM machinetypes WHERE machinetype = 'PC - Server' LIMIT 1)
- WHEN 'Laptop' THEN (SELECT machinetypeid FROM machinetypes WHERE machinetype = 'PC - Laptop' LIMIT 1)
- ELSE (SELECT machinetypeid FROM machinetypes WHERE machinetype = 'PC - Standard' LIMIT 1)
+ WHEN 'Standard' THEN 33 -- Standard PC (Phase 2)
+ WHEN 'Shopfloor' THEN 35 -- Shopfloor PC (Phase 2)
+ WHEN 'Engineer' THEN 34 -- Engineering PC (Phase 2)
+ WHEN 'Server' THEN 33 -- Map to Standard PC
+ WHEN 'VM' THEN 33 -- Map to Standard PC
+ WHEN 'Laptop' THEN 33 -- Map to Standard PC
+ WHEN 'CMM' THEN 33 -- CMM is mislabeled, map to Standard PC
+ WHEN 'Uncategorized' THEN 33 -- Map to Standard PC
+ WHEN 'Wax / Trace' THEN 33 -- Map to Standard PC
+ ELSE 33 -- Default to Standard PC
END
FROM pctype pt;
@@ -92,9 +95,9 @@ SELECT
m.target_machinetypeid AS machinetypeid,
-- PC identification fields
- p.hostname AS machinenumber, -- Use hostname as machine number for PCs
+ COALESCE(p.hostname, CONCAT('PC_', p.pcid)) AS machinenumber, -- Use hostname or generate PC_[id]
p.hostname AS hostname,
- p.hostname AS alias, -- Set alias same as hostname
+ COALESCE(p.hostname, CONCAT('PC_', p.pcid)) AS alias, -- Set alias same as machinenumber
p.loggedinuser,
-- Model and vendor
diff --git a/sql/migration_phase2/08_update_schema_for_api.sql b/sql/migration_phase2/08_update_schema_for_api.sql
index 8527cad..7ab0e0d 100644
--- a/sql/migration_phase2/08_update_schema_for_api.sql
+++ b/sql/migration_phase2/08_update_schema_for_api.sql
@@ -31,9 +31,23 @@ PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
--- Add index for performance
-ALTER TABLE pc_comm_config
-ADD INDEX IF NOT EXISTS idx_machineid (machineid);
+-- Add index for performance (MySQL 5.6 compatible)
+SET @index_exists = (
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.STATISTICS
+ WHERE TABLE_SCHEMA = 'shopdb'
+ AND TABLE_NAME = 'pc_comm_config'
+ AND INDEX_NAME = 'idx_machineid'
+);
+
+SET @sql = IF(@index_exists = 0,
+ 'ALTER TABLE pc_comm_config ADD INDEX idx_machineid (machineid)',
+ 'SELECT "Index idx_machineid already exists on pc_comm_config" AS status'
+);
+
+PREPARE stmt FROM @sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
SELECT 'pc_comm_config updated successfully' AS result;
@@ -60,9 +74,23 @@ PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
--- Add index for performance
-ALTER TABLE pc_dnc_config
-ADD INDEX IF NOT EXISTS idx_machineid (machineid);
+-- Add index for performance (MySQL 5.6 compatible)
+SET @index_exists = (
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.STATISTICS
+ WHERE TABLE_SCHEMA = 'shopdb'
+ AND TABLE_NAME = 'pc_dnc_config'
+ AND INDEX_NAME = 'idx_machineid'
+);
+
+SET @sql = IF(@index_exists = 0,
+ 'ALTER TABLE pc_dnc_config ADD INDEX idx_machineid (machineid)',
+ 'SELECT "Index idx_machineid already exists on pc_dnc_config" AS status'
+);
+
+PREPARE stmt FROM @sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
SELECT 'pc_dnc_config updated successfully' AS result;
diff --git a/sql/migration_phase2/FIX_migrate_remaining_pcs.sql b/sql/migration_phase2/FIX_migrate_remaining_pcs.sql
new file mode 100644
index 0000000..9f467ff
--- /dev/null
+++ b/sql/migration_phase2/FIX_migrate_remaining_pcs.sql
@@ -0,0 +1,277 @@
+-- =============================================================================
+-- FIX: Migrate Remaining PCs
+-- =============================================================================
+-- Date: 2025-11-21
+-- Purpose: Migrate PCs that weren't migrated in the initial run due to:
+-- 1. NULL pctypeid (60 PCs)
+-- 2. Unmapped pctype names (CMM, etc.)
+--
+-- This fixes the machine type name mismatch:
+-- Script looked for: 'PC - Standard', 'PC - Shopfloor', etc.
+-- But we created: 'Standard PC', 'Shopfloor PC', etc.
+-- =============================================================================
+
+USE shopdb;
+SET SQL_SAFE_UPDATES = 0;
+
+SELECT '============================================================' AS '';
+SELECT 'FIX: MIGRATE REMAINING PCS' AS '';
+SELECT '============================================================' AS '';
+SELECT CONCAT('Start time: ', NOW()) AS '';
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 1: Check unmigrated PCs
+-- =============================================================================
+
+SELECT 'Checking for unmigrated PCs...' AS '';
+SELECT '' AS '';
+
+SELECT 'PCs in original pc table (active):' AS category, COUNT(*) AS count FROM pc WHERE isactive = 1
+UNION ALL
+SELECT 'PCs already migrated', COUNT(*) FROM pc_to_machine_id_mapping
+UNION ALL
+SELECT 'PCs NOT yet migrated', COUNT(*)
+FROM pc p
+WHERE p.isactive = 1
+ AND NOT EXISTS (SELECT 1 FROM pc_to_machine_id_mapping m WHERE m.pcid = p.pcid);
+
+SELECT '' AS '';
+
+-- Show distribution of unmigrated PCs
+SELECT 'Unmigrated PCs by type:' AS '';
+SELECT
+ COALESCE(pt.typename, 'NULL') AS pctype,
+ COUNT(*) AS count
+FROM pc p
+LEFT JOIN pctype pt ON p.pctypeid = pt.pctypeid
+WHERE p.isactive = 1
+ AND NOT EXISTS (SELECT 1 FROM pc_to_machine_id_mapping m WHERE m.pcid = p.pcid)
+GROUP BY COALESCE(pt.typename, 'NULL');
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 2: Create corrected PC type mapping
+-- =============================================================================
+
+SELECT 'Creating corrected PC type mapping...' AS '';
+
+DROP TEMPORARY TABLE IF EXISTS temp_pctype_mapping_fixed;
+CREATE TEMPORARY TABLE temp_pctype_mapping_fixed (
+ pctypeid INT,
+ typename VARCHAR(50),
+ target_machinetypeid INT
+);
+
+-- Map all PC types to correct machine type IDs
+INSERT INTO temp_pctype_mapping_fixed (pctypeid, typename, target_machinetypeid)
+SELECT
+ pt.pctypeid,
+ pt.typename,
+ CASE pt.typename
+ WHEN 'Standard' THEN 33 -- Standard PC
+ WHEN 'Shopfloor' THEN 35 -- Shopfloor PC
+ WHEN 'Engineer' THEN 34 -- Engineering PC
+ WHEN 'Server' THEN 33 -- Map to Standard PC (no Server PC type exists)
+ WHEN 'VM' THEN 33 -- Map to Standard PC
+ WHEN 'Laptop' THEN 33 -- Map to Standard PC
+ WHEN 'CMM' THEN 33 -- CMM is mislabeled, map to Standard PC
+ WHEN 'Uncategorized' THEN 33 -- Map to Standard PC
+ WHEN 'Wax / Trace' THEN 33 -- Map to Standard PC
+ ELSE 33 -- Default to Standard PC
+ END AS target_machinetypeid
+FROM pctype pt;
+
+-- Handle NULL pctypeid case
+INSERT INTO temp_pctype_mapping_fixed (pctypeid, typename, target_machinetypeid)
+VALUES (NULL, 'NULL', 33); -- Map NULL to Standard PC
+
+SELECT '✓ Corrected mapping created' AS status;
+SELECT * FROM temp_pctype_mapping_fixed ORDER BY pctypeid;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 3: Migrate remaining PCs
+-- =============================================================================
+
+SELECT 'Migrating remaining PCs...' AS '';
+
+-- Insert remaining PCs into machines table
+INSERT INTO machines (
+ machinetypeid,
+ pctypeid,
+ machinenumber,
+ hostname,
+ alias,
+ loggedinuser,
+ modelnumberid,
+ controllertypeid,
+ controllerosid,
+ serialnumber,
+ osid,
+ machinestatusid,
+ businessunitid,
+ printerid,
+ mapleft,
+ maptop,
+ isvnc,
+ islocationonly,
+ requires_manual_machine_config,
+ machinenotes,
+ isactive,
+ lastupdated,
+ dateadded
+)
+SELECT
+ COALESCE(m.target_machinetypeid, 33) AS machinetypeid, -- Default to Standard PC (33)
+ p.pctypeid,
+ COALESCE(p.hostname, CONCAT('PC_', p.pcid)) AS machinenumber, -- Use hostname or generate
+ p.hostname,
+ p.hostname AS alias, -- Use hostname as alias (pc table has no alias column)
+ p.loggedinuser,
+ p.modelnumberid,
+ NULL AS controllertypeid,
+ NULL AS controllerosid,
+ p.serialnumber,
+ p.osid,
+ COALESCE(p.pcstatusid, 3) AS machinestatusid, -- Default to status 3
+ COALESCE(
+ (SELECT businessunitid FROM machines WHERE machinenumber = p.machinenumber AND isactive = 1 LIMIT 1),
+ 1
+ ) AS businessunitid,
+ (SELECT printerid FROM machines WHERE machinenumber = p.machinenumber AND isactive = 1 LIMIT 1) AS printerid,
+ (SELECT mapleft FROM machines WHERE machinenumber = p.machinenumber AND isactive = 1 LIMIT 1) AS mapleft,
+ (SELECT maptop FROM machines WHERE machinenumber = p.machinenumber AND isactive = 1 LIMIT 1) AS maptop,
+ 0 AS isvnc,
+ 0 AS islocationonly,
+ p.requires_manual_machine_config,
+ NULL AS machinenotes,
+ p.isactive,
+ p.lastupdated,
+ p.dateadded
+FROM pc p
+LEFT JOIN temp_pctype_mapping_fixed m ON COALESCE(p.pctypeid, -999) = COALESCE(m.pctypeid, -999)
+WHERE p.isactive = 1
+ AND NOT EXISTS (
+ SELECT 1 FROM pc_to_machine_id_mapping map WHERE map.pcid = p.pcid
+ )
+ AND NOT EXISTS (
+ -- Don't insert if PC already exists in machines table
+ SELECT 1 FROM machines mach
+ WHERE mach.pctypeid IS NOT NULL
+ AND (
+ (p.hostname IS NOT NULL AND mach.hostname = p.hostname)
+ OR
+ (p.hostname IS NULL AND mach.machinenumber = CONCAT('PC_', p.pcid))
+ )
+ );
+
+SELECT CONCAT('✓ Migrated ', ROW_COUNT(), ' additional PCs') AS status;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 4: Fix NULL pctypeid in machines table
+-- =============================================================================
+
+SELECT 'Fixing PCs with NULL pctypeid in machines table...' AS '';
+
+-- Fix PCs that were migrated with NULL pctypeid
+UPDATE machines
+SET pctypeid = 1 -- Set to Standard (pctypeid 1)
+WHERE machinetypeid IN (33, 34, 35)
+ AND pctypeid IS NULL;
+
+SELECT CONCAT('✓ Fixed ', ROW_COUNT(), ' PCs with NULL pctypeid') AS status;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 5: Update mapping table
+-- =============================================================================
+
+SELECT 'Updating PC to machine ID mapping...' AS '';
+
+INSERT INTO pc_to_machine_id_mapping (pcid, new_machineid, migration_date)
+SELECT
+ p.pcid,
+ m.machineid,
+ NOW()
+FROM pc p
+JOIN machines m ON (
+ -- Match by hostname (if both have hostname)
+ (p.hostname IS NOT NULL AND m.hostname = p.hostname)
+ OR
+ -- Match by PC_[pcid] pattern for NULL hostname PCs
+ (p.hostname IS NULL AND m.machinenumber = CONCAT('PC_', p.pcid))
+)
+WHERE p.isactive = 1
+ AND m.pctypeid IS NOT NULL
+ AND NOT EXISTS (
+ SELECT 1 FROM pc_to_machine_id_mapping map WHERE map.pcid = p.pcid
+ )
+ AND p.pcid IS NOT NULL;
+
+SELECT CONCAT('✓ Updated mapping table with ', ROW_COUNT(), ' new mappings') AS status;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 6: Verification
+-- =============================================================================
+
+SELECT 'Verifying fix...' AS '';
+SELECT '' AS '';
+
+SELECT 'Final counts:' AS '';
+SELECT 'Active PCs in pc table' AS category, COUNT(*) AS count FROM pc WHERE isactive = 1
+UNION ALL
+SELECT 'PCs in mapping table', COUNT(*) FROM pc_to_machine_id_mapping
+UNION ALL
+SELECT 'PCs in machines table (pctypeid NOT NULL)', COUNT(*) FROM machines WHERE pctypeid IS NOT NULL
+UNION ALL
+SELECT 'PCs still unmigrated', COUNT(*)
+FROM pc p
+WHERE p.isactive = 1
+ AND NOT EXISTS (SELECT 1 FROM pc_to_machine_id_mapping m WHERE m.pcid = p.pcid);
+
+SELECT '' AS '';
+
+-- Check if any PCs still unmigrated
+SET @unmigrated_count = (
+ SELECT COUNT(*)
+ FROM pc p
+ WHERE p.isactive = 1
+ AND NOT EXISTS (SELECT 1 FROM pc_to_machine_id_mapping m WHERE m.pcid = p.pcid)
+);
+
+SELECT CASE
+ WHEN @unmigrated_count = 0 THEN '✓✓✓ ALL PCS MIGRATED SUCCESSFULLY ✓✓✓'
+ ELSE CONCAT('⚠️ Still ', @unmigrated_count, ' PCs unmigrated - review above')
+END AS status;
+
+SELECT '' AS '';
+
+-- Show sample of newly migrated PCs
+SELECT 'Sample of newly migrated PCs:' AS '';
+SELECT
+ m.machineid,
+ m.machinenumber,
+ m.hostname,
+ mt.machinetype,
+ pt.typename AS original_pctype
+FROM machines m
+JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid
+LEFT JOIN pc p ON m.hostname = p.hostname
+LEFT JOIN pctype pt ON p.pctypeid = pt.pctypeid
+WHERE m.pctypeid IS NOT NULL
+ AND m.machineid NOT IN (SELECT new_machineid FROM pc_to_machine_id_mapping WHERE migration_date < NOW() - INTERVAL 1 MINUTE)
+LIMIT 10;
+
+SELECT '' AS '';
+SELECT CONCAT('Completed at: ', NOW()) AS '';
+SELECT '============================================================' AS '';
+
+SET SQL_SAFE_UPDATES = 1;
diff --git a/sql/migration_phase2/FIX_pc_machine_types.sql b/sql/migration_phase2/FIX_pc_machine_types.sql
new file mode 100644
index 0000000..c627ea6
--- /dev/null
+++ b/sql/migration_phase2/FIX_pc_machine_types.sql
@@ -0,0 +1,184 @@
+-- =============================================================================
+-- FIX: Migrate PCs from Old Machine Types to New Machine Types
+-- =============================================================================
+-- Date: 2025-11-21
+-- Purpose: Fix PCs that were migrated to old machine type IDs (36-38) instead
+-- of the new Phase 2 machine type IDs (33-35)
+--
+-- Issue: Production database had old PC machine types:
+-- - ID 36: "PC - Standard" (should be ID 33: "Standard PC")
+-- - ID 37: "PC - Shopfloor" (should be ID 35: "Shopfloor PC")
+-- - ID 38: "PC - Engineer" (should be ID 34: "Engineering PC")
+--
+-- Impact: 212 of 286 PCs are using old machine type IDs
+-- =============================================================================
+
+USE shopdb;
+SET SQL_SAFE_UPDATES = 0;
+
+SELECT '============================================================' AS '';
+SELECT 'FIX: MIGRATE PCS TO NEW MACHINE TYPE IDS' AS '';
+SELECT '============================================================' AS '';
+SELECT CONCAT('Start time: ', NOW()) AS '';
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 1: Check current distribution
+-- =============================================================================
+
+SELECT 'Current PC distribution across machine types:' AS '';
+SELECT '' AS '';
+
+SELECT
+ mt.machinetypeid,
+ mt.machinetype,
+ CASE
+ WHEN mt.machinetypeid IN (33, 34, 35) THEN 'NEW (Phase 2)'
+ WHEN mt.machinetypeid IN (36, 37, 38) THEN 'OLD (Phase 1)'
+ ELSE 'OTHER'
+ END AS type_status,
+ COUNT(*) as pc_count
+FROM machines m
+JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid
+WHERE m.pctypeid IS NOT NULL
+GROUP BY mt.machinetypeid, mt.machinetype
+ORDER BY mt.machinetypeid;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 2: Migrate PCs to new machine type IDs
+-- =============================================================================
+
+SELECT 'Migrating PCs to new machine type IDs...' AS '';
+SELECT '' AS '';
+
+-- Migrate PC - Standard (36) to Standard PC (33)
+UPDATE machines
+SET machinetypeid = 33
+WHERE machinetypeid = 36
+ AND pctypeid IS NOT NULL;
+
+SELECT CONCAT('✓ Migrated ', ROW_COUNT(), ' PCs from "PC - Standard" (36) to "Standard PC" (33)') AS status;
+
+-- Migrate PC - Engineer (38) to Engineering PC (34)
+UPDATE machines
+SET machinetypeid = 34
+WHERE machinetypeid = 38
+ AND pctypeid IS NOT NULL;
+
+SELECT CONCAT('✓ Migrated ', ROW_COUNT(), ' PCs from "PC - Engineer" (38) to "Engineering PC" (34)') AS status;
+
+-- Migrate PC - Shopfloor (37) to Shopfloor PC (35)
+UPDATE machines
+SET machinetypeid = 35
+WHERE machinetypeid = 37
+ AND pctypeid IS NOT NULL;
+
+SELECT CONCAT('✓ Migrated ', ROW_COUNT(), ' PCs from "PC - Shopfloor" (37) to "Shopfloor PC" (35)') AS status;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 3: Verify migration
+-- =============================================================================
+
+SELECT 'Verifying migration...' AS '';
+SELECT '' AS '';
+
+SELECT 'PC distribution after migration:' AS '';
+SELECT
+ mt.machinetypeid,
+ mt.machinetype,
+ CASE
+ WHEN mt.machinetypeid IN (33, 34, 35) THEN 'NEW (Phase 2)'
+ WHEN mt.machinetypeid IN (36, 37, 38) THEN 'OLD (Phase 1)'
+ ELSE 'OTHER'
+ END AS type_status,
+ COUNT(*) as pc_count
+FROM machines m
+JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid
+WHERE m.pctypeid IS NOT NULL
+GROUP BY mt.machinetypeid, mt.machinetype
+ORDER BY mt.machinetypeid;
+
+SELECT '' AS '';
+
+-- Check if any PCs still using old types
+SET @old_type_count = (
+ SELECT COUNT(*)
+ FROM machines
+ WHERE machinetypeid IN (36, 37, 38)
+ AND pctypeid IS NOT NULL
+);
+
+SELECT CASE
+ WHEN @old_type_count = 0 THEN '✓✓✓ ALL PCS MIGRATED TO NEW MACHINE TYPES ✓✓✓'
+ ELSE CONCAT('⚠️ Still ', @old_type_count, ' PCs using old machine types!')
+END AS status;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 4: Summary by PC type
+-- =============================================================================
+
+SELECT 'Summary by PC type:' AS '';
+SELECT
+ pt.typename AS pctype,
+ mt.machinetype,
+ COUNT(*) as count
+FROM machines m
+JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid
+JOIN pctype pt ON m.pctypeid = pt.pctypeid
+WHERE m.pctypeid IS NOT NULL
+GROUP BY pt.typename, mt.machinetype
+ORDER BY pt.typename, count DESC;
+
+SELECT '' AS '';
+
+-- =============================================================================
+-- STEP 5: Deactivate old machine types (optional - can be done later)
+-- =============================================================================
+
+SELECT 'Old machine types status (not deactivated - can be done manually):' AS '';
+SELECT machinetypeid, machinetype, isactive
+FROM machinetypes
+WHERE machinetypeid IN (36, 37, 38)
+ORDER BY machinetypeid;
+
+SELECT '' AS '';
+SELECT '============================================================' AS '';
+SELECT '✓✓✓ PC MACHINE TYPE MIGRATION COMPLETE ✓✓✓' AS '';
+SELECT '============================================================' AS '';
+SELECT '' AS '';
+
+SELECT 'Final counts:' AS '';
+SELECT CONCAT('Total PCs: ', COUNT(*)) AS summary FROM machines WHERE pctypeid IS NOT NULL
+UNION ALL
+SELECT CONCAT('Standard PCs (ID 33): ', COUNT(*)) FROM machines WHERE machinetypeid = 33 AND pctypeid IS NOT NULL
+UNION ALL
+SELECT CONCAT('Engineering PCs (ID 34): ', COUNT(*)) FROM machines WHERE machinetypeid = 34 AND pctypeid IS NOT NULL
+UNION ALL
+SELECT CONCAT('Shopfloor PCs (ID 35): ', COUNT(*)) FROM machines WHERE machinetypeid = 35 AND pctypeid IS NOT NULL;
+
+SELECT '' AS '';
+SELECT CONCAT('Completed at: ', NOW()) AS '';
+SELECT '============================================================' AS '';
+
+SET SQL_SAFE_UPDATES = 1;
+
+-- =============================================================================
+-- NOTES
+-- =============================================================================
+--
+-- Old machine types (36-38) can be deactivated or deleted after verification:
+-- UPDATE machinetypes SET isactive = 0 WHERE machinetypeid IN (36, 37, 38);
+--
+-- Website queries should filter by pctypeid, not machinetypeid for PC types:
+-- - All PCs: WHERE pctypeid IS NOT NULL
+-- - Shopfloor PCs: WHERE pctypeid = (SELECT pctypeid FROM pctype WHERE typename = 'Shopfloor')
+-- - Standard PCs: WHERE pctypeid = (SELECT pctypeid FROM pctype WHERE typename = 'Standard')
+-- - Engineer PCs: WHERE pctypeid = (SELECT pctypeid FROM pctype WHERE typename = 'Engineer')
+--
+-- =============================================================================
diff --git a/sql/migration_phase2/RUN_ALL_PHASE2_SCRIPTS.sql b/sql/migration_phase2/RUN_ALL_PHASE2_SCRIPTS.sql
index 5990983..8bdacf5 100644
--- a/sql/migration_phase2/RUN_ALL_PHASE2_SCRIPTS.sql
+++ b/sql/migration_phase2/RUN_ALL_PHASE2_SCRIPTS.sql
@@ -223,6 +223,24 @@ SELECT CONCAT('Time: ', NOW()) AS '';
SELECT '============================================================' AS '';
SELECT '' AS '';
+-- =====================================================
+-- FIX: Migrate PCs from Old Machine Types
+-- =====================================================
+
+SELECT '============================================================' AS '';
+SELECT 'FIX: MIGRATE PCS FROM OLD MACHINE TYPES' AS '';
+SELECT '============================================================' AS '';
+SELECT CONCAT('Start time: ', NOW()) AS '';
+SELECT '' AS '';
+
+source FIX_pc_machine_types.sql
+
+SELECT '' AS '';
+SELECT 'Machine type fix completed' AS '';
+SELECT CONCAT('Time: ', NOW()) AS '';
+SELECT '============================================================' AS '';
+SELECT '' AS '';
+
-- =====================================================
-- VERIFICATION
-- =====================================================
diff --git a/v2/api_printers.asp b/v2/api_printers.asp
index d10db61..cbc27f1 100644
--- a/v2/api_printers.asp
+++ b/v2/api_printers.asp
@@ -110,22 +110,30 @@ Do While Not rs.EOF
End If
End If
- ' Build standard name: CSFName-Location-VendorModel (no dash between vendor and model)
- If csfName <> "" And csfName <> "NONE" And csfName <> "gage lab " Then
- ' Has CSF name
- ' Check if CSF name already matches the machine location (avoid duplication)
- If cleanMachine <> "" And LCase(csfName) <> LCase(cleanMachine) Then
- standardName = csfName & "-" & cleanMachine & "-" & vendor & shortDescription
- Else
- ' CSF name same as location, or no location - just use CSF-VendorModel
- standardName = csfName & "-" & vendor & shortDescription
- End If
+ ' Determine printer name to use
+ ' Prefer Windows Name from database if it's already in standardized format (contains dashes)
+ ' Otherwise generate standardized name automatically
+ If InStr(printerName, "-") > 0 Then
+ ' Use database Windows Name as-is (user manually set it)
+ standardName = printerName
Else
- ' No CSF name - use Location-VendorModel
- If cleanMachine <> "" Then
- standardName = cleanMachine & "-" & vendor & shortDescription
+ ' Generate standard name: CSFName-Location-VendorModel (no dash between vendor and model)
+ If csfName <> "" And csfName <> "NONE" And csfName <> "gage lab " Then
+ ' Has CSF name
+ ' Check if CSF name already matches the machine location (avoid duplication)
+ If cleanMachine <> "" And LCase(csfName) <> LCase(cleanMachine) Then
+ standardName = csfName & "-" & cleanMachine & "-" & vendor & shortDescription
+ Else
+ ' CSF name same as location, or no location - just use CSF-VendorModel
+ standardName = csfName & "-" & vendor & shortDescription
+ End If
Else
- standardName = "Printer" & rs("printerid") & "-" & vendor & shortDescription
+ ' No CSF name - use Location-VendorModel
+ If cleanMachine <> "" Then
+ standardName = cleanMachine & "-" & vendor & shortDescription
+ Else
+ standardName = "Printer" & rs("printerid") & "-" & vendor & shortDescription
+ End If
End If
End If
standardName = Replace(standardName, """", "\""")
diff --git a/v2/install_printer.asp b/v2/install_printer.asp
index f900b2d..91e6a21 100644
--- a/v2/install_printer.asp
+++ b/v2/install_printer.asp
@@ -156,75 +156,21 @@ Else
Response.Write(" echo ERROR: Could not download installer" & vbCrLf)
Response.Write(")" & vbCrLf)
Else
- ' No installer - use universal driver
- Dim driverName, driverInf
-
- Select Case UCase(printer("vendor"))
- Case "HP"
- driverName = "HP Universal Printing PCL 6"
- driverInf = "hpcu255u.inf"
- Case "XEROX"
- driverName = "Xerox Global Print Driver PCL6"
- driverInf = "xeroxgpd.inf"
- Case "HID"
- driverName = "HP Universal Printing PCL 6"
- driverInf = "hpcu255u.inf"
- Case Else
- driverName = "Generic / Text Only"
- driverInf = ""
- End Select
-
- Response.Write("echo Using universal driver: " & driverName & vbCrLf)
+ ' No specific installer - use universal PrinterInstaller.exe
+ Response.Write("echo Using universal printer installer..." & vbCrLf)
Response.Write("echo." & vbCrLf)
-
- ' Generate PowerShell script to install printer
- Response.Write("powershell -NoProfile -ExecutionPolicy Bypass -Command """ & vbCrLf)
- Response.Write(" Write-Host 'Installing printer with universal driver...' -ForegroundColor Cyan;" & vbCrLf)
- Response.Write(" " & vbCrLf)
- Response.Write(" $printerName = '" & Replace(printer("name"), "'", "''") & "';" & vbCrLf)
- Response.Write(" $address = '" & Replace(printer("address"), "'", "''") & "';" & vbCrLf)
- Response.Write(" $driverName = '" & Replace(driverName, "'", "''") & "';" & vbCrLf)
- Response.Write(" $portName = 'IP_' + $address;" & vbCrLf)
- Response.Write(" " & vbCrLf)
-
- ' Check if driver is installed
- If driverInf <> "" Then
- Response.Write(" # Check if driver exists" & vbCrLf)
- Response.Write(" $driver = Get-PrinterDriver -Name $driverName -ErrorAction SilentlyContinue;" & vbCrLf)
- Response.Write(" if (-not $driver) {" & vbCrLf)
- Response.Write(" Write-Host 'ERROR: Universal driver not found!' -ForegroundColor Red;" & vbCrLf)
- Response.Write(" Write-Host 'Please install ' $driverName ' from Windows Update or driver package' -ForegroundColor Yellow;" & vbCrLf)
- Response.Write(" exit 1;" & vbCrLf)
- Response.Write(" }" & vbCrLf)
- Response.Write(" " & vbCrLf)
- End If
-
- ' Create port
- Response.Write(" # Create TCP/IP port" & vbCrLf)
- Response.Write(" $port = Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue;" & vbCrLf)
- Response.Write(" if (-not $port) {" & vbCrLf)
- Response.Write(" Write-Host 'Creating printer port...' -ForegroundColor Yellow;" & vbCrLf)
- Response.Write(" Add-PrinterPort -Name $portName -PrinterHostAddress $address -ErrorAction Stop;" & vbCrLf)
- Response.Write(" Write-Host 'Port created successfully' -ForegroundColor Green;" & vbCrLf)
- Response.Write(" } else {" & vbCrLf)
- Response.Write(" Write-Host 'Port already exists' -ForegroundColor Green;" & vbCrLf)
- Response.Write(" }" & vbCrLf)
- Response.Write(" " & vbCrLf)
-
- ' Add printer
- Response.Write(" # Add printer" & vbCrLf)
- Response.Write(" $existingPrinter = Get-Printer -Name $printerName -ErrorAction SilentlyContinue;" & vbCrLf)
- Response.Write(" if (-not $existingPrinter) {" & vbCrLf)
- Response.Write(" Write-Host 'Adding printer...' -ForegroundColor Yellow;" & vbCrLf)
- Response.Write(" Add-Printer -Name $printerName -DriverName $driverName -PortName $portName -ErrorAction Stop;" & vbCrLf)
- Response.Write(" Write-Host 'Printer installed successfully!' -ForegroundColor Green;" & vbCrLf)
- Response.Write(" } else {" & vbCrLf)
- Response.Write(" Write-Host 'Printer already exists' -ForegroundColor Green;" & vbCrLf)
- Response.Write(" }" & vbCrLf)
- Response.Write("""" & vbCrLf)
- Response.Write("" & vbCrLf)
- Response.Write("if %ERRORLEVEL% neq 0 (" & vbCrLf)
- Response.Write(" echo ERROR: Failed to install printer" & vbCrLf)
+ Response.Write("echo Downloading PrinterInstaller.exe..." & vbCrLf)
+ Response.Write("powershell -NoProfile -Command """ & _
+ "$ProgressPreference = 'SilentlyContinue'; " & _
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; " & _
+ "Invoke-WebRequest -Uri 'https://tsgwp00525.rd.ds.ge.com/shopdb/installers/PrinterInstaller.exe' " & _
+ "-OutFile '%TEMP%\PrinterInstaller.exe' -UseBasicParsing -UseDefaultCredentials""" & vbCrLf)
+ Response.Write("if exist ""%TEMP%\PrinterInstaller.exe"" (" & vbCrLf)
+ Response.Write(" echo Running installer..." & vbCrLf)
+ Response.Write(" ""%TEMP%\PrinterInstaller.exe"" ""/PRINTER=" & printer("standardname") & """" & vbCrLf)
+ Response.Write(" del ""%TEMP%\PrinterInstaller.exe"" 2>nul" & vbCrLf)
+ Response.Write(") else (" & vbCrLf)
+ Response.Write(" echo ERROR: Could not download PrinterInstaller.exe" & vbCrLf)
Response.Write(")" & vbCrLf)
End If