Tính số tồn còn lại theo mặt hàng và ngày

Liên hệ QC

Đình Phán

Thành viên thường trực
Tham gia
23/11/10
Bài viết
232
Được thích
68
Giới tính
Nam
Nghề nghiệp
kt
Chào các anh, chị.

Hiện em có 3 bảng Đầu kỳ, Phát sinh tăng và Phát sinh giảm.
Tại mỗi bảng sẽ có chi tiết các giao dịch theo ngày.
Nhu cầu cần tìm của em là Dùng số tại bảng Phát sinh giảm để bù cho các giao dịch Đầu kỳ và Phát sinh tăng (kiểu như Nhập trước - Xuất trước đó ạ)
Em đang có 2 phương án
1. Sử dụng ADODB để dùng câu lênh SQL truy vấn ra dữ liệu cần, nhưng câu lệnh SQL khó quá, anh/chị nào rành về SQL giúp em cách này ạ
2. Sử dụng VBA để tính ra kết quả
Chi tiết như file đính kèm

Em cảm ơn.
 

File đính kèm

  • Test.xlsx
    16.2 KB · Đọc: 34
ADODB thì tôi không rành nhưng cũng không thấy khó khi dùng nó để làm chuyện của bạn, nhưng với VBA thì lại quá dễ.

P/S: nếu chỉ làm với số lượng thì không có khái niệm FIFO, LIFO hay bình quân gì cả. Nó chỉ phức tạp như thế khi dính tới giá cả, hạch toán kinh tế và pháp luật.
 
Lần chỉnh sửa cuối:
Upvote 0
ADODB thì tôi không rành nhưng cũng không thấy khó khi dùng nó để làm chuyện của bạn, nhưng với VBA thì lại quá dễ.

P/S: nếu chỉ làm với số lượng thì không có khái niệm FIFO, LIFO hay bình quân gì cả. Nó chỉ phức tạp như thế khi dính tới giá cả, hạch toán kinh tế và pháp luật.
Em chỉ dùng để tìm ra số lượng thôi. Không liên quan đến tính giá.
Mong anh/chị trợ giúp
 
Upvote 0
SQL:
Phát sinh của bạn riêng rẻ 3 bảng như vậy thì bắt buộc phải dùng câu truy vấn con gộp chúng lại rồi mới tính được.
Có hai cách gộp bằng truy ván con:
1. Dùng Join
2. Dùng Union
Tôi chỉ dẫn sơ qua cách 2
Câu con
(Select MaHang, Ngay, TonDau, 0 As [PSGiam], 0 As [PSTang] From BangTon
Union Select MaHang, Ngay, 0, PhatSinh, 0 From BangGiam
Union Select MaHang, Ngay, 0, 0, Phátinh From BangTang) As [BangGop]

Câu mẹ
Select MaHang, Sum(TonDau) As TD, Max(Ngay) As Ng, Sum(PSGiam) As Giam, Sum(PSTang) As Tang, Sum(TonDau) - Sum(PSGiam) + Sum(PSTang) As TC
From [BangGop]
Group By [BangGop].MaHang
Having [BangGop].Ngay Between NgayDauKy And NgayCuoiKy

Như vậy câu trọn là:
Select MaHang, Sum(TonDau) As TD, Max(Ngay) As Ng, Sum(PSGiam) As Giam, Sum(PSTang) As Tang, Sum(TonDau) - Sum(PSGiam) + Sum(PSTang) As TC
From (Select MaHang, Ngay, TonDau, 0 As [PSGiam], 0 As [PSTang] From BangTon
Union Select MaHang, Ngay, 0, PhatSinh, 0 From BangGiam
Union Select MaHang, Ngay, 0, 0, Phátinh From BangTang) As [BangGop]
Group By [BangGop].MaHang
Where [BangGop].Ngay Between NgayDauKy And NgayCuoiKy

Chú thích: đừng bảo tôi viết code VBA. Tôi không thích ADODB lắm cho nên lười viết.
 
