Chương Trình Quản Lý Kho Vật Tư (phụ Tùng)

Liên hệ QC
3. Có rất nhiều thao tác rất bình thường (save, thoát, v.v...) (2 nút này đồng thời là nút SAVE - cho nên hơi lâu) chạy chả hiểu sao ... lâu thế và 90% các thao tác như vậy dẫn đến treo Excel (hình như nó ghi vào file Excel). Tôi thường thao tác vài cái thì ... treo excel (hay là tôi chưa quen làm việc với Excel nhỉ???). Lần nào Excel của tôi mở các chương trình có VBA là y như rằng Excel của tôi cứ lăn quay ra chết (Đúng là bác chưa quen với sử dụng ex rồi)
Còn nữa, đây đúng là vấn đề ... tệ nhất của các ứng dụng trên Excel. Tức là làm xong thì phải ... save file (trong khi các CSDL khác ko có khái niệm này, chỉ cần record added, updated nhoằng cái là xong). Mà ... save file của Excel thì lâu dã man (cứ thử với file Excel >= 5Mbs thì biết). Cái này thì ko chỉ tớ kêu mà các em sài Excel với dữ liệu kế toán nhớn nhớn 1 chút thường hay kêu trời lên.

Tuy nhiên, tớ thấy cái báo giá, hoặc các files có dữ liệu cần phải tính toán (như cái project costing estimate, simple project plan,... mà tớ hay làm), và dung lượng chỉ khoảng vài trăm Kbs mà làm trên excel thì nhất quả đất (chả nên làm trên phần mềm nào khác cả). :D
 
Lần chỉnh sửa cuối:
File mới cập nhật, sửa & bổ sung một số chức năng trong phần DANH MỤC + Một số chức năng khác.

Bây giờ các bạn vào mở file' Từ Menu chính của chương trình chọn vào nút DANH MỤC sau đó chọn các danh mục như: Phụ tùng, Máy móc thiết bị, Giấy đề nghị, Tìm theo mã hàng ... để test thử hộ tôi nhé.
 

File đính kèm

  • Quan ly kho phu tung (Ver02)24-10-07.rar
    369.9 KB · Đọc: 1,073
Lần chỉnh sửa cuối:
Cám ơn rất nhiều nhé. Ráng cho mấy ẻm nhờ.
"Người thổi và hàng tổng", mệt chưa? Phải chi ta giúp được gì nhỉ! Khổ là về form ta chưa rành.
 
vungoc đã viết:
File mới cập nhật, sửa & bổ sung một số chức năng trong phần DANH MỤC + Một số chức năng khác.

Bây giờ các bạn vào mở file' Từ Menu chính của chương trình chọn vào nút DANH MỤC sau đó chọn các danh mục như: Phụ tùng, Máy móc thiết bị, Giấy đề nghị, Tìm theo mã hàng ... để test thử hộ tôi nhé.

Mình vẫn thích cách tìm danh mục cũ hơn.
Đây là File đã sửa tốc độ hiển thị của Danh mục. Bác VuNgoc xem nhé.

Ngày mai mình sẽ cải thiện về tốc độ lưu File lại.
Thân!
 

File đính kèm

  • Quan ly kho phu tung (Ver02)22-10-07-Hieu.rar
    294.1 KB · Đọc: 710
Cảm ơn Mr. Hiếu, nhưng mình thấy tốc độ vẫn chậm bạn ạ !

Với lại cái file "Quan ly kho phu tung (Ver02) 24-10-2007" mình đã sửa đổi rất nhiều, nếu có sửa gì bạn vui lòng sửa vào trong file này dùm mình với nhé.

Cảm ơn nhiều !
 
Mr Okebab đã viết:
Vâng, Bác Duyệt làm nhiều rồi, nhưng bác ấy cứ như là Yang Can Cook ấy.
Làm món thì cực ngon (em khỏi phải quảng cáo), luôn miệng nói là : Yang làm được, các bạn cũng làm được.
Híc híc, em làm theo bác ấy mà được thì em . . chết liền. Cũng bởi bì em . . ngu quá đấy mà.+-+-+-++-+-+-+
Với ý định đã hình thành từ lâu, nhưng vì VEC còn mới quá, chúng ta sẽ có khoản hai buổi chuyên đề về ADO.

To: Vungoc,
Dạo này đang chuẩn bị cho buổi sinh hoạt lần 2 nên chưa thể làm cùng em được. !$@!! Thông cảm nha.

Tôi hy vọng rằng sau khi xong chuyên đề ADO, các bạn sẽ phát triển ứng dụng của mình tốt hơn.

Lê Văn Duyệt
 
Quản lý kho vật tư

Xin chào các bạn . Tôi cũng viết một chương trình nhỏ về quản lý vật tư hiện đang áp dụng trong công ty . xin gởi lên cho các bạn góp ý .
 

File đính kèm

  • QLTKVT.rar
    465.3 KB · Đọc: 1,466
