Giới thiệu Cơ bản về vòng lặp For . . . next

Liên hệ QC

ptm0412

Bad Excel Member
Thành viên BQT
Administrator
Tham gia
4/11/07
Bài viết
13,777
Được thích
36,272
Donate (Momo)
Donate
Giới tính
Nam
Nghề nghiệp
Consultant
Nhân có người bạn hỏi về For . . . next, nay mình xin đóng góp những gì mình biết để các bạn chưa biết xem qua.
Trong các ngôn ngữ lập trình mình biết: VBA, VB6, FoxPro, Pascal đều có các cấu trúc vòng lặp. Vòng lặp là 1 cấu trúc chương trình cho phép 1 câu lệnh hoặc 1 nhóm câu lệnh thực hiện 1 số lần có giới hạn. Giới hạn này có thể biết trước và có thể không, nhưng phải có để máy tính ngừng lại khi đủ số lần lặp ấn định trước. Giới hạn này có thể xác định bằng 1 con số cụ thể, 1 con số là kết quả của 1 phép tính, và cũng có thể là 1 điều kiện thoát ra khỏi vòng lặp.
Vòng lặp for là đơn giản và dễ sử dụng hơn so với while do vì nó giới hạn cụ thể số vòng lặp.
Thí dụ: for i = 1 to 10, for i = 1 to len(chuoiA), for i = 0 to k*2 . . .
Như vậy, dòng lệnh nào đặt giữa For và Next sẽ thực hiện n lần, kết quả của dòng lệnh đó sẽ bị thay đổi n lần. Kết quả sau lần thực hiện thứ n mới được dùng cho các dòng lệnh sau cấu trúc For này hoặc là kết quả cuối cùng.
Ta có nhận xét rằng sau 1 vòng, biến i tăng lên 1 cho đến khi bằng số lần quy định.
Vậy vấn đề căn bản của chúng ta là gì?

1. Xác định rằng bài toán phải thực hiện nhiều lần 1 phép tính mới ra kết quả.
2. Xác định số lần tính đó.
3. xác định câu lệnh nào để thực hiện sự tính toán.

THí dụ đơn giản nhất: tính giai thừa của 6:
ta biết n! = 1 x 2 x 3 x.... x n.

1. vậy là thích hợp để dùng For.
2. xác định số lần tính: ta thấy 6! có 5 bài toán nhân. Ta chọn số vòng lặp là n. ta viết for i =1 to 5
3. xác định câu lệnh thực hiện nhân:
a. Phải đặt 1 biến là kq
b. giá trị của kq là giá trị của kết quả trước đó nhân với giá trị hiện tại của i vì i tăng lên sau mỗi vòng lặp, ta lấy luôn i làm thừa số cho phép nhân.
Vậy ta có câu lệnh: kq = kq * i
Đến đây ta phải giả định rằng khi chạy vòng đầu tiên, có trục trặc gì không. Có. Có ở chỗ chưa có giá trị ban đầu của kq nên không nhân đưộc. vậy ta gán giá trị ban đầu của kq là 1:
ta viết kq = 1 ở bên trên For
Thứ hai ta giả định rằng sau 5 vòng lặp giá trị của kq là như thế nào. ta được kq = 1 * 1 * 2 * 3 * 4 * 5
số 1 đỏ là giá trị ban đầu, số 1 đen đến số 5 là 5 giá trị của i, nhân 5 lần là do ta quy định.
Không phải là 6! mà chỉ là 5!. vậy ta sửa lại For i = 1 to 6

Cuối cùng ta có vòng lặp hoàn chỉnh:

kq = 1
For i = 1 to 6
kq = kq * i
next i

Để ứng dụng bài tập này lên Excel, ta cần đưa nó vào giữa cặp Private sub và end sub. Mở 1 Worksheet mới, tại cell A1 gõ vào 1 số bất kỳ để tính giai thừa. Ta muốn kết quả nằm ở cell B1. Ta cũng muốn xem sau 1 vòng tính, giá trị của kq là bao nhiêu nằm lần lượt ở A2, A3, . . .
Bạn đừng chê cái ý muốn này (tính giai thừa trò trẻ ấy mà có gì mà xem), có ích đấy khi bạn thử ở những vòng lặp phức tạp hơn, hãy đi từ dễ đến khó.
Tạo 1 nút lệnh đặt tên là cmb1, double click vào cmb1 vào cửa sổ code chèn vào giữa sub và end sub để có 1 macro hoàn chỉnh như sau:

Private Sub Cmb1_click()
num=range("sheet1!A1").value
Range("sheet1!A1:A100").clear
kq = 1
For i = 1 to num
kq = kq * i
range("sheet1!A1").Offset(i,0).value = kq
next i
range("sheet1!B1").value = kq
end sub


Sau đó trở lại Excel, click nút lệnh xem kết quả.

chú ý range("sheet1!A1").Ofset(i,0).value = kq đặt bên trong For next nên chạy 6 lần hiện lên 6 cell, vị trí quy định bởi Offset

Còn range("sheet1!B1").value = kq đặt ngoài vòng For next nên chỉ chạy 1 lần hiện lên ở 1 cell B1.

Lần sau mình sẽ giới thiệu những thí dụ khác khó dần lên, rồi 2 vòng For lồng nhau.
 
Lần chỉnh sửa cuối:
ý của mình là muốn biết sử dụng các trường hợp đó khi nào sẽ bị khác nhau và bị sai thôi

Cái này là xem lại nha, vì 3 năm nay mình hay sửa cái này và áp dụng rất tốt

Sheet1 là codename đã đuợc đổi tên thành AA

Xem hình và tự suy nghĩ nhé:

Capture.JPG



































Nghĩ xem Sheet1.Range("...") có tương đương với Sheets("Sheet1").Range("...") hay không?
 
Upvote 0
Không bao giờ sai cả vì cách dùng khác nhau, nếu bình thường sheet1 (sheet1) thì
- Dùng codename: sheet1.activate, sheet1.range...
- Dùng name sheets("sheet1").activate...
ý mình muốn là sự khác biệt của nó ấy, chắc rằng là có vì các tiền bối ở đây tiếp xúc rất nhiều nên có nhiều trường hợp mà mình chưa biết, nên muốn học hỏi thêm thôi cảm ơn bạn đã quan tâm vấn đề này nha
 
Upvote 0
Xem hình và tự suy nghĩ nhé:
Nghĩ xem Sheet1.Range("...") có tương đương với Sheets("Sheet1").Range("...") hay không?
dạ cái này em biết mà, em cũng hay đổi tên lại và truy xuất như thế này
Picture2.jpg
Ý em nói là mình truy xuất AA.range..sheets("A").range... là có khác nhau hay không thôi?
 
Upvote 0
dạ cái này em biết mà, em cũng hay đổi tên lại và truy xuất như thế này
View attachment 135139
Ý em nói là mình truy xuất AA.range..sheets("A").range... là có khác nhau hay không thôi?
Không khác nhau nếu đó là file của bạn và bạn biết chắc rằng Sheet CodeName AA chính là sheet có tên là "A"
Nhưng tự nhiên mà nói rằng Sheet1.Range("...") tương đương với Sheets("Sheet1").Range("...") là SAI HOÀN TOÀN
Vậy thôi
 
Lần chỉnh sửa cuối:
Upvote 0
ý của mình là muốn biết sử dụng các trường hợp đó khi nào sẽ bị khác nhau và bị sai thôi

Cái này là xem lại nha, vì 3 năm nay mình hay sửa cái này và áp dụng rất tốt
View attachment 135136
Sheet1 là codename đã đuợc đổi tên thành AA
Đúng là có thể thay đổi trong mục (Name) ở sheet property, sh.codename không có dấu cách, nhưng dùng lệnh thì không gán được.
Còn property name ở dòng dưới là tên hiển thị trên sheet thì có thể có dấu cách (sh.name)
 
Upvote 0
Đúng là có thể thay đổi trong mục (Name) ở sheet property, sh.codename không có dấu cách, nhưng dùng lệnh thì không gán được.

Sao lại không được chứ. Ví dụ:
Mã:
Thisworkbook.VBProject.VBComponents(Thisworkbook.Sheets("Sheet1").CodeName).Name = "NDU"
Với điều kiện phải check mục "Trust Access to VBA project... " trong Excel Options
 
