|
การเขียนโปรแกรมเพื่อจัดการกับระบบฐานข้อมูล จะใช้ความสัมพันธ์อยู่ 2 แบบหลักๆ คือ One To One เช่น ลูกค้า 1 รายสามารถซื้อสินค้าได้ทีละ 1 ครั้ง และแบบ One To Many เช่น รายการซื้อสินค้าของลูกค้า 1 รายการ สามารถซื้อสินค้าได้หลายชนิด หรือ รายการ ... ซึ่งวันนี้แอดมินจะนำเสนอโค้ดในแบบ One To Many เป็นการขายสินค้าแบบเงินสด คือการที่ลูกค้านำสินค้ามาให้พนักงานยิงบาร์โค้ด เพื่อทำรายการขายสินค้า โดยสามารถตัดสต็อกได้ ทั้งยังสามารถลด / เพิ่มจำนวนสินค้าได้ และยังคืนสินค้าบางรายการทั้งหมดได้ ...
หลักการคิดจะใช้แบบ Flow Control ตามวิธีขายสินค้าทั่วๆไป คือ (1) ทำรายการขายใหม่ (2) แก้ไขจำนวนรายการขายสินค้าเดิม (3) ลบบางรายการสินค้าทิ้งไป ... เทคนิคในการตัดสต็อกของแอดมิน คือ สร้างหลัก (Column) ท้ายสุดเอาไว้ที่ตารางกริด เพื่อให้เก็บจำนวนที่ขายออกไปในแต่ละครั้ง แต่ให้ซ่อนหลักนี้ หรือ Visible = False เอาไว้ ... เราจะใช้สูตรเดียวกันทั้งหมด คือ ...
จำนวนสต็อก = จำนวนสต็อก + จำนวนขายเดิม - จำนวนขายใหม่
(1) ทำรายการขายใหม่ สินค้าเดิมจะมีค่าเท่ากับ 0 อันนี้คงไม่ต้องอธิบายมากมาย
(2) แก้ไขจำนวนรายการขายสินค้าเดิม ... อันนี้ตามภาพประกอบเลยครับ
(3) การลบบางรายการทิ้งไป ... อันนี้จะยุ่งยากขึ้นมาหน่อย เหตุการณ์นี้จะเกิดเฉพาะตอนแก้ไขรายการเดิมเท่านั้น ดังนั้นเราจะต้องทำการตรวจสอบรายการเดิมที่ขายสินค้าออกไปในตารางข้อมูล InvoiceDetail ซึ่งจะเก็บรายการขายสินค้าและจำนวนเอาไว้ แล้วนำไปเปรียบเทียบกับข้อมูลแถวในตารางกริด หากพบในตารางข้อมูล (InvoiceDetail) แต่ไม่มีในตารางกริด แสดงว่ารายการขายสินค้าตัวนี้ถูกลบออกไปแล้ว จะต้องนำค่าจำนวนที่ขายสินค้าที่อยู่ในตาราง InvoiceDetail เอากลับไปคืนให้กับจำนวนสต็อกในตารางสินค้า หรือ Product ก่อน ...
โค้ดต่างๆในการตัดสต็อกจะอยู่ที่โปรแกรมย่อย SaveData ที่เดียว ไม่ว่าจะเป็นการเพิ่ม หรือแก้ไข ...
- ' / --------------------------------------------------------------------
- ' / SAVE DATA
- Sub SaveData()
- Try
- If Conn.State = ConnectionState.Closed Then Conn.Open()
- '// NewData = True, It's Add New Mode
- If NewData Then
- '// (SetupNewPK อยู่ใน modDataBase.vb)
- '// การหาค่า Primary Key ของ InvoicePK จะอยู่ในกระบวนการสุดท้าย เพื่อป้องกัน Primary Key ซ้ำกัน
- PK = SetupNewPK("SELECT MAX(Invoice.InvoicePK) AS MaxPK FROM Invoice")
- strSQL = _
- " INSERT INTO Invoice(" & _
- " InvoicePK, CustomerFK, TotalAmount, DateInvoice) " & _
- " VALUES(" & _
- "'" & PK & "', " & _
- "'1', " & _
- "'" & CDbl(txtTotalAmount.Text) & "', " & _
- "'" & Now.ToString("dd/MM/yyyy") & "'" & _
- ")"
- '// การแก้ไข
- Else
- '// START UPDATE
- strSQL = _
- " UPDATE Invoice SET " & _
- " [InvoicePK]='" & PK & "', " & _
- " [CustomerFK]='1', " & _
- " [TotalAmount]='" & CDbl(txtTotalAmount.Text) & "'" & _
- " WHERE InvoicePK = " & PK & ""
- End If
- '// บันทึกข้อมูลลงในตาราง Invoice (ฟังค์ชั่น DoSQL อยู่ใน modDataBase.vb)
- Call DoSQL(strSQL)
- '/ -----------------------------------------------------------------
- '// I M P O R T A N T
- '// ก่อนลบข้อมูลขายสินค้า จากตารางข้อมูล InvoiceDetail ณ ปัจจุบัน ต้องคืนจำนวนสินค้ากลับไปให้ตารางสินค้าก่อน เพราะบางแถวรายการของตารางกริดอาจจะถูกลบออกไป
- '// เช่น เดิมมีการขายสินค้าออกไป 2 รายการ แต่แถวที่ 2 ถูกลบออกไป ซึ่งมักจะเกิดจากการคืนสินค้า ดังนั้นรายการแถวในตารางกริด จะไม่ตรงกับการขายของเดิม
- '// ต้องคืนจำนวนสินค้าจากรายการที่ 2 กลับเข้าไปในสต็อกก่อนจะลบ InvoiceDetail ปัจจุบันทิ้ง แล้วค่อย INSERT กลับไปใหม่อีกครั้ง ด้วย InvoicePK ตัวเดิม
- '// ***** กรณีของการแก้ไขข้อมูลเท่านั้น *****
- If Not NewData Then
- Dim SqlStmt As String = " SELECT * FROM InvoiceDetail WHERE InvoicePK = " & PK
- Cmd = New OleDb.OleDbCommand
- Cmd.Connection = Conn
- Cmd.CommandText = SqlStmt
- Dim DR As OleDbDataReader = Cmd.ExecuteReader
- '// Set Flag
- Dim blnDataExist As Boolean = False
- While DR.Read
- Dim PFK As Long = CLng(DR.Item("ProductFK").ToString)
- For iRow = 0 To dgvData.RowCount - 1
- '// ค่า Primary Key ใน DB มีค่าเท่ากับใน DataGrid หรือไม่
- If CLng(DR.Item("ProductFK").ToString) = dgvData.Rows(iRow).Cells(0).Value Then
- '// เพื่อให้วนรอบไปที่ While ต่อทันที
- blnDataExist = True
- '// ไม่ต้องเช็คต่ออีกแล้ว ... กลับไปวนรอบ While เพื่อเช็คแถวรายการในตารางข้อมูล InVoiceDetail ใหม่อีกรอบ
- Exit For
- Else
- blnDataExist = False
- End If
- Next
- '// ไม่มีข้อมูลในตารางกริด แต่พบในตารางข้อมูล แสดงว่าถูกลบแถวรายการออกไป
- '// เอาค่าจำนวนสินค้าในตาราง InvoiceDetail ณ ปัจจุบัน กลับไปบวกเข้าในสต็อก (ตารางสินค้า - Product) ก่อน
- '// Not True = False (ไม่จริง = เท็จ) เพราะต้องการเงื่อนไขเท็จ
- If Not blnDataExist Then
- '// เอากลับมาบวกคืนในสต็อคตารางสินค้า
- '// ProductFK คือการอ่านค่ามาจาก InvoiceDetail
- strSQL = _
- " UPDATE Product SET " & _
- " [Stocked]=Stocked + '" & CDbl(DR.Item("Quantity").ToString) & "'" & _
- " WHERE ProductPK = " & CLng(DR.Item("ProductFK").ToString) & ""
- '// UPDATE
- Call DoSQL(strSQL)
- '//
- DS.Dispose()
- DA.Dispose()
- End If
- End While
- DR.Close()
- End If
- '/ -----------------------------------------------------------------
- '/ -----------------------------------------------------------------
- '// ลบข้อมูลในตารางย่อย (InvoiceDetail) ณ ปัจจุบันออกให้หมดก่อน แล้วค่อยทำการเพิ่มเข้าไปใหม่
- '// การเพิ่มข้อมูลใหม่ จะไม่มีผลอะไร
- strSQL = " DELETE FROM InvoiceDetail WHERE InvoicePK = " & PK
- Call DoSQL(strSQL)
- '/ -----------------------------------------------------------------
- '/ เพราะเราลบข้อมูลการขายทุกรายการออกจากตาราง InvoiceDetail ดังนั้นจึงต้องทำการ INSERT เข้าไปใหม่
- '// INSERT ข้อมูลปัจจุบันจากตารางกริด เข้าไปใหม่ในตาราง InvoiceDetail ด้วยการ Loop ข้อมูลจากตารางกริด
- For iRow = 0 To dgvData.RowCount - 1
- strSQL = _
- " INSERT INTO InvoiceDetail(" & _
- " InvoicePK, ProductFK, Quantity, UnitPrice ,TotalAmount) " & _
- " VALUES(" & _
- "'" & PK & "'," & _
- "'" & dgvData.Rows(iRow).Cells(0).Value & "'," & _
- "'" & dgvData.Rows(iRow).Cells(3).Value & "'," & _
- "'" & dgvData.Rows(iRow).Cells(4).Value & "'," & _
- "'" & dgvData.Rows(iRow).Cells(5).Value & "'" & _
- ")"
- '// UPDATE
- Call DoSQL(strSQL)
- Next
- '// -----------------------------------------------------------------
- '// ปรับสต็อคใหม่
- '// จำนวนคงเหลือ = จำนวนคงเหลือ + จำนวนการขายเดิม (หลักที่ 6 - Qty) - จำนวนสินค้าที่ขายใหม่ (หลักที่ 3 - Quantity)
- '// ตอนนี้จะทำงานเฉพาะตารางข้อมูลสินค้าเท่านั้น (tblProduct)
- For iRow = 0 To dgvData.RowCount - 1
- strSQL = _
- " UPDATE Product SET " & _
- " Stocked = Stocked + '" & CLng(dgvData.Rows(iRow).Cells(6).Value) - CLng(dgvData.Rows(iRow).Cells(3).Value) & "'" & _
- " WHERE ProductPK = " & CLng(dgvData.Rows(iRow).Cells(0).Value) & ""
- '// UPDATE
- Call DoSQL(strSQL)
- Next
- MessageBox.Show("ปรับปรุงข้อมูลเรียบร้อย.", "รายงานสถานะ", MessageBoxButtons.OK, MessageBoxIcon.Information)
- Catch ex As Exception
- MessageBox.Show(ex.Message)
- End Try
- End Sub
คัดลอกไปที่คลิปบอร์ด
In Line Edit ... แอดมินขอนำเสนอเรื่องของ In Line Edit คือการที่ผู้ใช้งานสามารถแก้ไขข้อมูลจำนวนสินค้า (เลขจำนวนเต็ม) และราคาสินค้า (เลขทศนิยม) ได้ในรายการขายสินค้านั้นๆในเซลล์ของตารางกริดได้ทันที เพื่ออำนวยความสะดวกให้กับผู้ใช้งาน ... เหตุการณ์ทั้งหมดจะอยู่ที่ Events ของ DataGridView
โค้ดในการควบคุมเซลล์ของตารางกริด ... ต้องใช้การทำ Debugger (F8) ถึงจะสามารถเข้าใจขั้นตอนการทำงานได้ดีครับผม
- ' / --------------------------------------------------------------------------------
- ' / Before Edit Data
- Private Sub dgvData_CellBeginEdit(sender As Object, e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles dgvData.CellBeginEdit
- Dim Quantity As Integer = dgvData.Rows(e.RowIndex).Cells(3).Value
- Dim UnitPrice As Double = dgvData.Rows(e.RowIndex).Cells(4).Value
- dgvData.Rows(e.RowIndex).Cells(5).Value = (Quantity * UnitPrice).ToString("#,##0.00")
- ' / Summary
- Call CalSumTotal()
- End Sub
- ' / --------------------------------------------------------------------------------
- ' / After you press Enter
- Private Sub dgvData_CellEndEdit(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvData.CellEndEdit
- Select Case e.ColumnIndex
- Case 3, 4 ' Column Index = 3 (Quantity), Column Index = 4 (UnitPrice)
- ' Quantity, If Null Value
- If IsDBNull(dgvData.Rows(e.RowIndex).Cells(3).Value) Then dgvData.Rows(e.RowIndex).Cells(3).Value = 0
- Dim Quantity As Integer = dgvData.Rows(e.RowIndex).Cells(3).Value
- '/------------------------------------------------------------
- ' Keep original value in temporary. (Tag Properties)
- dgvData.Tag = Quantity
- '/------------------------------------------------------------
- If Quantity <= 0 Then
- dgvData.Rows(e.RowIndex).Cells(3).Value = 1
- Quantity = 1
- End If
- ' / ----------------------------------------------------------------------------
- ' / UNIT PRICE = Index = 4
- ' UnitPrice, If Null Value
- If IsDBNull(dgvData.Rows(e.RowIndex).Cells(4).Value) Then dgvData.Rows(e.RowIndex).Cells(4).Value = 0.0
- Dim UnitPrice As Double = dgvData.Rows(e.RowIndex).Cells(4).Value
- ' / ----------------------------------------------------------------------------
- ' Quantity x UnitPrice
- dgvData.Rows(e.RowIndex).Cells(5).Value = (Quantity * UnitPrice).ToString("#,##0.00")
- ' / SUM
- CalSumTotal()
- End Select
- End Sub
- ' / --------------------------------------------------------------------------------
- Private Sub dgvData_EditingControlShowing(sender As Object, e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvData.EditingControlShowing
- Select Case dgvData.Columns(dgvData.CurrentCell.ColumnIndex).Name
- ' / Can use both ColumeIndex or Field Name
- Case "Quantity", "UnitPrice"
- RemoveHandler e.Control.KeyPress, AddressOf ValidKeyPress
- AddHandler e.Control.KeyPress, AddressOf ValidKeyPress
- End Select
- End Sub
- ' / --------------------------------------------------------------------------------
- ' / ดักการกดคีย์ลงในหลัก 3 (Quantity) ซึ่งเป็นเลขจำนวนเต็ม
- Private Sub ValidKeyPress(sender As System.Object, e As System.Windows.Forms.KeyPressEventArgs)
- Dim tb As TextBox = sender
- Select Case dgvData.CurrentCell.ColumnIndex
- Case 3 ' Quantity is Integer
- Select Case e.KeyChar
- Case "0" To "9" ' digits 0 - 9 allowed
- Case ChrW(Keys.Back) ' backspace allowed for deleting (Delete key automatically overrides)
- Case Else ' everything else ....
- 'True = CPU cancel the KeyPress event
- e.Handled = True ' and it's just like you never pressed a key at all
- End Select
- ' / UnitPrice is Double
- Case 4
- Select Case e.KeyChar
- Case "0" To "9"
- ' Allowed "."
- Case "."
- ' can present "." only one
- If InStr(tb.Text, ".") Then e.Handled = True
- Case ChrW(Keys.Back)
- 'Case ChrW(Keys.Delete)
- Case Else
- e.Handled = True
- End Select
- End Select
- End Sub
คัดลอกไปที่คลิปบอร์ด
โค้ดในส่วนของการค้นหาสินค้า และให้คืนค่า ProductPK กลับคืนมาให้ฟอร์มหลัก ... แอดมินเขียนคอมเมนท์เอาไว้ให้ถึงวิธีการคืนค่ากลับ สามารถทำได้หลายวิธี ก็ลองไปคิดเพิ่มเติมดูกันน่ะครับ
- Imports System.Data.OleDb
- Public Class frmSearchProduct
- '// โค้ดชุดนี้แสดงตัวอย่างของการแสดงผลรายการสินค้าทั้งหมด ด้วยการใช้ DataSet (ListtAllDataSet)
- '// ส่วนการค้นหาจะใช้ DataReader (SearchDataReader)
- '// การใช้งานจริงจะใช้อย่างใดอย่างหนึ่งเท่านั้นก็ได้ครับผม
- '/ เกิดเหตุการณ์ดับเบิ้ลคลิ๊กในแต่ละแถวของตารางกริด จะคืนค่า ProductPK กลับไปให้กับฟอร์ม (frmInvoiceDetail.vb) ที่เรียกมันมา
- Private Sub dgvData_CellDoubleClick(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvData.CellDoubleClick
- ' / ส่งค่า ProductPK ที่ถูกซ่อนในหลักแรก (Index = 0) กลับคืนไป
- ' / ---- มีหลายวิธีในการคืนค่ากลับไป โดยที่ไม่ต้องกลับไปค้นหารายการสินค้าใหม่ เช่น ส่งกลับแบบ Array หรือ List ----
- ' / ไปลองคิดค้นหาวิธีการเพิ่มเติมด้วยล่ะกันครับ
- SearchPK = dgvData.Item(0, dgvData.CurrentRow.Index).Value
- Me.Close()
- End Sub
- Private Sub frmSearchProduct_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
- Me.Dispose()
- GC.SuppressFinalize(Me)
- End Sub
- Private Sub frmSearchProduct_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
- Me.CenterToScreen()
- Me.Text = "ค้นหารายการสินค้า"
- txtSearch.Text = ""
- '// แสดงรายการทั้งหมดด้วยการใช้ DataSet
- Call ListAllDataSet()
- lblRecordCount.Text = "[จำนวน : " & dgvData.RowCount & " รายการ.]"
- End Sub
- Private Sub txtSearch_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles txtSearch.KeyPress
- ' ดักจับตัวอักขระอันไม่พึงปรารถนาสำหรับฐานข้อมูล เช่น ', * หรือ %
- txtSearch.Text = txtSearch.Text.Trim.Replace("'", "").Replace("%", "").Replace("*", "")
- If Trim(txtSearch.Text) = "" Or Len(Trim(txtSearch.Text)) = 0 Then Exit Sub
- '/ ค้นหาข้อมูลด้วยการใช้ DataReader
- If e.KeyChar = Chr(13) Then
- e.Handled = True
- Call SearchDataReader()
- End If
- End Sub
- ' / ----------------------------------------------------------------------------------------
- ' / Show All Data with DATASET
- Private Sub ListAllDataSet()
- strSQL = _
- " SELECT Product.ProductPK, Product.ProductID, Product.ProductName, Product.Stocked, Product.UnitPrice " & _
- " FROM Product " & _
- " WHERE " & _
- " ProductID LIKE '%" & txtSearch.Text & "%'" & _
- " OR " & _
- " ProductName LIKE '%" & txtSearch.Text & "%'" & _
- " ORDER BY ProductPK, ProductID "
- DS.Clear()
- Try
- ' ยัดใส่ตารางที่มาจากการทำ Query ให้กับ DataAdapter
- DA = New OleDb.OleDbDataAdapter(strSQL, Conn)
- DA.Fill(DS, "Product")
- ' / --------------------------------------------------------------------------------
- ' / B O U N D --- D A T A --- C O N T R O L
- ' / Bind Data into DataGridView
- dgvData.DataSource = DS.Tables("Product")
- ' / --------------------------------------------------------------------------------
- ' ตั้งค่าคุณสมบัติ DataGridView
- Call SetupDataGrid()
- lblRecordCount.Text = "[จำนวน : " & dgvData.RowCount & " รายการ.]"
- DS.Dispose()
- DA.Dispose()
- Catch ex As Exception
- MessageBox.Show(ex.Message)
- End Try
- txtSearch.Focus()
- txtSearch.Text = ""
- End Sub
- ' / ----------------------------------------------------------------------------------------
- ' / Search with DATAREADER
- Private Sub SearchDataReader()
- strSQL = _
- " SELECT Product.ProductPK, Product.ProductID, Product.ProductName, Product.Stocked, Product.UnitPrice " & _
- " FROM Product " & _
- " WHERE " & _
- " ProductID LIKE '%" & txtSearch.Text & "%'" & _
- " OR " & _
- " ProductName LIKE '%" & txtSearch.Text & "%'" & _
- " ORDER BY ProductPK, ProductID "
- '//
- Try
- If Conn.State = ConnectionState.Closed Then Conn.Open()
- Cmd = New OleDb.OleDbCommand(strSQL, Conn)
- Dim Reader As OleDb.OleDbDataReader = Cmd.ExecuteReader
- Dim DT As DataTable = New DataTable
- DT.Load(Reader)
- dgvData.DataSource = DT
- DT.Dispose()
- Call SetupDataGrid()
- Catch ex As Exception
- MessageBox.Show(ex.Message)
- End Try
- lblRecordCount.Text = "[จำนวน : " & dgvData.RowCount & " รายการ.]"
- txtSearch.Text = ""
- txtSearch.Focus()
- End Sub
- ' / --------------------------------------------------------------------------------
- ' ตั้งค่าคุณสมบัติ DataGridView
- Private Sub SetupDataGrid()
- With dgvData
- .RowHeadersVisible = True
- .AllowUserToAddRows = False
- .AllowUserToDeleteRows = False
- .AllowUserToResizeRows = False
- .MultiSelect = False
- .SelectionMode = DataGridViewSelectionMode.FullRowSelect
- .ReadOnly = True
- '// Data rows
- .Font = New Font("Tahoma", 9)
- .RowTemplate.MinimumHeight = 28
- .RowTemplate.Height = 28
- '// ซ่อนหลัก 0 ProductPK
- .Columns(0).Visible = False
- .Columns("ProductID").HeaderText = "รหัสสินค้า"
- .Columns("ProductName").HeaderText = "ชื่อสินค้า"
- With .Columns("Stocked")
- .HeaderText = "จำนวนที่มีอยู่"
- .DefaultCellStyle.Format = "n0"
- .HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight
- .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
- End With
- With .Columns("UnitPrice")
- .HeaderText = "ราคาขาย"
- .DefaultCellStyle.Format = "n2"
- .HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleRight
- .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
- End With
- '// Column Header
- .ColumnHeadersHeight = 30
- .ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing
- '// Autosize Column
- .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill
- '// Even-Odd Color
- '// แสดงสีสลับแถวคู่-คี่
- .AlternatingRowsDefaultCellStyle.BackColor = Color.AliceBlue
- With .ColumnHeadersDefaultCellStyle
- .BackColor = Color.Navy
- .ForeColor = Color.Black
- .Font = New Font("Tahoma", 9, FontStyle.Bold)
- End With
- End With
- End Sub
- Private Sub btnExit_Click(sender As System.Object, e As System.EventArgs) Handles btnExit.Click
- Me.Close()
- End Sub
- End Class
คัดลอกไปที่คลิปบอร์ด
ดาวน์โหลดโค้ดต้นฉบับ VB.NET (2010) ได้ที่นี่ ...
|
ขออภัย! โพสต์นี้มีไฟล์แนบหรือรูปภาพที่ไม่ได้รับอนุญาตให้คุณเข้าถึง
คุณจำเป็นต้อง ลงชื่อเข้าใช้ เพื่อดาวน์โหลดหรือดูไฟล์แนบนี้ คุณยังไม่มีบัญชีใช่ไหม? ลงทะเบียน
x
|