Upvote 0
SQL:
Phát sinh của bạn riêng rẻ 3 bảng như vậy thì bắt buộc phải dùng câu truy vấn con gộp chúng lại rồi mới tính được.
Có hai cách gộp bằng truy ván con:
1. Dùng Join
2. Dùng Union
Tôi chỉ dẫn sơ qua cách 2
Câu con
(Select MaHang, Ngay, TonDau, 0 As [PSGiam], 0 As [PSTang] From BangTon
Union Select MaHang, Ngay, 0, PhatSinh, 0 From BangGiam
Union Select MaHang, Ngay, 0, 0, Phátinh From BangTang) As [BangGop]

Câu mẹ
Select MaHang, Sum(TonDau) As TD, Max(Ngay) As Ng, Sum(PSGiam) As Giam, Sum(PSTang) As Tang, Sum(TonDau) - Sum(PSGiam) + Sum(PSTang) As TC
From [BangGop]
Group By [BangGop].MaHang
Having [BangGop].Ngay Between NgayDauKy And NgayCuoiKy

Như vậy câu trọn là:
Select MaHang, Sum(TonDau) As TD, Max(Ngay) As Ng, Sum(PSGiam) As Giam, Sum(PSTang) As Tang, Sum(TonDau) - Sum(PSGiam) + Sum(PSTang) As TC
From (Select MaHang, Ngay, TonDau, 0 As [PSGiam], 0 As [PSTang] From BangTon
Union Select MaHang, Ngay, 0, PhatSinh, 0 From BangGiam
Union Select MaHang, Ngay, 0, 0, Phátinh From BangTang) As [BangGop]
Group By [BangGop].MaHang
Where [BangGop].Ngay Between NgayDauKy And NgayCuoiKy

Chú thích: đừng bảo tôi viết code VBA. Tôi không thích ADODB lắm cho nên lười viết.
Cảm ơn anh VetMini!

SQL của anh ra kết quả đúng số tồn, nhưng mục đích của em hơi khác một chút. Em xin lỗi vì giải thích chưa rõ.
Cụ thể BangGiam em chỉ đưa vào làm số phụ, dùng để tham chiếu cho phần đã giảm của Tồn và Tăng.
Ví dụ: theo dữ liệu đó, MaHang C sẽ còn lại 3 của ngày 31/12/2019
Tức là em muốn tìm số còn lại của BangTon va BangTang theo đúng ngày của BangTon và ngày của BangTang đó. Không GroupBy MaHang ạ
 
Upvote 0
Cảm ơn anh VetMini!

SQL của anh ra kết quả đúng số tồn, nhưng mục đích của em hơi khác một chút. Em xin lỗi vì giải thích chưa rõ.
Cụ thể BangGiam em chỉ đưa vào làm số phụ, dùng để tham chiếu cho phần đã giảm của Tồn và Tăng.
Ví dụ: theo dữ liệu đó, MaHang C sẽ còn lại 3 của ngày 31/12/2019
Tức là em muốn tìm số còn lại của BangTon va BangTang theo đúng ngày của BangTon và ngày của BangTang đó. Không GroupBy MaHang ạ
có hình ảnh kết quả ko bạn
 
Upvote 0
Cho hỏi:
1) Mã A tồn đầu kỳ -1 là do lỗi nhập liệu sai hay thực tế nó vậy?
2) Trong ví dụ của bạn, 1 mã trong 1 ngày chỉ phát sinh 1 lần nhập, hoặc/và 1 lần xuất.
Trên thực tế, trong 1 ngày, 1 mã có thể phát sinh nhiều lần nhập, hoặc nhiều lần xuất, hoặc cả 2 không?
Theo mình đoán thì trong thực tế có thể phát sinh nhiều nhập, nhiều xuất 1 mã trong 1 ngày.
Nếu đúng như vậy thì bạn cho lại ví dụ khác bao quát hơn nhé.
 
Lần chỉnh sửa cuối:
Upvote 0
có hình ảnh kết quả ko bạn
Có kết quả ở file đính kèm ở bài #1 đó bạn ạ.
Bài đã được tự động gộp:

Cho hỏi:
1) Mã A tồn đầu kỳ -1 là do lỗi nhập liệu sai hay thực tế nó vậy?
2) Trong ví dụ của bạn, 1 mã trong 1 ngày chỉ phát sinh 1 lần nhập, hoặc/và 1 lần xuất.
Trên thực tế, trong 1 ngày, 1 mã có thể phát sinh nhiều lần nhập, hoặc nhiều lần xuất, hoặc cả 2 không?
Theo mình đoán thì trong thực tế có thể phát sinh nhiều nhập, nhiều xuất 1 mã trong 1 ngày.
Nếu đúng như vậy thì bạn cho lại ví dụ khác bao quát hơn nhé.
Đúng là thực tế có thể 1 ngày phát sinh nhiều giao dịch. Nhưng em sẽ sắp xếp trước khi chạy SQL
 
