diff --git a/app/controllers/BoardsController.asp b/app/controllers/BoardsController.asp index 83a4454..99076bb 100644 --- a/app/controllers/BoardsController.asp +++ b/app/controllers/BoardsController.asp @@ -50,12 +50,14 @@ Class BoardsController_Class slug = boards_Repository().UniqueSlug(GenerateSlug(boardName), 0) Dim board : Set board = New POBO_boards - board.name = boardName - board.slug = slug - board.created_at = Now() - board.created_by = currentUsername - board.updated_at = Now() - board.updated_by = currentUsername + board.name = boardName + board.slug = slug + board.import_from_printstream = (Request.Form("import_from_printstream") = "on") + board.printstream_job_name = Trim(CStr(Request.Form("printstream_job_name"))) + board.created_at = Now() + board.created_by = currentUsername + board.updated_at = Now() + board.updated_by = currentUsername boards_Repository().AddNew board @@ -117,6 +119,11 @@ Class BoardsController_Class """swim_lane_id"":" & cardItem.swim_lane_id & "," & _ """job_number"":" & JsonStr(cardItem.job_number) & "," & _ """job_name"":" & JsonStr(cardItem.job_name) & "," & _ + """customer_name"":" & JsonStr(cardItem.customer_name) & "," & _ + """delivery_date"":" & JsonDateStr(cardItem.delivery_date) & "," & _ + """quantity"":" & JsonStr(cardItem.quantity) & "," & _ + """notes"":" & JsonStr(cardItem.notes) & "," & _ + """full_note"":" & JsonStr(cardItem.full_note) & "," & _ """position"":" & cardItem.position & "}" firstCard = False Loop @@ -168,10 +175,12 @@ Class BoardsController_Class Dim newSlug : newSlug = boards_Repository().UniqueSlug(GenerateSlug(newName), CLng(board.id)) - board.name = newName - board.slug = newSlug - board.updated_at = Now() - board.updated_by = GetCurrentUsername() + board.name = newName + board.slug = newSlug + board.import_from_printstream = (Request.Form("import_from_printstream") = "on") + board.printstream_job_name = Trim(CStr(Request.Form("printstream_job_name"))) + board.updated_at = Now() + board.updated_by = GetCurrentUsername() boards_Repository().Update board @@ -226,6 +235,24 @@ Class BoardsController_Class JsonStr = """" & v & """" End Function + Private Function JsonDateStr(d) + If IsNull(d) Or IsEmpty(d) Then + JsonDateStr = "null" + Exit Function + End If + On Error Resume Next + Dim dt, s + dt = CDate(d) + s = Year(dt) & "-" & Right("0" & Month(dt), 2) & "-" & Right("0" & Day(dt), 2) + If Err.Number <> 0 Then + Err.Clear + JsonDateStr = "null" + Else + JsonDateStr = """" & s & """" + End If + On Error GoTo 0 + End Function + End Class Dim BoardsController_Class__Singleton diff --git a/app/controllers/CardsController.asp b/app/controllers/CardsController.asp index 0d03abc..ead4d08 100644 --- a/app/controllers/CardsController.asp +++ b/app/controllers/CardsController.asp @@ -37,22 +37,39 @@ Class CardsController_Class Dim username : username = GetCurrentUsername() Dim card : Set card = New POBO_cards - card.board_id = boardId - card.column_id = columnId - card.swim_lane_id = swimLaneId - card.job_number = jobNum - card.job_name = jobName - card.position = nextPos - card.created_at = Now() - card.created_by = username - card.updated_at = Now() - card.updated_by = username + card.board_id = boardId + card.column_id = columnId + card.swim_lane_id = swimLaneId + card.job_number = jobNum + card.job_name = jobName + card.customer_name = Trim(CStr(Request.Form("customer_name") & "")) + card.delivery_date = Trim(CStr(Request.Form("delivery_date") & "")) + card.quantity = Trim(CStr(Request.Form("quantity") & "")) + card.notes = Trim(CStr(Request.Form("notes") & "")) + card.full_note = CStr(Request.Form("full_note") & "") + card.position = nextPos + card.created_at = Now() + card.created_by = username + card.updated_at = Now() + card.updated_by = username + On Error Resume Next cards_Repository().AddNew card + If Err.Number <> 0 Then + Response.Write "{""ok"":false,""error"":" & JsonString(Err.Description) & "}" + Err.Clear + Exit Sub + End If + On Error GoTo 0 Response.Write "{""ok"":true,""id"":" & card.id & "," & _ """job_number"":" & JsonString(card.job_number) & "," & _ """job_name"":" & JsonString(card.job_name) & "," & _ + """customer_name"":" & JsonString(card.customer_name) & "," & _ + """delivery_date"":" & JsonDateStr(card.delivery_date) & "," & _ + """quantity"":" & JsonString(card.quantity) & "," & _ + """notes"":" & JsonString(card.notes) & "," & _ + """full_note"":" & JsonString(card.full_note) & "," & _ """column_id"":" & card.column_id & "," & _ """swim_lane_id"":" & card.swim_lane_id & "," & _ """position"":" & card.position & "}" @@ -77,14 +94,31 @@ Class CardsController_Class card.job_number = Trim(CStr(Request.Form("job_number"))) card.job_name = Trim(CStr(Request.Form("job_name"))) + card.customer_name = Trim(CStr(Request.Form("customer_name") & "")) + card.delivery_date = Trim(CStr(Request.Form("delivery_date") & "")) + card.quantity = Trim(CStr(Request.Form("quantity") & "")) + card.notes = Trim(CStr(Request.Form("notes") & "")) + card.full_note = CStr(Request.Form("full_note") & "") card.updated_at = Now() card.updated_by = GetCurrentUsername() + On Error Resume Next cards_Repository().Update card + If Err.Number <> 0 Then + Response.Write "{""ok"":false,""error"":" & JsonString(Err.Description) & "}" + Err.Clear + Exit Sub + End If + On Error GoTo 0 Response.Write "{""ok"":true," & _ """job_number"":" & JsonString(card.job_number) & "," & _ - """job_name"":" & JsonString(card.job_name) & "}" + """job_name"":" & JsonString(card.job_name) & "," & _ + """customer_name"":" & JsonString(card.customer_name) & "," & _ + """delivery_date"":" & JsonDateStr(card.delivery_date) & "," & _ + """quantity"":" & JsonString(card.quantity) & "," & _ + """notes"":" & JsonString(card.notes) & "," & _ + """full_note"":" & JsonString(card.full_note) & "}" End Sub ' POST /cards/:id/move — form: column_id, swim_lane_id, position, [sibling_ids CSV for reorder] @@ -147,7 +181,31 @@ Class CardsController_Class End Function Private Function JsonString(s) - JsonString = """" & Replace(Replace(CStr(s), "\", "\\"), """", "\""") & """" + Dim v : v = CStr(s & "") + v = Replace(v, "\", "\\") + v = Replace(v, """", "\""") + v = Replace(v, vbCrLf, "\n") + v = Replace(v, vbLf, "\n") + v = Replace(v, vbCr, "\n") + JsonString = """" & v & """" + End Function + + Private Function JsonDateStr(d) + If IsNull(d) Or IsEmpty(d) Then + JsonDateStr = "null" + Exit Function + End If + On Error Resume Next + Dim dt, s + dt = CDate(d) + s = Year(dt) & "-" & Right("0" & Month(dt), 2) & "-" & Right("0" & Day(dt), 2) + If Err.Number <> 0 Then + Err.Clear + JsonDateStr = "null" + Else + JsonDateStr = """" & s & """" + End If + On Error GoTo 0 End Function End Class diff --git a/app/controllers/ColumnsController.asp b/app/controllers/ColumnsController.asp index 697080f..01a2305 100644 --- a/app/controllers/ColumnsController.asp +++ b/app/controllers/ColumnsController.asp @@ -102,19 +102,42 @@ Class ColumnsController_Class End If Dim rawJson : rawJson = GetRawJsonFromRequest() - Dim parsed : Set parsed = JSON.parse(rawJson) + Dim parser : Set parser = New aspJSON + Dim parsed - If IsNull(parsed) Or IsEmpty(parsed) Then - Response.Write "{""ok"":false,""error"":""Invalid JSON""}" + On Error Resume Next + parser.loadJSON rawJson + If Err.Number <> 0 Then + Err.Clear + Set parser = Nothing + Response.Write "{""ok"":false,""error"":""Invalid JSON payload""}" + Exit Sub + End If + Set parsed = parser.data + On Error GoTo 0 + + If parsed Is Nothing Or parsed.Count = 0 Then + Set parser = Nothing + Response.Write "{""ok"":false,""error"":""Invalid JSON payload""}" Exit Sub End If Dim username : username = GetCurrentUsername() Dim i, item + On Error Resume Next For i = 0 To parsed.Count - 1 Set item = parsed.Item(i) board_columns_Repository().UpdatePosition CLng(item.Item("id")), CLng(item.Item("position")), Now(), username + If Err.Number <> 0 Then + Err.Clear + On Error GoTo 0 + Set parser = Nothing + Response.Write "{""ok"":false,""error"":""Invalid reorder item at index " & i & """}" + Exit Sub + End If Next + On Error GoTo 0 + Set parser = Nothing Response.Write "{""ok"":true}" End Sub diff --git a/app/controllers/SwimLanesController.asp b/app/controllers/SwimLanesController.asp index 0b0a4ed..fd6dbe4 100644 --- a/app/controllers/SwimLanesController.asp +++ b/app/controllers/SwimLanesController.asp @@ -102,19 +102,42 @@ Class SwimLanesController_Class End If Dim rawJson : rawJson = GetRawJsonFromRequest() - Dim parsed : Set parsed = JSON.parse(rawJson) + Dim parser : Set parser = New aspJSON + Dim parsed - If IsNull(parsed) Or IsEmpty(parsed) Then - Response.Write "{""ok"":false,""error"":""Invalid JSON""}" + On Error Resume Next + parser.loadJSON rawJson + If Err.Number <> 0 Then + Err.Clear + Set parser = Nothing + Response.Write "{""ok"":false,""error"":""Invalid JSON payload""}" + Exit Sub + End If + Set parsed = parser.data + On Error GoTo 0 + + If parsed Is Nothing Or parsed.Count = 0 Then + Set parser = Nothing + Response.Write "{""ok"":false,""error"":""Invalid JSON payload""}" Exit Sub End If Dim username : username = GetCurrentUsername() Dim i, item + On Error Resume Next For i = 0 To parsed.Count - 1 Set item = parsed.Item(i) swim_lanes_Repository().UpdatePosition CLng(item.Item("id")), CLng(item.Item("position")), Now(), username + If Err.Number <> 0 Then + Err.Clear + On Error GoTo 0 + Set parser = Nothing + Response.Write "{""ok"":false,""error"":""Invalid reorder item at index " & i & """}" + Exit Sub + End If Next + On Error GoTo 0 + Set parser = Nothing Response.Write "{""ok"":true}" End Sub diff --git a/app/models/POBO_boards.asp b/app/models/POBO_boards.asp index 6e2ba45..b50446d 100644 --- a/app/models/POBO_boards.asp +++ b/app/models/POBO_boards.asp @@ -5,20 +5,24 @@ Class POBO_boards Private p_id Private p_name Private p_slug + Private p_import_from_printstream + Private p_printstream_job_name Private p_created_at Private p_created_by Private p_updated_at Private p_updated_by Private Sub Class_Initialize() - p_id = 0 - p_name = "" - p_slug = "" - p_created_at = #1/1/1970# - p_created_by = "" - p_updated_at = #1/1/1970# - p_updated_by = "" - Properties = Array("id","name","slug","created_at","created_by","updated_at","updated_by") + p_id = 0 + p_name = "" + p_slug = "" + p_import_from_printstream = False + p_printstream_job_name = "" + p_created_at = #1/1/1970# + p_created_by = "" + p_updated_at = #1/1/1970# + p_updated_by = "" + Properties = Array("id","name","slug","import_from_printstream","printstream_job_name","created_at","created_by","updated_at","updated_by") End Sub Public Property Get PrimaryKey() : PrimaryKey = "id" : End Property @@ -33,6 +37,12 @@ Class POBO_boards Public Property Get slug() : slug = p_slug : End Property Public Property Let slug(v) : p_slug = CStr(v) : End Property + Public Property Get import_from_printstream() : import_from_printstream = p_import_from_printstream : End Property + Public Property Let import_from_printstream(v) : p_import_from_printstream = CBool(v) : End Property + + Public Property Get printstream_job_name() : printstream_job_name = p_printstream_job_name : End Property + Public Property Let printstream_job_name(v) : p_printstream_job_name = CStr(v) : End Property + Public Property Get created_at() : created_at = p_created_at : End Property Public Property Let created_at(v) : p_created_at = CDate(v) : End Property diff --git a/app/models/POBO_cards.asp b/app/models/POBO_cards.asp index 619388c..dd26dc9 100644 --- a/app/models/POBO_cards.asp +++ b/app/models/POBO_cards.asp @@ -8,6 +8,11 @@ Class POBO_cards Private p_swim_lane_id Private p_job_number Private p_job_name + Private p_customer_name + Private p_delivery_date + Private p_quantity + Private p_notes + Private p_full_note Private p_position Private p_created_at Private p_created_by @@ -15,18 +20,23 @@ Class POBO_cards Private p_updated_by Private Sub Class_Initialize() - p_id = 0 - p_board_id = 0 - p_column_id = 0 - p_swim_lane_id = 0 - p_job_number = "" - p_job_name = "" - p_position = 0 - p_created_at = #1/1/1970# - p_created_by = "" - p_updated_at = #1/1/1970# - p_updated_by = "" - Properties = Array("id","board_id","column_id","swim_lane_id","job_number","job_name","position","created_at","created_by","updated_at","updated_by") + p_id = 0 + p_board_id = 0 + p_column_id = 0 + p_swim_lane_id = 0 + p_job_number = "" + p_job_name = "" + p_customer_name = "" + p_delivery_date = Null + p_quantity = "" + p_notes = "" + p_full_note = "" + p_position = 0 + p_created_at = #1/1/1970# + p_created_by = "" + p_updated_at = #1/1/1970# + p_updated_by = "" + Properties = Array("id","board_id","column_id","swim_lane_id","job_number","job_name","customer_name","delivery_date","quantity","notes","full_note","position","created_at","created_by","updated_at","updated_by") End Sub Public Property Get PrimaryKey() : PrimaryKey = "id" : End Property @@ -50,6 +60,23 @@ Class POBO_cards Public Property Get job_name() : job_name = p_job_name : End Property Public Property Let job_name(v) : p_job_name = CStr(v) : End Property + Public Property Get customer_name() : customer_name = p_customer_name : End Property + Public Property Let customer_name(v) : p_customer_name = CStr(v & "") : End Property + + Public Property Get delivery_date() : delivery_date = p_delivery_date : End Property + Public Property Let delivery_date(v) + If IsDate(v) Then p_delivery_date = CDate(v) Else p_delivery_date = Null + End Property + + Public Property Get quantity() : quantity = p_quantity : End Property + Public Property Let quantity(v) : p_quantity = CStr(v & "") : End Property + + Public Property Get notes() : notes = p_notes : End Property + Public Property Let notes(v) : p_notes = CStr(v & "") : End Property + + Public Property Get full_note() : full_note = p_full_note : End Property + Public Property Let full_note(v) : p_full_note = CStr(v & "") : End Property + Public Property Get position() : position = p_position : End Property Public Property Let position(v) : p_position = CDbl(v) : End Property diff --git a/app/repositories/boards_Repository.asp b/app/repositories/boards_Repository.asp index 3465774..14e7d59 100644 --- a/app/repositories/boards_Repository.asp +++ b/app/repositories/boards_Repository.asp @@ -2,7 +2,7 @@ Class boards_Repository_Class Public Function FindByID(id) - Dim sql : sql = "SELECT [id],[name],[slug],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] WHERE [id] = ?" + Dim sql : sql = "SELECT [id],[name],[slug],[import_from_printstream],[printstream_job_name],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] WHERE [id] = ?" Dim rs : Set rs = DAL.Query(sql, Array(id)) If rs.EOF Then Err.Raise 1, "boards_Repository_Class", "Board not found with id = " & id @@ -13,7 +13,7 @@ Class boards_Repository_Class End Function Public Function FindBySlug(slug) - Dim sql : sql = "SELECT [id],[name],[slug],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] WHERE [slug] = ?" + Dim sql : sql = "SELECT [id],[name],[slug],[import_from_printstream],[printstream_job_name],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] WHERE [slug] = ?" Dim rs : Set rs = DAL.Query(sql, Array(slug)) If rs.EOF Then Set FindBySlug = Nothing @@ -24,7 +24,7 @@ Class boards_Repository_Class End Function Public Function GetAll() - Dim sql : sql = "SELECT [id],[name],[slug],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] ORDER BY [name] ASC" + Dim sql : sql = "SELECT [id],[name],[slug],[import_from_printstream],[printstream_job_name],[created_at],[created_by],[updated_at],[updated_by] FROM [boards] ORDER BY [name] ASC" Dim rs : Set rs = DAL.Query(sql, Empty) Dim list : Set list = New LinkedList_Class Do Until rs.EOF @@ -60,8 +60,8 @@ Class boards_Repository_Class End Function Public Sub AddNew(ByRef model) - Dim sql : sql = "INSERT INTO [boards] ([name],[slug],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?)" - DAL.Execute sql, Array(model.name, model.slug, model.created_at, model.created_by, model.updated_at, model.updated_by) + Dim sql : sql = "INSERT INTO [boards] ([name],[slug],[import_from_printstream],[printstream_job_name],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?,?,?)" + DAL.Execute sql, Array(model.name, model.slug, model.import_from_printstream, model.printstream_job_name, model.created_at, model.created_by, model.updated_at, model.updated_by) Dim rsId : Set rsId = DAL.Query("SELECT @@IDENTITY AS NewID", Empty) If Not rsId.EOF Then If Not IsNull(rsId(0)) Then model.id = rsId(0) @@ -70,8 +70,8 @@ Class boards_Repository_Class End Sub Public Sub Update(model) - Dim sql : sql = "UPDATE [boards] SET [name]=?,[slug]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?" - DAL.Execute sql, Array(model.name, model.slug, model.updated_at, model.updated_by, model.id) + Dim sql : sql = "UPDATE [boards] SET [name]=?,[slug]=?,[import_from_printstream]=?,[printstream_job_name]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?" + DAL.Execute sql, Array(model.name, model.slug, model.import_from_printstream, model.printstream_job_name, model.updated_at, model.updated_by, model.id) End Sub Public Sub Delete(id) diff --git a/app/repositories/cards_Repository.asp b/app/repositories/cards_Repository.asp index 05026f2..90a322a 100644 --- a/app/repositories/cards_Repository.asp +++ b/app/repositories/cards_Repository.asp @@ -2,7 +2,7 @@ Class cards_Repository_Class Private Function SelectBase() - SelectBase = "SELECT [id],[board_id],[column_id],[swim_lane_id],[job_number],[job_name],[position],[created_at],[created_by],[updated_at],[updated_by] FROM [cards]" + SelectBase = "SELECT [id],[board_id],[column_id],[swim_lane_id],[job_number],[job_name],[customer_name],[delivery_date],[quantity],[notes],[full_note],[position],[created_at],[created_by],[updated_at],[updated_by] FROM [cards]" End Function Public Function FindByID(id) @@ -52,8 +52,15 @@ Class cards_Repository_Class End Function Public Sub AddNew(ByRef model) - Dim sql : sql = "INSERT INTO [cards] ([board_id],[column_id],[swim_lane_id],[job_number],[job_name],[position],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?,?,?,?,?)" - DAL.Execute sql, Array(model.board_id, model.column_id, model.swim_lane_id, model.job_number, model.job_name, model.position, model.created_at, model.created_by, model.updated_at, model.updated_by) + Dim sql, params + If QuantityIsBlank(model.quantity) Then + sql = "INSERT INTO [cards] ([board_id],[column_id],[swim_lane_id],[job_number],[job_name],[customer_name],[delivery_date],[quantity],[notes],[full_note],[position],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?,?,NULL,?,?,?,?,?,?,?)" + params = Array(model.board_id, model.column_id, model.swim_lane_id, model.job_number, model.job_name, model.customer_name, model.delivery_date, model.notes, model.full_note, model.position, model.created_at, model.created_by, model.updated_at, model.updated_by) + Else + sql = "INSERT INTO [cards] ([board_id],[column_id],[swim_lane_id],[job_number],[job_name],[customer_name],[delivery_date],[quantity],[notes],[full_note],[position],[created_at],[created_by],[updated_at],[updated_by]) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" + params = Array(model.board_id, model.column_id, model.swim_lane_id, model.job_number, model.job_name, model.customer_name, model.delivery_date, model.quantity, model.notes, model.full_note, model.position, model.created_at, model.created_by, model.updated_at, model.updated_by) + End If + DAL.Execute sql, params Dim rsId : Set rsId = DAL.Query("SELECT @@IDENTITY AS NewID", Empty) If Not rsId.EOF Then If Not IsNull(rsId(0)) Then model.id = rsId(0) @@ -62,10 +69,21 @@ Class cards_Repository_Class End Sub Public Sub Update(model) - Dim sql : sql = "UPDATE [cards] SET [job_number]=?,[job_name]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?" - DAL.Execute sql, Array(model.job_number, model.job_name, model.updated_at, model.updated_by, model.id) + Dim sql, params + If QuantityIsBlank(model.quantity) Then + sql = "UPDATE [cards] SET [job_number]=?,[job_name]=?,[customer_name]=?,[delivery_date]=?,[quantity]=NULL,[notes]=?,[full_note]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?" + params = Array(model.job_number, model.job_name, model.customer_name, model.delivery_date, model.notes, model.full_note, model.updated_at, model.updated_by, model.id) + Else + sql = "UPDATE [cards] SET [job_number]=?,[job_name]=?,[customer_name]=?,[delivery_date]=?,[quantity]=?,[notes]=?,[full_note]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?" + params = Array(model.job_number, model.job_name, model.customer_name, model.delivery_date, model.quantity, model.notes, model.full_note, model.updated_at, model.updated_by, model.id) + End If + DAL.Execute sql, params End Sub + Private Function QuantityIsBlank(v) + QuantityIsBlank = (Len(Trim(CStr(v & ""))) = 0) + End Function + Public Sub Move(id, columnId, swimLaneId, position, updatedAt, updatedBy) Dim sql : sql = "UPDATE [cards] SET [column_id]=?,[swim_lane_id]=?,[position]=?,[updated_at]=?,[updated_by]=? WHERE [id]=?" DAL.Execute sql, Array(columnId, swimLaneId, position, updatedAt, updatedBy, id) diff --git a/app/views/Boards/Create.asp b/app/views/Boards/Create.asp index 735ec4a..9e941d1 100644 --- a/app/views/Boards/Create.asp +++ b/app/views/Boards/Create.asp @@ -19,6 +19,18 @@
 
+
+
+ + +
+
+
Cancel @@ -31,11 +43,19 @@ diff --git a/app/views/Boards/Show.asp b/app/views/Boards/Show.asp index dfab647..4e0b162 100644 --- a/app/views/Boards/Show.asp +++ b/app/views/Boards/Show.asp @@ -10,8 +10,11 @@ Response.CodePage = 65001 - - + + + + + @@ -23,6 +26,20 @@ Response.CodePage = 65001 <%= H(board.name) %>
+
<%= H(vLaneItem.name) %>
diff --git a/app/views/Cards/_modal.asp b/app/views/Cards/_modal.asp index f2a8a05..609bbc2 100644 --- a/app/views/Cards/_modal.asp +++ b/app/views/Cards/_modal.asp @@ -19,6 +19,28 @@ +
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
diff --git a/db/migrations/20260423100000_add_printstream_to_boards.asp b/db/migrations/20260423100000_add_printstream_to_boards.asp new file mode 100644 index 0000000..1f21cb3 --- /dev/null +++ b/db/migrations/20260423100000_add_printstream_to_boards.asp @@ -0,0 +1,15 @@ +<% +'======================================================================================================================= +' MIGRATION: add_printstream_to_boards +'======================================================================================================================= + +Sub Migration_Up(migration) + migration.ExecuteSQL "ALTER TABLE [boards] ADD COLUMN [import_from_printstream] YESNO" + migration.ExecuteSQL "ALTER TABLE [boards] ADD COLUMN [printstream_job_name] MEMO" +End Sub + +Sub Migration_Down(migration) + migration.ExecuteSQL "ALTER TABLE [boards] DROP COLUMN [printstream_job_name]" + migration.ExecuteSQL "ALTER TABLE [boards] DROP COLUMN [import_from_printstream]" +End Sub +%> diff --git a/db/migrations/20260424100000_add_printstream_fields_to_cards.asp b/db/migrations/20260424100000_add_printstream_fields_to_cards.asp new file mode 100644 index 0000000..907596d --- /dev/null +++ b/db/migrations/20260424100000_add_printstream_fields_to_cards.asp @@ -0,0 +1,19 @@ +<% +'======================================================================================================================= +' MIGRATION: add_printstream_fields_to_cards +'======================================================================================================================= + +Sub Migration_Up(migration) + migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [customer_name] VARCHAR(255)" + migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [delivery_date] DATETIME" + migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [quantity] VARCHAR(50)" + migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [notes] MEMO" +End Sub + +Sub Migration_Down(migration) + migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [notes]" + migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [quantity]" + migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [delivery_date]" + migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [customer_name]" +End Sub +%> diff --git a/db/migrations/20260424200000_add_full_note_to_cards.asp b/db/migrations/20260424200000_add_full_note_to_cards.asp new file mode 100644 index 0000000..e360855 --- /dev/null +++ b/db/migrations/20260424200000_add_full_note_to_cards.asp @@ -0,0 +1,13 @@ +<% +'======================================================================================================================= +' MIGRATION: add_full_note_to_cards +'======================================================================================================================= + +Sub Migration_Up(migration) + migration.ExecuteSQL "ALTER TABLE [cards] ADD COLUMN [full_note] MEMO" +End Sub + +Sub Migration_Down(migration) + migration.ExecuteSQL "ALTER TABLE [cards] DROP COLUMN [full_note]" +End Sub +%> diff --git a/db/webdata.accdb b/db/webdata.accdb index e294807..d843e93 100644 Binary files a/db/webdata.accdb and b/db/webdata.accdb differ diff --git a/public/css/kanban.css b/public/css/kanban.css index 64c4f80..1603cc5 100644 --- a/public/css/kanban.css +++ b/public/css/kanban.css @@ -27,7 +27,6 @@ body.kanban-page .navbar { body.kanban-page .navbar-brand { color: #f4f8ff !important; - font-family: "Fraunces", Georgia, serif; letter-spacing: -0.01em; } @@ -39,6 +38,33 @@ body.kanban-page .navbar-brand { flex-shrink: 0; } +.board-header-search { + width: min(440px, 36vw); + margin: 0 0.75rem; +} + +.board-header-search .input-group-text { + border-color: rgba(214, 229, 250, 0.7); + background: rgba(255, 255, 255, 0.15); + color: #eff6ff; +} + +.board-header-search .form-control { + border-color: rgba(214, 229, 250, 0.7); + background: rgba(255, 255, 255, 0.17); + color: #f5f9ff; +} + +.board-header-search .form-control::placeholder { + color: rgba(235, 243, 255, 0.72); +} + +.board-header-search .form-control:focus { + border-color: rgba(235, 245, 255, 0.95); + box-shadow: 0 0 0 0.2rem rgba(157, 194, 245, 0.25); + background: rgba(255, 255, 255, 0.23); +} + .kanban-board-title { display: block; min-width: 0; @@ -67,21 +93,27 @@ body.kanban-page .navbar .btn-outline-secondary:hover { width: 100%; margin: 0 auto; height: calc(100vh - 65px); - overflow: auto; + overflow-x: auto; + overflow-y: auto; padding: 0.9rem 1rem 1.1rem; -webkit-overflow-scrolling: touch; scroll-behavior: smooth; touch-action: pan-x pan-y; + overscroll-behavior: contain; + scrollbar-width: thin; + scrollbar-color: #8fb0e0 #dce8f8; + scrollbar-gutter: stable; } .kanban-grid { display: grid; - min-width: max(75vw, max-content); + width: max-content; + min-width: 100%; border: 1px solid var(--line, #d9e3f5); border-radius: 14px; background: rgba(255, 255, 255, 0.72); box-shadow: 0 12px 34px rgba(22, 48, 92, 0.12); - overflow: hidden; + overflow: clip; } /* Sticky corner and headers */ @@ -121,6 +153,9 @@ body.kanban-page .navbar .btn-outline-secondary:hover { left: 0; z-index: 20; min-width: 240px; + display: flex; + align-items: center; + gap: 0.42rem; padding: 0.85rem 0.82rem; font-size: clamp(0.7rem, 0.14vw + 0.66rem, 0.78rem); font-weight: 700; @@ -139,6 +174,33 @@ body.kanban-page .navbar .btn-outline-secondary:hover { overflow-wrap: anywhere; } +.kanban-lane-header .lane-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.34rem; + height: 1.34rem; + padding: 0; + border: 0; + border-radius: 999px; + background: rgba(26, 74, 145, 0.12); + color: #1f4f96; + cursor: pointer; + flex: 0 0 auto; +} + +.kanban-lane-header .lane-toggle:hover { + background: rgba(26, 74, 145, 0.22); +} + +.kanban-lane-header .lane-toggle i { + transition: transform 140ms ease; +} + +.kanban-lane-header.lane-collapsed .lane-toggle i { + transform: rotate(-90deg); +} + /* Cells */ .kanban-cell { min-width: 230px; @@ -154,6 +216,20 @@ body.kanban-page .navbar .btn-outline-secondary:hover { linear-gradient(180deg, rgba(255, 255, 255, 0.88) 0%, rgba(244, 248, 255, 0.93) 100%); } +.kanban-cell.lane-collapsed { + min-height: 0; + max-height: 0; + padding-top: 0; + padding-bottom: 0; + border-top-color: transparent; + overflow: hidden; + pointer-events: none; +} + +.kanban-cell.lane-collapsed .kanban-card { + display: none !important; +} + .kanban-cell.drag-over { background: linear-gradient(180deg, #e9f2ff 0%, #deecff 100%); box-shadow: inset 0 0 0 2px rgba(19, 99, 223, 0.24); @@ -187,9 +263,19 @@ body.kanban-page .navbar .btn-outline-secondary:hover { box-shadow: 0 14px 30px rgba(17, 46, 94, 0.22); } +.kanban-card-hidden { + display: none !important; +} + +.card-headline { + display: flex; + align-items: center; + gap: 0.38rem; + min-width: 0; +} + .card-job-number { display: inline-block; - margin-bottom: 0.36rem; padding: 0.08rem 0.42rem; border-radius: 999px; font-size: 0.66rem; @@ -198,12 +284,45 @@ body.kanban-page .navbar .btn-outline-secondary:hover { text-transform: uppercase; color: #0e4fae; background: #e7f0ff; + flex: 0 0 auto; +} + +.card-customer { + font-size: 0.78rem; + font-weight: 600; + color: #2b4a80; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -.card-job-name { - color: #1f2b43; - font-size: 0.86rem; - line-height: 1.32; +.card-meta { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 0.25rem; +} + +.card-meta-label { + font-size: 0.63rem; + font-weight: 700; + color: #7a90b2; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.card-delivery, +.card-qty { + font-size: 0.73rem; + color: #3a5080; +} + +.card-notes { + margin-top: 0.22rem; + font-size: 0.71rem; + color: #647899; + line-height: 1.3; } /* Settings panel */ @@ -294,25 +413,30 @@ body.kanban-page .navbar .btn-outline-secondary:hover { /* Scrollbars */ .kanban-wrapper::-webkit-scrollbar, .settings-body::-webkit-scrollbar { - width: 10px; - height: 10px; + width: 12px; + height: 12px; } .kanban-wrapper::-webkit-scrollbar-track, .settings-body::-webkit-scrollbar-track { - background: #eaf0fb; + background: #dce8f8; + border-radius: 999px; } .kanban-wrapper::-webkit-scrollbar-thumb, .settings-body::-webkit-scrollbar-thumb { - background: #b8c9e6; + background: #8fb0e0; border-radius: 999px; - border: 2px solid #eaf0fb; + border: 2px solid #dce8f8; } .kanban-wrapper::-webkit-scrollbar-thumb:hover, .settings-body::-webkit-scrollbar-thumb:hover { - background: #97afd8; + background: #5e8ecb; +} + +.kanban-wrapper::-webkit-scrollbar-corner { + background: #dce8f8; } /* Small screens */ @@ -340,6 +464,11 @@ body.kanban-page .navbar .btn-outline-secondary:hover { gap: 0.35rem !important; } + .board-header-search { + width: min(270px, 44vw); + margin: 0 0.35rem; + } + .board-header-actions .btn { padding-left: 0.48rem; padding-right: 0.48rem; @@ -347,6 +476,25 @@ body.kanban-page .navbar .btn-outline-secondary:hover { } @media (max-width: 640px) { + .board-header-search { + width: 100%; + margin: 0.5rem 0 0; + order: 3; + } + + body.kanban-page .navbar { + flex-wrap: wrap; + align-items: flex-start !important; + } + + .board-header-main { + flex: 1 1 auto; + } + + .board-header-actions { + flex: 0 0 auto; + } + .kanban-settings-panel { width: 100vw; border-left: 0; diff --git a/public/js/kanban-board.js b/public/js/kanban-board.js index 24b2f73..64f4a67 100644 --- a/public/js/kanban-board.js +++ b/public/js/kanban-board.js @@ -3,6 +3,11 @@ 'use strict'; var boardId = KANBAN.boardId; + var laneCollapseStorageKey = 'kanban_lane_collapsed_' + String(boardId); + var collapsedLaneIds = loadCollapsedLaneIds(); + var searchState = { + query: '' + }; var dragState = { active: false, x: 0, @@ -35,27 +40,123 @@ var grid = document.getElementById('kanban-grid'); var colHs = grid.querySelectorAll('.kanban-col-header'); var cols = '240px'; - colHs.forEach(function () { cols += ' 220px'; }); + colHs.forEach(function () { cols += ' 230px'; }); grid.style.gridTemplateColumns = cols; } + function loadCollapsedLaneIds() { + var laneMap = {}; + try { + var raw = window.localStorage.getItem(laneCollapseStorageKey); + if (!raw) return laneMap; + var arr = JSON.parse(raw); + if (!Array.isArray(arr)) return laneMap; + arr.forEach(function (laneId) { + laneMap[String(laneId)] = true; + }); + } catch (e) { + console.warn('Failed to load lane collapse state', e); + } + return laneMap; + } + + function saveCollapsedLaneIds() { + try { + window.localStorage.setItem(laneCollapseStorageKey, JSON.stringify(Object.keys(collapsedLaneIds))); + } catch (e) { + console.warn('Failed to save lane collapse state', e); + } + } + + function setLaneCollapsed(laneId, isCollapsed) { + var laneKey = String(laneId); + var header = document.querySelector('.kanban-lane-header[data-lane-id="' + laneKey + '"]'); + if (!header) return; + + var laneCells = document.querySelectorAll('.kanban-cell[data-lane-id="' + laneKey + '"]'); + header.classList.toggle('lane-collapsed', isCollapsed); + laneCells.forEach(function (cell) { + cell.classList.toggle('lane-collapsed', isCollapsed); + }); + + var toggleBtn = header.querySelector('.lane-toggle'); + if (toggleBtn) { + toggleBtn.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true'); + toggleBtn.title = isCollapsed ? 'Expand swim lane' : 'Collapse swim lane'; + toggleBtn.setAttribute('aria-label', toggleBtn.title); + } + + if (isCollapsed) { + collapsedLaneIds[laneKey] = true; + } else { + delete collapsedLaneIds[laneKey]; + } + saveCollapsedLaneIds(); + } + + function toggleLaneCollapsed(laneId) { + var laneKey = String(laneId); + setLaneCollapsed(laneKey, !collapsedLaneIds[laneKey]); + } + + function bindLaneHeaderToggle(headerEl) { + if (!headerEl) return; + var toggleBtn = headerEl.querySelector('.lane-toggle'); + if (!toggleBtn) return; + + if (!toggleBtn.dataset.boundToggle) { + toggleBtn.addEventListener('click', function (evt) { + evt.preventDefault(); + evt.stopPropagation(); + toggleLaneCollapsed(headerEl.dataset.laneId); + }); + toggleBtn.dataset.boundToggle = '1'; + } + } + + function initLaneHeaderToggles() { + document.querySelectorAll('.kanban-lane-header').forEach(function (headerEl) { + bindLaneHeaderToggle(headerEl); + if (collapsedLaneIds[String(headerEl.dataset.laneId)]) { + setLaneCollapsed(headerEl.dataset.laneId, true); + } + }); + } + + function cardBodyHtml(card) { + var html = '
' + + '' + esc(card.job_number || '') + ''; + + if (card.customer_name) { + html += '' + esc(card.customer_name) + ''; + } + + html += '
'; + + return html; + } + + function buildCardSearchText(card) { + return [ + card.job_number || '', + card.job_name || '', + card.customer_name || '', + card.notes || '' + ].join(' ').toLowerCase(); + } + function buildCardEl(card) { var div = document.createElement('div'); div.className = 'kanban-card'; div.dataset.id = card.id; div.dataset.columnId = card.column_id; div.dataset.laneId = card.swim_lane_id; - div.innerHTML = - '
' + esc(card.job_number || '') + '
' + - '
' + esc(card.job_name || '') + '
'; + div.dataset.searchText = buildCardSearchText(card); + div.innerHTML = cardBodyHtml(card); div.addEventListener('click', function () { - window.KanbanModal.openEdit( - card.id, - card.column_id, - card.swim_lane_id, - card.job_number, - card.job_name - ); + var c = KANBAN.cards.find(function (x) { return String(x.id) === String(div.dataset.id); }); + if (!c) return; + window.KanbanModal.openEdit(c.id, c.column_id, c.swim_lane_id, c.job_number, c.job_name, c.customer_name, c.delivery_date, c.quantity, c.notes, c.full_note); }); return div; } @@ -69,6 +170,26 @@ cell.appendChild(buildCardEl(card)); } }); + applyCardFilter(); + } + + function applyCardFilter() { + var activeQuery = searchState.query; + document.querySelectorAll('.kanban-card').forEach(function (el) { + var searchableText = (el.dataset.searchText || '').toLowerCase(); + var isMatch = activeQuery === '' || searchableText.indexOf(activeQuery) > -1; + el.classList.toggle('kanban-card-hidden', !isMatch); + }); + } + + function initJobSearch() { + var searchInput = document.getElementById('job-search-input'); + if (!searchInput) return; + + searchInput.addEventListener('input', function () { + searchState.query = String(searchInput.value || '').toLowerCase().trim(); + applyCardFilter(); + }); } function handleDragEnd(evt) { @@ -206,23 +327,31 @@ if (cell) { cell.appendChild(buildCardEl(card)); } + applyCardFilter(); }, - onCardUpdated: function (id, jobNumber, jobName) { + onCardUpdated: function (id, data) { var card = KANBAN.cards.find(function (c) { return String(c.id) === String(id); }); if (card) { - card.job_number = jobNumber; - card.job_name = jobName; + card.job_number = data.job_number || ''; + card.job_name = data.job_name || ''; + card.customer_name = data.customer_name || ''; + card.delivery_date = data.delivery_date || null; + card.quantity = data.quantity || ''; + card.notes = data.notes || ''; + card.full_note = data.full_note !== undefined ? data.full_note : (card.full_note || ''); } var el = document.querySelector('.kanban-card[data-id="' + id + '"]'); - if (el) { - el.querySelector('.card-job-number').textContent = jobNumber; - el.querySelector('.card-job-name').textContent = jobName; + if (el && card) { + el.innerHTML = cardBodyHtml(card); + el.dataset.searchText = buildCardSearchText(card); } + applyCardFilter(); }, onCardDeleted: function (id) { KANBAN.cards = KANBAN.cards.filter(function (c) { return String(c.id) !== String(id); }); var el = document.querySelector('.kanban-card[data-id="' + id + '"]'); if (el) el.remove(); + applyCardFilter(); }, addColumn: function (col) { var grid = document.getElementById('kanban-grid'); @@ -263,8 +392,13 @@ var lh = document.createElement('div'); lh.className = 'kanban-lane-header'; lh.dataset.laneId = lane.id; - lh.innerHTML = '' + esc(lane.name) + ''; + lh.innerHTML = + '' + + '' + esc(lane.name) + ''; grid.appendChild(lh); + bindLaneHeaderToggle(lh); colHeaders.forEach(function (ch) { var cell = document.createElement('div'); @@ -275,12 +409,20 @@ createCellSortable(cell); }); + if (collapsedLaneIds[String(lane.id)]) { + setLaneCollapsed(lane.id, true); + } + applyGridTemplate(); }, removeLane: function (laneId) { document.querySelector('.kanban-lane-header[data-lane-id="' + laneId + '"]').remove(); document.querySelectorAll('.kanban-cell[data-lane-id="' + laneId + '"]').forEach(function (el) { el.remove(); }); KANBAN.cards = KANBAN.cards.filter(function (c) { return String(c.swim_lane_id) !== String(laneId); }); + if (collapsedLaneIds[String(laneId)]) { + delete collapsedLaneIds[String(laneId)]; + saveCollapsedLaneIds(); + } }, renameColumn: function (colId, name) { var hdr = document.querySelector('.kanban-col-header[data-col-id="' + colId + '"] .col-label'); @@ -295,4 +437,6 @@ applyGridTemplate(); renderCards(); initSortables(); + initJobSearch(); + initLaneHeaderToggles(); })(); diff --git a/public/js/kanban-modal.js b/public/js/kanban-modal.js index 46a8e5e..07300e7 100644 --- a/public/js/kanban-modal.js +++ b/public/js/kanban-modal.js @@ -14,6 +14,12 @@ var btnSave = document.getElementById('btn-save-card'); var btnDelete = document.getElementById('btn-delete-card'); + var custNameEl = document.getElementById('card-customer-name'); + var delivDateEl = document.getElementById('card-delivery-date'); + var qtyEl = document.getElementById('card-quantity'); + var notesEl = document.getElementById('card-notes'); + var fullNoteEl = document.getElementById('card-full-note'); + var boardId = KANBAN.boardId; /* ── Helpers ─────────────────────────────────────────────── */ @@ -44,6 +50,11 @@ laneIdEl.value = laneId || ''; jobNumEl.value = ''; jobNameEl.value = ''; + custNameEl.value = ''; + delivDateEl.value = ''; + qtyEl.value = ''; + notesEl.value = ''; + fullNoteEl.value = ''; btnDelete.classList.add('d-none'); clearError(); bsModal.show(); @@ -51,13 +62,18 @@ } /* ── Open for edit ───────────────────────────────────────── */ - function openEdit(id, colId, laneId, jobNum, jobName) { + function openEdit(id, colId, laneId, jobNum, jobName, custName, delivDate, qty, notes, fullNote) { titleEl.textContent = 'Edit Card'; cardIdEl.value = id; colIdEl.value = colId; laneIdEl.value = laneId; - jobNumEl.value = jobNum || ''; - jobNameEl.value = jobName || ''; + jobNumEl.value = jobNum || ''; + jobNameEl.value = jobName || ''; + custNameEl.value = custName || ''; + delivDateEl.value = delivDate || ''; + qtyEl.value = qty || ''; + notesEl.value = notes || ''; + fullNoteEl.value = fullNote || ''; btnDelete.classList.remove('d-none'); clearError(); bsModal.show(); @@ -72,6 +88,11 @@ var laneId = laneIdEl.value; var jNum = jobNumEl.value.trim(); var jName = jobNameEl.value.trim(); + var cust = custNameEl.value.trim(); + var dDate = delivDateEl.value; + var qty = qtyEl.value.trim(); + var notes = notesEl.value.trim(); + var fullNote = fullNoteEl.value; if (!jNum && !jName) { showError('Enter at least a job number or job name.'); @@ -80,10 +101,10 @@ if (id) { // Update existing - post('/cards/' + id, { job_number: jNum, job_name: jName }, function (res) { + post('/cards/' + id, { job_number: jNum, job_name: jName, customer_name: cust, delivery_date: dDate, quantity: qty, notes: notes, full_note: fullNote }, function (res) { if (res.ok) { bsModal.hide(); - window.KanbanBoard.onCardUpdated(id, res.job_number, res.job_name); + window.KanbanBoard.onCardUpdated(id, res); } else { showError(res.error || 'Save failed.'); } @@ -95,11 +116,16 @@ return; } post('/cards', { - board_id: boardId, - column_id: colId, + board_id: boardId, + column_id: colId, swim_lane_id: laneId, - job_number: jNum, - job_name: jName + job_number: jNum, + job_name: jName, + customer_name: cust, + delivery_date: dDate, + quantity: qty, + notes: notes, + full_note: fullNote }, function (res) { if (res.ok) { bsModal.hide(); diff --git a/scripts/importPrintStreamJobs.vbs b/scripts/importPrintStreamJobs.vbs new file mode 100644 index 0000000..69f682a Binary files /dev/null and b/scripts/importPrintStreamJobs.vbs differ diff --git a/scripts/migrate_isbusiness_to_households.vbs b/scripts/migrate_isbusiness_to_households.vbs deleted file mode 100644 index 5f0a8b9..0000000 --- a/scripts/migrate_isbusiness_to_households.vbs +++ /dev/null @@ -1,90 +0,0 @@ -' migrate_isbusiness_to_households.vbs -' Moves IsBusiness from HouseholderNames to Households. -' -' Usage: -' cscript //nologo scripts\migrate_isbusiness_to_households.vbs "C:\path\to\myAccessFile.accdb" -' -' What it does: -' 1) Adds Households.IsBusiness (SMALLINT) if missing -' 2) Copies data: sets Households.IsBusiness=1 if any related HouseholderNames.IsBusiness<>0 -' 3) Sets NULLs to 0 -' 4) Drops HouseholderNames.IsBusiness if present -' -Option Explicit - -Dim dbPath -If WScript.Arguments.Count < 1 Then - WScript.Echo "ERROR: missing db path." - WScript.Echo "Usage: cscript //nologo scripts\migrate_isbusiness_to_households.vbs ""C:\path\to\db.accdb""" - WScript.Quit 1 -End If - -dbPath = WScript.Arguments(0) - -Dim conn -Set conn = CreateObject("ADODB.Connection") -conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbPath & ";Persist Security Info=False;" - -On Error Resume Next - -If Not ColumnExists(conn, "Households", "IsBusiness") Then - Exec conn, "ALTER TABLE [Households] ADD COLUMN [IsBusiness] SMALLINT" - If Err.Number <> 0 Then - WScript.Echo "ERROR adding Households.IsBusiness: " & Err.Description - WScript.Quit 1 - End If - WScript.Echo "Added Households.IsBusiness" -Else - WScript.Echo "Households.IsBusiness already exists" -End If - -' Copy data (only if the old column exists) -If ColumnExists(conn, "HouseholderNames", "IsBusiness") Then - ' Normalize all existing households first so the column is never left NULL. - Exec conn, "UPDATE [Households] SET [IsBusiness]=0" - If Err.Number <> 0 Then - WScript.Echo "ERROR initializing Households.IsBusiness: " & Err.Description - WScript.Quit 1 - End If - - ' Promote households to business when any related name was previously marked as a business. - Exec conn, "UPDATE [Households] SET [IsBusiness]=1 WHERE [Id] IN (SELECT [HouseholdId] FROM [HouseholderNames] WHERE [IsBusiness]<>0)" - If Err.Number <> 0 Then - WScript.Echo "ERROR copying IsBusiness to Households: " & Err.Description - WScript.Quit 1 - End If - WScript.Echo "Copied IsBusiness values to Households" - - Exec conn, "ALTER TABLE [HouseholderNames] DROP COLUMN [IsBusiness]" - If Err.Number <> 0 Then - WScript.Echo "ERROR dropping HouseholderNames.IsBusiness: " & Err.Description - WScript.Quit 1 - End If - WScript.Echo "Dropped HouseholderNames.IsBusiness" -Else - WScript.Echo "HouseholderNames.IsBusiness does not exist; nothing to drop" -End If - -conn.Close -Set conn = Nothing -WScript.Echo "Done." - -' --- helpers --- -Sub Exec(c, sql) - Err.Clear - c.Execute sql -End Sub - -Function ColumnExists(c, tableName, colName) - Dim rs - ColumnExists = False - Err.Clear - Set rs = c.OpenSchema(4, Array(Empty, Empty, tableName, colName)) ' adSchemaColumns=4 - If Err.Number <> 0 Then - Err.Clear - Exit Function - End If - If Not rs.EOF Then ColumnExists = True - rs.Close - Set rs = Nothing -End Function