# Vendor Type Refactoring Plan

## Overview
Refactor the `vendors` table to use a normalized many-to-many relationship for vendor types instead of multiple boolean columns.

---

## Current Design (Problems)

### Vendors Table Structure:
```sql
vendorid INT(11) PK
vendor VARCHAR(50)
isactive CHAR(50)         -- Should be BIT(1)!
isprinter BIT(1)          -- Boolean flag
ispc BIT(1)               -- Boolean flag
ismachine BIT(1)          -- Boolean flag
isserver BIT(1)           -- Boolean flag
isswitch BIT(1)           -- Boolean flag
iscamera BIT(1)           -- Boolean flag
```

### Issues:
1. **Not Normalized**: Multiple boolean columns for types
2. **Not Scalable**: Adding new device types requires ALTER TABLE
3. **Inefficient Queries**: Need to check multiple columns
4. **Data Type Issue**: `isactive` is CHAR(50) instead of BIT(1)
5. **No Multi-Type Support**: Hard to query "vendors that are both printer AND pc"

---

## Proposed Design (Solution)

### New Tables:

#### 1. `vendortypes` (Lookup Table)
```sql
CREATE TABLE vendortypes (
    vendortypeid INT(11) PRIMARY KEY AUTO_INCREMENT,
    vendortype VARCHAR(50) NOT NULL UNIQUE,
    description VARCHAR(255),
    isactive BIT(1) DEFAULT b'1'
);

-- Initial Data:
INSERT INTO vendortypes (vendortype, description) VALUES
('TBD', 'To be determined / Unassigned'),
('Printer', 'Printer manufacturers'),
('PC', 'Computer manufacturers'),
('Machine', 'CNC machine manufacturers'),
('Server', 'Server manufacturers'),
('Switch', 'Network switch manufacturers'),
('Camera', 'Security camera manufacturers');
```

#### 2. Updated `vendors` Table (One-to-Many):
```sql
-- Add vendortypeid, remove old flags, fix isactive
ALTER TABLE vendors
    ADD COLUMN vendortypeid INT(11) DEFAULT 1 AFTER vendorid,
    ADD INDEX idx_vendortypeid (vendortypeid),
    ADD FOREIGN KEY (vendortypeid) REFERENCES vendortypes(vendortypeid) ON DELETE SET NULL,
    DROP COLUMN isprinter,
    DROP COLUMN ispc,
    DROP COLUMN ismachine,
    DROP COLUMN isserver,
    DROP COLUMN isswitch,
    DROP COLUMN iscamera,
    MODIFY COLUMN isactive BIT(1) DEFAULT b'1';
```

**Note**: Each vendor has ONE type. Default is vendortypeid=1 (TBD).

---

## Benefits

✅ **Normalized**: Proper relational design
✅ **Scalable**: Add new types without schema changes
✅ **Simpler**: One type per vendor (one-to-many relationship)
✅ **Cleaner Queries**: `JOIN vendortypes WHERE vendortypeid = 2`
✅ **Better Reporting**: Easy to query "all vendors by type"
✅ **Maintainable**: Type list managed in one place
✅ **TBD Support**: Default type for unassigned/unknown vendors

---

## Data Migration Strategy

### Step 1: Create New Tables
```sql
CREATE TABLE vendortypes (...);
CREATE TABLE vendor_vendortypes (...);
```

### Step 2: Migrate Existing Data
```sql
-- Set vendortypeid based on first TRUE flag found (priority order)
UPDATE vendors SET vendortypeid = 2 WHERE isprinter = 1;    -- Printer
UPDATE vendors SET vendortypeid = 3 WHERE ispc = 1;         -- PC
UPDATE vendors SET vendortypeid = 4 WHERE ismachine = 1;    -- Machine
UPDATE vendors SET vendortypeid = 5 WHERE isserver = 1;     -- Server
UPDATE vendors SET vendortypeid = 6 WHERE isswitch = 1;     -- Switch
UPDATE vendors SET vendortypeid = 7 WHERE iscamera = 1;     -- Camera
-- Vendors with all flags = 0 will remain vendortypeid = 1 (TBD)
```

### Step 3: Update Application Code (see below)

