Xin giúp đỡ code tính nguyên vật liệu sử dụng qua doanh số hàng bán

Liên hệ QC

LYSM

Thành viên thường trực
Tham gia
16/3/11
Bài viết
290
Được thích
26
Em chào các anh chị!
Em có 1 file nhờ các anh chị giúp đỡ như sau:
Trong file có 1 sheet chứa công thức pha chế đồ uống và 1 sheet số lượng các loại đồ uống đã bán ra, nguyên vật liệu cần tính sẽ dựa vào số lượng các loại đồ uống * số lượng nguyên liệu của từng đồ uống rồi tổng hợp lại những loại trùng nhau. Qua đó có số liệu để so sánh với số liệu thực tế xem phần trăm hao hụt đang ở mức nào?
Em cám ơn, chúc các anh chị cuối tuần vui vẻ.
 

File đính kèm

  • Tinh NVL.xlsx
    21.8 KB · Đọc: 24
Mình dùng sumproduct kết hợp thêm các cột phụ mã sản phẩm. Bạn xem thử.
 

File đính kèm

  • Test_Tinh NVL.xlsx
    16.9 KB · Đọc: 9
Upvote 0
Em chào các anh chị!
Em có 1 file nhờ các anh chị giúp đỡ như sau:
Trong file có 1 sheet chứa công thức pha chế đồ uống và 1 sheet số lượng các loại đồ uống đã bán ra, nguyên vật liệu cần tính sẽ dựa vào số lượng các loại đồ uống * số lượng nguyên liệu của từng đồ uống rồi tổng hợp lại những loại trùng nhau. Qua đó có số liệu để so sánh với số liệu thực tế xem phần trăm hao hụt đang ở mức nào?
Em cám ơn, chúc các anh chị cuối tuần vui vẻ.
Bạn thử code do dữ liệu của bạn thiếu mã và tên không chuẩn nên khác với kết quả của bạn.
Mã:
Sub laydulieu()
  Dim i As Long, lr As Long, dic As Object, arr, data, kq, a As Long, b As Long, T, s As String
  Dim dk As String
  Set dic = CreateObject("scripting.dictionary")
     With Sheets("CT")
          lr = .Range("B" & Rows.Count).End(xlUp).Row
          data = .Range("B7:F" & lr).Value
          For i = 1 To UBound(data)
              dk = data(i, 1)
              If Not dic.exists(dk) Then
                 dic.Add dk, i
              Else
                 dic.Item(dk) = dic.Item(dk) & "#" & i
              End If
          Next i
    End With
    With Sheets("DM")
         lr = .Range("C" & Rows.Count).End(xlUp).Row
         arr = .Range("C6:E" & lr).Value
         ReDim kq(1 To UBound(data) * UBound(arr), 1 To 5)
   End With
         For i = 1 To UBound(arr)
             dk = arr(i, 1)
             If dic.exists(dk) Then
                s = dic.Item(dk)
                For Each T In Split(s, "#")
                    dk = data(T, 2)
                    If Not dic.exists(dk) Then
                       a = a + 1
                       dic.Add dk, a
                       kq(a, 1) = a
                       kq(a, 2) = data(T, 3)
                       kq(a, 3) = data(T, 2)
                       kq(a, 4) = data(T, 4) * arr(i, 3)
                       kq(a, 5) = data(T, 5)
                     Else
                        b = dic.Item(dk)
                        kq(b, 4) = kq(b, 4) + data(T, 4) * arr(i, 3)
                     End If
                Next
            End If
         Next i
  With Sheets("tong hop")
       lr = .Range("B" & Rows.Count).End(xlUp).Row
       If lr > 5 Then .Range("A6:E" & lr).ClearContents
       .Range("A6:E6").Resize(a).Value = kq
  End With
  Set dic = Nothing
End Sub
 
Upvote 0
Mình dùng sumproduct kết hợp thêm các cột phụ mã sản phẩm. Bạn xem thử.
Cám ơn bác, em muốn làm VBA cho nhẹ bảng ạ, vì số liệu có thể sẽ là cả năm nên dùng sumproduct file sẽ chậm.
Bài đã được tự động gộp:

Bạn thử code do dữ liệu của bạn thiếu mã và tên không chuẩn nên khác với kết quả của bạn.
Mã:
Sub laydulieu()
  Dim i As Long, lr As Long, dic As Object, arr, data, kq, a As Long, b As Long, T, s As String
  Dim dk As String
  Set dic = CreateObject("scripting.dictionary")
     With Sheets("CT")
          lr = .Range("B" & Rows.Count).End(xlUp).Row
          data = .Range("B7:F" & lr).Value
          For i = 1 To UBound(data)
              dk = data(i, 1)
              If Not dic.exists(dk) Then
                 dic.Add dk, i
              Else
                 dic.Item(dk) = dic.Item(dk) & "#" & i
              End If
          Next i
    End With
    With Sheets("DM")
         lr = .Range("C" & Rows.Count).End(xlUp).Row
         arr = .Range("C6:E" & lr).Value
         ReDim kq(1 To UBound(data) * UBound(arr), 1 To 5)
   End With
         For i = 1 To UBound(arr)
             dk = arr(i, 1)
             If dic.exists(dk) Then
                s = dic.Item(dk)
                For Each T In Split(s, "#")
                    dk = data(T, 2)
                    If Not dic.exists(dk) Then
                       a = a + 1
                       dic.Add dk, a
                       kq(a, 1) = a
                       kq(a, 2) = data(T, 3)
                       kq(a, 3) = data(T, 2)
                       kq(a, 4) = data(T, 4) * arr(i, 3)
                       kq(a, 5) = data(T, 5)
                     Else
                        b = dic.Item(dk)
                        kq(b, 4) = kq(b, 4) + data(T, 4) * arr(i, 3)
                     End If
                Next
            End If
         Next i
  With Sheets("tong hop")
       lr = .Range("B" & Rows.Count).End(xlUp).Row
       If lr > 5 Then .Range("A6:E" & lr).ClearContents
       .Range("A6:E6").Resize(a).Value = kq
  End With
  Set dic = Nothing
End Sub
à vâng, tên chưa được thống nhất ạ, nhưng code NL thì vẫn là 1, em cám ơn nhiều! Em hỏi ngoài lề xíu, vậy có thể làm 1 cảnh báo nếu trùng code mà tên lại khác nhau không ạ? Tất nhiên em sẽ thống nhất code với tên nhưng đề phòng trường hợp có sai sót gì xảy ra.
 
Lần chỉnh sửa cuối:
Upvote 0
Cám ơn bác, em muốn làm VBA cho nhẹ bảng ạ, vì số liệu có thể sẽ là cả năm nên dùng sumproduct file sẽ chậm.
Chậm hay không là do người sử dụng thôi, hàm có sẵn vẫn là tiện nhất. Có lẽ nghe đến cột phụ thường ít người thích nhưng riêng mình thêm cột phụ chính là cách hay nhất để giải quyết vấn đề khi bảng biểu thiết kế không thống nhất. Còn nếu file mình xây dựng thì chắc chắn cột phụ đó sẽ là cột chính trong bảng.
p/s: buồn vì giúp bạn nhưng đến file bạn cũng không xem (view = 0 tại thời điểm này)
 
Upvote 0
Cám ơn bác, em muốn làm VBA cho nhẹ bảng ạ, vì số liệu có thể sẽ là cả năm nên dùng sumproduct file sẽ chậm.
Bài đã được tự động gộp:


à vâng, tên chưa được thống nhất ạ, nhưng code NL thì vẫn là 1, em cám ơn nhiều! Em hỏi ngoài lề xíu, vậy có thể làm 1 cảnh báo nếu trùng code mà tên lại khác nhau không ạ? Tất nhiên em sẽ thống nhất code với tên nhưng đề phòng trường hợp có sai sót gì xảy ra.
Nếu kiểm tra thì viết cái code khác cho nó tiện gộp vào đó cũng được.Dữ liệu bạn đưa lên bị thiếu mã nên mình viết theo tên nên không muốn thêm.
 
