หากมีคำถาม ขอให้ไปโพสต์ลง เว็บบอร์ดจีทูจีเน็ตดอตคอม ตัวใหม่แทนน่ะครับ

หรือติดต่อเข้ามาทาง Inbox ที่ เฟซบุ๊ค ผมครับ

หน้าหลัก
ข่าวสาร - บทความ ทั้งหมด
VB 6/VB.Net
ASP/ASP.Net
จับฉ่ายคอมพิวเตอร์
เรียนรู้ผ่าน Flash Movie
บทความที่มีผู้ตอบล่าสุด  
 RSS Feeds
 ดาวน์โหลดโปรแกรม RSS Reader ได้ที่นี่ ...   Download โปรแกรม RSS Reader

Forum - www.g2gnet.com
Webmaster - www.g2gnet.com
Visitors - Session views
 4 5 6 1 7 7 0

7 ธันวาคม พ.ศ.2549
97 Users On-Line.
Visitors - Page views
 7 7 1 1 4 1 5
1 กุมภาพันธ์ พ.ศ.2551

Google   
เว็บ g2gnet.com
ขนาดตัวอักษร:  

แจกฟรีซอร์สโค้ดโปรแกรมระบบขายสินค้า - Point Of Sale

Category »  VB 6/VB.Net
โดย : Webmaster เมื่อ 29/4/2553   เวลา: 14:31
(อ่าน : 230780) 
คุยกันก่อน ...
ระบบงานฐานข้อมูล ไล่มาจาก DBase, FoxBase, FoxPro และ Visual Basic ผมต้องใช้เวลาในการเรียนรู้ด้วยตนเอง และ หาประสบการณ์มานานหลายปี ลองผิด ลองถูกมาก็เยอะ แต่จะไม่มีวันนี้ หากเมื่อครั้งที่ผมทำงานครั้งแรกในตำแหน่ง "ช่างเทคนิค" อยู่นั้น ถ้าผมบอกว่า "งานแบบนี้มันไม่ใช่หน้าที่ของผม" ... โค้ดที่เห็นอยู่ในบทความชิ้นนี้ แม้มันจะไม่ใช่โค้ดจริงที่ผมเขียนโปรแกรมขาย และ ยังต้องตัดทอนออกไปจากระดับงานสอนแบบ "เรียนรู้นอกระบบ รบนอกตำรา" ของผมอีก เพราะเวลาถ่ายทอดจริง เด็กๆเขาจะเห็นจากหน้าจอ พร้อมๆกับผมเลย หากสงสัยสิ่งใดก็สามารถสอบถามผมได้ทันที แต่ถึงอย่างไรก็ตาม แนวคิดต่างๆของการออกแบบ ก็ไม่ได้ต่างกันเลยครับ นอกจากนี้แล้ว ผมยืนยันได้เลยว่าสายภาษาโปรแกรมอื่นๆ ก็ยังสามารถที่จะนำหลักการนี้ไปใช้ได้เช่นเดียวกัน ... เนื่องจากช่วงนี้ผมติดงานอยู่หลายตัว จึงยังไม่มีเวลาเพียงพอที่จะมาอธิบายถึงขั้นตอนต่างๆได้ทั้งหมดครับ ทำได้ตอนนี้ คือ สรุปสาระสำคัญเอาไว้ให้ดูก่อน หากมีข้อสงสัยอื่นใด สามารถสอบถามได้ครับ เพราะนี่คืออีกหนึ่งแห่งความภูมิใจของ "คนไทย" คนหนึ่ง ที่มอบให้กับ "คนไทย" อีกนับหลายๆคน
    ข้อแนะนำก่อนที่จะศึกษาโค้ดโปรแกรมระบบขายสินค้าของผม
  • ลืมทฤษฎีที่ศึกษามาจากห้องเรียน หรือ ความรู้จากตำราที่มีอยู่ในอ้อมกอดออกไปให้หมด เพราะสิ่งที่จะเห็นต่อไปนี้ มันต่างกันอย่างสิ้นเชิง
  • ต้องเข้าใจการเขียนโปรแกรมในลักษณะของ 1 : 1 มาก่อน เช่น การบันทึกรายการลูกค้า รายการสินค้า ซึ่งก็มีอยู่มากมายในเว็บไซต์ของผม
  • ต้องเข้าใจการใช้งาน Control พื้นฐานต่างๆ และ MS FlexGrid ซึ่งผมเขียนอธิบายเอาไว้ก่อนหน้านี้ไม่นาน
  • ต้องใจเย็นๆ ค่อยๆไล่แกะ และ คิดพิจารณาตามไปด้วย ... พยายามตั้งคำถามว่าทำไมให้บ่อยๆ
  • ที่สำคัญ ... ต้องมีมานะ พยายาม ขยัน และ อดทนในการเรียนรู้ฝึกฝน ...
  • วิธีการนี้มันคงไม่ใช่วิธีการที่ดีที่สุด ... แต่มันดีที่สุดสำหรับผม ณ ขณะเวลานี้ (และมานานหลายปีแล้ว)