### Step 4: Drop Old Columns
```sql
ALTER TABLE vendors
    DROP COLUMN isprinter,
    DROP COLUMN ispc,
    DROP COLUMN ismachine,
    DROP COLUMN isserver,
    DROP COLUMN isswitch,
    DROP COLUMN iscamera;
```

---

## Code Changes Required

### Pattern: Old vs New

**OLD WAY:**
```sql
SELECT vendorid, vendor
FROM vendors
WHERE isprinter = 1 AND isactive = 1
```

**NEW WAY:**
```sql
SELECT v.vendorid, v.vendor
FROM vendors v
INNER JOIN vendortypes vt ON v.vendortypeid = vt.vendortypeid
WHERE vt.vendortype = 'Printer' AND v.isactive = 1
```

Or using vendortypeid directly (more efficient):
```sql
SELECT v.vendorid, v.vendor
FROM vendors v
WHERE v.vendortypeid = 2 AND v.isactive = 1  -- 2 = Printer
```

### Files Requiring Updates (31 files found):

#### Printer-Related (7 files):
- `/addprinter.asp` - Line 53, 90
- `/displayprinter.asp` - Line 291
- `/editprinter.asp`
- `/saveprinter_direct.asp`
- `/includes/data_cache.asp` - Line 30 (RenderVendorOptions function)

#### Machine-Related (4 files):
- `/addmachine.asp` - Line 62, 98
- `/displaymachine.asp` - Line 236
- `/editmacine.asp`
- `/savemachine_direct.asp`

#### PC-Related (3 files):
- `/displaypc.asp`
- `/editdevice.asp` - Line 158, 199
- `/updatedevice_direct.asp`
- `/updatepc_direct.asp`

#### Model/Vendor Management (6 files):
- `/addmodel.asp`
- `/savemodel.asp`
- `/savemodel_direct.asp`
- `/addvendor.asp`
- `/savevendor.asp`
- `/savevendor_direct.asp`

#### Application-Related (7 files):
- `/addapplication.asp`
- `/displayapplication.asp`
- `/editapplication.asp`
- `/editapplication_direct.asp`
- `/editapplication_v2.asp`
- `/editapp_standalone.asp`
- `/saveapplication.asp`
- `/saveapplication_direct.asp`
- `/quickadd_application.asp`

#### Knowledge Base (2 files):
- `/addlink_direct.asp`
- `/updatelink_direct.asp`

#### Search (1 file):
- `/search.asp` - Lines 493-556 (machine and printer search with vendor joins)

---

## Recommended Approach

### Option 1: Create Helper View (Easier Migration)
Create a view that mimics the old structure:

```sql
CREATE VIEW vw_vendors_with_types AS
SELECT
    v.vendorid,
    v.vendor,
    v.isactive,
    v.vendortypeid,
    vt.vendortype,
    CASE WHEN vt.vendortype = 'Printer' THEN 1 ELSE 0 END AS isprinter,
    CASE WHEN vt.vendortype = 'PC' THEN 1 ELSE 0 END AS ispc,
    CASE WHEN vt.vendortype = 'Machine' THEN 1 ELSE 0 END AS ismachine,
    CASE WHEN vt.vendortype = 'Server' THEN 1 ELSE 0 END AS isserver,
    CASE WHEN vt.vendortype = 'Switch' THEN 1 ELSE 0 END AS isswitch,
    CASE WHEN vt.vendortype = 'Camera' THEN 1 ELSE 0 END AS iscamera
FROM vendors v
LEFT JOIN vendortypes vt ON v.vendortypeid = vt.vendortypeid;
```

**Benefit**: Minimal code changes - just replace `vendors` with `vw_vendors_with_types` in SELECT queries

### Option 2: Update All Queries (Better Long-Term)
Update all 30 files to use proper JOINs with new tables.

**Benefit**: Cleaner code, better performance, proper normalization

---

## Helper Functions Needed

### ASP Include: `/includes/vendor_helpers.asp`