Lần chỉnh sửa cuối:
Upvote 0
Có kết quả ở file đính kèm ở bài #1 đó bạn ạ.
Bài đã được tự động gộp:


Đúng là thực tế có thể 1 ngày phát sinh nhiều giao dịch. Nhưng em sẽ sắp xếp trước khi chạy SQL
Còn câu hỏi 1, tồn đầu kỳ âm, thì sao bạn?
 
Upvote 0
Chào các anh, chị.

Hiện em có 3 bảng Đầu kỳ, Phát sinh tăng và Phát sinh giảm.
Tại mỗi bảng sẽ có chi tiết các giao dịch theo ngày.
Nhu cầu cần tìm của em là Dùng số tại bảng Phát sinh giảm để bù cho các giao dịch Đầu kỳ và Phát sinh tăng (kiểu như Nhập trước - Xuất trước đó ạ)
Em đang có 2 phương án
1. Sử dụng ADODB để dùng câu lênh SQL truy vấn ra dữ liệu cần, nhưng câu lệnh SQL khó quá, anh/chị nào rành về SQL giúp em cách này ạ
2. Sử dụng VBA để tính ra kết quả
Chi tiết như file đính kèm

Em cảm ơn.
Rón rén nộp bài :
Mã:
Option Explicit

Public Sub Em_chua_hieu_bai_nhung_ma_thoi_ke_nha()
    
    Dim dic As Object, key As Variant
    Dim sheet As Worksheet, rng As Range
    Dim arr As Variant
    Dim dRemain As Double, dbQty As Double
    Dim i As Long, j As Long, k As Long, r As Long, x As Long, d As Long
    Dim c As Integer
    Dim dbNhap As Boolean
    
    Set dic = CreateObject("Scripting.Dictionary")
    dic.CompareMode = vbTextCompare
    
    c = 4
    Set sheet = ThisWorkbook.Worksheets("DK")
    Set rng = Sheet1.Range("H25")
    rng.Resize(1000, 10).ClearContents
    arr = sheet.Range("A1").CurrentRegion.Value
    r = r + k: k = UBound(arr, 1)
    rng.Offset(r).Resize(k, c).Value = arr
    arr = sheet.Range("H1").CurrentRegion.Value
    r = r + k: k = UBound(arr, 1)
    rng.Offset(r).Resize(k, c).Value = arr
    arr = sheet.Range("M1").CurrentRegion.Value
    r = r + k: k = UBound(arr, 1): c = c + 2
    ReDim Preserve arr(1 To k, 1 To c)
    For i = LBound(arr, 1) To UBound(arr, 1)
        arr(i, 5) = arr(i, 4): arr(i, 4) = 0
    Next i
    rng.Offset(r).Resize(k, c).Value = arr
    r = r + k
    rng.Offset(, c - 1).Value = "Con lai"
    rng.Offset(, c - 2).Value = "Giam"
    rng.Resize(r, c).Sort Key1:=rng, Order1:=xlAscending, _
        Key2:=rng.Offset(, 1), Order2:=xlAscending, _
        Orientation:=xlTopToBottom, Header:=xlYes

    arr = rng.Resize(r, c).Value
    k = 1
    
    For i = 2 To UBound(arr, 1)
        If Not IsDate(arr(i, 1)) Then Exit For
        dbNhap = arr(i, 4)
        k = k + 1: key = arr(i, 2)
        If key = "A" Then
            Debug.Print
        End If
        If Not dic.Exists(key) Then
            dRemain = arr(i, 4)
            dic.Item(key) = Array(dRemain, i)
            x = i
        Else
            dRemain = dic.Item(key)(0) + arr(i, 4)
            x = dic.Item(key)(1)
        End If
        If dbNhap Then
            If dRemain > 0 And arr(i, 3) <> "DK" Then
                arr(k, c) = arr(i, 4)
            Else
                arr(x, c) = 0
            End If
        Else
            dRemain = dRemain + arr(i, 5)
            arr(x, c) = dRemain
        End If
        dic.Item(key) = Array(dRemain, i)
    Next i
    rng.Resize(r, c).ClearContents
    rng.Resize(k, c).Value = arr
    
