Đánh giá chủ đề:
  • 1 Votes - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Sử dụng Class Module và Kết nối dữ liệu SQL SERVER trong Access VBA
#21
Chào các Bạn,

Có Bạn hỏi: muốn thiết kế 1 form có SubForm theo UnBound Form thì phải làm sao? Chẳng hạn như thiết kế 1 form để nhập chứng từ nhập / xuất hàng hoá (với SubForm trình bày chi tiết hàng hoá phát sinh của chứng từ).

Do quá bận nên tôi chưa thể trao đổi cụ thể được về vấn đề này, xin hẹn các Bạn trong những ngày tới. Hôm nay chỉ xin trao đổi một số gợi ý để các Bạn tham khảo như sau:

- Với MainForm để đăng ký các thông tin chung của chứng từ chúng ta thiết kế form và viết thủ tục để truy xuất dữ liệu có liên quan theo cách tương tự như ta đã làm trong file ứng dụng minh hoạ với Danh sách trong Danh bạ điện thoại.

- Để hiển thị thông tin hàng hoá chi tiết phát sinh của chứng từ, các Bạn có thể thiết kế theo 1 trong các cách sau:
+ Thiết kế 1 ListBox gồm có các cột dữ liệu phản ảnh thông tin chi tiết của hàng hoá
+ Hoặc thiết kế 1 Form độc lập để làm SubForm, lấy dữ liệu nguồn là Recordset được lọc theo số chứng từ phát sinh xác định, số chứng từ này ta sẽ lấy từ ô ghi số chứng từ trên MainForm. Các Bạn cần chú ý thiết lập kiểu dữ liệu của Recordset này phù hợp với nhu cầu chỉ để hiển thị thông tin thôi.
+ Thiết kế các ô để nhập dữ liệu chi tiết hàng hoá phát sinh (như: mã hàng, tên hàng, đơn vị tính, số lượng, đơn giá,...), các ô dữ liệu này cũng không gắn liền với nguồn dữ liệu xác định nào cả (nghĩa là không khai báo ControlSource). Để cập nhật thông tin nhập trên các ô này vào bảng dữ liệu có liên quan ta sẽ viết 1 thủ tục cập nhật (tương tự như thủ tục cập nhật danh sách phát sinh trong danh bạ vậy).

Để giúp các Bạn có điều kiện test dữ liệu qua internet, tôi đã bổ sung vào database "danhba" (là nguồn dữ liệu SQL SERVER được sử dụng trong file ứng dụng minh hoạ ta đã dùng từ bài đầu đến nay) 2 bảng dữ liệu sau đây:

1. Bảng dữ liệu để đăng ký thông tin chung của chứng từ nhập / xuất:
- Tên bảng: "tblctunx"
- Các Field dữ liệu:
+ Id (PK - numeric theo dạng AutoNumber)
+ soctu kiểu nchar(20)
+ ngay kiểu smalldatetime
+ msnv kiểu nchar(10) - dùng để đăng ký nghiệp vụ phát sinh là nhập hay xuất (và loại nhập xuất cụ thể nào, nếu các Bạn muốn phân biệt tới mức chi tiết như vậy)
+ mskh kiểu numeric(18,0) - dùng để đăng ký mã số khách hàng
+ tsuatvat kiểu numeric(18,0) - dùng để đăng ký thuế suất thuế VAT

