# Printer Mapping Migration Report

**Date:** 2025-10-22
**Author:** Development Team
**Status:** Analysis Complete - Ready for Implementation

---

## Executive Summary

The `printers` table now has `maptop` and `mapleft` columns added for direct printer location mapping on the shop floor map. This migration report outlines the necessary code changes to transition from machine-based printer positioning to direct printer positioning.

### Database Changes Completed ✅
- Added `maptop INT(11)` column to `printers` table
- Added `mapleft INT(11)` column to `printers` table
- Both columns are nullable (default NULL)
- Positioned after `machineid` column

---

## Current Implementation Analysis

### 1. **printermap.asp** - Main Map View

**Current Behavior:**
- Queries printers joined with machines to get map coordinates
- Uses `machines.maptop` and `machines.mapleft` for printer positioning
- Shows printer at machine location
- Requires `printers.machineid != 1` (excludes unassigned printers)

**SQL Query (Lines 186-189):**
```sql
SELECT machines.mapleft, machines.maptop, machines.machinenumber,
       printers.printerid, printers.printercsfname, printers.printerwindowsname,
       models.modelnumber, models.image, printers.ipaddress, printers.fqdn,
       machines.machinenotes, machines.alias
FROM machines, printers, models
WHERE printers.modelid = models.modelnumberid
  AND printers.machineid != 1
  AND printers.machineid = machines.machineid
  AND printers.isactive = 1
```

**Location Display (Lines 202-207):**
```vbscript
' Uses alias if available, otherwise machinenumber
if NOT IsNull(rs("alias")) AND rs("alias") <> "" THEN
    location = rs("alias")
else
    location = rs("machinenumber")
end if
```

**Issues:**
- ❌ Printers without machine assignment (`machineid=1`) are excluded from map
- ❌ Multiple printers at same machine appear stacked on same coordinate
- ❌ Cannot position printer independently of machine

---

### 2. **addprinter.asp** - Add New Printer Form

**Current Behavior:**
- Form includes machine dropdown (required field)
- Uses machineid to determine printer location
- No map coordinate input fields

**Location Field (Lines 174-197):**
```vbscript
<div class="form-group">
  <label for="machineid">Location (Machine) <span class="text-danger">*</span></label>
  <select class="form-control" id="machineid" name="machineid" required>
    <option value="">-- Select Location --</option>
    <!-- Lists all machines -->
  </select>
  <small class="form-text text-muted">Which machine/location is this printer at?</small>
</div>
```

**Issues:**
- ❌ No way to set `maptop`/`mapleft` during printer creation
- ❌ Printer position tied to machine selection
- ❌ Cannot add printer without machine assignment

---

### 3. **saveprinter_direct.asp** - Save New Printer

**Current Behavior:**
- Inserts printer with machineid
- Does not handle maptop/mapleft

**INSERT Statement (Line 191-192):**
```vbscript
strSQL = "INSERT INTO printers (modelid, serialnumber, ipaddress, fqdn,
          printercsfname, printerwindowsname, machineid, isactive) " & _
         "VALUES (...)"
```

**Issues:**
- ❌ Does not insert `maptop`/`mapleft` values
- ❌ New printers won't have coordinates

---

### 4. **editprinter.asp** - Edit Printer Form

**Current Behavior:**
- Similar to addprinter.asp
- Shows machine dropdown
- No map coordinate fields

**Issues:**
- ❌ Cannot edit printer coordinates
- ❌ No map picker interface

---

### 5. **saveprinter.asp** - Update Printer

**Current Behavior:**
- Updates printer fields including machineid
- Does not update maptop/mapleft

**UPDATE Statement (Lines 168-176):**
```vbscript
strSQL = "UPDATE printers SET " & _
         "modelid = " & modelid & ", " & _
         "serialnumber = '" & serialnumber & "', " & _
         "ipaddress = '" & ipaddress & "', " & _
         "fqdn = '" & fqdn & "', " & _
         "printercsfname = '" & printercsfname & "', " & _
         "printerwindowsname = '" & printerwindowsname & "', " & _
         "machineid = " & machineid & " " & _
         "WHERE printerid = " & printerid
```

**Issues:**
- ❌ Does not update `maptop`/`mapleft`

---

### 6. **displayprinter.asp** - View Printer Details

**Current Behavior:**
- Shows printer details
- Displays location as machine number/alias
- Has clickable location link

**Location Display (Lines 87-91):**
```vbscript
<p class="mb-2">
    <span class="location-link" data-machineid="<%Response.Write(machineid)%>"
          style="cursor:pointer; color:#007bff;">
        <i class="zmdi zmdi-pin" style="margin-right:4px;"></i>
        <%Response.Write(rs("machinenumber"))%>
    </span>
</p>
```