End Sub
 
Upvote 0
Rón rén nộp bài :
Mã:
Option Explicit

Public Sub Em_chua_hieu_bai_nhung_ma_thoi_ke_nha()
   
    Dim dic As Object, key As Variant
    Dim sheet As Worksheet, rng As Range
    Dim arr As Variant
    Dim dRemain As Double, dbQty As Double
    Dim i As Long, j As Long, k As Long, r As Long, x As Long, d As Long
    Dim c As Integer
    Dim dbNhap As Boolean
   
    Set dic = CreateObject("Scripting.Dictionary")
    dic.CompareMode = vbTextCompare
   
    c = 4
    Set sheet = ThisWorkbook.Worksheets("DK")
    Set rng = Sheet1.Range("H25")
    rng.Resize(1000, 10).ClearContents
    arr = sheet.Range("A1").CurrentRegion.Value
    r = r + k: k = UBound(arr, 1)
    rng.Offset(r).Resize(k, c).Value = arr
    arr = sheet.Range("H1").CurrentRegion.Value
    r = r + k: k = UBound(arr, 1)
    rng.Offset(r).Resize(k, c).Value = arr
    arr = sheet.Range("M1").CurrentRegion.Value
    r = r + k: k = UBound(arr, 1): c = c + 2
    ReDim Preserve arr(1 To k, 1 To c)
    For i = LBound(arr, 1) To UBound(arr, 1)
        arr(i, 5) = arr(i, 4): arr(i, 4) = 0
    Next i
    rng.Offset(r).Resize(k, c).Value = arr
    r = r + k
    rng.Offset(, c - 1).Value = "Con lai"
    rng.Offset(, c - 2).Value = "Giam"
    rng.Resize(r, c).Sort Key1:=rng, Order1:=xlAscending, _
        Key2:=rng.Offset(, 1), Order2:=xlAscending, _
        Orientation:=xlTopToBottom, Header:=xlYes

    arr = rng.Resize(r, c).Value
    k = 1
   
    For i = 2 To UBound(arr, 1)
        If Not IsDate(arr(i, 1)) Then Exit For
        dbNhap = arr(i, 4)
        k = k + 1: key = arr(i, 2)
        If key = "A" Then
            Debug.Print
        End If
        If Not dic.Exists(key) Then
            dRemain = arr(i, 4)
            dic.Item(key) = Array(dRemain, i)
            x = i
        Else
            dRemain = dic.Item(key)(0) + arr(i, 4)
            x = dic.Item(key)(1)
        End If
        If dbNhap Then
            If dRemain > 0 And arr(i, 3) <> "DK" Then
                arr(k, c) = arr(i, 4)
            Else
                arr(x, c) = 0
            End If
        Else
            dRemain = dRemain + arr(i, 5)
            arr(x, c) = dRemain
        End If
        dic.Item(key) = Array(dRemain, i)
    Next i
    rng.Resize(r, c).ClearContents
    rng.Resize(k, c).Value = arr
   
End Sub
Cảm ơn chị Phương,
Kết quả chính xác, ngoài ra lại còn giúp em sắp xếp theo ngày.
Tuyệt vời!
 
Upvote 0
Chào chị Phương,
Chị có thể giải thích giúp em đoạn này không ạ?

arr = rng.Resize(r, c).Value

k = 1

For i = 2 To UBound(arr, 1)

If Not IsDate(arr(i, 1)) Then Exit For

dbNhap = arr(i, 4)

k = k + 1: key = arr(i, 2)

If key = "A" Then

Debug.Print

End If

If Not dic.Exists(key) Then

dRemain = arr(i, 4)

dic.Item(key) = Array(dRemain, i)

x = i

Else

dRemain = dic.Item(key)(0) + arr(i, 4)

x = dic.Item(key)(1)

End If

If dbNhap Then

If dRemain > 0 And arr(i, 3) <> "DK" Then

arr(k, c) = arr(i, 4)

Else

arr(x, c) = 0

End If

Else