Hình như lần trước tớ có nói về các lỗi mà bạn vungngoc chưa nhận ra nhỉ? Lần này vào mọi thứ y trang như lần trước. Đây, tôi attach cái file lỗi để bạn xem.

Bạn làm như sau thì sẽ thấy lỗi:

- Vào chức năng "Báo cáo"
- Chọn một nhà cung ứng phụ tùng từ Combo NhaCungUng
- Sau đó xóa nhà cung ứng đó khỏi Combo Text đi --> Lỗi Runtime xảy ra.

Lý do lỗi: Sau khi xóa (Clear Text trên Combo) thì NNhaCungUng.ListIndex = -1

Và khi đó cái dòng lệnh sẽ bị lỗi
S111.Range("F5") = Me.NNhaCungUng.Column(1)

Bạn cần phải sửa là:

Mã:
   [B]If NNhaCungUng.ListIndex <> -1 Then[/B]
        S111.Range("E5") = Me.NNhaCungUng
        S111.Range("F5") = Me.NNhaCungUng.Column(1)
    End If

Tóm lại, trước khi bạn làm cái gì cũng phải kiểm tra trước khi thực hiện. Trước khi xóa 1 file phải kiểm tra xem file đó có tồn tại ko, có readonly ko, có v.v... ko rồi mới Kill file. Bạn hiểu ý tôi chứ???? (Đây cũng là 1 lý do mà các PM trong nước thường xuyên xảy ra lỗi "lặt vặt" kiểu này - Mà khách hàng, người sử dụng thì cứ thấy lỗi là chán rồi cho dù lỗi nhỏ hay lỗi lớn)

Một vấn đề nữa. Khi tôi vào 1 form hầu như ko thấy giá trị mặc định đâu cả. Tỷ như lúc vào form danh mục, ko hiểu cái danh sách phía bên phải là của danh mục nào (vì chả có cái option bên trái nào được chọn)??? Đây có thể đối với tôi là 1 lỗi trầm trọng, ko biết với các bạn thì đó có phải là 1 lỗi hay ko. Tôi bắt lỗi phần mềm hơi chặt (chỉ sai chính tả thôi tôi cũng coi đó là 1 lỗi ko thể chấp nhận được rồi).

Tương tự như vậy, khi vào form báo cáo, 2 ô Từ ngày, Đến ngày cần phải có giá trị mặc định (đầu kỳ hiện thời tới ngày hôm nay hoặc ngày cuối kỳ). Cũng ở màn hình Báo cáo này, lựa chọn (Option) Nhập/Xuất lại ko được mặc định lựa chọn. Vả lại, các báo cáo nhập/xuất này chưa mang tính tổng quát. Có thể yêu cầu chỗ bạn chỉ đơn giản là xem báo cáo NX của từng đối tượng cụ thể chứ ko xem báo cáo dạng Tổng hợp/Chi tiết.

Ý kiến này có vẻ làm bạn sẽ phật lòng đây: Việc nhập dữ liệu có vẻ ko được logic và hợp lý. Tớ vào form Nhập hàng mà chả biết thế nào là thêm mới 1 chứng từ, thế nào là sửa chứng từ, cập nhật là gì, v.v... và sau khi thêm mới/sửa thì nó lưu vào đâu (tức là danh sách chứng từ ở đâu) và xem lại chứng từ bằng cách nào.

Bạn thử suy nghĩ cách làm như thế này xem:
1/ Màn hình "Hiển thị danh sách":
a. Định nghĩa: Đây là 1 đối tượng (nôm na gọi là Form đi cho đơn giản, còn tớ thì viết hẳn thành 1 object) cho phép list tất cả các đối tượng cần quản lý trong hệ thống như: Các danh mục, các chứng từ....
b. Các actions trên màn hình hiển thị danh sách:
- Thêm mới: Gọi 1 form (màn hình "chi tiết") để nhập mới đối tượng tương ứng
- Sửa: Gọi 1 form (màn hình "chi tiết") để sửa một đối tượng hiện thời trong màn hình danh sách
- Xóa: Xóa đối tượng hiện thời trong màn hình danh sách
- Lọc: Cho phép lọc theo tất cả các thông tin liên quan tới đối tượng được hiển thị
- In: Cho phép in danh sách đối tượng hiện thời (kể cả sau khi lọc)
c. Chú ý:
- Các nút Sửa/Xóa/In phải mờ đi (Enable = false) khi danh sách rỗng (Rows = 1 - include header row)
- Không refresh cả danh sách sau khi thêm mới hoặc sửa đối tượng mà chỉ insert/update current row

2. Màn hình "chi tiết"

Thôi tớ phải làm cho khách hàng cái đã, lúc nào rảnh viết tiếp

P/S: Có lẽ tớ chỉ xem qua về phần mềm đến thế thôi. Nhưng tớ khuyên bạn là: Hãy nên tham khảo các phần mềm có tính thương mại xem cách họ bố trí như thế nào (tốt nhất nên xem các PM của nước ngoài ấy).
 

File đính kèm

  • Combo Error.jpg
    Combo Error.jpg
    80.8 KB · Đọc: 433