```vbscript
<%
' Get vendors by type (returns recordset)
Function GetVendorsByType(vendorType)
    Dim sql, rs
    sql = "SELECT v.vendorid, v.vendor " & _
          "FROM vendors v " & _
          "INNER JOIN vendortypes vt ON v.vendortypeid = vt.vendortypeid " & _
          "WHERE vt.vendortype = '" & Replace(vendorType, "'", "''") & "' " & _
          "AND v.isactive = 1 " & _
          "ORDER BY v.vendor ASC"
    Set rs = objConn.Execute(sql)
    Set GetVendorsByType = rs
End Function

' Get vendors by type ID (more efficient)
Function GetVendorsByTypeId(vendortypeid)
    Dim sql, rs
    sql = "SELECT vendorid, vendor " & _
          "FROM vendors " & _
          "WHERE vendortypeid = " & vendortypeid & " " & _
          "AND isactive = 1 " & _
          "ORDER BY vendor ASC"
    Set rs = objConn.Execute(sql)
    Set GetVendorsByTypeId = rs
End Function

' Get vendor type name for a vendor
Function GetVendorType(vendorId)
    Dim sql, rs
    sql = "SELECT vt.vendortype " & _
          "FROM vendors v " & _
          "INNER JOIN vendortypes vt ON v.vendortypeid = vt.vendortypeid " & _
          "WHERE v.vendorid = " & vendorId
    Set rs = objConn.Execute(sql)
    If Not rs.EOF Then
        GetVendorType = rs("vendortype")
    Else
        GetVendorType = "TBD"
    End If
    rs.Close
    Set rs = Nothing
End Function
%>
```

---

## Example Code Updates

### Before (addprinter.asp line 90):
```vbscript
strSQL = "SELECT vendorid, vendor FROM vendors WHERE isprinter = 1 AND isactive = 1 ORDER BY vendor ASC"
Set rsVendors = objConn.Execute(strSQL)
```

### After (Option 1 - Using View):
```vbscript
strSQL = "SELECT vendorid, vendor FROM vw_vendors_with_types WHERE isprinter = 1 AND isactive = 1 ORDER BY vendor ASC"
Set rsVendors = objConn.Execute(strSQL)
```

### After (Option 2 - Using Helper):
```vbscript
Set rsVendors = GetVendorsByType("Printer")
```

### After (Option 3 - Direct Query with JOIN):
```vbscript
strSQL = "SELECT v.vendorid, v.vendor " & _
         "FROM vendors v " & _
         "INNER JOIN vendortypes vt ON v.vendortypeid = vt.vendortypeid " & _
         "WHERE vt.vendortype = 'Printer' AND v.isactive = 1 " & _
         "ORDER BY v.vendor ASC"
Set rsVendors = objConn.Execute(strSQL)
```

### After (Option 4 - Direct Query with ID - FASTEST):
```vbscript
' Printer = vendortypeid 2
strSQL = "SELECT vendorid, vendor FROM vendors WHERE vendortypeid = 2 AND isactive = 1 ORDER BY vendor ASC"
Set rsVendors = objConn.Execute(strSQL)
```

### Search.asp Special Case:

The search.asp file (lines 493-556) searches machines and printers with vendor joins. Currently it searches by vendor name, which will continue to work. However, if we want to enable searching by vendor type (e.g., "printer vendors", "machine vendors"), we need to update the query:

**Current (machine search):**
```vbscript
strSQL = "SELECT m.machineid, m.machinenumber, m.alias, mt.machinetype " & _
         "FROM machines m " & _
         "INNER 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 " & _
         "WHERE (m.machinenumber LIKE ? OR m.alias LIKE ? OR m.machinenotes LIKE ? OR mt.machinetype LIKE ? OR v.vendor LIKE ?) " & _
         "  AND m.isactive = 1 " & _
         "LIMIT 10"
```

**New (with vendortype support):**
```vbscript
strSQL = "SELECT m.machineid, m.machinenumber, m.alias, mt.machinetype " & _
         "FROM machines m " & _
         "INNER 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 vendortypes vt ON v.vendortypeid = vt.vendortypeid " & _
         "WHERE (m.machinenumber LIKE ? OR m.alias LIKE ? OR m.machinenotes LIKE ? OR mt.machinetype LIKE ? OR v.vendor LIKE ? OR vt.vendortype LIKE ?) " & _
         "  AND m.isactive = 1 " & _
         "LIMIT 10"
```

**Note**: This is optional - the search will continue to work with just vendor names. Only add vendortype searching if desired.

---

## Testing Plan

1. **Create migration script** with new tables
2. **Migrate data** from boolean flags to junction table
3. **Create view** for backward compatibility
4. **Test all 30 files** with view in place
5. **Gradually update** code to use new structure
6. **Drop view** once all code is updated
7. **Drop old columns** from vendors table