dRemain = dRemain + arr(i, 5)

arr(x, c) = dRemain

End If

dic.Item(key) = Array(dRemain, i)

Next i

Do em muốn chỉnh một chút mà không hiểu được chỗ này.
 
Upvote 0
Chào chị Phương,
Chị có thể giải thích giúp em đoạn này không ạ?

arr = rng.Resize(r, c).Value

k = 1

For i = 2 To UBound(arr, 1)

If Not IsDate(arr(i, 1)) Then Exit For

dbNhap = arr(i, 4)

k = k + 1: key = arr(i, 2)

If key = "A" Then

Debug.Print

End If

If Not dic.Exists(key) Then

dRemain = arr(i, 4)

dic.Item(key) = Array(dRemain, i)

x = i

Else

dRemain = dic.Item(key)(0) + arr(i, 4)

x = dic.Item(key)(1)

End If

If dbNhap Then

If dRemain > 0 And arr(i, 3) <> "DK" Then

arr(k, c) = arr(i, 4)

Else

arr(x, c) = 0

End If

Else

dRemain = dRemain + arr(i, 5)

arr(x, c) = dRemain

End If

dic.Item(key) = Array(dRemain, i)

Next i

Do em muốn chỉnh một chút mà không hiểu được chỗ này.
Chào bạn,
cũng rất may mắn là kết quả đúng ý của bạn.
Còn về Khả năng diễn đạt của mình rất kém, dưới đây là lời giải thích của GPT có lẽ ai cũng biết đến, mình trích dẫn để bạn tham khảo thêm:
Xin chào!

Đoạn mã trông giống như một đoạn mã VBA được sử dụng để xử lý một mảng đa chiều có tên arr, sau đó lưu trữ dữ liệu vào một đối tượng từ điển dic. Sau đây là một số giải thích chi tiết về các lệnh trong đoạn mã:

rng.Resize(r, c).Value: Lấy giá trị của vùng dữ liệu được chỉ định bởi đối tượng rng và thay đổi kích thước của vùng đó thành r hàng và c cột.

For i = 2 To UBound(arr, 1): Duyệt qua từng phần tử của mảng arr, bắt đầu từ phần tử thứ hai (do đoạn mã này giả định rằng phần tử đầu tiên của mảng là tiêu đề).

If Not IsDate(arr(i, 1)) Then Exit For: Kiểm tra xem phần tử thứ i của cột đầu tiên của mảng có phải là ngày hay không. Nếu không phải, đoạn mã sẽ dừng lại và không xử lý tiếp các phần tử khác.

dbNhap = arr(i, 4): Gán giá trị của phần tử thứ i của cột thứ 4 của mảng vào biến dbNhap.

k = k + 1: key = arr(i, 2): Tăng giá trị của biến k lên 1 và gán giá trị của phần tử thứ i của cột thứ 2 của mảng vào biến key.

If key = "A" Then Debug.Print: Kiểm tra xem giá trị của biến key có phải là "A" hay không. Nếu đúng, in ra một thông báo sử dụng hàm Debug.Print.

If Not dic.Exists(key) Then: Kiểm tra xem giá trị của biến key đã tồn tại trong đối tượng từ điển dic chưa.

dRemain = arr(i, 4): Gán giá trị của phần tử thứ i của cột thứ 4 của mảng vào biến dRemain.

dic.Item(key) = Array(dRemain, i): Gán giá trị của một mảng bao gồm dRemain và i vào đối tượng từ điển dic với khóa là key.

x = i: Gán giá trị của biến i vào biến x.

dRemain = dic.Item(key)(0) + arr(i, 4): Lấy giá trị đầu tiên (vị trí 0) của mảng được lưu trữ tại khóa key trong đối tượng từ điển dic, sau
đó cộng thêm giá trị của phần tử thứ i của cột thứ 4 của mảng và gán tổng này vào biến dRemain.

x = dic.Item(key)(1): Lấy giá trị thứ hai (vị trí 1) của mảng được lưu trữ tại khóa key trong đối tượng từ điển dic và gán giá trị này vào biến x.

If dbNhap Then: Kiểm tra xem biến dbNhap có đúng là một giá trị Boolean True hay không.