Upvote 0
Range("sheet1!A1").Ofset(i, 0).Value = kq
Bạn sai chỗ đậm đậm đó
Gửi bác QuangHai1969 và bác phihndhsp:
Bởi đoạn code em lấy từ bài bắt đầu của chủ topic mà. Nhưng nếu đoạn code đó sai thì không lẽ không ai phát hiện, và nó đã tồn tại cho đến bây giờ nghĩa là ban đầu là đúng, nên em mới tự đặt câu hỏi là tại sao??? bây giờ em phải thay đổi đoạn code đó cho đúng thì mới chạy được. Em mới bắt đầu nên mù mờ lắm ạ &&&%$R
 
Upvote 0
Gửi bác QuangHai1969 và bác phihndhsp:
Bởi đoạn code em lấy từ bài bắt đầu của chủ topic mà. Nhưng nếu đoạn code đó sai thì không lẽ không ai phát hiện, và nó đã tồn tại cho đến bây giờ nghĩa là ban đầu là đúng, nên em mới tự đặt câu hỏi là tại sao??? bây giờ em phải thay đổi đoạn code đó cho đúng thì mới chạy được. Em mới bắt đầu nên mù mờ lắm ạ
Bài 1 topic này tôi gõ mà không chú ý bộ gõ đang là telex, nên gõ Offset bị biến thành Ofset (1 chữ f). Phải 2 f mới đúng.
 
Upvote 0
Mình dùng for lồng for để tìm dựa trên từng số Hoá đơn trong Sheet bccn có 8000 dòng
để lấy số tiền đã thanh toán trong Sheet ZPP Phyto có 6000 dòng nếu trùng số HD để ra Sheet bccn
Khi chạy thì treo gần 16p mới chạy xong lệnh này. Cho mình hỏi có cách nào nhanh hơn không?
For i = 2 To ActiveWorkbook.Sheets("bccn").Range("D" & Rows.Count).End(xlUp).Row
SoHD = ActiveWorkbook.Sheets("bccn").Range("D" & i).Value
For j = 2 To ActiveWorkbook.Sheets("ZPP Phyto").Range("N" & Rows.Count).End(xlUp).Row
If SoHD = ActiveWorkbook.Sheets("ZPP Phyto").Range("N" & j).Value Then
ActiveWorkbook.Sheets("bccn").Range("F" & i).Value = ActiveWorkbook.Sheets("ZPP Phyto").Range("Q" & j).Value
Exit For
End If
Next j
Next i

Cảm ơn các bạn nhiều
 
Upvote 0
Mình đã xoá bớt dòng mới up được
Bạn xem giúp mình. Cảm ơn bạn

dùng mảng có thể tăng tốc được chút ít
Mã:
Sub congNo()
Application.ScreenUpdating = False
    Dim maKH, tenKH As String
    Dim tienPhaiTra, tienDaTra, congNo, tongCongNo As Currency
    Dim ngayHD As Date
    Dim SoHD As Long
    Dim bccn, zpp As Variant
    With Sheet4
        .[A:f].Clear
        .[a:b].Value = Sheet2.[e:f].Value ' MaKH ' Ten KH
        .[c:c].Value = Sheet2.[a:a].Value ' Ngay HD
        .[d:d].Value = Sheet2.[c:c].Value ' So HD
        .[e:e].Value = Sheet2.[e:e].Value ' TTien
        bccn = .[a2].Resize(.[a60000].End(xlUp).Row, 6).Value
    End With
  zpp = Sheet1.[a2].Resize(Sheet1.[p60000].End(3).Row, 17).Value
    For i = 1 To UBound(bccn)
        For j = 1 To UBound(zpp)
            If bccn(i, 4) = zpp(j, 14) Then
                bccn(i, 6) = zpp(j, 17)
                Exit For
            End If
        Next j
    Next i
    Sheet4.[a2].Resize(UBound(bccn), 6).Value = bccn
Application.ScreenUpdating = True
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Bài này dùng Dictionary sẽ nhanh thêm gấp nhiều lần
Bài này nếu các hóa đơn đã được sort như file đính kèm thì dùng mảng kết hợp vòng lặp do loop lồng trong for sẽ nhanh hơn. Do cả 2 cùng sort tăng dần nên biến đếm j của Do loop có thể sử dụng lại sau khi biến đếm i của vòng For tăng.
 
Upvote 0
Bài này dùng Dictionary sẽ nhanh thêm gấp nhiều lần