Upvote 0
Em chào các anh chị!
Em có 1 file nhờ các anh chị giúp đỡ như sau:
Trong file có 1 sheet chứa công thức pha chế đồ uống và 1 sheet số lượng các loại đồ uống đã bán ra, nguyên vật liệu cần tính sẽ dựa vào số lượng các loại đồ uống * số lượng nguyên liệu của từng đồ uống rồi tổng hợp lại những loại trùng nhau. Qua đó có số liệu để so sánh với số liệu thực tế xem phần trăm hao hụt đang ở mức nào?
Em cám ơn, chúc các anh chị cuối tuần vui vẻ.
Code cho bạn:
Rich (BB code):
Sub TinhNVL()
    Dim aCT, aDS, aRsl, Dic As Object
    Dim i&, j&, k&, dKey$
    
    aCT = Sheets("CT").Range("B7:F" & Sheets("CT").Range("B" & Rows.Count).End(xlUp).Row)
    aDS = Sheets("DM").Range("C6:E" & Sheets("DM").Range("C" & Rows.Count).End(xlUp).Row)
    ReDim aRsl(1 To UBound(aCT), 1 To 5)
    Set Dic = CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(aDS)
        For j = 1 To UBound(aCT)
            If aDS(i, 1) = aCT(j, 1) Then
                dKey = aCT(j, 3)
                If Not IsEmpty(dKey) And Not Dic.Exists(dKey) Then
                    k = k + 1
                    Dic.Add dKey, k
                    aRsl(k, 1) = k
                    aRsl(k, 2) = dKey
                    aRsl(k, 3) = aCT(j, 2)
                    aRsl(k, 4) = aCT(j, 4) * aDS(i, 3)
                    aRsl(k, 5) = aCT(j, 5)
                Else
                    aRsl(Dic.Item(dKey), 4) = aRsl(Dic.Item(dKey), 4) + aCT(j, 4) * aDS(i, 3)
                End If
            End If
        Next
    Next
    Sheets("Tong hop").Range("G1").Resize(k, 5) = aRsl
    Set Dic = Nothing
End Sub
 
Upvote 0
Nếu kiểm tra thì viết cái code khác cho nó tiện gộp vào đó cũng được.Dữ liệu bạn đưa lên bị thiếu mã nên mình viết theo tên nên không muốn thêm.
Code cho bạn:
Rich (BB code):
Sub TinhNVL()
    Dim aCT, aDS, aRsl, Dic As Object
    Dim i&, j&, k&, dKey$
   
    aCT = Sheets("CT").Range("B7:F" & Sheets("CT").Range("B" & Rows.Count).End(xlUp).Row)
    aDS = Sheets("DM").Range("C6:E" & Sheets("DM").Range("C" & Rows.Count).End(xlUp).Row)
    ReDim aRsl(1 To UBound(aCT), 1 To 5)
    Set Dic = CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(aDS)
        For j = 1 To UBound(aCT)
            If aDS(i, 1) = aCT(j, 1) Then
                dKey = aCT(j, 3)
                If Not IsEmpty(dKey) And Not Dic.Exists(dKey) Then
                    k = k + 1
                    Dic.Add dKey, k
                    aRsl(k, 1) = k
                    aRsl(k, 2) = dKey
                    aRsl(k, 3) = aCT(j, 2)
                    aRsl(k, 4) = aCT(j, 4) * aDS(i, 3)
                    aRsl(k, 5) = aCT(j, 5)
                Else
                    aRsl(Dic.Item(dKey), 4) = aRsl(Dic.Item(dKey), 4) + aCT(j, 4) * aDS(i, 3)
                End If
            End If
        Next
    Next
    Sheets("Tong hop").Range("G1").Resize(k, 5) = aRsl
    Set Dic = Nothing
End Sub
Dạ, em cám ơn nhiều, việc trùng mã chắc cũng ko xảy ra vì hàm vlookup là xong.
Bài đã được tự động gộp:

Chậm hay không là do người sử dụng thôi, hàm có sẵn vẫn là tiện nhất. Có lẽ nghe đến cột phụ thường ít người thích nhưng riêng mình thêm cột phụ chính là cách hay nhất để giải quyết vấn đề khi bảng biểu thiết kế không thống nhất. Còn nếu file mình xây dựng thì chắc chắn cột phụ đó sẽ là cột chính trong bảng.
p/s: buồn vì giúp bạn nhưng đến file bạn cũng không xem (view = 0 tại thời điểm này)
Dạ ko phải bác ơi, em download và xem trên điện thoại nên có thể thống kê không đúng. Sorry bác nhiều!
 

File đính kèm

  • D866AF0D-D676-48C7-A1F8-F08823EBAE1B.jpeg
    D866AF0D-D676-48C7-A1F8-F08823EBAE1B.jpeg
    64.3 KB · Đọc: 7
Upvote 0
Mã BB004 thì dù tên là Ống hút to Phi 12 hay Ống hút size 12 cũng là một và tổng số là 28 cái
Em hỏi thêm một chút ạ, bác có thể giải thích giúp em phần khai báo biến " Dim i&, j&, k&, dKey$" ý nghĩa là gì không ạ? Đây là lần đầu em gặp khai báo biến kiểu này. Trong code em thấy có 2 vòng lặp lồng nhau, giả sử sau này file nó nhiều dữ liệu lên (file CT thì không nhiều lắm nhưng file DM có thể lên đến 3000 dòng/tháng) vậy có ảnh hưởng đến tốc độ chạy code không ạ? Em cám ơn!
P/S: Em đã thử đến 3000 dòng thì thấy code vẫn chạy rất nhanh :)
 