**Issues:**
- ❌ Still references machine location
- ❌ No display of printer's actual map coordinates

---

## Required Code Changes

### Priority 1: Core Map Functionality

#### 1. **printermap.asp** - Update Query to Use Printer Coordinates

**Change SQL Query (Lines 186-189):**

```vbscript
<%
' OLD (commented out):
' strSQL = "SELECT machines.mapleft, machines.maptop, machines.machinenumber, ... FROM machines, printers ..."

' NEW - Use printer coordinates, fallback to machine if not set
strSQL = "SELECT " &_
         "COALESCE(printers.mapleft, machines.mapleft) AS mapleft, " &_
         "COALESCE(printers.maptop, machines.maptop) AS maptop, " &_
         "machines.machinenumber, machines.alias, " &_
         "printers.printerid, printers.printercsfname, printers.printerwindowsname, " &_
         "models.modelnumber, models.image, printers.ipaddress, printers.fqdn, " &_
         "printers.maptop AS printer_maptop, printers.mapleft AS printer_mapleft " &_
         "FROM printers " &_
         "INNER JOIN models ON printers.modelid = models.modelnumberid " &_
         "LEFT JOIN machines ON printers.machineid = machines.machineid " &_
         "WHERE printers.isactive = 1 " &_
         "  AND (printers.maptop IS NOT NULL OR machines.maptop IS NOT NULL)"

set rs = objconn.Execute(strSQL)
while not rs.eof
    mapleft = rs("mapleft")
    maptop = rs("maptop")
    maptop = 2550 - maptop  ' Coordinate transformation
    ' ... rest of code
%>
```

**Benefits:**
- ✅ Uses printer coordinates if available
- ✅ Falls back to machine coordinates if printer coordinates not set
- ✅ Includes printers without machine assignment (if they have coordinates)
- ✅ Backward compatible during migration

---

#### 2. **addprinter.asp** & **editprinter.asp** - Add Map Picker

**Add New Form Fields (after line 197 in addprinter.asp):**

```html
<div class="form-group">
  <label>Map Position</label>
  <div class="row">
    <div class="col-md-6">
      <label for="maptop">Map Top</label>
      <input type="number" class="form-control" id="maptop" name="maptop"
             placeholder="Y coordinate (0-2550)" min="0" max="2550">
    </div>
    <div class="col-md-6">
      <label for="mapleft">Map Left</label>
      <input type="number" class="form-control" id="mapleft" name="mapleft"
             placeholder="X coordinate (0-3300)" min="0" max="3300">
    </div>
  </div>
  <small class="form-text text-muted">
    Leave blank to use machine location.
    <a href="./printermap.asp" target="_blank" style="color:#007bff;">
      <i class="zmdi zmdi-map"></i> Open map in new tab
    </a> to find coordinates.
  </small>
</div>

<!-- Optional: Add map picker button -->
<div class="form-group">
  <button type="button" class="btn btn-sm btn-info" id="openMapPicker">
    <i class="zmdi zmdi-map"></i> Pick Location on Map
  </button>
</div>
```

**Add JavaScript for Map Picker Modal (before closing `</body>`):**

```javascript
<script>
// Map picker functionality
$('#openMapPicker').on('click', function() {
    // Option 1: Simple - open map in new window
    window.open('./printermap.asp', 'mapPicker', 'width=1200,height=800');

    // Option 2: Advanced - inline modal with Leaflet (future enhancement)
    // Show modal with embedded map, allow clicking to select coordinates
});

// If machine is selected, optionally pre-fill coordinates from machine
$('#machineid').on('change', function() {
    var machineid = $(this).val();
    if (machineid && machineid != '1') {
        // AJAX call to get machine coordinates
        $.get('./api_machine_coordinates.asp?machineid=' + machineid, function(data) {
            if (data.maptop && data.mapleft) {
                // Optionally suggest coordinates
                if (confirm('Use machine location coordinates?\nTop: ' + data.maptop + ', Left: ' + data.mapleft)) {
                    $('#maptop').val(data.maptop);
                    $('#mapleft').val(data.mapleft);
                }
            }
        });
    }
});
</script>
```

---

#### 3. **saveprinter_direct.asp** - Handle Map Coordinates on Insert

**Add Input Collection (after line 18):**