quangiang đã viết:
Xin chào các bạn . Tôi cũng viết một chương trình nhỏ về quản lý vật tư hiện đang áp dụng trong công ty . xin gởi lên cho các bạn góp ý .

- Nút "thoát" chỉ được sử dụng khi thoát hẳn khỏi chương trình, còn nếu chỉ Close 1 cái cửa sổ thì người ta dùng "Đóng".
- Khi sửa 1 danh mục (mặt hàng chẳng hạn) thì lại báo trùng mã. Sửa đúng danh mục đó chứ có phải thêm mới đâu (Lúc check trùng mã thì phải chừa cái mã đang sửa ra).
- Các chứng từ ko có dạng Master/Detail. --> KO hợp lý rồi.
 
Trước hết mình xin cảm ơn smbsolutions đã có những đóng góp rất thiết thực dưới góc độ nhà chuyên môn. vungoc cũng nên tạo bẫy lỗi và kiểm tra trước nhất là đối với xóa dòng dễ dẫn đến lỗi. Thật tình mình thấy file này giao diện rất đẹp ( Không sến) và xử lý nhanh. Mình chưa có điều kiện xem hết nhưng thấy các báo cáo bạn chưa dùng lệnh dùng màn hình vì vậy vẫn còn giật khi thực hiện lệnh. Và mình không biết trở lại form màn hình chính thế nào khi đã vào sheet. Thứ hai là các form dùng cho màn hình >=15"" chứ màn hình 14" xem như chịu chết các nút lệnh phía dưới form không thực hiện được. Chúc bạn thành công.
 
Cải thiện về tốc độ lập báo cáo

Cảm ơn bác Vungoc đã gửi tặng thêm cho GPE một ứng dụng kinh tế vầ rất thực tế. Đã có nhiều bài góp ý về bố cục, giao diện của ứng dụng rồi. Mình xin góp ý thêm về tốc độ thực hiện của chương trình, đặc biệt trong báo cáo.

Hiện nay chương trình có một báo cáo "BÁO CÁO TỔNG HỢP NHẬP - XUẤT - TỒN PHỤ TÙNG" mình thấy tốc độ thực hiện quá chậm, số bản ghi của sheet DATA chỉ mới có 190 thôi, khi làm thực tế thì con số này phải gấp 100 lần (vẫn còn ít).

Có 3 nguyên nhân chính làm cho thủ tục báo cáo chạy chậm:
1) Do Name động
Trong tất cả các name dùng trong công thức (SumProduct) đều là name động

Mã:
DataSo=COUNTA(DATA!$B$1:$B$65000)
DataNgay=OFFSET(DATA!$C$2,0,0,DataSo,1)
DataPhieu=OFFSET(DATA!$B$2,0,0,DataSo,1)
...
Data*==OFFSET(DATA!$***$2,0,0,DataSo,1)

Cứ khi trong công thức dùng tới các name DataNgay, DataPhieu,...thì đều phải chạy hàm COUNTA (vì trong name có tham chiếu DataSo=COUNTA(DATA!$B$1:$B$65000) ). Với một bảng tính nhỏ, , ít dữ liệu, ít tính toán thống kê thì việc tạo name này rất linh động và thuận tiện nhưng với một Chương trình quản trị dữ liệu thì đây là giải pháp thực sự là không tốt.

2) Chưa ngăn các sự kiện của Workbook và Sheet
Mỗi một lệnh Range().Select hay gán giá trị Range().Value thì một số các sự kiện của Excel sẽ chạy vì vậy cần phải chặn chúng lại khi không cần thiết.
Để thực hiện, dùng theo cách dưới đây:
Mã:
Sub ThuTuc
On Error Goto EndSub
   Application.EnableEvents = False
   ....
   Các lệnh
   ....
   'Cuối thủ tục
   '
EndSub:
   Application.EnableEvents = True
   If Err <> 0 Then MsgBox Err.Description, vbCritical, "Error: THNXT " & Err.Number
End Sub


3) Dư thừa công thức - Nguyên nhân chính
Trong sổ tổng hợp Vungoc lập công thức tính cho cả 1094 mã phụ tùng. Số cột phải tính là 8:
TDK_SLG|TDK_Giatri|Nhap_SLG|Nhap_Giatri|Xuat_SLG|Xuat_Giatri|TCK_SLG|TCK_Giatri|
6 cột đầu công thức dùng hàm SumProduct(tính trong một vùng DATA có 190 dòng). Số công thức báo cáo của Vungoc phải thực hiện là 8*1094 = 8752 (tính một cách tương đối).
-->Thực tế trong sheet DATA, số mã phụ tùng chỉ có 31 mã khác nhau. Như vậy, báo cáo chỉ phải tính số công thức là 8*31=248 .
-->Theo phân tích trên thì số công thức đã tính bị thừa là 8752-248 = 8504:=\+


Mình đã viết thêm một số hàm và thủ tục phục vụ cho việc lập báo cáo trong Excel, viết lại thủ tục THXNT_ext tốc độ tăng nhanh hơn thủ tục THXNT trước đây gấp nhiều lần (~+58.29 lần).

Thêm:
Sub CreateDataName() 'Tạo tên cho DATA, thủ tục này chạy trong nút "Save" của 2 form FrNhap, FrXuat
Function GetEndRow(ByVal Sh As Worksheet, Optional ByVal ColumnBase As String = "A") As Long 'Xác định dòng cuối
Sub PasteIDs(ByVal FromRange As Range, ByVal CellToPaste As Range) 'Nhận danh sách duy nhất
Sub THXNT_ext() 'Tính nhập xuất tồn


Chúc cho chương trình của bác Vungoc ngày càng hoàn thiện!
 

File đính kèm

  • Quan ly kho phu tung (Ver02)24-10-07_Tuan.zip
    401.5 KB · Đọc: 1,013
Lần chỉnh sửa cuối:
TuanVNUNI đã viết:
3) Dư thừa công thức - Nguyên nhân chính
Trong sổ tổng hợp Vungoc lập công thức tính cho cả 1094 mã phụ tùng. Số cột phải tính là 8:
TDK_SLG|TDK_Giatri|Nhap_SLG|Nhap_Giatri|Xuat_SLG|Xuat_Giatri|TCK_SLG|TCK_Giatri|
6 cột đầu công thức dùng hàm SumProduct(tính trong một vùng DATA có 190 dòng). Số công thức báo cáo của Vungoc phải thực hiện là 8*1094 = 8752 (tính một cách tương đối).
-->Thực tế trong sheet DATA, số mã phụ tùng chỉ có 31 mã khác nhau. Như vậy, báo cáo chỉ phải tính số công thức là 8*31=248 .
-->Theo phân tích trên thì số công thức đã tính bị thừa là 8752-248 = 8504
Chúc cho chương trình của bác Vungoc ngày càng hoàn thiện!
Vâng, quả thực là nhanh hơn thật.
Em mới xem qua Sheet TH XNT và có một số góp ý thế này :
  • Các giá trị bác nên copy dán giá trị lên thôi, ta không nên để công thức "sống" như thế. Dung lượng File sẽ tăng và việc lưu cũng như tính toán sẽ chậm đi (Khi kích hoạt sự kiện tính toán cả File)
  • Việc dùng AF để lọc ra danh mục duy nhất làm tăng tốc độ tính toán rất nhiều. Tuy nhiên đây cũng chính là yếu điểm của nó :
    • Tiêu đề cột trên báo cáo phải giống trên Data : Mã Phụ Tùng
    • Báo cáo chỉ tính toán được các mã có phát sinh (trong Data) mà lại không tính toán được các mã không phát sinh nhưng lại có số dư - Đây là cái chính yếu
  • Mã cuối cùng luôn bị xóa đi (cái này thì đơn giản, tăng thêm 1 dòng thôi)
  • Chưa có ghi ra bộ phận trong cột ghi chú (cũng đơn giản, chỉ thêm một công thức nữa thôi)
Cảm ơn bác nhiều!!

Thân!
 
Lần chỉnh sửa cuối:
TuanVNUNI [B đã viết:
1) Do Name động[/B]
Trong tất cả các name dùng trong công thức (SumProduct) đều là name động

Mã:
DataSo=COUNTA(DATA!$B$1:$B$65000)
DataNgay=OFFSET(DATA!$C$2,0,0,DataSo,1)
DataPhieu=OFFSET(DATA!$B$2,0,0,DataSo,1)
...
Data*==OFFSET(DATA!$***$2,0,0,DataSo,1)
Cứ khi trong công thức dùng tới các name DataNgay, DataPhieu,...thì đều phải chạy hàm COUNTA (vì trong name có tham chiếu DataSo=COUNTA(DATA!$B$1:$B$65000) ). Với một bảng tính nhỏ, , ít dữ liệu, ít tính toán thống kê thì việc tạo name này rất linh động và thuận tiện nhưng với một Chương trình quản trị dữ liệu thì đây là giải pháp thực sự là không tốt.

Riêng cái này (dùng công thức cho các bảng tổng hợp, báo cáo ) mong bác cho giải pháp ???
Chạy vòng lặp (chạy 1 lần cả Data) thì nhanh hơn, tuy nhiên lập trình vất vả hơn, hơn nữa mỗi lần thay đổi thì sửa oải lắm.--=0--=0
Mong bác cho giải pháp!!!

Thân!
 
Các giá trị bác nên copy dán giá trị lên thôi, ta không nên để công thức "sống" như thế. Dung lượng File sẽ tăng và việc lưu cũng như tính toán sẽ chậm đi (Khi kích hoạt sự kiện tính toán cả File)

Mình cũng quên chưa xoá dấu phảy (') trong đoạn
Mã:
    With Range("A9:N" & i + 2)
        [COLOR="Green"][COLOR="Red"]'[/COLOR].Value = .Value[/COLOR]
    End With

Chỉ cần chữa thành
Mã:
    With Range("A9:N" & i + 2)
        .Value = .Value
    End With

Báo cáo chỉ tính toán được các mã có phát sinh (trong Data) mà lại không tính toán được các mã không phát sinh nhưng lại có số dư - Đây là cái chính yếu

Trong công thức vẫn tính cả tồn đầu và tồn cuối mà.

Riêng cái này (dùng công thức cho các bảng tổng hợp, báo cáo ) mong bác cho giải pháp ???
Chạy vòng lặp (chạy 1 lần cả Data) thì nhanh hơn, tuy nhiên lập trình vất vả hơn, hơn nữa mỗi lần thay đổi thì sửa oải lắm.
Mong bác cho giải pháp!!!

Vùng chỉ thay đổi khi ta thêm/xoá phiếu N/X. Sau mỗi lần như vậy thì cho update lại các Name trong table đó, chạy thủ tục "CreateDataName" cũng nhanh lắm. Nếu giải pháp dùng công thức để lập sổ tổng hợp thì nên dùng Name tĩnh (như mình đã làm trong thủ tục "THNXT_ext". Mình đã nghiên cứu, nếu vẫn các lệnh trong thủ tục "THNXT" trước đay chỉ cần thay Name động (tham chiếu bởi hàm OffSet) thành Name tĩnh thì tốc độ đã tăng gấp đôi!

Giải pháp mà mình đưa ra trong thủ tục "THNXT_ext" là mọt trong những giải pháp tối ưu, có thể áp dụng chung cho tất cả các dạng báo cáo TH mà lại dễ. Tuy nhiên, số mã mã phụ tùng tham gia trong sheet DATA càng nhiều thì tốc độ càng chậm vì vẫn phải tính tổng độc lập cho từng mã. Mình vẫn còn một chiêu nữa còn nhanh hơn cái đã nói ở trên nhiều kể cả khi khối dữ liệu lớn, mình cũng dùng cách "chạy vòng lặp (chạy 1 lần cả Data) " không biết có giống cách của Mr Okebab không? Nhưng quả thật cách này viết code vất vả và tính tổng quát không cao lắm (mình định viết một bài về cái này nhưng vì hơi khó trình bày nên chưa viết).

Để giảm bới việc lập trình khi có sự sửa chữa về CSDL, tất cả cách lệnh phải làm việc trên các biến và hằng. Trong code không nên chỉ rõ cụ thể giá trị mà nên thông qua biến hoặc hằng số.
Ví dụ đơn giản thế này:
Range("A9:N" & EndRow).ClearContents
Range("C9:N"...
.......
Nên viết thành

Const StartRow = 9
....
Range("A" & StartRow & ":N & EndRow).ClearContents

Nếu sau này sửa lại cấu trúc của báo cáo dòng bắt đầu là dòng 11 thì chỉ cần thay Const StartRow = 11 là tất cả chạy đúng theo logic.

Thực tế trong một ứng dụng quản trị thì thời gian để tạo báo cáo sẽ chiếm 2/3 của cả chương trình. Vì vậy, nếu không chọn được một giải pháp tổng quát thì công việc coding thực sự rất vất vả mà hiệu quả sẽ vẫn không cao. Mình hay dùng kỹ thuật T-SQL, hình như ở đâu đó trên diễn đàn mình cũng khuyên mọi người hướng nghiên cứu về cái này nhưng không thấy ai hưởng ứng? Thực sự nó không khó, chỉ là do mọi người ngại phải thay đổi thói quen trước đây mà thôi!
 
TuanVNUNI đã viết:
Trong công thức vẫn tính cả tồn đầu và tồn cuối mà.
Bác đọc lại nhé :

Báo cáo chỉ tính toán được các mã có phát sinh (trong Data) mà lại không tính toán được các mã không phát sinh nhưng lại có số dư
Tức là với mã có mặt trong Data thì OK. Còn mã không (hoặc chưa) có trong Data, nhưng lại có số dư trong Danh Mục thì lại không tính được.




Vùng chỉ thay đổi khi ta thêm/xoá phiếu N/X. Sau mỗi lần như vậy thì cho update lại các Name trong table đó, chạy thủ tục "CreateDataName" cũng nhanh lắm. Nếu giải pháp dùng công thức để lập sổ tổng hợp thì nên dùng Name tĩnh (như mình đã làm trong thủ tục "THNXT_ext". Mình đã nghiên cứu, nếu vẫn các lệnh trong thủ tục "THNXT" trước đay chỉ cần thay Name động (tham chiếu bởi hàm OffSet) thành Name tĩnh thì tốc độ đã tăng gấp đôi!
Cái này có lẽ là vậy, để em thử xem sao.



Giải pháp mà mình đưa ra trong thủ tục "THNXT_ext" là mọt trong những giải pháp tối ưu, có thể áp dụng chung cho tất cả các dạng báo cáo TH mà lại dễ. Tuy nhiên, số mã mã phụ tùng tham gia trong sheet DATA càng nhiều thì tốc độ càng chậm vì vẫn phải tính tổng độc lập cho từng mã. Mình vẫn còn một chiêu nữa còn nhanh hơn cái đã nói ở trên nhiều kể cả khi khối dữ liệu lớn, mình cũng dùng cách "chạy vòng lặp (chạy 1 lần cả Data) " không biết có giống cách của Mr Okebab không? Nhưng quả thật cách này viết code vất vả và tính tổng quát không cao lắm (mình định viết một bài về cái này nhưng vì hơi khó trình bày nên chưa viết).
Chắc là giống thôi (ở cách làm, còn từng dòng code thì . . . em sao bằng bác được!!--=0)



Để giảm bới việc lập trình khi có sự sửa chữa về CSDL, tất cả cách lệnh phải làm việc trên các biến và hằng. Trong code không nên chỉ rõ cụ thể giá trị mà nên thông qua biến hoặc hằng số.
Ví dụ đơn giản thế này:
Range("A9:N" & EndRow).ClearContents
Range("C9:N"...
.......
Nên viết thành

Const StartRow = 9
....
Range("A" & StartRow & ":N & EndRow).ClearContents

Nếu sau này sửa lại cấu trúc của báo cáo dòng bắt đầu là dòng 11 thì chỉ cần thay Const StartRow = 11 là tất cả chạy đúng theo logic.
Thay vì số 9, lại viết StartRow, có lẽ dài dòng hơn!!!:=\+




Thực tế trong một ứng dụng quản trị thì thời gian để tạo báo cáo sẽ chiếm 2/3 của cả chương trình. Vì vậy, nếu không chọn được một giải pháp tổng quát thì công việc coding thực sự rất vất vả mà hiệu quả sẽ vẫn không cao. Mình hay dùng kỹ thuật T-SQL, hình như ở đâu đó trên diễn đàn mình cũng khuyên mọi người hướng nghiên cứu về cái này nhưng không thấy ai hưởng ứng? Thực sự nó không khó, chỉ là do mọi người ngại phải thay đổi thói quen trước đây mà thôi!
Em hoàn toàn nhất trí và sẽ cố gắng!!!

Cảm ơn bác .

Thân!
 
Tức là với mã có mặt trong Data thì OK. Còn mã không (hoặc chưa) có trong Data, nhưng lại có số dư trong Danh Mục thì lại không tính được.

Theo cách làm của mình thì số dư đầu để trong DATA chứ không để trong danh mục, làm vậy dữ liệu tập trung dễ dàng cho việc tổng hợp. Trong file mình gửi đã chuyển số dư trong danh muc vào sheet DATA với ngày 31/13/06 (dư đầu của năm 2007). Cơ sở phân biệt Dư đầu, Phát sinh là theo ngày tháng.


Chắc là giống thôi (ở cách làm, còn từng dòng code thì . . . em sao bằng bác được!!)

Mr Okebab khiêm tốn quá! Khi viết bài góp ý trên, mình chỉ nhắm đưa ra giải pháp về tốc độ để mọi người tham khảo chứ Không có ý so sánh hơn thấp. Không dám đâu!:=\+

Thay vì số 9, lại viết StartRow, có lẽ dài dòng hơn!!!

Nếu viétcode dài nhưng nó đảm bảo hệ thống chặt chẽ thì vẫn nên làm.
 
Chữ đỏ là của em !!! (Chiêu này học bác Vũ Ngọc)--=0--=0

TuanVNUNI đã viết:
Theo cách làm của mình thì số dư đầu để trong DATA chứ không để trong danh mục, làm vậy dữ liệu tập trung dễ dàng cho việc tổng hợp. Trong file mình gửi đã chuyển số dư trong danh muc vào sheet DATA với ngày 31/13/06 (dư đầu của năm 2007). Cơ sở phân biệt Dư đầu, Phát sinh là theo ngày tháng.

Vâng, đây cũng là giải pháp của em khi có số dư bên danh mục. Giả sử ngày bắt đầu là ngày 1/1/07 thì lấy hết số dư đầu nhập vào Data vào ngày 31/12/2006. Như vậy sẽ tiện theo dõi hơn.
Còn cái ngày 31/13/06 : Em tra lịch hoài mà tìm không thấy!!--=0--=0



Mr Okebab khiêm tốn quá! Khi viết bài góp ý trên, mình chỉ nhắm đưa ra giải pháp về tốc độ để mọi người tham khảo chứ Không có ý so sánh hơn thấp. Không dám đâu!:=\+

Em lại được ngồi trên vai người khổng lồ rồi. Sướng ghê!!}}}}}}}}}}