คุณจะเข้าถึงความสุขนี้ได้ ก็ต่อเมื่อคุณรู้จักกับคำว่าให้
ดาวน์โหลด
ดาวน์โหลด Source Code สำหรับ MS Visual Basic 6.0 - Service Pack 6
 ดาวน์โหลด Visual Basic 6.0 SP5: Run-Time Redistribution Pack
 ดาวน์โหลด Microsoft Data Access Object (MDAC) และ Jet 4.0 Update
 ดาวน์โหลด Microsoft Visual Basic Service Pack 6
ข้อมูลเพิ่มเติม
ตารางกริด (MS Flex Grid) ธรรมดา ที่ไม่ธรรมดา ... VB 6.0
การอ่านข้อมูลแบบ Text File เข้าสู่ MS FlexGrid ด้วย MS Visual Basic 6.0
ตารางกริด (MS Flex Grid) ธรรมดา ที่ไม่ธรรมดา ... ตอนการจัดเรียงข้อมูลในการแสดงผลให้ MS FlexGrid
ตารางกริด (MS Flex Grid) ธรรมดา ที่ไม่ธรรมดา ... ตอนใช้ Wheel Mouse ใน MS FlexGrid
ตารางกริด (MS Flex Grid) ธรรมดา ที่ไม่ธรรมดา ... ตอนจับยัดข้อมูลเข้าไปใน MS FlexGrid ได้
พื้นฐานในการใช้งาน MS FlexGrid ก่อนสร้างความสัมพันธ์แบบ One To Many (ภาค 1)
พื้นฐานในการใช้งาน MS FlexGrid ก่อนสร้างความสัมพันธ์แบบ One To Many (ภาค 2)
พื้นฐานในการใช้งาน MS FlexGrid ก่อนสร้างความสัมพันธ์แบบ One To Many (ภาค 3)
เริ่มต้นการสร้างความสัมพันธ์ระหว่างตารางการขายสินค้า กับ ตารางลูกค้า
ตอนนี้ขอให้มองมาที่ ใบขายสินค้า 1 ใบ (ตาราง tblInvoice จะเป็น Master หรือ ตารางหลัก) จะขายสินค้าให้กับลูกค้าได้เพียง 1 รายเท่านั้น (ตาราง tblCustomer จะเป็น Detail หรือ ตารางย่อย) ดังนั้นมันคือความสัมพันธ์กันแบบ 1 : 1 (เช่นเดียวกับใบเช่าหนังสือ 1 ใบ ก็จะให้ลูกค้า/สมาชิก เช่าได้เพียง 1 รายเท่านั้นเช่นกัน) ... มองแบบนี้จะทำให้ง่ายต่อการวิเคราะห์และออกแบบ จะปรากฏดังภาพด้านล่างนี้

มองไปที่ใบขายสินค้า (tblInvoice) เป็นหลักก่อน (ผมถึงเรียกมันว่าตารางหลัก หรือ Master ยังไงล่ะครับ) เวลาเรียกใช้งานจริงเพื่อทำการแสดงผล เราไม่จำเป็นต้องเอา CustomerPK/CustomerFK มาก็ได้ เพราะขณะนี้มันเกิดความสัมพันธ์ขึ้นระหว่างทั้ง 2 ตารางแล้ว

ตารางข้อมูลการขายสินค้าหลัก tblInvoice (Master)

ชื่อฟิลด์

ชนิดข้อมูล

คำอธิบาย

InvoicePK

Long Integer

จะเก็บค่า Primary Key ของ InvoicePK เอาไว้ ค่านี้จะซ้ำไม่ได้ ... และที่สำคัญอย่างมาก ขอให้สังเกตว่าผมจะไม่ใช้ Primary Key ที่เป็น Autonumber เลย แต่จะมีวิธีการหาค่านี้ได้แบบไม่ซ้ำ ทั้งแบบ Stand Alone หรือ ต่อให้ทำงานบน LAN ... เพราะ Autonumber มันยากต่อการสร้างข้อมูล (ตุ๊กตา) ขึ้นมาทดสอบ ก่อนการลงโค้ดโปรแกรม และมีข้อเสียอื่นๆอีกแยะ

InvoiceCode

Text