2. Bảng đăng ký thông tin chi tiết hàng hoá:
- Tên bảng: "tblctunxct"
- Các Fields dữ liệu:
+ Id (PK, kiểu numeric(18,0)
+ soctu (PK, nchar(20)
+ mshh (PK, numeric(18,0) - đăng ký mã số hàng hoá

3 Field trên đều được khai báo là khoá chính của bảng (PK) để tránh trùng dữ liệu theo quy tắc: mỗi mặt hàng chỉ được đăng ký 1 dòng trong bảng.

+ dvt kiểu smallint - đăng ký đơn vị tính, tạm thời ta quy ước đơn vị tính thấp nhât với chỉ số = 1, sau này ta sẽ thiết kế bảng đăng ký hệ thống đơn vị tính cho hàng hoá (theo hướng 1 mặt hàng có thể đăng ký nhiều đơn vị tính khác nhau, các đơn vị tính này có liên quan với nhau thông qua 1 chỉ số quy số lượng về đơn vị tính thấp nhất)
+ soluong kiểu numeric(18,0)
+ dongia kiểu numeric(18,0)

Để cho đơn giản, trước mắt ta cho nhập tự do mã số khách hàng và mã số hàng hoá; sau này ta sẽ tạo thêm 2 bảng ghi danh sách khách hàng và ghi danh mục hàng hoá.

Mong các Bạn góp ý kiến trao đổi thêm.
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn Noname , quanghoasla , maidinhdan , Minh Tiên
#22
Chào các Bạn,

Hôm nay xin trao đổi tiếp tục vấn đề đang bỏ dỡ hôm trước:
Thiết kế 1 UnBound Form có SubForm kết nối dữ liệu tới SQL Server

Nhu cầu ứng dụng: Ta cần 1 form để quản lý chứng từ nhập xuất kho hàng, bao gồm các chức năng: cho nhập chứng từ mới phát sinh, cho truy xuất lại chứng từ đã lập, cho cập nhật lại các thông tin của chứng từ đã nhập.

Các bảng dữ liệu SQL Server phục vụ cho nhu cầu trên đã được tôi chuẩn bị sẵn gồm có:

1. Bảng ghi danh mục hàng hóa: tbldmhanghoa
Gồm các cột dữ liệu sau:
+ mshh: PK, numeric(18,0)
+ tenhanghoa: nvarchar(255)
+ xuatxu: nvarchar(50)
+ dactrung: nvarchar(255)

2. Bảng ghi hệ thống đơn vị tính của từng mặt hàng: tbldonvitinh
Gồm các cột dữ liệu sau:
+ mshh: PK, numeric(18,0)
+ cap: PK, smallint, đăng ký cấp của đơn vị tính
+ kihieu: nchar(10)
+ mota: nvarchar(50)
+ quycap1: numeric(18,0)
+ dongianhap: numeric(18,0)
+ dongiaxuat1: numeric(18,0)
+ dongiaxuat2: numeric(18,0)
+ dongiaxuat3: numeric(18,0)

3. Bảng ghi các thông tin chung của chứng từ nhập xuất phát sinh: tblctunx
Gồm các cột dữ liệu sau:
+ Id: PK, numeric(18,0)
+ soctu: nchar(20)
+ ngay: smalldatetime
+ msnv: nchar(10)
+ mskh: numeric(18,0)
+ tsuatvat: numeric(18,0)
+ nguoigiaodich: nvarchar(255)

4. Bảng ghi các thông tin về chi tiết hàng hóa của chứng từ nhập xuất phát sinh: tblctunxct
Gồm các cột dữ liệu sau:

+ Id: PK, numeric(18,0)
+ soctu: nchar(20)
+ mshh: numeric(18,0)
+ dvt: smallint
+ soluong: numeric(18,0)
+ dongia: numeric(18,0)
+ lacktyle: bit, đăng ký nội dung: có phải là chiết khấu theo tỷ lệ hay không?
+ mucck: decimal(18,2), đăng ký nội dung: mức chiết khấu cụ thể là bao nhiêu? Nếu là chiết khấu tỷ lệ thì nhập nguyên không có chia phần trăm (thí dụ: nếu chiết khấu với tỷ lệ là 2,5%, ta nhập 2,5)

5. Bảng đăng ký danh mục các nghiệp vụ phát sinh: tbldmnghiepvu
Khi lập chứng từ nhập xuất, để xác định nghiệp vụ phát sinh cụ thể (cần thống nhất mã nghiệp vụ phát sinh để tiện quản lý về sau này)
Gồm các cột dữ liệu sau:
+ msnv: PK, nchar(5), đăng ký mã số nghiệp vụ
+ tennghiepvu: nvarchar(255)

Sau đây là link tải file ứng dụng minh họa cập nhật ngày 16/7/2012:
http://www.mediafire.com/?43n5qckyc3q1s18

Với file ứng dụng minh họa này,
- Để hiển thị nội dung thông tin chi tiết các mặt hàng trong chứng từ phát sinh, tôi thiết kế 1 Subform với nguồn dữ liệu được nạp một cách linh hoạt, không cố định, tùy thuộc vào số chứng từ đang mở trên form chính.

- Khi thiết kế UnBound Form theo nhu cầu như trên đã nêu, theo tôi chúng ta cần phải chú ý những vấn đề sau đây:

1. Việc nạp nguồn dữ liệu cho SubForm nên chọn nạp thông qua property “Recordset” của SubForm, điều này khác với cách hay làm thông thường là xác định thông qua thuộc tính “RecordSource”.
Các Bạn có thể thấy cách thức tôi đã làm trong file ứng dụng mẫu, để nạp nguồn dữ liệu cho SubForm tôi đã viết thủ tục sau trong module “modQuanlyDulieu”:
Mã:
Sub SetSourceRecForSubForm(mForm As Form, sForm As String)
    Dim SQLst As String
    Dim SQLrec As ADODB.Recordset
    Dim tblName As String
    Dim vSoCtu, stChema As String
    vSoCtu = mForm!cmbSoCtu
    If Not IsNull(vSoCtu) Then
        tblName = "tblctunxct"
        stChema = GetSchemaTable(tblName)
        SQLst = "SELECT " & stChema & ".tbldmhanghoa.tenhanghoa, " & stChema & ".tblctunxct.*"
        SQLst = SQLst & " FROM " & stChema & ".tbldmhanghoa INNER JOIN " & stChema & ".tblctunxct"
        SQLst = SQLst & " ON " & stChema & ".tbldmhanghoa.mshh=" & stChema & ".tblctunxct.mshh"
        SQLst = SQLst & " WHERE " & stChema & ".tblctunxct.soctu = '" & vSoCtu & "'"
      
        Set SQLrec = ProcessRecordset(SQLst)
      
        Set mForm(sForm).Form.Recordset = SQLrec

        With mForm(sForm).Form
            .Requery
            !txtId.ControlSource = "id"
            !txtMSHH.ControlSource = "mshh"
            !txtTenHanghoa.ControlSource = "tenhanghoa"
            !txtCapDvt.ControlSource = "dvt"
            !txtDvt.ControlSource = "=IIF(not isnull(dvt),flookup('kihieu','tbldonvitinh','cap=' & [dvt]),'')"
            !txtSoluong.ControlSource = "soluong"
            !txtDongia.ControlSource = "dongia"
            !chkCKTL.ControlSource = "lacktyle"
            !txtMucCK.ControlSource = "mucck"
        End With

          ‘Nhớ đóng Recordset đã gán cho SubForm bằng 2 dòng lệnh sau nhằm mục đích tiết kiệm tài nguyên hệ thống:
        SQLrec.Close
        Set SQLrec = Nothing
    End If
End Sub

Như các Bạn đã thấy trong thủ tục trên, ngay sau khi đã gán Recordset SQLrec cho SubForm qua dòng lệnh:
[Code]
Set mForm(sForm).Form.Recordset = SQLrec
Tôi đã cho đóng Recordset SQLrec này lại. Việc đóng Recordset SQLrec không dẫn đến việc đóng Recordset của SubForm.

2. Chúng ta cũng cần lưu ý đến nhu cầu kép đối với nguồn dữ liệu của SubForm phải vừa cho hiển thị nội dung, vừa cho cập nhật lại hoặc xóa chi tiết hàng hóa phát sinh.
Để đáp ứng nhu cầu trên, tôi đã cho SubForm chỉ làm nhiệm vụ hiển thị nội dung thông tin chi tiết về hàng hóa phát sinh.
Đối với nhu cầu cập nhật lại hoặc xóa tôi cho thực hiện bằng cách:
+ Trên Form chính, tôi thiết kế các ô dữ liệu tương ứng với các cột dữ liệu của chi tiết hàng hóa cần cập nhật lại hoặc nhập mới, đồng thời viết thủ tục cho cập nhật các chi tiết này ngay trong class module của Form chính.
Nút lệnh gọi thủ tục cập nhật này được bố trí bên phải của các ô dữ liệu tương ứng, có hình Floppy-Disk
Nút lệnh gọi thủ tục xóa chi tiết hàng đang chọn được bố trí bên trái của các ô dữ liệu tương ứng, có hình gạch chéo màu đỏ. Muốn xóa 1 dòng chi tiết hàng nào đó, trước hết ta phải cho nạp dòng đó lên các ô dữ liệu tương ứng đang nói ở đoạn này.

Thủ tục cập nhật về chi tiết hàng hóa của chứng từ như sau:
Mã:
Sub SaveToInvoiceDetailFromForm(Optional InVoiceDetailId)
    'Luu thong tin tren form vao tblctunxCT
    'UpdateInvoiceDetail
    On Error GoTo HandleError
  
    Dim SQLst As String, tblName As String
    Dim vId
    Dim MucCK As Double, CKTL As Byte
  
    Call OpenMyConnection
  
    tblName = "tblctunxct"
    With Me
        vId = Me.txtDetailId
        MucCK = Nz(.txtMucCK)
        If IsNull(.chkCKTL) Then
            CKTL = 0
        Else
            If .chkCKTL.Value = True Then
                CKTL = 1
            Else
                CKTL = 0
            End If
        End If
        If Not IsNull(vId) Then
            SQLst = "UPDATE " & GetSchemaTable(tblName) & "." & tblName & " SET "
            SQLst = SQLst & " soctu ='" & Trim(.cmbSoCtu) & "',"
            SQLst = SQLst & " mshh =" & .cmbMSHH & ","
            SQLst = SQLst & " dvt =" & .cmbDvt & ","
            SQLst = SQLst & " soluong =" & .txtSoluong & ","
            SQLst = SQLst & " dongia =" & .txtDongia & ","
            SQLst = SQLst & " lacktyle =" & CKTL & ","
    '        SQLst = SQLst & " mucck =" & Format(MucCK, "#,###.0#")
            SQLst = SQLst & " mucck =" & MucCK
            SQLst = SQLst & " WHERE ("
            SQLst = SQLst & " soctu='" & Trim(Me.cmbSoCtu) & "'"
            SQLst = SQLst & " AND id=" & InVoiceDetailId
            SQLst = SQLst & ")"
        Else
            SQLst = "INSERT INTO " & GetSchemaTable(tblName) & "." & tblName
            SQLst = SQLst & "(soctu, mshh, dvt, soluong, dongia, lacktyle, mucck)"
            SQLst = SQLst & " VALUES ("
            SQLst = SQLst & " '" & Trim(.cmbSoCtu) & "',"
            SQLst = SQLst & " " & .cmbMSHH & ","
            SQLst = SQLst & " " & .cmbDvt & ","
            SQLst = SQLst & " " & .txtSoluong & ","
            SQLst = SQLst & " " & .txtDongia & ","
            SQLst = SQLst & " " & CKTL & ","
            SQLst = SQLst & " " & Nz(MucCK)
            SQLst = SQLst & ")"
        End If
    End With
  
    Debug.Print SQLst
  
    MyConn.Execute SQLst
  
    Call CloseMyConnection
  
HandleError:
        If Err > 0 Then
            GeneralErrorHandler Err.Number, Err.Description, NhapXuat_FORM, "SaveToInvoiceDetailFromForm"
            Exit Sub
        End If
End Sub

3. Với các ComboBox, chúng ta cũng cần cân nhắc việc nạp nguồn dữ liệu cho các ComboBox này (để có danh sách sổ xuống) sao cho phù hợp, chỉ nạp khi cần và với giới hạn xác định.
Để đáp ứng nhu cầu này, tôi chỉ cho nạp nguồn dữ liệu cho ComboBox khi nào ta cho gọi hiện danh sách sổ xuống (thường là bằng cách bấm phím F4 hoặc Alt + phím mũi tên xuống). Do vậy, tôi viết thủ tục sau để gán nguồn dữ liệu cho ComboBox, và khai báo thủ tục sự kiện KeyDown (khi có phím bấm xuống) tại ComboBox.

Thủ tục gán dữ liệu nguồn:
Mã:
Private Sub SetComboRowSource(ComboName As String, RecSourceSt As String, stFilter As String)
    'Nap RowSource cho ComboBox có tên qua biến ComboName
  
    Dim SQLst As String
    Dim SourceRec As ADODB.Recordset
  
    SQLst = RecSourceSt & " WHERE " & stFilter 'ten LIKE N'%" & stFilter & "%'"
    Set SourceRec = ProcessRecordset(SQLst)
    Set Me(ComboName).Recordset = SourceRec

    SourceRec.Close
    Set SourceRec = Nothing
End Sub

Và nội dung thủ tục bẩy sự kiện tương tự như sau (ở đây là bẩy sự kiện KeyDown của ComboBox lấy danh sách khách hàng từ nguồn là bảng tblDanhsach):
Mã:
Private Sub cmbKhachhang_KeyDown(KeyCode As Integer, Shift As Integer)
    Dim srcSt As String, sCri As String
    Dim tblName As String
    Dim InputSt
    'Set RowSource For CmbKhachhang
    'SetComboRowSource
    If KeyCode = vbKeyF4 Or (KeyCode = vbKeyDown And Shift = acAltMask) Then
        InputSt = Me.cmbKhachhang.Text
        tblName = "tblDanhsach"
        srcSt = "SELECT * FROM " & GetSchemaTable(tblName) & "." & tblName
        sCri = " ten LIKE N'%" & InputSt & "%'"
      
        SetComboRowSource "cmbkhachhang", srcSt, sCri
    End If
    '
End Sub

4. Về việc cập nhật thông tin chung của chứng từ chúng ta cũng cần cân nhắc với 2 trường hợp phân biệt là Thêm chứng từ mới hay Cập nhật lại các thay đổi của chứng từ đã lập.
Tôi giải quyết vấn đề trên như sau:
- Trong cấu trúc bảng tblctunx có 1 field được xác định là khóa chính (PK) là field “Id”. Trên Form chính tôi bố trí 1 TextBox để nhận giá trị của field khóa chính này:
+ Khi TextBox này có giá trị xác định, nghĩa là trường hợp form đang hiển thị nội dung của 1 chứng từ xác định đang hiện hữu trong bảng tblctunx. Việc cập nhật thay đổi được thực hiện thông qua thủ tục SaveToInvoiceFromForm sau đây với biến InvoiceId xác định (trong thủ tục này InvoiceId là 1 biến tùy chọn – với từ khóa Optional phía trước)

Thủ tục đó như sau:
Mã:
Sub SaveToInvoiceFromForm(Optional InvoiceId)
    'Luu thong tin tren form vao tblctunx
    On Error GoTo HandleError
  
    Dim SQLst As String, tblName As String
    Dim vId
  
    Call OpenMyConnection
  
    tblName = "tblctunx"
  
    With Me
        vId = Me.txtId
        If Not IsNull(vId) Then ‘Nếu giá trị của TextBox txtId không là Null nghĩa là Form đang hiển thị thông tin của chứng từ đang hiện hữu.
            SQLst = "UPDATE " & GetSchemaTable(tblName) & "." & tblName & " SET "
            SQLst = SQLst & " soctu ='" & .cmbSoCtu & "',"
            SQLst = SQLst & " ngay ='" & Format$(.txtNgay, "dd-mmm-yy") & "',"
            SQLst = SQLst & " msnv ='" & .cmbNghiepvu & "',"
            SQLst = SQLst & " mskh ='" & .cmbKhachhang & "',"
            SQLst = SQLst & " nguoigiaodich ='" & .txtNguoiGiaodich & "',"
            SQLst = SQLst & " tsuatvat =" & .txtTsuat
            SQLst = SQLst & " WHERE ("
            SQLst = SQLst & " soctu='" & InvoiceId & "'"
            SQLst = SQLst & ")"
        Else ‘Nếu giá trị của TextBox txtId là Null nghĩa là Form đang hiển thị thông tin của chứng từ chờ lưu mới.
            If IsNull(.cmbSoCtu) Then Exit Sub
            SQLst = "INSERT INTO " & GetSchemaTable(tblName) & "." & tblName
            SQLst = SQLst & "(soctu, ngay, msnv, mskh, tsuatvat)"
            SQLst = SQLst & " VALUES ("
            SQLst = SQLst & " '" & .cmbSoCtu & "',"
            SQLst = SQLst & " '" & Format$(.txtNgay, "dd-mmm-yy") & "',"
            SQLst = SQLst & " '" & .cmbNghiepvu & "',"
            SQLst = SQLst & " '" & .cmbKhachhang & "',"
            SQLst = SQLst & " '" & .txtNguoiGiaodich & "',"
            SQLst = SQLst & " " & .txtTsuat
            SQLst = SQLst & ")"
        End If
    End With
  
    MyConn.Execute SQLst
  
    Call CloseMyConnection
  
HandleError:
        If Err > 0 Then
            GeneralErrorHandler Err.Number, Err.Description, NhapXuat_FORM, "SaveToInvoiceFromForm"
            Exit Sub
        End If
End Sub

Và thủ tục để nạp thông tin của chứng từ đang hiện hữu trong abrng tblctunx lên Form chính như sau:
Mã:
Sub LoadInvoiceInfoToForm(SoCtuSt)
    Dim SQLst As String, SQLrec As ADODB.Recordset
    Dim KHrec As ADODB.Recordset
    Dim tblName As String, MsKH As Long
    tblName = "tblctunx"
    If IsNull(SoCtuSt) Then Exit Sub
    SQLst = "SELECT * FROM " & GetSchemaTable(tblName) & "." & tblName
    SQLst = SQLst & " WHERE soctu ='" & SoCtuSt & "'"
    Set SQLrec = ProcessRecordset(SQLst)
    '
    If SQLrec.RecordCount > 0 Then
        Set objKhachHang = New clsDanhba
        With Me
            .txtId = SQLrec!id
            .txtNgay = SQLrec!ngay
            .cmbNghiepvu = SQLrec!msnv
            .txtTsuat = SQLrec!tsuatvat
            .txtNguoiGiaodich = SQLrec!nguoigiaodich
          
            MsKH = SQLrec!MsKH
          
            SQLst = "SELECT * FROM " & GetSchemaTable("tblDanhsach") & ".tblDanhsach"
            SQLst = SQLst & " WHERE danhbaid = " & MsKH
            Set KHrec = ProcessRecordset(SQLst)
            objKhachHang.PopulatePropertiesFromRecordset KHrec
                  
            .cmbKhachhang = MsKH
            .cmbKhachhang.RowSourceType = "Value List"
            .cmbKhachhang.RowSource = objKhachHang.Ten & ";" & MsKH
          
            .txtDiachi = objKhachHang.Diachi
            .txtPhone = objKhachHang.Dtvp
            .txtMasoThue = objKhachHang.Msthue
          
            KHrec.Close
            Set KHrec = Nothing

            'Dòng sau để cho nạp nguồn dữ liệu chi tiết hàng hóa tương ứng của chứng từ đã xác định
             SetSourceRecForSubForm Me, "frmCtuNXCT"
          
        End With
    End If
    '
    SQLrec.Close
    Set SQLrec = Nothing
End Sub

Còn các vấn đề có liên quan khác như: tìm và xóa chứng từ, các Bạn tự làm nhé.

Như vậy là tôi đã trình bày xong 1 trong những cách thiết kế UnBound Form có chứa SubForm kết nối đến dữ liệu SQL Server.
Và cũng xin nhắc lại rằng: có nhiều cách để ứng dụng cho nhu cầu này. Ở đây tôi chỉ trình bày cách dễ làm nhất thôi.

Có Bạn nào muốn thiết kế các Object tự tạo để quản lý các chứng từ nhập xuất phát sinh kiểu như ta đã làm để quản lý Danh bạ đã đề cập trong các bài trước không? Các Bạn thử xem sao nhé.

Rất mong các Bạn tham gia trao đổi thêm.

Cũng xin thông tin thêm về tình trạng các bảng dữ liệu mới bổ sung:
- Danh mục hàng hóa và đơn vị tính đã được nạp sẵn trên 1.000 mặt hàng, mỗi mặt hàng đều có từ 2 đến 3 đơn vị tính.
- Mới chỉ có vài chứng từ phát sinh
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn Noname , jason , quanghoasla , maidinhdan , Minh Tiên
#23
Chào các Bạn,

Có một Bạn đã phát hiện lỗi không cập nhật được chứng từ mới phát sinh.
Tôi đã kiểm tra và phát hiện lỗi ở thủ tục sau, nằm bên trong Class module của Form "frmCtuNX":
Mã:
Sub SaveToInvoiceFromForm(Optional InvoiceId)
    'Luu thong tin tren form vao tblctunx
    
    'UpdateOrInsert:
    '+ True: Luu thong tin thay doi vao mau tin dang hien huu
    '+ Flase: Them mau tin moi
    
    'InvoiceId: so chung tu
    '
    On Error GoTo HandleError
    
    Dim SQLst As String, tblName As String
    Dim vId
    
    Call OpenMyConnection
    
    tblName = "tblctunx"
    
    With Me
        vId = Me.txtId
        If Not IsNull(vId) Then
            If IsNull(InvoiceId) Then Exit Sub
            SQLst = "UPDATE " & GetSchemaTable(tblName) & "." & tblName & " SET "
            SQLst = SQLst & " soctu ='" & .cmbSoCtu & "',"
            SQLst = SQLst & " ngay ='" & Format$(.txtNgay, "dd-mmm-yy") & "',"
            SQLst = SQLst & " msnv ='" & .cmbNghiepvu & "',"

            'SQLst = SQLst & " mskh ='" & .cmbKhachhang & "',"  'Đây là dòng sai, vì mskh có kiểu numeric nhưng ở đây có 2 dấu nháy ở 2 đầu nên thành kiểu Text

            SQLst = SQLst & " mskh =" & .cmbKhachhang & "," 'Đây là dòng đã được hiệu chỉnh cho đúng, bỏ dấu nháy ở 2 đầu

            SQLst = SQLst & " nguoigiaodich = N'" & .txtNguoiGiaodich & "'," 'Và sẵn tiện sửa luôn dòng này để lưu được chuỗi Unicode

            SQLst = SQLst & " tsuatvat =" & .txtTsuat
            SQLst = SQLst & " WHERE ("
            SQLst = SQLst & " soctu='" & InvoiceId & "'"
            SQLst = SQLst & ")"
        Else
            If IsNull(.cmbSoCtu) Then Exit Sub
            SQLst = "INSERT INTO " & GetSchemaTable(tblName) & "." & tblName
            SQLst = SQLst & "(soctu, ngay, msnv, mskh, nguoigiaodich, tsuatvat)"
            SQLst = SQLst & " VALUES ("
            SQLst = SQLst & " '" & .cmbSoCtu & "',"
            SQLst = SQLst & " '" & Format$(.txtNgay, "dd-mmm-yy") & "',"
            SQLst = SQLst & " '" & .cmbNghiepvu & "',"

            SQLst = SQLst & " " & .cmbKhachhang & ","

            SQLst = SQLst & " N'" & .txtNguoiGiaodich & "',"

            SQLst = SQLst & " " & .txtTsuat
            SQLst = SQLst & ")"
        End If
    End With
    
    MyConn.Execute SQLst
    
    Call CloseMyConnection
    '
    LoadInvoiceInfoToForm Me.cmbSoCtu
    
HandleError:
        If Err > 0 Then
            GeneralErrorHandler Err.Number, Err.Description, NhapXuat_FORM, "SaveToInvoiceFromForm"
            Exit Sub
        End If
End Sub

Và lỗi ở thủ tục sau đây, cũng ở trong Class module của form "frmCtuNX":
Mã:
Private Sub SetComboRowSource(ComboName As String, RecSourceSt As String, stFilter As String)
    'Nap RowSource cho ComboBox
    
    Dim SQLst As String
    Dim SourceRec As ADODB.Recordset
    
    SQLst = RecSourceSt & " WHERE " & stFilter 'ten LIKE N'%" & stFilter & "%'"
    Set SourceRec = ProcessRecordset(SQLst)
   'thêm 3 dòng kế bên dưới. Tôi viết kiểu With ... End With để phòng khi phải khai báo thêm gì nữa cho ComboBox    
    With Me(ComboName)
        .RowSourceType = "Table/Query"
    End With
    
    Set Me(ComboName).Recordset = SourceRec
    
    SourceRec.Close
    Set SourceRec = Nothing

End Sub

Xin cảm ơn các Bạn đã quan tâm.
Có Bạn nào thấy sai ở chỗ nào nữa không?
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn Noname , quanghoasla , maidinhdan , Minh Tiên
#24
Chào các Bạn,
Xin trao đổi thêm nội dung còn thiếu về file ứng dụng minh họa được cập nhật hôm nay (16/7/2012):

1. Trên form chính "frmCtuNX":
+ Để nạp lại nội dung các chứng từ đã lưu trước đây, tại ô nhập số chứng từ xin bấm 1 vài ký tự số để lọc nhanh và cho sổ danh sách chứng từ xuống (với các chứng từ do tôi nhập đều có số 3 trong chuỗi số chứng từ, nên các Bạn nhập số 3), sau đó chọn số chứng từ xác định từ danh sách sổ xuống, chương trình sẽ cho nạp nội dung của chứng từ đó lên Form.


+ Để chọn khách hàng có sẵn từ danh sách: tại ô nhập khách hàng, cũng thao tác tương tự như trên, nghĩa là nhập vào 1 vài từ cần tìm rồi cho sổ danh sách xuống (thí dụ như nhập từ "Công ty"), sau đó chọn khách hàng thích hợp. Danh sách này truy xuất từ bảng dữ liệu lưu Danh bạ (tblDanhsach) ta đã xem xét trong các bài trước có sẵn trên 15.000 mẫu tin.


2. Để xóa trống các ô nhập chi tiết hàng phát sinh trong chứng từ: kích kép tại ô nhập mã số hàng hóa.
Khi chọn hoặc nhập mới số chứng từ, các ô này cũng sẽ tự động được xóa trống.


3. Với SubForm "frmCtuNXCT": xin các Bạn chú ý các thuộc tính được khai báo trong ảnh đính kèm.
Trong các thuộc tính này, các Bạn chú ý thuộc tính "Recordset-Type" đã được khai báo là kiểu "Snapshot".
Với kiểu Snapshot, Recordset sẽ được đặt ở chế độ chỉ xem, không hiệu chỉnh, không thêm, không xóa được. Access sẽ dành ít tài nguyên nhất để nạp Recordset kiểu "Snapshot"


Các Bạn có thể tham khảo các hướng dẫn của Microsoft về Recordset-Type của 1 Access Form tại link sau:
http://office.microsoft.com/en-us/access...32788.aspx
Và các khuyến cáo nhằm tăng khả năng truy xuất dữ liệu SQL Server của ứng dụng Access từ link sau:
http://msdn.microsoft.com/en-us/library/...l.90).aspx
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn Noname , tanthuc , maidinhdan , Minh Tiên
#25
Chào các Bạn,

Tối hôm qua có Bạn hỏi qua email:

Vì sao trong thủ tục "SetSourceRecForSubForm" (module modQuanlyDulieu) để gán Recordset cho SubForm tôi lại dùng câu lệnh:

Mã:
Set mForm(sForm).Form.Recordset = SQLrec

mà không phải là:

Mã:
mForm(sForm).Form.Recordset = SQLrec

Câu trả lời thật ngắn gọn là:
Theo quy ước của VBA:
+ Recordset là 1 Object (các Bạn sử dụng thư viện ADO hay DAO cũng đều như vậy cả)
+ Trong thủ tục nêu trên SQLrec là 1 Recordset
+ Câu lệnh gán giá trị cho 1 biến Object phải tuân theo cú pháp: [COLOR="red"]SET [/COLOR]<[COLOR="#2e8b57"]Biến Object hoặc Property của Object[/COLOR]> = [COLOR="#2e8b57"]Giá trị là 1 Object[/COLOR]
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn Noname , tanthuc , maidinhdan , Minh Tiên
#26
Chào các Bạn,
Có một số Bạn gọi điện hỏi tôi vì sao truy xuất chậm quá, không giống như lần đầu sử dụng file minh họa?
Tôi đã kiểm tra lại và thấy tốc độ truy xuất vẫn như trước. Tôi đã cho nạp thử tiện ích VPN ảo thì thấy ứng dụng chạy chậm hẳn, lý do ở đây là khi nạp tiện ích này (và các tiện ích tương tự) máy tính của Bạn thay vì truy xuất trực tiếp đến host đang lưu file dữ liệu cần truy xuất, thì lại đi vòng qua 1 hoặc nhiều host khác nữa, nên bị chậm hẳn. Trong trường hợp này, các Bạn chỉ cần tắt hoặc DisConnect đến VPN ảo đi là nhanh trở lại.
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn
#27
Chào các Bạn,

Có Bạn bảo tôi: đã lỡ làm được tới đó rồi sao không tiện thể cho tự động đề nghị đơn giá mỗi khi chọn 1 mặt hàng hoặc chọn lại đơn vị tính?
Thấy nhu cầu này cũng cần để thêm phần sâu sắc cho vấn đề được minh họa nên tôi đã bổ sung nhu cầu trên vào file ứng dụng được cập nhật lúc 13 giờ trưa nay. Bạn nào có nhu cầu xin tải xuống từ link sau:
http://www.mediafire.com/?7qesy6y1ec1d50z

Nội dung bổ sung được tôi sử dụng 1 thủ tục tự tạo thay thế cho hàm Dlookup của VBA, thủ tục này có tên là fLookup nằm trong module "modUtilities".
Nội dung thủ tục này như sau:
Mã:
Function fLookup(WhatField As String, WhatTable As String, CriSt As String)
    On Error GoTo xulynull
    Dim SrcRec As ADODB.Recordset
    Dim srcSt As String

    If Len(CriSt) = 0 Then Exit Function

    srcSt = "SELECT TOP 1 " & WhatField & " FROM " & GetSchemaTable(WhatTable) & "." & WhatTable
    srcSt = srcSt & " WHERE " & CriSt
    Set SrcRec = ProcessRecordset(srcSt)
    
    If SrcRec.RecordCount > 0 Then fLookup = Trim(SrcRec(WhatField))
    
    SrcRec.Close
    Set SrcRec = Nothing
    
    Exit Function
    
xulynull:
    If Err > 0 Then fLookup = Null
    Exit Function
End Function
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn Noname , tanthuc , maidinhdan , Minh Tiên
#28
Chào các Bạn,

Để giúp các Bạn có căn cứ đánh giá và tối ưu hoá hiệu quả truy xuất dữ liệu của các thủ tục đang có trong file ứng dụng minh hoạ và các thủ tục do chính các Bạn viết hoặc hiệu chỉnh, tôi đã cho nạp vào file dữ liệu trên SQL SERVER:
+ Trên 12.000 chứng từ phát sinh (trong bảng "tblctunx")
+ Với trên 48.000 chi tiết hàng hoá phát sinh (trong bảng "tblctunxct")

Rất mong các Bạn cùng tham gia trao đổi để chúng ta cùng làm sáng tỏ những vấn đề đang thảo luận trong chuyên đề này.
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn Noname , maidinhdan , thucgia , Minh Tiên
#29
Chào các Bạn,

Theo dõi thấy có nhiều Bạn đọc chuyên đề này, nhưng sao không thấy ý kiến gì trao đổi thêm, làm tôi thấy băn khoăn. Không biết những gì tôi trao đổi có mang đến cho các Bạn điều gì ích lợi không? Có gì chưa đúng hay sai chăng?

Thật tình, tôi cũng chỉ muốn chứng minh rằng Microsoft Access giúp ta được rất nhiều việc, trong đó có những việc mà bấy lâu nay chúng ta tưởng, và cũng có rất nhiều người chê Access cũng tưởng lầm rằng Access chỉ làm được ba cái ứng dụng "lẹt đẹt" mang tính "local" thôi, chứ đụng tới NET là chào thua.

Rất mong các Bạn cùng tham gia trao đổi.
Chữ ký của lehongduc Lê Hồng Đức
Số ĐT: 0913.941.144
Email: lhongduc@gmail.com, lehongduc@ymail.com
Website: http://quantribanhang.vn
Reply
Những người đã cảm ơn Noname , Minh Tiên , quanghoasla , maidinhdan
#30
Hay lắm bạn ạ, mình muốn hỏi làm thế nào để tận dụng các chức năng form wizard trong access nếu dùng chuỗi kết nối như bạn nói.
Chữ ký của tanthuc Xin chào, mình là tanthuc, Tham gia http://thuthuataccess.com/forum từ ngày 04-10 -12.
Reply
Những người đã cảm ơn


Có thể liên quan đến chủ đề
Chủ đề: Tác giả Trả lời: Xem: Bài mới nhất
Question [Help] Link tất các table trong một file.mdb bằng VBA MinhnHang 9 226 Hôm qua, 06:11 PM
Bài mới nhất: maidinhdan
  [Thủ Thuật] Tìm số thứ tự bị thiếu trong dãy toanle 8 204 07-12-16, 02:25 PM
Bài mới nhất: toanle
  Sựa khác nhau giữa Module và Class Module, phạm vi áp dụng của từng loại. MinhnHang 6 307 29-11-16, 09:11 PM
Bài mới nhất: ongke0711
  Tránh xung đột dữ liệu trong access quocdung9999 16 1,694 23-11-16, 11:13 AM
Bài mới nhất: quocdung9999
  [Hỏi] Kết nối nhiều lần hay kết nối 1 lần trong ADO ưu điểm hơn Minh Tiên 1 124 10-11-16, 10:41 AM
Bài mới nhất: maidinhdan

Chuyển nhanh:


User(s) browsing this thread: 1 Guest(s)
Diễn Đàn Thơ Văn Thi Ẩm Lâu|Nhà Hàng Sông Thơ