---

## Timeline Estimate

- **Database Migration**: 1 hour
- **Create Helper Functions**: 30 minutes
- **Update 30 Files**: 4-6 hours (depends on approach)
- **Testing**: 2-3 hours
- **Total**: ~1 day of development work

---

## Rollback Plan

If issues arise:
1. Keep old columns during testing phase
2. View provides backward compatibility
3. Can revert code changes easily
4. Only drop columns after full validation

---

## Recommendation

**Use Option 1 (Helper View) for initial migration:**
1. Create new tables and migrate data
2. Create compatibility view
3. Update queries to use view (minimal changes)
4. Keep old columns as backup
5. After validation, gradually refactor to use new structure directly
6. Drop old columns once confident

This provides a safe, gradual migration path with easy rollback capability.

---

## Implementation Checklist

See the TODO list for detailed tracking. High-level implementation order:

### Phase 1: Database Migration (Complete)
- ✅ Migration script created: `/sql/refactor_vendor_types.sql`
- ⏳ Run migration script on test database
- ⏳ Verify vendortypes table populated with 7 types (TBD, Printer, PC, Machine, Server, Switch, Camera)
- ⏳ Verify vendors.vendortypeid column added with proper foreign key
- ⏳ Verify data migrated correctly from boolean flags
- ⏳ Verify compatibility view `vw_vendors_with_types` works
- ⏳ Verify isactive column fixed (CHAR(50) → BIT(1))

### Phase 2: Code Updates (31 files)
Update all files to use new vendortypeid structure. Use one of these approaches:
- **Quick**: Replace table name `vendors` with `vw_vendors_with_types` (minimal changes)
- **Better**: Use `WHERE vendortypeid = X` (direct column check)
- **Best**: Use helper functions from vendor_helpers.asp

**File Groups**:
- ⏳ Data cache include (1 file) - **START HERE** (affects all dropdowns)
- ⏳ Printer files (7 files)
- ⏳ Machine files (4 files)
- ⏳ PC files (4 files)
- ⏳ Model/Vendor management (6 files)
- ⏳ Application files (9 files)
- ⏳ Knowledge base files (2 files)
- ⏳ Search file (1 file - optional enhancement)

### Phase 3: Testing
- ⏳ Test vendor dropdowns in all add/edit forms
- ⏳ Test filtering by vendor type works correctly
- ⏳ Test data integrity (vendors show correct type)
- ⏳ Test search functionality still works
- ⏳ Verify no SQL errors in any page

### Phase 4: Cleanup (FINAL STEP - ONLY AFTER FULL VALIDATION)
- ⏳ Create cleanup script to drop old boolean columns
- ⏳ Run cleanup script to remove isprinter, ispc, ismachine, isserver, isswitch, iscamera
- ⏳ Drop compatibility view if no longer needed
- ⏳ Update documentation

---

## Files Reference

**Migration Script**: `/home/camp/projects/windows/shopdb/sql/refactor_vendor_types.sql`
**Design Document**: `/home/camp/projects/windows/shopdb/docs/VENDOR_TYPE_REFACTORING_PLAN.md` (this file)
**Helper Functions** (to be created): `/home/camp/projects/windows/shopdb/includes/vendor_helpers.asp`

---

## Quick Reference

**Vendor Type IDs**:
- 1 = TBD (default for unassigned)
- 2 = Printer
- 3 = PC
- 4 = Machine
- 5 = Server
- 6 = Switch
- 7 = Camera

**Common Query Patterns**:
```sql
-- Get all printer vendors
SELECT * FROM vendors WHERE vendortypeid = 2 AND isactive = 1

-- Get vendor with type name
SELECT v.*, vt.vendortype
FROM vendors v
INNER JOIN vendortypes vt ON v.vendortypeid = vt.vendortypeid
WHERE v.vendorid = ?

-- Get all vendors of a specific type by name
SELECT v.* FROM vendors v
INNER JOIN vendortypes vt ON v.vendortypeid = vt.vendortypeid
WHERE vt.vendortype = 'Printer' AND v.isactive = 1
```

---

**Document Version**: 2.0
**Last Updated**: 2025-10-22
**Status**: Ready for Implementation