เลขที่ใบการขาย เช่น INV-53000001 เพื่อใช้อ้างอิงในเอกสารเท่านั้น และผมขอร้องอย่าเอามันมาเป็น Primary Key เลยครับ ... เชื่อผมเหอะ หากไม่อยากมีปัญหาต้องปวดหัวตามมาในภายหลัง 55555+
เห็นหรือยังครับว่า แค่นี้ผมก็ต่างไปจากชาวบ้านชาวเมืองเขาแล้ว (Thongkorn's Theory)

CustomerFK

Long Integer

เป็นคีย์รอง หรือ Foreign Key เชื่อมความสัมพันธ์ไปยังตารางลูกค้า (เพราะคีย์หลัก หรือ Primary Key ของตารางนี้ คือ InvoicePK)
ดังนั้นแล้วการออกแบบของผม มันจะไม่มีหรอกครับที่มีรูปกุญแจ (Primary Key) มากกว่า 1 ตัว

TotalAmount

Currency

รวมเงินค่าสินค้าทุกรายการทั้งหมด ในใบขายใบนี้ใบเดียว

TotalDiscount

Currency

รวมเงินค่าส่วนลดสินค้าทุกรายการทั้งหมด ในใบขายใบนี้ใบเดียว

นั่นก็คือ ใบขาย 1 ใบ เช่น InvoicePK = 1 จะขายให้กับลูกค้าทั่วไป CustomerPK = 0 เพียงคนเดียวเท่านั้น เราถึงเรียกมันว่าเป็นความสัมพันธ์กันแบบ 1 : 1 ต่อไปพิจารณาว่าเราจะเอารายการสินค้าเข้าไปได้อย่างไร หากเราเอา ProductFK ยัดใส่เข้าไปในตารางหลัก tblInvoice เพื่อเชื่อมความสัมพันธ์เข้าสู่ตารางสินค้า (tblProduct) ผลที่ได้จะทำให้ความสัมพันธ์เดิมคลาดเคลื่อนไป


ตัวอย่างคือการขายสินค้า 2 รายการให้กับลูกค้า แทนที่ความสัมพันธ์มันจะเป็นแบบ 1 : 1 กลับกลายเป็นแบบ 1 : Many ไปซ่ะงั้น (มันมี 2 แถวรายการ) นอกจากนี้แล้วยังมีการเก็บข้อมูลซ้ำซ้อนกันเกิดขึ้นอีก เช่น InvoiceCode มันซ้ำๆตลอดทุกรายการ การรวมราคาสินค้าในการขายแต่ละใบขาย ก็ทำไม่ได้อีก ... ทางแก้คือ ต้องหาตารางข้อมูลอีก 1 ตารางมารับรายการขายสินค้าแต่ละรายการแทน โดยการเชื่อมความสัมพันธ์ด้วยใบขายสินค้าหลัก (InvoicePK) (เหมือนกับเชื่อมไปหาตารางลูกค้านั่นเอง) ...
โอกาสหน้าจะอธิบายวิธีการแยกแยะให้ละเอียดมากกว่านี้ครับ ... เรื่องนี้มันก็เข้าสูตรเดิมของผม คือ ฟิลด์อันไหนที่ซ้ำกันให้จับแยกออกไปอยู่อีกตาราง ฟิลด์ที่แยกออกไปนั้น นำมันกลับมาเชื่อมความสัมพันธ์กันใหม่อีกครั้ง (ตามตำราเขาเรียกมันว่า Normalization ครับพี่น้อง ... 55555+)

การออกแบบตารางข้อมูลความสัมพันธ์แบบ One To Many
ตารางข้อมูลการขายสินค้าย่อย tblInvoiceDetail (Detail)

ชื่อฟิลด์

ชนิดข้อมูล

คำอธิบาย

InvoicePK

Long Integer

จะเก็บค่า Primary Key ของ InvoicePK เอาไว้ ค่านี้จะซ้ำได้ (Duplicate) เพราะใบขาย 1 ใบ สามารถขายสินค้าออกไปได้มากกว่า 1 รายการ ดังนั้นตาราง tblInvoiceDetail ตัวนี้จะไม่มี และ ไม่จำเป็นต้องมี Primary Key เลย (ก็มันเป็นสูตรของผมเองน่ะครับ ... อย่างง เดี๋ยวไปดูโค้ดก็จะเข้าใจเอง)

ProductFK

Long Integer

เชื่อมความสัมพันธ์ไปยังข้อมูลสินค้า (tblProduct) ซึ่งมันก็เป็นไปตามธรรมชาติเหมือนตารางลูกค้ากับใบขายสินค้า โดยเรามองว่าใบขายสินค้า 1 ใบ จะต้องขายสินค้ารายการนั้นๆได้เพียงรายการเดียวเท่านั้น (จำนวนไม่เกี่ยว) แบบนี้ก็เรียกความสัมพันธ์ว่า 1 : 1 อีกเช่นเดียวกัน

PriceUnit

Currency

ราคาของสินค้า สาเหตุที่ต้องเก็บค่านี้ไว้ เพราะว่าการขายสินค้าแต่ละครั้งมันมีค่าไม่เท่ากันหรอกครับ

Quantity

Integer

จำนวนสินค้าที่ขายไป

TotalAmount

Currency

รวมเงินค่าสินค้ารายการนี้ทั้งหมด (จำนวน คูณ ราคาขาย)

TotalDiscount

Currency

รวมเงินส่วนลดราคาสินค้า


พอเราเชื่อมความสัมพันธ์กันแล้วอย่างถูกต้อง ... นี่คือใบขายสินค้า 1 ใบ ที่มี ค่า InvoicePK = 1 สามารถขายสินค้าได้มากกว่า 1 รายการ (ในตัวอย่างมีสินค้า 2 รายการ) เราถึงเรียกมันว่าเป็นความสัมพันธ์แบบ 1 : Many ยังไงล่ะครับพี่น้อง ... และการเชื่อมความสัมพันธ์ในลักษณะนี้ เราจะเอาไปใส่ไว้ในตารางกริดเพื่อทำรายการขายสินค้า ที่สามารถเข้าไปแก้ไข เพิ่มเติมการขายสินค้าในแต่ละครั้งต่อไปได้ครับพี่น้อง

ในขั้นตอนของการออกแบบตารางข้อมูล เราจะต้องสมมุติข้อมูลตัวอย่างขึ้นมาก่อน เพื่อทำการทดสอบความถูกต้อง (คือขณะนี้ผมยังไม่ได้เขียนเป็นโปรแกรมขึ้นมาเลยน่ะครับ) ดังนั้นหากเราเข้าใจในเรื่องความสัมพันธ์ของตารางข้อมูลแล้ว ก็ไม่ยาก แต่จะยากก็เพราะเราไม่เข้าใจ (พยายามลืมๆทฤษฎีตามหนังสือ หรือ ตามห้องเรียนก่อนด้วยครับ)

หน้าจอการแสดงผลในส่วนของใบขายสินค้า (tblInvoice) และ ตารางลูกค้า (tblCustomer)

จำกัดเงื่อนไขเรื่องเวลา ... ผมขอนำเสนอเฉพาะบางอย่างไว้หน้าเว็บไซต์ก่อนน่ะครับ

เทคนิคที่สำคัญของการทำรายการขายสินค้า อยู่ที่ frmInvoiceDetail.frm และ โปรแกรมย่อย SaveData
ขั้นตอนกระบวนการที่สำคัญ จะมีอยู่ทั้งหมด 5 ขั้นตอน (หรือ 4 ขั้นตอน ... ให้ไปคิดเป็นการบ้าน) ขอให้ทุกท่านค่อยๆพยายามทำความเข้าใจไปน่ะครับ ... และขอให้สังเกตว่าตัวแปร PK (Primary Key) ตัวนี้ เป็นตัวสำคัญที่สามารถใช้งานได้ทั้งตาราง tblInvoice และ ตาราง tblInvoiceDetail
  • ขั้นตอนที่ 1
    เราจะสนใจพิจารณา คือ ตารางการขายหลัก (tblInvoice) เท่านั้น อย่างอื่นไม่ได้มาเกี่ยวข้องน่ะครับ
    • กรณีของการเพิ่มข้อมูล เราต้องทำการค้นหาค่า Primary Key (InvoicePK) ตัวใหม่
    • กรณีของการแก้ไขข้อมูล อาศัยค่า Primary Key ที่ส่งออกมาจากฟอร์มหลัก (frmInvoiceMain)
      (ดูที่ Form_Load ของ frmInvoiceDetail)
      
          ' #########################################################
          ' หากเป็นข้อมูลใหม่ NewData = True
          If NewData Then
          
              ' #########################################################
              ' ค้นหาค่า Primary Key ของ InvoicePK  ใหม่ ซึ่งเราจะต้องมาคำนวณหาในตอนจะ Save ข้อมูลเท่านั้น
              ' นี่คือปัญหาที่หลายคนเจอ ทำไมเวลาใช้งานจริง ค่า Primary Key หรือ InvoiceCode มันถึงซ้ำกันได้
              ' ก็เพราะจัดเรียงเหตุการณ์ หรือ ลำดับการทำงาน (Flow Control) ไม่ถูกต้องนั่นเองครับ ... พี่น้อง
              '
              Call SetupNewInvoice
              '
              ' จะเกิดการส่งค่า Primary Key ของ InvoicePK มาตัวแปร PK ตัวแปรตัวนี้ใช้ได้ทั้งตอนเพิ่ม และ แก้ไข
              ' #########################################################
              
              Statement = "SELECT * FROM tblInvoice ORDER BY InvoicePK"
              RS.Open Statement, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
              RS.AddNew
              ' ค่า Primary Key ที่คำนวณหาได้จากโปรแกรมย่อย SetupNewInvoice
              RS("InvoicePK") = PK
              RS("InvoiceCode") = Trim(txtInvoiceCode.Text)
              RS("DateAdded") = FormatDateTime(Now(), vbGeneralDate)
              RS("DateModified") = FormatDateTime(Now, vbGeneralDate)
              ' บันทึกผู้ใช้งาน เผื่อไว้กรณีที่ใช้งานแบบ Multi User
              RS("AddedByFK") = 0
              RS("LastUserFK") = 0
          
          '======================== แก้ไขข้อมูล ========================
          Else
              ' ก็เทียบค่าจาก Primary Key ที่ส่งมาจากฟอร์มหลัก (frmInvoiceMain)
              Statement = "SELECT * FROM tblInvoice WHERE InvoicePK = " & PK
              RS.Open Statement, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
          End If
          
          ' #########################################################
          '
          ' เริ่มต้นการบันทึกข้อมูลส่วนที่เหลือลงในตาราง tblInvoice เท่านั้น
          '
          ' #########################################################
          
          RS("InvoiceCode") = "" & Trim(txtInvoiceCode.Text)
          ' #########################################################
          ' รายการลูกค้า ที่เราซ่อน หรือ ฝาก Primary Key เอาไว้ใน txtCustomerCode.Tag
          ' และเราจะไม่ไปเชื่อมกับตารางลูกค้า tblCustomer เลย เพราะนี่แหละครับที่เรียกว่าการสร้างความสัมพันธ์
          '
          RS("CustomerFK") = CLng(txtCustomerCode.Tag)
          '
          ' #########################################################
          
          ' #########################################################
          ' รวมจำนวนเงินของสินค้าทั้งหมดทุกๆรายการ
          Dim TotalAmount As Currency
          TotalAmount = 0
          ' คำนวณหาราคาสินค้าทั้งหมดทุกชิ้นตามจำนวนแถวในตารางกริด ถามว่าทำไมต้องจัดเก็บลงตาราง tblInvoice
          ' ก็เพราะจะได้สะดวกต่อการนำไปแสดงผลในฟอร์มหลัก frmInvoiceMain ยังไงล่ะครับ
          ' จะได้ไม่ต้องมาคำนวณผลใหม่ทุกรอบ ทุกครั้งไป และ สามารถทำรายงานได้ง่ายขึ้นอีก
          For Rec = 1 To fgData.Rows - 1
              RS("TotalAmount") = Format(TotalAmount + CDbl(fgData.TextMatrix(Rec, 6)), "#,##0.00")
              TotalAmount = RS("TotalAmount")
          Next
          ' #########################################################
          
          ' บันทึกเวลาของการแก้ไขไว้ด้วย
          RS("DateModified") = FormatDateTime(Now(), vbGeneralDate)
          ' บันทึกผู้ใช้งาน เผื่อไว้กรณีที่ใช้งานแบบ Multi User (เมื่อ 0 คือ Administrator)
          RS("LastUserFK") = 0
          RS.Update
          RS.Close: Set RS = Nothing
          ' =================================================================
          ' หมดแล้วสำหรับข้อมูลในตาราง tblInvoice หากมีภาษี หรือ อื่นๆเราก็จะกระทำที่นี่ทั้งหมด
          ' =================================================================
      
  • ขั้นตอนที่ 2
    จะเกิดเฉพาะตอนที่ทำการแก้ไขข้อมูลเท่านั้น ... นั่นคือเิกิดบันทึกรายการขายสินค้าไปแล้ว แต่ลูกค้าเกิดคืนสินค้ากลับมา
    เราจึงต้องนำ จำนวนสินค้า ที่ขายออกไปเดิม ใน tblInvoiceDetail คืนกลับเข้าในรายการสินค้า (tblProduct) เสียก่อน
    หากคุณไม่ตัดสต็อค ก็ข้ามส่วนนี้ไปได้เลย

    การทำรายการขายเดิม


    การทำรายการขายใหม่ หรือ การแก้ไขข้อมูล จะเห็นได้ว่าในรายการที่ 2 เดิมมันหายไป


    แต่ในตารางข้อมูล tblInvoiceDetail มันยังคงมีรายการขายเดิมอยู่
    ดังนั้นเราต้องทำการคืนจำนวนสินค้านี้กลับเข้าสต็อคไปก่อน
    ... ทำไมเหรอครับ
    เพราะว่าต่อไปในใบขายสินค้าใบนี้ (InvoicePK = 1) ของตาราง tblInvoiceDetail
    เราจะลบรายการขายสินค้าเดิมในตารางข้อมูล tblInvoiceDetail ทิ้งออกไปให้หมดก่อน
    (อยู่ในขั้นตอนที่ 3)
    จากนั้นก็ค่อยเอาข้อมูลปัจจุบันที่อยู่ในตารางกริด มาทำการบันทึกเข้าไปแทนยังไงล่ะครับ (อยู่ในขั้นตอนที่ 4)
    
        ' #########################################################
        ' ทดสอบว่ามีข้อมูลสินค้าอยู่ในตาราง tblInvoiceDetail หรือไม่
        Dim blnData As Boolean
        ' รับค่า Primary Key ของสินค้านั้นๆไว้ก่อน
        Dim ProdPK As Long
        
        ' #########################################################
        ' ขั้นตอนนี้ คือ กรณีของการเข้าไปแก้ไขข้อมูล แล้วเกิดการลบข้อมูลสินค้าออกไปจากตารางกริด
        ' ดังนั้นต้องเอาข้อมูลที่อยู่ในแถวของตารางกริด ไปเปรียบเทียบกับข้อมูลเดิมตาม InvoicePK ใน tblInvoiceDetail
        ' หากพบข้อมูลในตาราง tblInvoiceDetail แต่ไม่เจอในตารางกริด แสดงว่ามีการลบข้อมูลสินค้าเดิมออกไป
        ' เราจะต้องทำการนำจำนวนสินค้าที่อยู่ในตาราง tblInvoiceDetail ส่งคืนกลับไปให้ตารางสินค้า tblProduct
        ' เพื่อปรับจำนวนสต็อคให้ตรงกับความเป็นจริง (ลูกค้าอาจคืนสินค้ากลับมาไงล่ะครับ)
        ' จะเกิดเหตุการณ์ตรวจสอบข้อมูลในส่วนนี้เฉพาะการแก้ไขข้อมูลเท่านั้น
        ' #########################################################
        
        ' ดังนั้น Not True ก็คือ ไม่จริง หรือ เท็จ นั่นเอง หรือจะเขียน If NewData = False ก็ได้
        If Not NewData Then
            '
            ' #########################################################
            ' เพราะเราจะทำการลบข้อมูลที่มีรหัส InvoicePK ตัวปัจจุบันนี้ทิ้งไปทั้งชุด ใน tblInvoiceDetail
            ' แล้วค่อยทำการบันทึกข้อมูลจากตารางกริดเข้าไปใหม่ (ในขั้นตอนที่ 4)
            ' #########################################################
            Set RS = New Recordset
            Statement = "SELECT * FROM tblInvoiceDetail WHERE InvoicePK = " & PK
            RS.CursorLocation = adUseClient
            RS.Open Statement, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
            
            blnData = False
            ' #########################################################
            ' เฉพาะการเข้ามาแก้ไขรายการขายสินค้าเท่านั้น วนรอบตามจำนวนค่า Primary Key ของใบขายสินค้า
            ' #########################################################
            Do Until RS.EOF
                ' วนรอบตามจำนวนแถวของตารางกริด
                For Rec = 1 To fgData.Rows - 1
                    ' เปรียบเทียบจากตารางข้อมูล (tblInvoiceDetail) หากมีค่าตรงกันกับ Primary Key ของสินค้า
                    ' ให้ออกจาก Loop For ไปเลย เพราะแสดงว่าเป็นข้อมูลสินค้าตัวเดิมอยู่แล้ว
                    ' หลัก 0 ที่ซ่อนเอาไว้ของตารางกริด คือ ค่า Primary Key ของสินค้านั้นๆ
                    If RS("ProductFK") = CLng(fgData.TextMatrix(Rec, 0)) Then
                        blnData = True
                        Exit For
                        
                    ' กรณีที่ที่เจอข้อมูลในตาราง tblInvoiceDetail แต่ไม่พบในตารางกริด แสดงว่า User ลบข้อมูลขายสินค้าเดิมออกไป
                    ' ต้องลงไปปรับจำนวนสต็อคสินค้าใหม่ ทางด้านล่าง
                    Else
                        ' เก็บค่า Primary Key ของสินค้าเพื่อนำจำนวนกลับไปคืนสต็อค
                        ProdPK = RS("ProductFK")
                        blnData = False
                    End If
                Next
                
                ' เมื่ออกจากวงรอบ FOR หากไม่พบรายการสินค้า blnData = False
                ' ทำการคืนกลับสินค้าเข้าสต็อคตรงนี้
                If Not blnData Then
                    Set DS = New Recordset
                    SQLStmt = "SELECT ProductPK, Stocked FROM tblProduct " & _
                                        " WHERE [ProductPK] = " & ProdPK & _
                                        " ORDER BY ProductPK"
                    DS.CursorLocation = adUseClient
                    DS.Open SQLStmt, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
                    If DS.RecordCount > 0 Then
                        ' #####################################################
                        ' สังเกตด้วยน่ะครับ RS("Quantity") เป็นค่าจำนวนสินค้าจากตาราง tblInvoiceDetail
                        ' ส่วน DS("Stocked") เป็นค่าที่อยู่ในตารางสินค้า tblProduct
                        ' ผมเคยเรียนให้ทราบแล้วว่า ผมประกาศใช้ RecordSet มาใช้งานไม่เกิน 3 ตัวหรอกครับ
                        ' เราอาศัยการเวียนนำมาใช้ ... หากประกาศพร่ำเพรื่อก็ต้องกิน Memory สูง และ โอกาสผิดพลาดก็มีสูงด้วย
                        '
                        DS("Stocked") = DS("Stocked") + RS("Quantity")
                        '
                        ' #####################################################
                        DS.Update
                    End If
                    DS.Close:   Set DS = Nothing
                End If
                ' เลื่อนรายการจากตารางข้อมูล
                RS.MoveNext
            Loop
    
            RS.Close:   Set RS = Nothing
        
        End If
        ' #########################################################
    
  • ขั้นตอนที่ 3 (กรณีเพิ่มข้อมูลใหม่ให้ข้ามไปได้เลย เพราะว่ายังไม่มีข้อมูลเดิม ... แต่คิดเอาเองครับจะทำยังไง)
    ทำการลบข้อมูลตาม Primary Key ของใบขาย (InvoicePK) ออกไปจากตารางข้อมูล tblInvoiceDetail
    
        ' #########################################################
        ' 3. คราวนี้ต้องลบข้อมูลในตารางย่อย tblInvoiceDetail ตามค่า Primary Key ออกไปก่อนให้หมด
        ' จากนั้นค่อยเอาข้อมูลจากตารางกริดที่กำลังทำรายการปัจจุบัน มาบันทึกใหม่เข้าไปแทนที่ของเดิม
        ' #########################################################
        SQLStmt = "DELETE tblInvoiceDetail.* FROM tblInvoiceDetail  " & _
                            " WHERE ([InvoicePK] = " & PK & ")"
                            
        ' สั่งลบข้อมูลออกไปทันทีเลย แม้ว่าจะไม่เจอ Primary Key ของ InvoicePK เลย
        Set DS = ConnDB.Execute(SQLStmt)
        ' #########################################################
    
  • ขั้นตอนที่ 4
    เป็นการนำข้อมูลรายการขายสินค้าที่อยู่ใน MS FlexGrid เข้าไปจัดเก็บไว้ในตาราง tblInvoiceDetail
    
        ' #########################################################
        For Rec = 1 To fgData.Rows - 1
            ' =========== เฉพาะตาราง tblInvoiceDetail ===========
            ' สำหรับตารางกริด
            ' หลัก 0 = ProductFK (หลัก 0 ที่ซ่อนไว้) เป็นคีย์รอง (Foreign Key) ที่เชื่อมโยงไปหาตาราง tblProduct ได้นั่นเอง
            ' หลัก 1 = รหัสสินค้า เช่น 0001 เราไม่เก็บ เพราะมี Primary Key (ProductPK) ของ tblProduct ชี้ไปหา
            ' Foreign Key (ProductFK) ใน tblInvoiceDetail อยู่แล้ว (นี่แหละคือความสัมพันธ์ระหว่างตาราง)
            ' หลัก 2 = ชื่อสินค้า (ไม่จัดเก็บ)
            ' หลัก 3 = หน่วยนับ (ไม่จัดเก็บ)
            ' หลัก 4 = เก็บราคาขายปัจจุบัน
            ' หลัก 5 = เก็บจำนวนการขาย
            ' หลัก 6 = คำนวณจำนวนเงินทั้งหมดเฉพาะสินค้าตัวนี้
            ' หลัก 7 = จำนวนสินค้าเดิมที่เราซ่อนเอาไว้ เพื่อนำไปตัดสต็อค
            Set RS = New Recordset
            Statement = "SELECT * FROM tblInvoiceDetail ORDER BY InvoicePK "
            RS.Open Statement, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
            With fgData
                RS.AddNew
                RS("InvoicePK") = PK
                RS("ProductFK") = CLng(.TextMatrix(Rec, 0))
                RS("PriceUnit") = Format(CDbl(.TextMatrix(Rec, 4)), "#,##0.00")
                RS("Quantity") = Format(CLng(.TextMatrix(Rec, 5)), "0")
                RS("TotalAmount") = Format(CDbl(.TextMatrix(Rec, 6)), "#,##0.00")
                RS.Update
                ' ====================================================
            End With
            RS.Close:   Set RS = Nothing
        Next
        ' #########################################################
    
  • ขั้นตอนที่ 5
    ปรับจำนวนสต็อคสินค้าที่ขายไปให้ถูกต้อง (อันที่จริงทำพร้อมๆกับขั้นตอนที่ 4 ก็ได้
    แต่ผมขอแยกออกมาเพื่อให้ดูได้ชัดเจน และ ง่ายขึ้นครับ)
    
        ' #########################################################
        ' 5. ปรับสต็อคสินค้าใหม่ทั้งหมด ............................................................ ขั้นตอนสุดท้ายแล้ว
        ' โดยนำค่าจำนวนสินค้าเดิมที่เราซ่อนในตารางกริด (หลักที่ 7) ไปบวกกลับคืนเข้าในสต็อคก่อน
        ' แล้วลบออกด้วยจำนวนสินค้าใหม่ในหลักที่ 5 แทน ... เทคนิคง่ายๆเท่านั้นเองครับ ไม่ยากเลย
        ' ตอนนี้จะทำงานเฉพาะตารางข้อมูลสินค้าเท่านั้น (tblProduct)
        ' #########################################################
        For Rec = 1 To fgData.Rows - 1
            ' ===== ตาราง tblProduct ===========
            Set RS = New Recordset
            ' เอาค่า Primary Key ในหลัก 0 ที่ซ่อนไว้ออกมาใช้ เพราะนั่นมันคือ Primary Key ของสินค้า
            Statement = "SELECT * FROM tblProduct WHERE [ProductPK] = " & _
                CLng(fgData.TextMatrix(Rec, 0)) & " ORDER BY ProductPK "
            RS.Open Statement, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
            With fgData
                ' เอาจำนวนในสต็อคเดิม RS("Stocked") มาบวกกับจำนวนเดิม (หลักที่ 7)
                ' แล้วลบออกจากจำนวนใหม่ (หลักที่ 5)
                RS("Stocked") = Format((RS("Stocked") + CLng(.TextMatrix(Rec, 7))) - _
                                                CLng(.TextMatrix(Rec, 5)), "0")
                RS.Update
                ' ====================================================
            End With
            RS.Close:   Set RS = Nothing
        Next
        ' #########################################################
        ' จบกระบวนการขั้นตอนของการทำงานแบบ 1 To Many
        ' ขั้นตอนที่ 4 และ 5 สามารถทำได้พร้อมๆกันน่ะครับ ... ลองไปคิดเองเป็นการบ้านด้วยล่ะกันครับ
        ' #########################################################
    
Conclusion:
กระบวนการบันทึกข้อมูลนี้ มันกระทำได้รวดเร็วมากน่ะครับ และจะเห็นได้อย่างชัดเจนเลยว่า มันไม่ได้มีกลไกอะไรที่สลับซับซ้อนมากมายแต่อย่างใดเลย มันเป็นวิธีการที่คิดง่ายๆแบบคณิตศาสตร์พื้นฐาน มองดูเหมือนตรรกะศาสตร์ขั้นสูง ทั้งๆที่มันก็มีเพียงแค่จริง กับ เท็จเท่านั้นเอง ... การที่จะสร้างนักพัฒนาซอฟท์แวร์รุ่นใหม่ๆ ตั้งแต่อยู่ในรั้วสถาบันการศึกษานั้น หลักที่สำคัญ คือ ต้องสร้างพื้นฐานทางปฏิบัติให้แน่น และ ต้องหาตัวอย่างจริง ของจริง มาสอน มาถ่ายทอด ให้กับเด็กๆเหล่านี้ ได้เห็น ได้สัมผัส หากทำได้แบบนี้ เราก็จะมีโอกาสสูงในการผลิตบุคลากรที่พร้อมจะทำงานจริงได้เลย หลังจากจบการศึกษาออกมาแล้วโดยทันที ... นี่คือสิ่งที่ผม "คิด" และ "ทำ" อย่างไรให้เด็กรุ่นใหม่ๆ ได้รับข้อมูล ความรู้ ที่ถูกต้อง เป็นจริง สามารถนำไปต่อยอดได้ ...

เพิ่มเติม

 


จี ทู จี เน็ต ดอต คอม - g2gNet Dot Com
เลขทะเบียนพาณิชย์อิเล็กทรอนิกส์ 0407314800231
CopyLeft © 2004 - 2099 g2gNet.Com All rights reserved.
Email: thongkorn@hotmail.com หรือ โทร. 08-6862-6560