Nếu viétcode dài nhưng nó đảm bảo hệ thống chặt chẽ thì vẫn nên làm.
Em lại chưa thấy chặt chẽ hơn chỗ nào cả. Con số 9 hay 11 hay 20 chỉ là 1 hằng số, mỗi Sub sử dụng 1 lần, thay vì phải : StartRow = 9 thì em cho = 9 luôn, thế thì hệ thống ảnh hưởng gì nhỉ.
Quan điểm của em là : Nhanh (về tốc độ, về viết code), đơn giản, hiệu quả và không gây lỗi.
Có thể do trình độ nên em chỉ nhìn được đến thế thôi, mong bác chỉ giáo thêm.

Cảm ơn bác nhiều.

Thân!
 
Cảm ơn sự đóng góp rất cụ thể và rất giá trị của bác TuanVNUNIMr.Okebab ***&&%_)(#;

Mình xin ghi nhận ý kiến của bác để cải tiến chương trình ngày càng hoàn thiện hơn.

Xin cảm ơn 2 bác / Chúc sức khỏe và thành công !
(Mong sớm gặp lại)

Thân mến !
 
Lần chỉnh sửa cuối:
Không biết tôi có làm sai chỗ nào không mà sao nó chạy nhanh lắm, nhanh hơn cách của Tuân luôn.
Add 1 sh và tên là S99 (tmp)
Copy dữ liệu thỏa dk vào S99. Đặt tên name, dùng Sumif lấy kết quả, xóa name.
Vũ Ngọc tham khảo.
PHP:
Option Explicit
    Dim HC As Long, ActSh As Worksheet
    Dim i As Long, j As Long
    Dim NgayDau As Date, NgayCuoi As Date
    Dim T1, T2
    Dim DuLieu As Range, MaPTBC As Range, NgayCT As Range, MaHang As Range
Sub TaoNXT()

Set ActSh = S99
ActSh.Visible = False
    With Application
        .ScreenUpdating = False
        .Calculation = xlCalculationManual
        .EnableEvents = False
    End With
    T1 = timeGetTime
    S109.Select
    With S109
        .Range("A9:O5000").ClearContents
        With Range("A10:O5000")
        Call NLine
        End With
    End With
    If S104.Range("B2") = "" Then
        MsgBox "No Data"
    Else 'chac chan data co du lieu
        HC = S104.Range("B1").End(xlDown).Row - 1
    End If
    NgayDau = S109.Range("G5").Value
    NgayCuoi = S109.Range("I5").Value
    'MsgBox HC
    With ActSh 'lay dulieu qua tmp
        .Range("A1:L1000").ClearContents
        .Range("A1:B" & HC + 1).Value2 = S104.Range("B1:C" & HC + 1).Value2
        .Range("C1:C" & HC + 1).Value = S104.Range("J1:J" & HC + 1).Value
         'Gan SL + Tien NX
        .Range("D1") = "SLNhap"
        .Range("E1") = "SLXuat"
        .Range("F1") = "GTNhap"
        .Range("G1") = "GTXuat"
        .Range("H1") = "SLTonDau"
        .Range("I1") = "GTTonDau"
         For i = 2 To HC + 1
        If .Range("B" & i).Value >= NgayDau And .Range("B" & i).Value <= NgayCuoi Then
            If Left(.Range("A" & i).Value, 2) = "PN" Then
                .Range("D" & i) = S104.Range("K" & i)
                .Range("F" & i) = S104.Range("M" & i)
            Else
                .Range("E" & i) = S104.Range("K" & i)
                .Range("G" & i) = S104.Range("M" & i)
            End If
        ElseIf .Range("B" & i).Value < NgayDau Then
            If Left(.Range("A" & i).Value, 2) = "PN" Then
                .Range("H" & i) = S104.Range("K" & i)
                .Range("I" & i) = S104.Range("M" & i)
            Else
                .Range("H" & i) = -S104.Range("K" & i)
                .Range("I" & i) = -S104.Range("M" & i)
            End If
        End If
    Next i
    End With
    Set DuLieu = ActSh.Range("A2:I" & HC + 1)
    'Sort lai du lieu
    With DuLieu
       .Sort Key1:=.Range("B2"), Order1:=xlAscending, Key2:=.Range("A2") _
        , Order2:=xlAscending, Header:=xlGuess, OrderCustom:=1, MatchCase:= _
        False, Orientation:=xlTopToBottom, DataOption1:=xlSortNormal, DataOption2 _
        :=xlSortNormal
    End With
    Set NgayCT = ActSh.Range("B2:B" & HC + 1)
    'Xoa nhung du lieu > ngaycuoi
    HC = WorksheetFunction.CountIf(NgayCT, "<=" & CLng(NgayCuoi)) + 1
    If HC > 0 Then
        ActSh.Range("A" & HC + 1 & ":I5000").ClearContents
    End If
    Set MaHang = ActSh.Range("C1:C" & HC + 1)
    With MaHang
        .AdvancedFilter Action:=xlFilterCopy, CopyToRange:=ActSh.Range( _
        "K1"), Unique:=True
    End With
    HC = ActSh.Range("K65000").End(xlUp).Row
    'MsgBox i
    ActSh.Range("K2:K" & HC).Name = "MaPTBC"
    With Range("MaPTBC")
        .Sort Key1:=ActSh.Range("K2"), Order1:=xlAscending, Header:=xlGuess, _
        OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
        DataOption1:=xlSortNormal
    End With
    'Dan vao NXT
    S109.Range("B9:B" & HC + 7).Value = ActSh.Range("MaPTBC").Value
    With ActSh ' Xoa name
        .Range("MaPTBC").ClearContents
        .Range("Extract").Delete
        .Names("Extract").Delete
    End With
    With Application
        .ActiveWorkbook.Names("MaPTBC").Delete
    End With
    HC = ActSh.Range("B65000").End(xlUp).Row
    'MsgBox HC
    'Gan lai hang cuoi cho ton dau ky, dat ten name
    With ActSh
        .Range("D2:D" & HC + 1).Name = "SLNhap"
        .Range("E2:E" & HC + 1).Name = "SLXuat"
        .Range("F2:F" & HC + 1).Name = "GTNhap"
        .Range("G2:G" & HC + 1).Name = "GTXuat"
        .Range("C2:C" & HC + 1).Name = "MaHang"
        .Range("H2:H" & HC + 1).Name = "SLTonDau"
        .Range("I2:I" & HC + 1).Name = "GTTonDau"
    End With
    
    'Dong cuoi cua NXT TH
    i = S109.Range("B65000").End(xlUp).Row
    'Gan so lieu vao NXTTH
    With S109
        .Range("G9:G" & i).FormulaR1C1 = "=SUMIF(MaHang,RC2,SLTonDau)"
        .Range("H9:H" & i).FormulaR1C1 = "=SUMIF(MaHang,RC2,GTTonDau)"
        .Range("I9:I" & i).FormulaR1C1 = "=SUMIF(MaHang,RC2,SLNhap)"
        .Range("J9:J" & i).FormulaR1C1 = "=SUMIF(MaHang,RC2,GTNhap)"
        .Range("K9:K" & i).FormulaR1C1 = "=SUMIF(MaHang,RC2,SLXuat)"
        .Range("L9:L" & i).FormulaR1C1 = "=SUMIF(MaHang,RC2,GTXuat)"
        .Range("M9:N" & HC + 7).FormulaR1C1 = "=RC[-6]+RC[-4]-RC[-2]"
        .Range("A9:A" & HC + 7).FormulaR1C1 = "=IF(SUM(RC7:RC9)>0,1,"""")"
        'gan ten hang va DVT
        With Range("C9:C" & i)
            .FormulaR1C1 = "=VLOOKUP(RC2,DMPTArray,2,0)"
        End With
        With Range("D9:D" & i)
            .FormulaR1C1 = "=VLOOKUP(RC2,DMPTArray,4,0)"
        End With
        With Range("O9:O" & i)
            .FormulaR1C1 = "=VLOOKUP(RC2,DMPTArray,5,0)"
        End With
    End With
     
    With S109.Range("A9:O" & HC + 7)
       .Value = .Value
        .Sort Key1:=.Range("A9"), Order1:=xlAscending, Key2:=.Range( _
            "B9"), Order2:=xlAscending, Header:=xlNo, OrderCustom:=1, MatchCase _
            :=False, Orientation:=xlTopToBottom, DataOption1:=xlSortNormal, _
            DataOption2:=xlSortNormal
    End With

    i = S109.Range("A65000").End(xlUp).Row
    If i < 10 Then i = 10
    S109.Range("A" & i + 1 & ":P" & HC + 7).ClearContents
       
    With Range("A9:A" & i)
        .FormulaR1C1 = "=ROW()-8"
        .Value = .Value
    End With
    With Range("E" & i + 2 & ":N" & i + 2)
        .FormulaR1C1 = "=SUM(R9C:R[-1]C)"
        .Value = .Value
    End With
    Range("C" & i + 2) = UCase("T" & ChrW$(7893) & "ng " & "c" & ChrW$(7897) & "ng")
'    Range("E9:F9").EntireColumn.Hidden = True
    S109.Range("A9:O" & i + 1).Select
    Call YLine
    S109.Range("A" & i + 2 & ":O" & i + 2).Select
    'MsgBox i
    Call YLineTC
    ActSh.Cells.ClearContents
    Range("D5").Select
With Application
    .Names("SLNhap").Delete
    .Names("SLXuat").Delete
    .Names("GTNhap").Delete
    .Names("GTXuat").Delete
    .Names("MaHang").Delete
    .Names("SLTonDau").Delete
    .Names("GTTonDau").Delete
End With
ActSh.Visible = False
With Application
        .ScreenUpdating = False
        .Calculation = xlCalculationManual
        .EnableEvents = False
End With
  
    T2 = timeGetTime
    MsgBox "Thuc hien het " & T2 - T1 & " millisecond(s)"
    
End Sub
 
Theo cách của bác ThuNghi dùng hàm SumIf nhưng công thức bị sai vì không có điều kiện về ngày tháng (từ ngày....đến....?), riêng cái này đã giảm bới điều kiện phân tích của công thức nên nó sẽ chạy nhanh hơn.
Bác ThuNghi thử làm hoàn thiện lại các công thức về SumIf xem thế nào? Mình vẫn tin là dùng SumIf sẽ làm nhanh hơn.
 
Web KT
Back
Top Bottom