Upvote 0
Em hỏi thêm một chút ạ, bác có thể giải thích giúp em phần khai báo biến " Dim i&, j&, k&, dKey$" ý nghĩa là gì không ạ? Đây là lần đầu em gặp khai báo biến kiểu này. Trong code em thấy có 2 vòng lặp lồng nhau, giả sử sau này file nó nhiều dữ liệu lên (file CT thì không nhiều lắm nhưng file DM có thể lên đến 3000 dòng/tháng) vậy có ảnh hưởng đến tốc độ chạy code không ạ? Em cám ơn!
P/S: Em đã thử đến 3000 dòng thì thấy code vẫn chạy rất nhanh :)
String $
Integer %
Long &
Single !
Double #
Currency @
Date # # (thằng này thì chỉ gán chứ không Dim được, tức là chẳng hạn a = #dữ liệu ngày tháng#)
 
Upvote 0
Em hỏi thêm một chút ạ, bác có thể giải thích giúp em phần khai báo biến " Dim i&, j&, k&, dKey$" ý nghĩa là gì không ạ? Đây là lần đầu em gặp khai báo biến kiểu này. Trong code em thấy có 2 vòng lặp lồng nhau, giả sử sau này file nó nhiều dữ liệu lên (file CT thì không nhiều lắm nhưng file DM có thể lên đến 3000 dòng/tháng) vậy có ảnh hưởng đến tốc độ chạy code không ạ? Em cám ơn!
P/S: Em đã thử đến 3000 dòng thì thấy code vẫn chạy rất nhanh :)
Chuyện khai báo như bạn hỏi gọi là khai báo tắt cho nhanh. Bài #13 đã liệt kê hết các trường hợp có thể khai báo tắt rồi đó.

Còn việc chạy 2 vòng lặp lồng nhau với cỡ vài chục ngàn dòng là không vấn đề gì với mảng. Dùng mảng là nhanh nhất có thể của VBA rồi. Vấn đề còn lại là giải thuật có giúp nhanh thêm không. Code cho bài của bạn có thể làm cho nhanh hơn nữa được, tôi thấy trong khi code rồi nhưng thấy không cần thiết vì nghĩ rằng cũng không nhiều dữ liệu lắm. Tôi sẽ sửa lại code 1 chút rồi gửi lại, có thêm tính thời gian chạy để biết nhanh hơn bao nhiêu so với code cũ.
 
Upvote 0
Chuyện khai báo như bạn hỏi gọi là khai báo tắt cho nhanh. Bài #13 đã liệt kê hết các trường hợp có thể khai báo tắt rồi đó.

Còn việc chạy 2 vòng lặp lồng nhau với cỡ vài chục ngàn dòng là không vấn đề gì với mảng. Dùng mảng là nhanh nhất có thể của VBA rồi. Vấn đề còn lại là giải thuật có giúp nhanh thêm không. Code cho bài của bạn có thể làm cho nhanh hơn nữa được, tôi thấy trong khi code rồi nhưng thấy không cần thiết vì nghĩ rằng cũng không nhiều dữ liệu lắm. Tôi sẽ sửa lại code 1 chút rồi gửi lại, có thêm tính thời gian chạy để biết nhanh hơn bao nhiêu so với code cũ.
Cám ơn bác nhiều ạ!
Bài đã được tự động gộp:

String $
Integer %
Long &
Single !
Double #
Currency @
Date # # (thằng này thì chỉ gán chứ không Dim được, tức là chẳng hạn a = #dữ liệu ngày tháng#)
Cám ơn bác nhiều
 
Upvote 0
@Chủ bài đăng: Bạn xem có thể tạo bảng tra công thức pha chế thành phẩm như vầy có tiện không:

Mã đồ uốngCC001CC002CC003CC004CC005CC006
TênHồng Trà MHồng Trà LSữa Tr Châu MSữa Tr Châu LTrà xanh H_MTrà xanh H_L
Mã NLĐơn vị
BB002Cái
1​
1​
1​
AA004Gam
4​
5.2​
AA011Gam
2​
2.2​
AA005Gam
15​
17​
AA003Gam
2​
2.2​
AA006Gam
30​
30​
15​
15​
15​
15​
BB004Cái
1​
1​
1​
1​
1​
1​
BB005Cái
1​
1​
1​
1​
1​
1​
BB001Cái
1​
1​
1​
AA012Gam
250​
300​
AA009Gam
45​
45​
4​
5.2​
AA013Gam
20​
20​
AA014Gam
15​
15​
AA009Gam
45​
45​
4​
5.2​
 
Upvote 0
Bạn thử code này xem nó báo bao nhiêu giây nhé, so với code cũ nhanh hơn nhiều hay ít. (Bạn cần thêm mấy chỗ màu đỏ đậm bên dưới vào code cũ để báo thời gian chạy)
Rich (BB code):
Sub TinhNVL_Faster()
    Dim aCT, aDS, aRsl, dic As Object
    Dim i&, j&, k&, dKey$, tmr
    tmr = Timer()
    
    aCT = Sheets("CT").Range("B7:F" & Sheets("CT").Range("B" & Rows.Count).End(xlUp).Row)
    aDS = Sheets("DM").Range("C6:E" & Sheets("DM").Range("C" & Rows.Count).End(xlUp).Row)
    ReDim aRsl(1 To UBound(aCT), 1 To 5)
    Set dic = CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(aDS)
        For j = 1 To UBound(aCT)
            If aDS(i, 1) = aCT(j, 1) Then
                Do
                    dKey = aCT(j, 3)
                    If Not IsEmpty(dKey) And Not dic.exists(dKey) Then
                        k = k + 1
                        dic.Add dKey, k
                        aRsl(k, 1) = k
                        aRsl(k, 2) = dKey
                        aRsl(k, 3) = aCT(j, 2)
                        aRsl(k, 4) = aCT(j, 4) * aDS(i, 3)
                        aRsl(k, 5) = aCT(j, 5)
                    Else
                        aRsl(dic.Item(dKey), 4) = aRsl(dic.Item(dKey), 4) + aCT(j, 4) * aDS(i, 3)
                    End If
                    j = j + 1
                Loop Until aCT(j, 1) <> aCT(j - 1, 1) Or j = UBound(aCT)
                Exit For
            End If
        Next
    Next
    Sheets("Tong hop").Range("G6").Resize(k + 1, 5) = aRsl
    Set dic = Nothing
    MsgBox Timer() - tmr
End Sub
 
Upvote 0
Bạn thử code này xem nó báo bao nhiêu giây nhé, so với code cũ nhanh hơn nhiều hay ít. (Bạn cần thêm mấy chỗ màu đỏ đậm bên dưới vào code cũ để báo thời gian chạy)
Rich (BB code):
Sub TinhNVL_Faster()
    Dim aCT, aDS, aRsl, dic As Object
    Dim i&, j&, k&, dKey$, tmr
    tmr = Timer()
    
    aCT = Sheets("CT").Range("B7:F" & Sheets("CT").Range("B" & Rows.Count).End(xlUp).Row)
    aDS = Sheets("DM").Range("C6:E" & Sheets("DM").Range("C" & Rows.Count).End(xlUp).Row)
    ReDim aRsl(1 To UBound(aCT), 1 To 5)
    Set dic = CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(aDS)
        For j = 1 To UBound(aCT)
            If aDS(i, 1) = aCT(j, 1) Then
                Do
                    dKey = aCT(j, 3)
                    If Not IsEmpty(dKey) And Not dic.exists(dKey) Then
                        k = k + 1
                        dic.Add dKey, k
                        aRsl(k, 1) = k
                        aRsl(k, 2) = dKey
                        aRsl(k, 3) = aCT(j, 2)
                        aRsl(k, 4) = aCT(j, 4) * aDS(i, 3)
                        aRsl(k, 5) = aCT(j, 5)
                    Else
                        aRsl(dic.Item(dKey), 4) = aRsl(dic.Item(dKey), 4) + aCT(j, 4) * aDS(i, 3)
                    End If
                    j = j + 1
                Loop Until aCT(j, 1) <> aCT(j - 1, 1) Or j = UBound(aCT)
                Exit For
            End If
        Next
    Next
    Sheets("Tong hop").Range("G6").Resize(k + 1, 5) = aRsl
    Set dic = Nothing
    MsgBox Timer() - tmr
End Sub
Sao bạn không tách 2 vòng lặp ra cho riêng biệt.Có thể duyệt tính tổng các mặt hàng rồi duyệt tính chi tiết sau.
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom