ตอนนี้เป็นภาค 3 แล้ว ซึ่งจะขอตัดฉากกลับมาที่การใช้ Line อีกครั้งนะครับ คราวนี้เราจะแสดงตัวอย่างการประยุกต์ใช้ Line Notify ในการส่งข้อความแจ้งเตือนจากข้อมูลที่อยู่ในไฟล์ Excel ของเราบ้างแล้วล่ะ ใครยังไม่ได้อ่านตอนแรกก็เชิญไปอ่านก่อนนะ ไม่งั้นทำต่อไม่ได้นะครับ
สารบัญ
ตัวอย่าง : ข้อความแจ้งเตือนเมื่อ Project เลยกำหนด Deadline
ก่อนอื่น ผมลองเตรียมข้อมูลสมมติว่าเป็นแบบนี้ก่อน นั่นคือมี Project หลายอัน แต่ละอันมี Deadline แล้วก็บอกว่าทำเสร็จไปแล้วรึยัง?
เป้าหมายคือ จะให้ส่ง Line Notify ไปเตือนเฉพาะอันที่เลย Deadline แล้วยังไม่เสร็จ (พร้อม Sticker หน้าโกรธ)
เพื่อให้ง่ายต่อการอ้างอิง ผมจะแปลงข้อมูลให้เป็น Table ซะก่อน (โดยกด Insert->Table หรือ Ctrl+T) และตั้งชื่อว่า ProjectTable
จากนั้นเราจะลองอ้างอิงข้อมูลใน Table บรรทัดที่สอง (Project-B) ดูว่าทำได้มั้ย?
หัดอ้างอิงข้อมูลจาก Table ด้วย VBA
ลองสร้าง Module ใหม่แล้วใส่ code แบบนี้ดู
Sub LoopTable()
Dim tbl As ListObject
'อ้างอิงจาก ชื่อ table
Set tbl = ActiveSheet.ListObjects("ProjectTable")
'ลองดึงข้อมูลในตำแหน่งที่ต้องการ
'หลักๆ อ้างโดย Cells(ลำดับแถว,ลำดับคอลัมน์) โดยมันอ้างใน range ที่กำหนด ในที่นี้คือ Data ใน Table
'ใช้ tbl.ListColumns("ชื่อคอลัมน์").Index เพื่อหาว่าอยู่คอลัมน์ที่เท่าไหร่
MsgBox tbl.DataBodyRange.Cells(2, tbl.ListColumns("Desc").Index)
End Sub
จากนั้นกด F5 จะได้ผลลัพธ์เป็นแบบนี้
แสดงว่าเราสามารถอ้างอิง Row ที่ 2 ของ Table และเอาข้อมูลในคอลัมน์ Desc มาได้แล้ว
ดังนั้นเดี๋ยวเราลองดึงให้ครบทั้งบรรทัดเลย โดยจะบอกให้ครบเลยว่า Project อะไร? Deadline เมื่อไหร่?
โดยที่เราแก้ Code ให้เป็นแบบนี้
โดยที่เราใส่ค่าแต่ละคอลัมน์ลงในตัวแปรก่อน จะได้อ้างอิงมาใช้ง่ายๆ
Sub LoopTable()
Dim tbl As ListObject
'อ้างอิงจาก ชื่อ table
Set tbl = ActiveSheet.ListObjects("ProjectTable")
'ลองดึงข้อมูลในตำแหน่งที่ต้องการ
'หลักๆ อ้างโดย Cells(ลำดับแถว,ลำดับคอลัมน์) โดยมันอ้างใน range ที่กำหนด ในที่นี้คือ Data ใน Table
'ใช้ tbl.ListColumns("ชื่อคอลัมน์").Index เพื่อหาว่าอยู่คอลัมน์ที่เท่าไหร่
projRow = 2
projName = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("ชื่อ Project").Index)
projDesc = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Desc").Index)
projDeadline = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Finish").Index)
projFullMsg = projName & " | " & projDesc & " | Dead Line : " & projDeadline & " | Finish : " & projFinish
MsgBox (projFullMsg)
End Sub
พอกด F5 จะได้แบบนี้เลย
เปลี่ยนโครงสร้าง Code ให้เรียกใช้สะดวกขึ้น
คราวนี้เพื่อให้สะดวกขึ้น เดี๋ยวเราย้าย code แยกกันเป็น Function กับ Sub เลย จะได้เรียกใช้ง่ายๆ ว่าจะเอา Table ไหน? แถวไหนมาแสดง ดังนี้
Public tbl As ListObject
Function GetTableData(projRow)
projName = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("ชื่อ Project").Index)
projDesc = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Desc").Index)
projDeadline = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Finish").Index)
projFullMsg = projName & " | " & projDesc & " | Dead Line : " & projDeadline & " | Finish : " & projFinish
GetTableData = projFullMsg
End Function
Sub LoopTable()
Set tbl = ActiveSheet.ListObjects("ProjectTable")
MsgBox GetTableData(2)
End Sub
ซึ่งถ้ากด F5 ก็จะแสดงผลได้ดังเดิมเลย
ทำให้เตือนเฉพาะตัวที่เลยกำหนด
คราวนี้เราจะเริ่มใช้ for loop + if เช็คแล้วว่าควรจะแสดงผลจากแถวไหนบ้าง?
Logic คือ ถ้า Finish ไม่ใช่ Y และ Deadline < วันปัจจุบัน ก็จะทำให้เด้งข้อความ
Public tbl As ListObject
Function GetTableData(projRow)
projName = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("ชื่อ Project").Index)
projDesc = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Desc").Index)
projDeadline = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Deadline").Index)
projFullMsg = projName & " | " & projDesc & " | Dead Line : " & projDeadline
GetTableData = projFullMsg
End Function
Sub LoopTable()
Set tbl = ActiveSheet.ListObjects("ProjectTable")
'get current date
dateToday = Date
'check number of rows
NumRows = tbl.DataBodyRange.Rows.Count
For i = 1 To NumRows
projDeadline = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Finish").Index)
If projFinish <> "Y" And projDeadline < dateToday Then
MsgBox GetTableData(i)
End If
Next i
End Sub
พอกด F5 มันก็จะเด้ง MsgBox มา 2 รอบ คือ Project B และ Project H
เริ่มส่งข้อความเข้า Line จริงๆ ละ
จากนั้นเราจะเปลี่ยนจาก MsgBox ไปใช้การส่งข้อความเข้า Line แทน ซึ่งเรามี Sub ตัวนึงที่ทำงานนั้นอยู่แล้ว (สร้างไว้ในบทแรก) นั่นคือ
Sub LineNotify(msg As String, Optional stickID As Integer, Optional stickPack As Integer)
ดังนั้น ใน Sub LoopTable() เราแค่เปลี่ยนจาก MsgBox GetTableData(i) เป็น LineNotify GetTableData(i) ก็จะได้ผลเลย
เมื่อลองกด F5 จะได้ผลดังรูป ว่ามันยิงข้อความมา 2 รอบ
ถ้าจะทำให้เตือนล่วงหน้าล่ะ?
ที่นี้ลองเปลี่ยน Code ทั้งหมดใน Module เป็นดังนี้
ใน Code ข้างล่างนี้ผมมีการแยกข้อมูลเป็น 2 กลุ่ม คือ เป็นกลุ่มที่เลยกำหนด กับกลุ่มที่เตือนล่วงหน้า โดยใช้ตัวแปร NumDayNoti = Range(“dayNoti”) ดึงค่ามาจากชื่อ dayNoti ที่ตั้งไว้ใน Sheet Excel แล้วเอามา Adjust กัย Logic ให้เหมาะสม
โดยผมลองกลับมาใช้ MsgBox เพื่อทดสอบก่อนว่าได้ผลหรือไม่ เดี๋ยวไว้แก้ทุกอย่างเสร็จเรียบร้อยจะเปลี่ยนกลับไปส่ง Line อีกที
Public tbl As ListObject
Function GetTableData(projRow)
projName = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("ชื่อ Project").Index)
projDesc = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Desc").Index)
projDeadline = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Deadline").Index)
projFullMsg = projName & " | " & projDesc & " | Dead Line : " & projDeadline
GetTableData = projFullMsg
End Function
Sub LoopTable()
Set tbl = ActiveSheet.ListObjects("ProjectTable")
'get current date
dateToday = Date
'check number of rows
NumRows = tbl.DataBodyRange.Rows.Count
'Due Already
For i = 1 To NumRows
projDeadline = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Finish").Index)
If projFinish <> "Y" And projDeadline < dateToday Then
MsgBox "เลยกำหนดแล้ว : " & GetTableData(i)
End If
Next i
'Pre Noti
'Get day to noti
NumDayNoti = Range("dayNoti")
For i = 1 To NumRows
projDeadline = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Finish").Index)
If projFinish <> "Y" And (projDeadline - NumDayNoti) < dateToday And projDeadline >= dateToday Then
MsgBox "กำลังจะครบกำหนด : " & GetTableData(i)
End If
Next i
End Sub
เมื่อลองรัน Code ก็จะมี Msgbox มาหลายอันเลย แต่จะแยกเป็นตัวที่ครบกำหนดแล้ว กับที่กำลังจะครบกำหนด
รวบข้อความให้เป็นแค่ 2 ข้อความ ข้อความละกลุ่ม
ทีนี้ถ้าเราจะส่งข้อความไป Line บรรทัดละ 1 ข้อความเลยมันก็จะดูรกเกินไปหน่อย ดังนั้นเราจะมารวบข้อความพวกนี้เข้าด้วยกันก่อน ดังนี้
Public tbl As ListObject
Function GetTableData(projRow)
projName = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("ชื่อ Project").Index)
projDesc = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Desc").Index)
projDeadline = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Deadline").Index)
projFullMsg = projName & " | " & projDesc & " | Dead Line : " & projDeadline
GetTableData = projFullMsg
End Function
Sub LoopTable()
Set tbl = ActiveSheet.ListObjects("ProjectTable")
'get current date
dateToday = Date
'check number of rows
NumRows = tbl.DataBodyRange.Rows.Count
'Due Already
CountDue = 0
For i = 1 To NumRows
projDeadline = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Finish").Index)
If projFinish <> "Y" And projDeadline < dateToday Then
DueMsg = DueMsg & GetTableData(i) & Chr(13) & Chr(10)
CountDue = CountDue + 1
End If
Next i
MsgBox "เลยกำหนดแล้ว : " & CountDue & " รายการ" & Chr(13) & Chr(10) & DueMsg
'Pre Noti
'Get day to noti
NumDayNoti = Range("dayNoti")
CountPreNoti = 0
For i = 1 To NumRows
projDeadline = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Finish").Index)
If projFinish <> "Y" And (projDeadline - NumDayNoti) < dateToday And projDeadline >= dateToday Then
PreNotiMsg = PreNotiMsg & GetTableData(i) & Chr(13) & Chr(10)
CountPreNoti = CountPreNoti + 1
End If
Next i
MsgBox "กำลังจะครบกำหนด : " & CountPreNoti & " รายการ" & Chr(13) & Chr(10) & PreNotiMsg
End Sub
พอกด F5 เพื่อ test จะพบว่ามี MsgBox ออกมาแค่ 2 ชุด ผมใส่แถมไปด้วยว่ามีกี่รายการ
Final Version ส่งเข้า Line จริงๆ + Sticker ด่า 555
เราเปลี่ยนจาก MsgBox เป็น LineNotify และใส่เงื่อนไขไปว่า ถ้าจำนวนเกิน Deadline >0 ให้ด่าไปด้วย จะได้ดังนี้
Public tbl As ListObject
Function GetTableData(projRow)
projName = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("ชื่อ Project").Index)
projDesc = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Desc").Index)
projDeadline = tbl.DataBodyRange.Cells(projRow, tbl.ListColumns("Deadline").Index)
projFullMsg = projName & " | " & projDesc & " | Dead Line : " & projDeadline
GetTableData = projFullMsg
End Function
Sub LoopTable()
Set tbl = ActiveSheet.ListObjects("ProjectTable")
'get current date
dateToday = Date
'check number of rows
NumRows = tbl.DataBodyRange.Rows.Count
'Due Already
CountDue = 0
For i = 1 To NumRows
projDeadline = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Finish").Index)
If projFinish <> "Y" And projDeadline < dateToday Then
DueMsg = DueMsg & GetTableData(i) & Chr(13) & Chr(10)
CountDue = CountDue + 1
End If
Next i
LineNotify "เลยกำหนดแล้ว : " & CountDue & " รายการ" & Chr(13) & Chr(10) & DueMsg
'Pre Noti
'Get day to noti
NumDayNoti = Range("dayNoti")
CountPreNoti = 0
For i = 1 To NumRows
projDeadline = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Deadline").Index)
projFinish = tbl.DataBodyRange.Cells(i, tbl.ListColumns("Finish").Index)
If projFinish <> "Y" And (projDeadline - NumDayNoti) < dateToday And projDeadline >= dateToday Then
PreNotiMsg = PreNotiMsg & GetTableData(i) & Chr(13) & Chr(10)
CountPreNoti = CountPreNoti + 1
End If
Next i
LineNotify "กำลังจะครบกำหนด : " & CountPreNoti & " รายการ" & Chr(13) & Chr(10) & PreNotiMsg
If CountDue > 0 Then
Call LineNotify("ทำไมถึงปล่อยให้มีงานเกินกำหนดส่ง คราวหน้าปรับปรุงด้วยนะ!!", 118, 1)
End If
End Sub
เมื่อกด F5 เพื่อ Run Code ก็จะได้ตามนี้
สั่ง Code ให้ Run เมื่อกดปุ่มบน Excel
ก่อนหน้านี้ทั้งหมด เราต้องกดปุ่ม F5 ใน VBA เพื่อรัน Code ซึ่งไม่สะดวกเลยในชีวิตจริง ดังนั้นเราจะมาทำปุ่มให้กดบน Sheet แล้วเมื่อกดปุ่ม มันก็จะมารัน Code ของ LoopTable ที่เราทำไว้ครับ
ไป Insert Button แบบ Form Control ปุ่มดังรูป (ถ้าไม่มี Ribbon Developer ให้ไปกดเพิ่มใน Excel Option -> Customize Ribbon )
พอกดลากเพื่อสร้างปุ่มมันจะมีหน้าต่างมาให้เลือกว่าจะรัน Code ไหน เราก็เลือก LoopTable ได้เลย
จากนั้นก็เปลี่ยนชื่อปุ่มให้เหมาะสม
จากนั้นลองกดปุ่มดู ก็จะพบว่ามีข้อความส่งมาใน Line เหมือนกับตอนที่เรากด F5 ใน Code เลย
ทำให้แจ้งเตือนอัตโนมัติเมื่อเปิดไฟล์ Excel
หากขี้เกียจกดปุ่มด้วย แต่อยากให้มันแจ้งเตือนอัตโนมัติเมื่อเปิดไฟล์เลย ก็ย่อมทำได้
หลักการของ VBA คือ หากมี event บางอย่างเกิดขึ้น เราก็จะสามารถสั่งให้มันทำ Action ที่เราต้องการได้
ซึ่งในที่นี้ event ก็คือ การเปิดไฟล์นี้ขึ้นมา และ action คือ การสั่ง sub LoopTable นั่นเอง
วิธีการคือให้เข้าไปใน VBA อีกที แล้วกดไปที่ ThisWorkbook (เลข 1 ในรูป) จากนั้นใน Dropdown (เลข 2) ให้เปลี่ยจาก General เป็น Workbook แล้วเลข 3 จะเปลี่ยนเป็น Open เอง
มันจะเกิด Private Sub Workbook_Open ขึ้นมาเอง และทุกสิ่งทุกอย่างที่เราเขียนในนี้ จะถูก Run ทันทีที่ไฟล์ถูกเปิด (ถ้าอยากได้ event อื่น ก็มีอีกใน Dropdown เลข 3 ในรูป)
ดังนั้นเราจะใส่ code ว่า
Private Sub Workbook_Open()
Call LoopTable
End Sub
แค่นี้จบเลย
จากนั้นลอง Save ไฟล์ แล้วเปิดขึ้นมาใหม่ จะพบว่ามัน Run Code อัตโนมัติ และมีการส่งข้อความไปที่ Line ทันที!! (แต่ผมว่ากดปุ่มเองดีกว่านะ 555)
Event อื่นๆ มีอะไรอีก
ถ้าอยากได้ Event อื่นอีก ก็มีเพียบเลย ลองไปกดดูได้ (ในรูปยังไม่ครบ)
นอกจากนั้นยังมี Event ระดับ Worksheet อีก (แต่ต้องไปเลือกด้านซ้ายสุดเป็น Sheet)
จะเห็นได้ว่าเราจะสามารถเอาไปประยุกต์ใช้ได้เพียบเลย เช่น เมื่อ Table อัปเดท เมื่อ Pivot Update เมื่อมีการเปลี่ยนข้อมูลในชีท หรืออะไรได้อีกเพียบ
สำหรับตัวอย่างนี้ก็ยาวมากอีกแล้ว ดังนั้นผมขอจบเพียงเท่านี้ดีกว่า ใครมีอะไรสงสัยก็ถามได้ครับ
ในตอนต่อไป เราจะมาดูวิธีส่งข้อความเข้า Group Chat และวิธีส่งรูปกันครับ
ปล. สำหรับการดึงข้อความจาก Line มาเข้า Excel นั้น ส่วนตัวคิดว่ามันใช้ Line Notify ไม่ได้ และไม่แน่ใจว่าใช้ Line Messaging API ได้หรือไม่? ผมยังไม่ได้ลอง แต่ถ้าได้ ผมคิดว่าทำเป็น Line BOT ไปเลยอาจจะง่ายกว่าใช้ Excel ครับ