tại vì lúc đầu nghĩ là đăng nào cũng phải xài 2 vòng lặp.
đúng là theo logic thì 2 vòng lặp đơn thì sẻ nhanh hơn vòng lặp lồng vào nhau
nhưng sau khi xài dic thì nó cũng ko nhanh hơn được là bao nhiêu (test trên laptop mini cũ 19s, mới 16s). anh xem dùm xem code như vậy đã tối ưu chưa
Mã:
Sub congNo()
s_time = Now()
Application.ScreenUpdating = False
    Dim maKH, tenKH As String
    Dim tienPhaiTra, tienDaTra, congNo, tongCongNo As Currency
    Dim ngayHD As Date
    Dim SoHD, k As Long
    Dim bccn, zpp As Variant, d As Object
    Set d = CreateObject("Scripting.Dictionary")
    With Sheet4
        .[A:f].Clear
        .[a:b].Value = Sheet2.[e:f].Value ' MaKH ' Ten KH
        .[c:c].Value = Sheet2.[a:a].Value ' Ngay HD
        .[d:d].Value = Sheet2.[c:c].Value ' So HD
        .[e:e].Value = Sheet2.[e:e].Value ' TTien
        bccn = .[a2].Resize(.[a60000].End(xlUp).Row, 6).Value
    End With
  zpp = Sheet1.[a2].Resize(Sheet1.[p60000].End(3).Row, 17).Value
    For i = 1 To UBound(bccn)
        If Not d.exists(bccn(i, 4)) Then
            k = k + 1
            d.Add (bccn(i, 4)), k
        End If
    Next i
    
        For j = 1 To UBound(zpp)
            If d.exists(zpp(j, 14)) Then
                bccn(d.Item(zpp(j, 14)), 6) = zpp(j, 17)
            End If
        Next j
    
    Sheet4.[a2].Resize(UBound(bccn), 6).Value = bccn
Application.ScreenUpdating = True
MsgBox Second(Now() - s_time)
End Sub
 
Upvote 0
tại vì lúc đầu nghĩ là đăng nào cũng phải xài 2 vòng lặp.
đúng là theo logic thì 2 vòng lặp đơn thì sẻ nhanh hơn vòng lặp lồng vào nhau
nhưng sau khi xài dic thì nó cũng ko nhanh hơn được là bao nhiêu (test trên laptop mini cũ 19s, mới 16s). anh xem dùm xem code như vậy đã tối ưu chưa
Nếu đúng như giả định của mình về 2 cột số hóa đơn ở 2 sheet đều được sort thì thủ tục sau sẽ nhanh hơn nhiều (khoảng 0.07s, thủ tục của bác là 6s trên laptop Core i7, RAM 16G)
Mã:
Sub CongNo4()
    Dim bccn_D(), bccn_F(), zpp_N(), zpp_Q(), EndRow_bccn&, EndRow_zpp&, MaxRow&, i&, j&, last_j&, t
    Application.ScreenUpdating = False
    t = Timer
    With Sheets("Tong hop theo HD")
        .Columns("E:F").Copy Range("A1")
        .Columns(1).Copy Range("C1")
        .Columns(3).Copy Range("D1")
        .Columns("X").Copy Range("E1")
    End With
    Rows(1).ClearContents
    
    MaxRow = Columns(1).Rows.Count
    
    EndRow_bccn = Range("A" & MaxRow).End(xlUp).Row
    bccn_D = Range("D2:D" & EndRow_bccn)
    ReDim bccn_F(1 To EndRow_bccn - 1, 1 To 1)
    
    With Sheets("ZPP Phyto HCM Daily Customer P")
        EndRow_zpp = .Range("A" & MaxRow).End(xlUp).Row
        zpp_N = .Range("N2:N" & EndRow_zpp).Value
        zpp_Q = .Range("Q2:Q" & EndRow_zpp).Value
    End With
    last_j = 1
    For i = 1 To EndRow_bccn - 1
        j = last_j
        Do While j < EndRow_zpp
            If bccn_D(i, 1) < zpp_N(j, 1) Then Exit Do
            If bccn_D(i, 1) = zpp_N(j, 1) Then
                bccn_F(i, 1) = zpp_Q(j, 1)
                last_j = j
                Exit Do
            End If
            j = j + 1
        Loop
    Next
    
    Range("F2:F" & EndRow_bccn) = bccn_F
    Application.ScreenUpdating = True
    MsgBox Timer - t
