Tạo nhanh bảng xác định thời gian bảo vệ và bảng tính tiền

Trước tiên mình rất cảm ơn tuhocvba.net luôn là 1 địa chỉ đáng tin cậy để chia sẻ những thắc mắc, chia sẻ những thành quả trong quá trình tự mày mò VBA và để học hỏi những kiến thức rộng lớn của VBA

Bối cảnh hôm nay của mình: Cần tạo các bảng biểu, gồm:
1 bảng để xác định thời gian cho việc trông xe, bảo vệ nhà sách (mình gọi tắt là thời gian Bảo vệ),
và 1 bảng để thông báo số tiền để yêu cầu khách hàng thanh toán.
Các quy tắc cơ bản và yêu cầu công việc:
+Quy tắc Xác định thời gian bảo vệ: 1 ngày chia 2 ca (ngày 14 h, đêm 10h) ,Thời gian bảo vệ 1 ngày bình thường là 24 tiếng, các ngày nghỉ lễ (ví dụ 30/04,01/05, ngày tết v..v) thời gian bổ sung nhân thêm 2 lần (phần cộng thêm này người làm tự bổ sung theo thực tế), tính tổng thời gian bảo vệ theo tháng
+Quy tắc Xác định số tiền thanh toán: đơn giá chưa tính thuế / 1 h :15.000 đ, Thuế 10%.
+Hàng tháng In ra 3 bản giấy gửi Khách hàng ký xác nhận bảng thời gian và Thông báo tiền
Phương án + ý tưởng:
Nếu như theo khả năng của chính mình với excel cách đây khoảng 2 năm: Thì phương án của mình làm sẽ là tạo ra 1 file xls có 13 sheet, trong đó 12 sheet đầu là đại diện cho 12 tháng và 1 sheet cuối là tính tiền. Và cứ thế đến tháng chốt số thì cập nhập thời gian theo từng sheet tương ứng. Rồi Ctrl+P chọn số lượng bản in, trang cần in.., còn nếu lùi thêm 4 năm trước nữa thì có lẽ là mình đã có tới 12 file xls. :p
Nhưng vui mừng là trên con đường tới VBA, hôm nay mình đã rút gọn chỉ 1 file với 2 sheet,và các thao tác in đều gán cho 1 Button.
Ý tưởng của mình ở đây là: thay vì tạo ra 12 cái bảng ứng với 12 tháng (số lượng cột của từng bảng phụ thuộc vào tên của từng tháng). Ta chỉ cần tạo 1 bảng, có số lượng cột ngày tối đa là 31 cột. Khi tên tháng thay đổi , mà có số ngày < 31ngày (ví dụ tháng 2,4,6...),lúc đó sẽ ẩn bớt (1 hoặc 3) cột
Cách thức:
Sử dụng VBA tạo bảng xác định thời gian bảo vệ:
Sheet1 (Xác định thời gian bảo vệ) : Tạo Vùng List 12 tháng và sử dụng Datavalidation gán cho 1 ô trong bảng tính

1/Gán điều kiện cho sự thay đổi của 1 ô, nếu ô này thay đổi giá trị thì bảng tính sẽ tự động cập nhập
Mã:
'Gan dieu kien neu M3 thay doi gia tri
If Not Intersect(Range("M3"), Target) Is Nothing Then
2/Refress vùng nội dung của bảng tính
Mã:
'Refress Xoa du lieu C6:AH10, Unhide cac cot tu A:AK
Range("c6:Ah10").ClearContents
Columns("A:AK").Select
Range("AK1").Activate
Selection.EntireColumn.Hidden = False
3/Sử dụng For...to Step 1 tạo các cập nhập:
+điền ngày bảo vệ từ ngày 1 đến ngày 31,
+điền thời gian bảo vệ theo quy tắc cơ bản cho từng ngày (14h và 10 h)
+điền công thức tính tổng cho từng ngày (24h cho 1 ngày)
Mã:
For i = 3 To 33 Step 1
Cells(6, i).Value = i - 2 ' Dien so ngay tu 1 - 31 bat dau tu (C6) den (AG6)
Cells(7, i).Value = 14 ' Gan gia tri 14 bat dau tu (C7) den (AG7)
Cells(8, i).Value = 10 ' Gan gia tri 10 bat dau tu (C8) den (AG8)
Cells(10, i).FormulaR1C1 = "=R[-3]C+R[-2]C+R[-1]C" ' Gan cong thuc tu (C10) den (AG10)
Next i
3/Sử dụng Tham chiếu Range(Row,Col) tạo công thức cho các cột tính tổng thời gian
Mã:
Range("AH6").Value = "T" & ChrW(7893) & "ng s" & ChrW(7889) & " gi" & ChrW(7901) 'Viet noi dung cho AH6'
Range("AH7").Formula = "=sum(C7:AG7)" 'Gan cong thuc sum cho AH7
Range("AH8").Formula = "=sum(C8:AG8)"  'Gan cong thuc sum cho AH8
Range("AH9").Formula = "=sum(C9:AG9)"   'Gan cong thuc sum cho AH9
Range("AH10").Formula = "=sum(C10:AG10)"  'Gan cong thuc sum cho AH10
4/Gán biến cho tham số cột cần ẩn
Mã:
'Xac dinh cot hide
a = Range("AM1").Value
b = Range("AN1").Value
'Xoa du lieu va Hide cac cot khai bao bien a, b
Range(a & "6:" & b & "10").Select
Selection.ClearContents
Columns(a & ":" & b).Hidden = True
Sử dụng VBA gán Button thao tác in 3 bản
Xác định chỉ số trên cùng; dưới cùng của trang cần in
Gán biến cho các chỉ số
Đưa các biến vào Marco in
Mã:
 Dim t1 As Integer
    Dim t2 As Integer
'Code in trang theo khai bao bien t1,t2
    t1 = Range("I1").Value 'o dau tien cua trang can in
    t2 = Range("I2").Value 'o cuoi cung cua trang can in
'Gan bien t1, t2 vao macro Print cua VBA
  Range(Cells(t1, 1), Cells(t2, 7)).PrintOut , Copies:=3, Preview:=True, Collate _
       :=True
Sheet2 (Thông báo số tiền) : Sheet này mình chỉ đơn thuần sử dụng DataValidation List kết hợp các hàm nhẹ như Vlookup; hàm nối chuỗi, Copy lại code tạo bản in tương tự như ở Sheet1

Như vậy là với VBA khi áp dụng cho công việc này; mỗi khi đến kỳ cần chốt số với khách hàng thao tác của mình sẽ đơn giản gồm những cú Click chuột, 4 bước tuần tự:

1/ Tại ô M3. Lựa chọn kỳ cần xác định thời gian bảo vệ
2/Điều chỉnh lại thời gian các ngày cho phù hợp với thực tế và các ngày nghỉ lễ
(ví dụ 30/04, 01/05 , 02/09 v...v thời gian nhân đôi)
3/ Sang sheet Thong bao, tại ô A3 Lựa chọn kỳ theo đúng kỳ của sheet 2019
4/ Nếu Cần in Click nút In (3 bản)
Đây là giao diện của 2 sheet tương ứng với bảng thời gian bảo vệ và bảng thông báo số tiền
Mình gửi link file ở đây:
Mình tạo bài viết này hoàn toàn với mục đích chia sẻ và cũng rất mong nhận được thêm góp ý từ các thành viên.
(Trong lúc làm việc với VBA trên bảng tính này, do mình cũng chưa hiểu lắm nên có 1 đoạn code mình bị mất kha khá thời gian để xử lý, nhưng Do thời gian có hạn, mình sẽ chia sẻ ý này sau)
Chúc các anh, các chị, các bạn cuối tuần vui vẻ
 
Sửa lần cuối:

tuhocvba

Administrator
Thành viên BQT
Cám ơn @Nguyễn Hồng Quang . Bài viết trình bày dễ hiểu, người đọc không chỉ được khái quát về mục đích mà còn được bạn tái hiện các điểm nhấn của file.
Bạn cần đăng nhập để thấy hình ảnh


Bạn cần đăng nhập để thấy hình ảnh


Mình có thắc mắc chút là ngày công của các ngày thứ 7, CN có khác ngày bình thường không, và nếu có thì macro sẽ tính toán dựa trên thông tin nào?
Trên file này, mình nghĩ có thể làm đẹp hơn nữa bằng cách thể hiện ngày thứ 7 hoặc chủ nhật, bằng cách tô xám cột có ngày T7 hoặc CN (từ dòng 6 tới dòng 10).
Bài viết của bạn luôn dễ hiểu, mình rất thích đọc các bài viết của bạn.
 
Bài viết trình bày dễ hiểu quá, cám ơn anh. Anh có thể kiểm tra lại file được không ạ? Em download không được.
Cảm ơn bạn đã qua tâm tới bài của mình
Mình đã sửa lại bài viết (link lại đường dẫn).
Rất vui được quen biết , học hỏi từ các bạn trên diễn đàn tuhocvba.net
 

giaiphapvba

Administrator
Thành viên BQT
Anh làm file đẹp quá. Nhìn dễ hiểu, gọn gàng, có hướng dẫn sử dụng. Em thấy chuyên nghiệp. Cái này chắc phải đầu tư nhiều thời gian lắm đúng không ạ.
Anh kiểm tra lại file xem để chế độ share chưa, em download không được ạ.
 
Cám ơn @Nguyễn Hồng Quang . Bài viết trình bày dễ hiểu, người đọc không chỉ được khái quát về mục đích mà còn được bạn tái hiện các điểm nhấn của file.
Bạn cần đăng nhập để thấy hình ảnh


Bạn cần đăng nhập để thấy hình ảnh


Mình có thắc mắc chút là ngày công của các ngày thứ 7, CN có khác ngày bình thường không, và nếu có thì macro sẽ tính toán dựa trên thông tin nào?
Trên file này, mình nghĩ có thể làm đẹp hơn nữa bằng cách thể hiện ngày thứ 7 hoặc chủ nhật, bằng cách tô xám cột có ngày T7 hoặc CN (từ dòng 6 tới dòng 10).
Bài viết của bạn luôn dễ hiểu, mình rất thích đọc các bài viết của bạn.
Cảm ơn bạn. Mình đã sửa lại link
Ngày thứ 7, CN bên mình không phân biệt bạn à. Vì bên mình thực hiện dịch vụ trông xe, bảo vệ cho nhà sách mà nhà sách họ hoạt động bán hàng liên tục tất cả các ngày trong tuần. Nhưng nếu có thể bôi được màu ngày thứ 7, Chủ Nhật thì Bảng biểu trông cũng dễ nhìn và chuyên nghiệp hơn.
Ngoài ra còn 1 vấn đề mình còn hơi lăn tăn. Là khi duy trì file này cho nhiều năm (ví dụ mỗi năm tạo 1 file), thì nếu đụng phải năm nào mà có tháng 2 có 29 ngày, thì mình chưa tính đến được trong code
Mong bạn hỗ trợ thêm
 
Sửa lần cuối:
Một bài viết chuyên nghiệp, dễ hiểu. Mình rất tò mò muốn download file về nhưng cũng chưa download được @Nguyễn Hồng Quang ơi.
Cảm ơn bạn. Mình đã sửa lại link rồi bạn nhé. Trong file mình có để tùm lum nhiều ý tưởng , nhưng cũng đã ẩn đi rồi. Vì Trong lúc làm có gặp 1 tình huống lạ so với kiến thức của mình nên mình cũng tính xoay cách nọ cách kia. Để khi nào mình có thời gian mình sẽ chia sẻ cái tình huống ấy.
 

tuhocvba

Administrator
Thành viên BQT
Cám ơn @Nguyễn Hồng Quang . Đầu tiên chúng ta sẽ cùng nhau thảo luận một chút.
Nội dung thảo luận: Mình rất quan tâm tới việc bạn xử lý tính toán các tháng có 28, 30 hay 31 ngày như thế nào.
Bạn sử dụng công thức như thế nào đó, mà mình chưa hiểu. Vậy nếu có thời gian, bạn thuyết minh về điều này được không?

Dưới đây là phương án mình đưa ra để giải quyết.
Bài toán: Tháng 7/2019 có bao nhiêu ngày?
Cách nghĩ: Ta sẽ lấy d (khai báo là date) gán bằng ngày đầu tiên của tháng 8: d = 1/8/2019.
Sau đó ta lấy d-1 tức là lùi một ngày, ta quay về ngày cuối cùng của tháng 7. Ví dụ: 31/7/2019
Ta lấy day(d) sẽ trả kết quả là ngày = 31.
Mã:
'Input: 1/8/2019
'Output: Tra ket qua la ngay cuoi cung cua thang 7
Function ngay(ByVal d1 As String) As Integer
    Dim d2 As String
    Dim d As Date
    d2 = Format(d1, "dd/mm/yyyy")
    d = CDate(d2)
    d = d - 1
    ngay = Val(CStr(Day(CDate(d))))
    'Cdate: Convert string to date
    'Cstr: Convert to string
    'Val : convert string to number
End Function
Sub test()
    MsgBox ngay("1/8/2019")
End Sub
Sửa lại code lần thứ 1 vào lúc 11h giờ Việt Nam 17/8/2019
 
Cám ơn @Nguyễn Hồng Quang . Đầu tiên chúng ta sẽ cùng nhau thảo luận một chút.
Nội dung thảo luận: Mình rất quan tâm tới việc bạn xử lý tính toán các tháng có 28, 30 hay 31 ngày như thế nào.
Bạn sử dụng công thức như thế nào đó, mà mình chưa hiểu. Vậy nếu có thời gian, bạn thuyết minh về điều này được không?
Vấn đề xác định 1 tháng X có 28,30,31:
Để xử lý vấn đề này. Mình sử dụng cách tạo Trong bảng tính 1 vùng chứa các tham số sẵn có để VBA lấy dữ liệu, trong vùng này mình đã nhập sẵn mặc định các giá trị để sao cho khi lựa chọn đến tháng X thì VBA sẽ lấy tham số tương ứng. Chỉ có hơi khác 1 chút là thay vì nhập số ngày 28,30,31 thì mình nhập hiệu số của 31-28,31-30. Và mã hóa cái hiệu số này thành tên các cột ví dụ hiệu số = 3 thì mã hóa thành AE và AG, ví dụ hiệu số = 1 thì mã hóa thành AG và AG
Vì như mình đã trình bày #1 ở đoạn Phương án+ Ý tưởng:
Ý tưởng của mình ở đây là: .....................tạo 1 bảng, có số lượng cột ngày tối đa là 31 cột. Khi tên tháng thay đổi , mà có số ngày < 31ngày (ví dụ tháng 2,4,6...),lúc đó sẽ ẩn bớt (1 hoặc 3) cột
Mình xin diễn giải rõ thêm ý này:
Trong bảng mặc định (default) , đối với vùng để hiện thị ngày sẽ luôn luôn có 31 cột (từ cột C đến cột AG). và khi Ví dụ nếu người dùng chọn tháng 2 , như vậy là có 28 ngày, hiệu số của 31 - 28 là = 3, tức là bảng tính lúc này sẽ điều chỉnh để ẩn đi 3 cột và chỉ được hiện 28 cột (các cột AE,AH,AG sẽ bị xóa dữ liệu và bị ẩn)
Và để cho VBA thực hiện thao tác ẩn bớt 3 cột AE,AH, AG, ở vùng tham chiếu dòng AM1 có value là AE, dòng AN2 có value là AG
Code trong VBA:
Mã:
a = Range("AM1").Value
b = Range("AN1").Value
Range(a & "6:" & b & "10").Select
Selection.ClearContents
Columns(a & ":" & b).Hidden = True
Với AM1, và AN1 đã có dữ liệu thì Lúc này Code diễn tả nôm na sẽ là
Mã:
Range("AE6:AG10").Select
Selection.ClearContents
Columns(AE:AG).Hidden = True
 
Sửa lần cuối:

tuhocvba

Administrator
Thành viên BQT
Cám ơn @Nguyễn Hồng Quang . Vậy nếu có thể hãy sử dụng phương án trên của mình, nó sẽ giúp bớt thao tác tạo bảng tính toán sẵn tháng nào có bao nhiêu ngày cho bạn.

Bây giờ ta giải quyết bài toán 2 nhé.
Nội dung: Xác định một ngày là thứ 7 hay chủ nhật:
Mã:
'Input: 17/8/2019
'Output: Thu 7  => output = 7

'Input: 18/7/2019
'Output: Chu nhat => Output = 1
Function kiemtrathu7_or_cn(ByVal d1 As String) As Integer
    Dim d2 As String
    Dim d As Date
    d2 = Format(d1, "dd/mm/yyyy") 'ngay thang nam. Ex: 07/08/2019
    d = CDate(d2)
    kiemtrathu7_or_cn = Weekday(d, vbSunday) 'Ngày bắt đầu của tuần thiết định là chủ nhật.
  
    
    'Cdate: Convert string to date
    'Cstr: Convert to string
    'Val : convert string to number
End Function
Sub test2()
    MsgBox kiemtrathu7_or_cn("18/8/2019") 'Nếu là thứ 7 kết quả là 7, nếu là chủ nhật thì kết quả là 1.
End Sub
 

Euler

Administrator
Thành viên BQT
File anh @Nguyễn Hồng Quang đã thiết kế gọn gàng, đẹp mắt. Nên em cũng không có ý kiến đóng góp gì thêm.
1. Như anh @tuhocvba góp ý, thì nếu có thể, anh can thiệp bằng VBA như trên thì sẽ đỡ phải dùng bảng phụ, tránh người dùng can thiệp ngoài ý muốn.
2. Về giao diện file, em thấy đẹp rồi. Nếu có thể, thì anh bỏ đường lưới đi, nhìn đẹp hơn. Anh thử xem nhé.
Bạn cần đăng nhập để thấy hình ảnh
 

vbano1

SMod
Thành viên BQT
Topic phát triển nhanh quá nên không kịp góp ý gì vì đã hết ý kiến. File này được thiết kế tốt, gọn gàng, mình nghĩ khi share ra ngoài như thế này, mọi người có thể tham khảo cách bố trí dữ liệu. Cảm ơn @Nguyễn Hồng Quang
 
Cám ơn @Nguyễn Hồng Quang . Đầu tiên chúng ta sẽ cùng nhau thảo luận một chút.
Nội dung thảo luận: Mình rất quan tâm tới việc bạn xử lý tính toán các tháng có 28, 30 hay 31 ngày như thế nào.
Bạn sử dụng công thức như thế nào đó, mà mình chưa hiểu. Vậy nếu có thời gian, bạn thuyết minh về điều này được không?

Dưới đây là phương án mình đưa ra để giải quyết.
Bài toán: Tháng 7/2019 có bao nhiêu ngày?
Cách nghĩ: Ta sẽ lấy d (khai báo là date) gán bằng ngày đầu tiên của tháng 8: d = 1/8/2019.
Sau đó ta lấy d-1 tức là lùi một ngày, ta quay về ngày cuối cùng của tháng 7. Ví dụ: 31/7/2019
Ta lấy day(d) sẽ trả kết quả là ngày = 31.
Mã:
'Input: 1/8/2019
'Output: Tra ket qua la ngay cuoi cung cua thang 7
Function ngay(ByVal d1 As String) As Integer
    Dim d2 As String
    Dim d As Date
    d2 = Format(d1, "dd/mm/yyyy")
    d = CDate(d2)
    d = d - 1
    ngay = Val(CStr(Day(CDate(d))))
    'Cdate: Convert string to date
    'Cstr: Convert to string
    'Val : convert string to number
End Function
Sub test()
    MsgBox ngay("1/8/2019")
End Sub
Sửa lại code lần thứ 1 vào lúc 11h giờ Việt Nam 17/8/2019
Cảm ơn bạn đã chia sẻ cách xác định số lượng ngày của 1 tháng bằng VBA. Có 1 số hàm mới lạ với mình là hàm Cdate, Cstr, Val (vì mình chưa bao giờ dùng) mình sẽ dành thời gian để đọc code của bạn và lưu tâm cho các phương án xử lý bảng tính
Ngoài ra cách bạn đưa 1 hàm tự tạo vào Msgbox để hiện kết quả, đối với mình là mới. (có lẽ trước đây khi mày mò VBA mình cũng đã thử cho các biến lr xác định lastrow vào Msgbox, nhưng mình không để ý thực ra đó là 1 bước trung gian của việc đưa luôn các hàm vào Msgbox để hiện kết quả) cảm ơn bạn
 

tuhocvba

Administrator
Thành viên BQT
Ừ, hàm tự tạo, bạn cho nó vào module bất kỳ. Và ở trong thủ tục bất kỳ bạn có thể gọi nó.
Ví dụ bạn đang làm việc với tháng 7.
Vậy thì:
Mã:
Dim s as string
Dim i as integer
s ="1/8/2019"
i = hàm_tự_tạo(s) 'Như vậy bạn có thể dùng for next : for j= 1 to i... để tự tạo form cũng như cho hiển thị thứ lên hàng trên sẽ đẹp hơn.
'không chỉ hiển thị ngày, mà ta cung cấp thêm thông tin ở hàng trên (hàng số 5) là thứ mấy
 
Ừ, hàm tự tạo, bạn cho nó vào module bất kỳ. Và ở trong thủ tục bất kỳ bạn có thể gọi nó.
Ví dụ bạn đang làm việc với tháng 7.
Vậy thì:
Mã:
Dim s as string
Dim i as integer
s ="1/8/2019"
i = hàm_tự_tạo(s) 'Như vậy bạn có thể dùng for next : for j= 1 to i... để tự tạo form cũng như cho hiển thị thứ lên hàng trên sẽ đẹp hơn.
'không chỉ hiển thị ngày, mà ta cung cấp thêm thông tin ở hàng trên (hàng số 5) là thứ mấy
Cảm ơn hướng dẫn và gợi ý của bạn. Rất vui vì bữa trước mình có áp dụng VBA để tạo bảng chấm công hàng tháng cho nhân viên, cũng gặp vấn đề về xác định thứ của ngày. Mình sẽ áp dụng hàm của bạn và chia sẻ lên diễn đàn sau.
 

tuhocvba

Administrator
Thành viên BQT
Cảm ơn hướng dẫn và gợi ý của bạn...tạo bảng chấm công hàng tháng cho nhân viên
Không có gì, chỉ là tí tẹo code.
Mong chờ bài viết tiếp theo của bạn, để chúng ta lại được cùng nhau bàn luận sôi nổi, cùng nhau đưa ra những ý tưởng. Hậu bối mai này đọc những topic như thế này, hẳn sẽ rất thích thú.
 
Top