```vbscript
Dim maptop, mapleft
maptop = Trim(Request.Form("maptop"))
mapleft = Trim(Request.Form("mapleft"))

' Validate coordinates if provided
If maptop <> "" And Not IsNumeric(maptop) Then
    Response.Write("<div class='alert alert-danger'>Error: Invalid map top coordinate.</div>")
    Response.Write("<a href='addprinter.asp'>Go back</a>")
    objConn.Close
    Response.End
End If

If mapleft <> "" And Not IsNumeric(mapleft) Then
    Response.Write("<div class='alert alert-danger'>Error: Invalid map left coordinate.</div>")
    Response.Write("<a href='addprinter.asp'>Go back</a>")
    objConn.Close
    Response.End
End If

' Convert to integers or NULL
Dim maptopSQL, mapleftSQL
If maptop <> "" And IsNumeric(maptop) Then
    maptopSQL = CLng(maptop)
Else
    maptopSQL = "NULL"
End If

If mapleft <> "" And IsNumeric(mapleft) Then
    mapleftSQL = CLng(mapleft)
Else
    mapleftSQL = "NULL"
End If
```

**Update INSERT Statement (line 191):**

```vbscript
strSQL = "INSERT INTO printers (modelid, serialnumber, ipaddress, fqdn, " &_
         "printercsfname, printerwindowsname, machineid, maptop, mapleft, isactive) " &_
         "VALUES (" & modelid & ", '" & serialnumber & "', '" & ipaddress & "', " &_
         "'" & fqdn & "', '" & printercsfname & "', '" & printerwindowsname & "', " &_
         machineid & ", " & maptopSQL & ", " & mapleftSQL & ", 1)"
```

---

#### 4. **saveprinter.asp** - Handle Map Coordinates on Update

**Add Same Input Collection Code as saveprinter_direct.asp**

**Update UPDATE Statement (line 168):**

```vbscript
strSQL = "UPDATE printers SET " &_
         "modelid = " & modelid & ", " &_
         "serialnumber = '" & serialnumber & "', " &_
         "ipaddress = '" & ipaddress & "', " &_
         "fqdn = '" & fqdn & "', " &_
         "printercsfname = '" & printercsfname & "', " &_
         "printerwindowsname = '" & printerwindowsname & "', " &_
         "machineid = " & machineid & ", " &_
         "maptop = " & maptopSQL & ", " &_
         "mapleft = " & mapleftSQL & " " &_
         "WHERE printerid = " & printerid
```

---

### Priority 2: Enhanced Features

#### 5. **displayprinter.asp** - Show Map Coordinates

**Add to Settings Tab (after line 81):**

```html
<p class="mb-2"><strong>Map Position:</strong></p>
```

**Add to Values Column (after line 93):**

```vbscript
<p class="mb-2">
<%
    If NOT IsNull(rs("maptop")) AND NOT IsNull(rs("mapleft")) Then
        Response.Write("Top: " & rs("maptop") & ", Left: " & rs("mapleft"))
        Response.Write(" <a href='./printermap.asp' target='_blank' style='color:#007bff;' title='View on Map'>")
        Response.Write("<i class='zmdi zmdi-map'></i></a>")
    ElseIf NOT IsNull(rs("machines.maptop")) Then
        Response.Write("Using machine location")
    Else
        Response.Write("<span style='color:#999;'>Not set</span>")
    End If
%>
</p>
```

---

#### 6. Create Helper API: **api_machine_coordinates.asp**

**New File:**

```vbscript
<%@ Language="VBScript" %>
<%
Response.ContentType = "application/json"
Response.Charset = "UTF-8"

<!--#include file="./includes/sql.asp"-->

Dim machineid
machineid = Request.QueryString("machineid")

If NOT IsNumeric(machineid) Then
    Response.Write("{""error"":""Invalid machine ID""}")
    objConn.Close
    Response.End
End If

Dim strSQL, rs
strSQL = "SELECT maptop, mapleft FROM machines WHERE machineid = " & CLng(machineid)
Set rs = objConn.Execute(strSQL)

If NOT rs.EOF Then
    Response.Write("{")
    Response.Write("""maptop"":" & rs("maptop") & ",")
    Response.Write("""mapleft"":" & rs("mapleft"))
    Response.Write("}")
Else
    Response.Write("{""error"":""Machine not found""}")
End If

rs.Close
Set rs = Nothing
objConn.Close
%>
```

---

### Priority 3: Data Migration

#### 7. Create Migration Script for Existing Printers

**New File: sql/migrate_printer_coordinates.sql**

```sql
-- ============================================================================
-- Migrate Printer Coordinates from Machine Locations
-- ============================================================================
-- This copies machine coordinates to printers that don't have their own coordinates
-- Run this ONCE after adding maptop/mapleft columns to printers

-- Update printers to inherit machine coordinates where not already set
UPDATE printers p
INNER JOIN machines m ON p.machineid = m.machineid
SET
    p.maptop = m.maptop,
    p.mapleft = m.mapleft
WHERE
    p.maptop IS NULL
    AND p.mapleft IS NULL
    AND m.maptop IS NOT NULL
    AND m.mapleft IS NOT NULL
    AND p.isactive = 1;

-- Report: Show printers with coordinates
SELECT
    'Printers with own coordinates' AS status,
    COUNT(*) AS count
FROM printers
WHERE maptop IS NOT NULL AND mapleft IS NOT NULL AND isactive = 1

UNION ALL

SELECT
    'Printers without coordinates' AS status,
    COUNT(*) AS count
FROM printers
WHERE (maptop IS NULL OR mapleft IS NULL) AND isactive = 1;

-- List printers still missing coordinates
SELECT
    p.printerid,
    p.printerwindowsname,
    p.ipaddress,
    m.machinenumber,
    p.machineid
FROM printers p
LEFT JOIN machines m ON p.machineid = m.machineid
WHERE (p.maptop IS NULL OR p.mapleft IS NULL)
  AND p.isactive = 1
ORDER BY p.printerid;
```

---

## Implementation Plan

### Phase 1: Core Changes (Day 1)
1. ✅ Add maptop/mapleft to printers table (COMPLETE)
2. ⬜ Update printermap.asp query
3. ⬜ Update saveprinter_direct.asp INSERT
4. ⬜ Update saveprinter.asp UPDATE
5. ⬜ Run data migration SQL script

### Phase 2: Form Updates (Day 2)
1. ⬜ Add coordinate fields to addprinter.asp
2. ⬜ Add coordinate fields to editprinter.asp
3. ⬜ Test printer creation with coordinates
4. ⬜ Test printer editing with coordinates

### Phase 3: Enhanced Features (Day 3)
1. ⬜ Add map picker button functionality
2. ⬜ Create api_machine_coordinates.asp
3. ⬜ Update displayprinter.asp to show coordinates
4. ⬜ Test full workflow

### Phase 4: Testing & Documentation (Day 4)
1. ⬜ Test with various printer scenarios
2. ⬜ Update user documentation
3. ⬜ Train users on new feature
4. ⬜ Monitor for issues

---

## Testing Checklist

### Backward Compatibility
- ⬜ Existing printers without coordinates still appear on map (using machine location)
- ⬜ Machine dropdown still functions
- ⬜ Printers assigned to machineid=1 can now have coordinates

### New Functionality
- ⬜ Can add printer with custom coordinates
- ⬜ Can edit printer coordinates
- ⬜ Can leave coordinates blank (uses machine location)
- ⬜ Multiple printers at same machine can have different positions
- ⬜ Printers without machine assignment can appear on map

### Edge Cases
- ⬜ Printer with machineid=1 and no coordinates (should not appear on map)
- ⬜ Printer with coordinates but machineid=1 (should appear on map)
- ⬜ Invalid coordinate values (should be rejected)
- ⬜ Null/empty coordinate values (should use machine location)

---

## Benefits of This Approach

1. **Backward Compatible**: Existing printers continue to work using machine locations
2. **Flexible**: Printers can be positioned independently of machines
3. **Gradual Migration**: Can update printer positions over time
4. **No Data Loss**: Machine associations are preserved
5. **Better Accuracy**: Printers can show actual physical location

---

## Future Enhancements

### Interactive Map Picker
Create a modal with embedded Leaflet map where users can:
- Click to select printer location
- See existing printers and machines
- Drag printer icon to new position
- Visual grid/snap-to-grid option

### Bulk Update Tool
Create admin page to:
- List all printers with/without coordinates
- Bulk copy coordinates from machines
- Bulk adjust coordinates (offset all by X/Y)
- Import coordinates from CSV

### Map Filtering
Add printermap.asp filters for:
- Show only printers with custom coordinates
- Show only printers using machine locations
- Highlight printers without any location
- Filter by printer model/vendor

---

## Questions for Stakeholders

1. Should we automatically copy machine coordinates to all existing printers? (Recommended: YES)
2. Should machineid still be required? (Recommended: Make optional, but keep for reference)
3. Do we need coordinate validation beyond 0-2550/0-3300 ranges?
4. Should we add a "sync with machine" button to copy machine coords to printer?
5. Priority level for interactive map picker vs manual coordinate entry?

---

## Files to Modify Summary

| File | Priority | Changes Required |
|------|----------|------------------|
| printermap.asp | P1 | Update SQL query to use printer coordinates |
| saveprinter_direct.asp | P1 | Add maptop/mapleft to INSERT |
| saveprinter.asp | P1 | Add maptop/mapleft to UPDATE |
| addprinter.asp | P2 | Add coordinate input fields |
| editprinter.asp | P2 | Add coordinate input fields |
| displayprinter.asp | P2 | Show coordinates in settings |
| api_machine_coordinates.asp | P3 | New file - coordinate lookup API |
| sql/migrate_printer_coordinates.sql | P1 | New file - data migration |

---

**End of Report**