End Sub
 
Upvote 0
tại vì lúc đầu nghĩ là đăng nào cũng phải xài 2 vòng lặp.
đúng là theo logic thì 2 vòng lặp đơn thì sẻ nhanh hơn vòng lặp lồng vào nhau
nhưng sau khi xài dic thì nó cũng ko nhanh hơn được là bao nhiêu (test trên laptop mini cũ 19s, mới 16s). anh xem dùm xem code như vậy đã tối ưu chưa
Mã:
Sub congNo()
s_time = Now()
Application.ScreenUpdating = False
    Dim maKH, tenKH As String
    Dim tienPhaiTra, tienDaTra, congNo, tongCongNo As Currency
    Dim ngayHD As Date
    Dim SoHD, k As Long
    Dim bccn, zpp As Variant, d As Object
    Set d = CreateObject("Scripting.Dictionary")
    With Sheet4
        .[A:f].Clear
        .[a:b].Value = Sheet2.[e:f].Value ' MaKH ' Ten KH
        .[c:c].Value = Sheet2.[a:a].Value ' Ngay HD
        .[d:d].Value = Sheet2.[c:c].Value ' So HD
        .[e:e].Value = Sheet2.[e:e].Value ' TTien
        bccn = .[a2].Resize(.[a60000].End(xlUp).Row, 6).Value
    End With
  zpp = Sheet1.[a2].Resize(Sheet1.[p60000].End(3).Row, 17).Value
    For i = 1 To UBound(bccn)
        If Not d.exists(bccn(i, 4)) Then
            k = k + 1
            d.Add (bccn(i, 4)), k
        End If
    Next i
    
        For j = 1 To UBound(zpp)
            If d.exists(zpp(j, 14)) Then
                bccn(d.Item(zpp(j, 14)), 6) = zpp(j, 17)
            End If
        Next j
    
    Sheet4.[a2].Resize(UBound(bccn), 6).Value = bccn
Application.ScreenUpdating = True
MsgBox Second(Now() - s_time)
End Sub
Cảm giác hình như bạn làm ngược thì phải? Lý ra phải lấy dữ liệu từ cột N của sheet "ZPP Phyto HCM Daily Customer P" để nạp vào Dictionary chứ nhỉ?
----------------
Tôi làm thế này:
Mã:
Sub Main()
  Dim dic As Object
  Dim n As Long
  Dim tmp1 As String
  Dim tmp2 As Double, t As Double
  Dim aSrc, aDes, aTitle
  Dim wks1 As Worksheet, wks2 As Worksheet, wks3 As Worksheet
  t = Timer
  aTitle = Array("Ma_KH", "Ho_Ten", "Ngay", "So_Phieu", "Ma_KH")
  Set wks1 = Worksheets("Tong hop theo HD")
  Set wks2 = Worksheets("ZPP Phyto HCM Daily Customer P")
  Set wks3 = Worksheets("temp bccn")
  With wks3
    .Range("A1:E1").Value = aTitle
    wks1.Range("A1:F10000").AdvancedFilter 2, , .Range("A1:E1")
  End With
  aSrc = wks2.Range("N2:Q10000").Value
  aDes = wks3.Range("D2:D10000").Value
  ReDim arr(1 To UBound(aDes, 1), 1 To 1)
  Set dic = CreateObject("Scripting.Dictionary")
  dic.CompareMode = vbTextCompare
  For n = 1 To UBound(aSrc, 1)
    tmp1 = CStr(aSrc(n, 1))
    tmp2 = CDbl(aSrc(n, 4))
    If Len(tmp1) Then
      If Not dic.Exists(tmp1) Then dic.Add tmp1, tmp2
    End If
  Next
  For n = 1 To UBound(aDes, 1)
    tmp1 = CStr(aDes(n, 1))
    If Len(tmp1) Then
      If dic.Exists(tmp1) Then arr(n, 1) = dic.Item(tmp1)
    End If
  Next
  wks3.Range("F2:F10000").Value = arr
  MsgBox Timer - t
End Sub
Đoán đại, làm bừa, cũng chưa chắc trúng
Ẹc... Ẹc...
 
Lần chỉnh sửa cuối:
Upvote 0