If dRemain > 0 And arr(i, 3) <> "DK" Then: Kiểm tra xem giá trị của biến dRemain lớn hơn 0 và giá trị của phần tử thứ i của cột thứ 3 của mảng khác "DK" hay không.

arr(k, c) = arr(i, 4): Gán giá trị của phần tử thứ i của cột thứ 4 của mảng vào phần tử thứ k của cột thứ c của mảng.

arr(x, c) = 0: Gán giá trị 0 vào phần tử thứ x của cột thứ c của mảng.

dRemain = dRemain + arr(i, 5): Cộng giá trị của biến dRemain với giá trị của phần tử thứ i của cột thứ 5 của mảng.

arr(x, c) = dRemain: Gán giá trị của biến dRemain vào phần tử thứ x của cột thứ c của mảng.

dic.Item(key) = Array(dRemain, i): Gán giá trị của một mảng bao gồm dRemain và i vào đối tượng từ điển dic với khóa là key.

Next i: Kết thúc vòng lặp For và tiếp tục thực hiện các lệnh khác trong chương trình.
Nếu bạn chưa hiểu được tốt hơn hết gửi lại đúng cấu trúc mẫu mà thực tế bạn đang sử dụng lên đây, nếu trong khả năng thì mình giúp bạn sửa lại.
Qua đó có thể bạn hiểu thêm được chăng.
 
Upvote 0
Do file dữ liệu trước em mô tả chưa cụ thể, nên khi em áp dụng vào dữ liệu thực tế thì kết quả chưa chính xác
Trường hợp này em có 2 trường hợp (em cũng tạo 2 Option Buttom cho nó)

1. Số dư cuối là dư Nợ
2. Số dư cuối là dư Có
Và em chủ động chọn sẵn được là Trường hợp nào rồi

Nếu chọn Option Button 1

Ghi dữ liệu ra sheet Data

- Bảng Đầu kỳ (OB) sẽ gộp với bảng PsNo (Debit)
- Dùng bảng PsCo (Credit) để gộp với bảng vừa tạo ở bước 1, cột số tiền chuyển sang cột Giam
- Sắp xếp theo Ngày tháng và Mã đối tượng

Tính toán cho cột CON LAI

- Dùng số tiền của cột Giam tương ứng với từng Mã đối tượng để bù cho số tiền ở cột So_tien (E)
- Nếu bù hết sẽ trả về giá trị 0 ở cột Con lai, và mang số còn thừa bù tiếp cho dòng tiếp theo
- Nếu bù không hết sẽ trả về phần số tiền còn lại của cột So_tien
- Các dòng chưa được bù bên dưới trả về đúng giá trị của cột So_tien
- Nếu Mã đối tượng đó chỉ có số dư đầu kỳ hoặc chỉ có PsNo thì trả về đúng giá trị cột So_Tien (do không dùng gì để bù cho nó)

Nếu chọn Option Button 2

Ghi dữ liệu ra sheet Data

- Bảng Đầu kỳ (OB) sẽ gộp với bảng PsCo (Credit)
- Dùng bảng PsNo (Debit) để gộp với bảng vừa tạo ở bước 1, cột số tiền chuyển sang cột Giam
- Sắp xếp theo Ngày tháng và Mã đối tượng

Tính toán cho cột CON LAI

- Dùng số tiền của cột Giam tương ứng với từng Mã đối tượng để bù cho số tiền ở cột So_tien (E)
- Nếu bù hết sẽ trả về giá trị 0 ở cột Con lai, và mang số còn thừa bù tiếp cho dòng tiếp theo
- Nếu bù không hết sẽ trả về phần số tiền còn lại của cột So_tien
- Các dòng chưa được bù bên dưới trả về đúng giá trị của cột So_tien
- Nếu Mã đối tượng đó chỉ có số dư Đầu kỳ hoặc chỉ có PsCo thì trả về đúng giá trị cột So_Tien (do không dùng gì để bù cho nó)

Cơ bản các bước gộp và sắp xếp hay chọn Option thì em đã làm được hết hướng dẫn của chị Hoàng Nhật Phương
Tuy nhiên phần Tính toán là phần khó nhất thì em đang bế tắc.

Em cảm ơn!
 

File đính kèm

  • Test1.xlsb
    246.3 KB · Đọc: 10
Upvote 0
Web KT
Back
Top Bottom