em có bài toán vòng lặp như hình trên.
cách làm của em là
for i = ... to ..... step 7' quet du lieu cot A
Rng1.copy => copy dữ liệu ứng với nguyen van a, nguyen van b, nguyen van c ....
for j = ... to ... step 7 ' quet tim vi tri cot G (hoac H) de paste
.paste => paste du lieu tuong ung voi nguyen van a, nguyen van b, nguyen van c vao cac vi tri cua cot G (hoac H deu duoc) giữ nguyên định dạng
next
next
nói chung là bài toán được giải
em hỏi là có cách nào tăng tốc hơn để giải quyết bài toán trên khi mà dữ liệu lên đến vài chục nghìn dòng thì lâu quá.
bài toán mở rộng lên khi cột F nằm trong sheet khác nữa.
Trân trọng cảm ơn.
Đọc từ đầu đến cuối bài vòng lặp for .... next phần đầu em còn hiểu tí tí, phần sau thì mù tịt
 

File đính kèm

  • Book1.xls
    41 KB · Đọc: 11
Lần chỉnh sửa cuối:
Upvote 0
em có bài toán vòng lặp như hình trên.
cách làm của em là

nói chung là bài toán được giải
em hỏi là có cách nào tăng tốc hơn để giải quyết bài toán trên khi mà dữ liệu lên đến vài chục nghìn dòng thì lâu quá.
bài toán mở rộng lên khi cột F nằm trong sheet khác nữa.
Trân trọng cảm ơn.
Đọc từ đầu đến cuối bài vòng lặp for .... next phần đầu em còn hiểu tí tí, phần sau thì mù tịt
+ Nhìn hình minh họa của bạn tôi vẫn chưa hiểu lắm! Bạn có thể nói rõ hơn yêu cầu của bạn 1 chút được không?
+ Bạn có thể đưa dữ liệu thực tế hơn 1 chút được ko? Chỉ cần tên của 3 người cũng được kèm kết quả mong muốn vào file nữa nhé!
 
Lần chỉnh sửa cuối:
Upvote 0

em có bài toán vòng lặp như hình trên.
cách làm của em là

nói chung là bài toán được giải
em hỏi là có cách nào tăng tốc hơn để giải quyết bài toán trên khi mà dữ liệu lên đến vài chục nghìn dòng thì lâu quá.
bài toán mở rộng lên khi cột F nằm trong sheet khác nữa.
Trân trọng cảm ơn.
Đọc từ đầu đến cuối bài vòng lặp for .... next phần đầu em còn hiểu tí tí, phần sau thì mù tịt

Nếu chỉ lấy giá trị thì đưa vào mảng, gán xuống sheet 1 lần là nhanh nhất.
Bạn lại muốn copy "y chang" cả định dạng thì phải lâu rồi.
Thử xem cái này.
PHP:
Public Sub GPE()
Application.ScreenUpdating = False
Dim I As Long, J As Long, Rws As Long, R As Long
R = 1
Rws = Range("A65536").End(xlUp).Row
For I = 1 To Rws Step 7
    For J = 1 To 3
        Range("A" & I).Resize(7, 2).Copy Range("F" & R)
        Range("H" & R).Value = "tien" & J
        R = R + 7
    Next J
Next I
End Sub
Nếu mở rộng cột F sang "sheet khác" thì thay .Copy Range("F" & R) thành .Copy "sheet khác". Range("F" & R).
 

File đính kèm

  • KyQua.rar
    16.5 KB · Đọc: 14
Lần chỉnh sửa cuối:
Upvote 0
em có bài toán vòng lặp như hình trên.
cách làm của em là
................................................................
nói chung là bài toán được giải
em hỏi là có cách nào tăng tốc hơn để giải quyết bài toán trên khi mà dữ liệu lên đến vài chục nghìn dòng thì lâu quá.
bài toán mở rộng lên khi cột F nằm trong sheet khác nữa.
Trân trọng cảm ơn.
Đọc từ đầu đến cuối bài vòng lặp for .... next phần đầu em còn hiểu tí tí, phần sau thì mù tịt
tôi dùng 1 vòng lặp thôi. Bạn xem thế nào do ko rõ yêu cầu của bạn nên tỉ lệ làm sai rất cao! Có gì hồi âm lại ngay nhé!
 

File đính kèm

  • Giup Aviaiva.xls
    40 KB · Đọc: 9
Upvote 0
Web KT
Back
Top Bottom