Forum Discussion
Allocating / Apportioning Challan Amounts to Different Receipts
(I Apologize Friends! I made the unpardonable mistake of uploading the wrong file here. Now I have Uploaded the correct file here. I sincerely apologize for the inconvenience !
Challan Allocation Apportionment to Receipts.xlsx.
I have also uploaded one new workbook
Challan Allocation Apportionment to Receipts DJClements 02.xlsm [with additonal columns]. If anyone is working fresh on this query, kindly consider working on this file
The attached file contains three Tables
1. Challan : This table contains the data for the Tax deposited via Challan.
2. Receipts : This table contains the data for the Amounts received against which the tax has been deposited.
3. ChallanAllocation : This table contains the mapping (from Challan Table) of challan number and date to all the amounts (in the Receipts Table)
My Request involves creation of ChallanAllocation like Information in an automated way
Here one challan amount might be sufficient to cover multiple receipts
or
One receipt might be sufficient to cover multiple challans
Considering the Receipts Table as base We need to Map (allocate and / or apportion) the Challan amount to all the receipts on FIFO basis, without leaving any balance in challan, and create a new table (Structured Data Table or simple data range). We might need to split the single challan across various receipts OR split the single receipts across several challans and might need to ADD more rows to accommodate such splits.
Notes:
1. Challan date and Receipt Date need to be in ascending order, but Challan Date may be before or after the Receipt Date. I will enter the data in ascending order but appreciate if the proposed solution can sort the data based on ID and CIN Date / Receipt Date.
2. I have given data for only Two IDs (i.e., A01, A02) but there are multiple IDs for which Data is maintained in the same table.
3. Total Challan Amount and Total Receipt Amount is matching in case of A01 but might be different in other cases (like in A02). In case Total Challan Amount is MORE than the Total Receipt Amount, the balance can be ignored. But if the Challan Amount is LESS than the Receipt Amount, the additional row should appear with "Short" in CIN Column in ChallanAllocation Table (like in Cell Q49)
Any solution (VBA, non VBA) to solve this and create the desired data is EQUALLY appreciated.
As I am not very good in VBA, I am trying my best to find a Non VBA solution using Dynamic Array Functions, but I am struggling to add new rows automatically to the existing table.
In real scenario, I am going to use three different sheets (Challan, Receipts, ChallanAllocation) and will be copying the new data on the Challan and Receipts sheet, hoping that the solution will create the desired data in Sheet ChallanAllocation. Here for the sake of easy matching, I have kept the data on one sheet.
and to all the Esteemed Experts
KanwalNo1 Thank you for providing the new file and additional clarification. The updated code below should do the trick for allocating amounts by ID and recording any shortages:
Option Explicit Sub AllocateReceipts() 'Clear the previous output range Application.ScreenUpdating = False Dim wsOutput As Worksheet Set wsOutput = Worksheets("ChallanAllocation") wsOutput.Range("A1").CurrentRegion.Offset(1).ClearContents 'Sort each table by id and date Dim loCha As ListObject, loRec As ListObject Set loCha = Worksheets("Challan").ListObjects("Challan") Set loRec = Worksheets("Receipts").ListObjects("Receipts") Call SortTable(loCha, 1, 3) Call SortTable(loRec, 1, 3) 'Load each table into an array Dim cha As Variant, rec As Variant cha = loCha.DataBodyRange.Value rec = loRec.DataBodyRange.Value 'Allocate receipts to challan amounts Dim arr(1 To 1, 1 To 6) As Variant, id As Variant, rowId As Long, i As Long, j As Long, k As Double rowId = 2 For i = 1 To UBound(rec, 1) ' check for new receipt id If rec(i, 1) <> id Then ' reset id and running total id = rec(i, 1) k = 0 End If ' validate receipt amount If rec(i, 4) > 0 Then ' loop through challan array until receipt amount has been exhausted For j = 1 To UBound(cha, 1) ' only allocate challan amounts with matching ids If cha(j, 1) = id And cha(j, 4) > 0 Then ' copy the transaction details to an array arr(1, 1) = rec(i, 1) '<-------------------------------------- ID arr(1, 2) = rec(i, 3) '<-------------------------------------- Receipt Date arr(1, 3) = IIf(cha(j, 4) < rec(i, 4), cha(j, 4), rec(i, 4)) ' Amount arr(1, 4) = arr(1, 3) + k '<---------------------------------- Running Total arr(1, 5) = cha(j, 2) '<-------------------------------------- CIN arr(1, 6) = cha(j, 3) '<-------------------------------------- CIN Date ' write the transaction array to the next output row wsOutput.Cells(rowId, 1).Resize(, 6).Value = arr rowId = rowId + 1 ' update the running total k = arr(1, 4) ' reduce the current amounts by the transaction amount cha(j, 4) = cha(j, 4) - arr(1, 3) rec(i, 4) = rec(i, 4) - arr(1, 3) If rec(i, 4) = 0 Then Exit For End If Next j ' verify receipt amount has been exhausted If rec(i, 4) > 0 Then ' record the shortage arr(1, 1) = rec(i, 1) '<--- ID arr(1, 2) = rec(i, 3) '<--- Receipt Date arr(1, 3) = rec(i, 4) '<--- Amount arr(1, 4) = arr(1, 3) + k ' Running Total arr(1, 5) = "Short" '<----- CIN arr(1, 6) = Null '<-------- CIN Date wsOutput.Cells(rowId, 1).Resize(, 6).Value = arr rowId = rowId + 1 k = arr(1, 4) End If End If Next i Application.ScreenUpdating = True End Sub Private Sub SortTable(table As ListObject, field1 As Long, field2 As Long) With table.Sort.SortFields .Clear .Add2 Key:=table.ListColumns(field1).Range, Order:=xlAscending .Add2 Key:=table.ListColumns(field2).Range, Order:=xlAscending End With With table.Sort .Header = xlYes .MatchCase = False .Orientation = xlTopToBottom .SortMethod = xlPinYin .Apply End With End Sub
Let me know how it goes. Cheers!
- djclementsBronze Contributor
KanwalNo1 Some of your notes are a bit unclear and/or don't apply to the sample workbook provided (there is no ID column containing A01, A02, etc., there is nothing in cell Q49, and nowhere does the word "Short" appear in the CIN column).
Having said that, I was able to reproduce the current ChallanAllocation table results using VBA:
Option Explicit Sub AllocateReceipts() 'Clear the previous output range Application.ScreenUpdating = False Sheet2.Range("A1").CurrentRegion.Offset(1).ClearContents 'Sort each table by date Call SortTable(Sheet1.ListObjects("Receipts"), 2) Call SortTable(Sheet1.ListObjects("Challan"), 2) 'Load each table into an array Dim rec As Variant, cha As Variant rec = Sheet1.ListObjects("Receipts").DataBodyRange.Value cha = Sheet1.ListObjects("Challan").DataBodyRange.Value 'Allocate receipts to challan amounts Dim arr(1 To 1, 1 To 5) As Variant, rowId As Long, i As Long, j As Long, k As Double rowId = 2 For i = 1 To UBound(rec, 1) If rec(i, 3) > 0 Then For j = 1 To UBound(cha, 1) If cha(j, 3) > 0 Then ' copy the transaction details to an array arr(1, 1) = rec(i, 2) '<-------- Receipt Date If cha(j, 3) < rec(i, 3) Then arr(1, 2) = cha(j, 3) '<---- Amount (Challan) Else arr(1, 2) = rec(i, 3) '<---- Amount (Receipt) End If k = k + arr(1, 2) '<------------ Running Total arr(1, 3) = k '<---------------- Running Total arr(1, 4) = cha(j, 1) '<-------- CIN arr(1, 5) = cha(j, 2) '<-------- CIN Date ' reduce the current amounts by the transaction amount cha(j, 3) = cha(j, 3) - arr(1, 2) rec(i, 3) = rec(i, 3) - arr(1, 2) ' write the transaction details to the next output row Sheet2.Cells(rowId, 1).Resize(, 5).Value = arr rowId = rowId + 1 If rec(i, 3) = 0 Then Exit For End If Next j End If Next i Application.ScreenUpdating = True End Sub Private Sub SortTable(table As ListObject, column_num As Long) With table.Sort.SortFields .Clear .Add2 Key:=table.ListColumns(column_num).Range, Order:=xlAscending End With With table.Sort .Header = xlYes .MatchCase = False .Orientation = xlTopToBottom .SortMethod = xlPinYin .Apply End With End Sub
The AllocateReceipts sub is then called in the Worksheet_Activate event of the output sheet (Sheet2 in the attached sample workbook):
Option Explicit Private Sub Worksheet_Activate() Call AllocateReceipts End Sub
Please note, this is just a starting point until additional feedback is provided. Also, the code does not currently allow for negative numbers in either the Challan table or the Receipts table. If negative numbers are a possibility, please explain how they should be handled and applied.
- KanwalNo1Iron ContributorHi,
I sincerely apologize for making the unpardonable and stupid mistake of uploading the wrong file and wasting your valuable time.
My sincerest apologies to djclements SergeiBaklan PeterBartholomew1 lori_m Chris_Gross and to all the Esteemed Experts who took time to view my query.
I have NOW uploaded the correct file.
Apologies Again Friends! - KanwalNo1Iron Contributor
Thanks Sir !
It seems to work perfectly as per the data provided in the original file.
Let me clarify that the actual purpose is to allocate the ID wise TOTAL Challan Amount to ID wise TOTAL Receipts, to the extent possible. It is not relevant whether the challan date is earlier or later than the receipt date. The JOINT sorting in the ChallanAllocation table is just to display the method of splitting the entries in case the challan need to be allocated.
So if for an ID
Challan Amount > Receipt Amount, we need to ignore that EXTRA amount and move to next ID. This Challan balance will be used in the next period for Allocation with the forthcoming period receipt entries.
Receipt Amount > Challan Amount, it means a shortfall in payment and so "Short" need to be mentioned instead of the CIN
- djclementsBronze Contributor
KanwalNo1 Thank you for providing the new file and additional clarification. The updated code below should do the trick for allocating amounts by ID and recording any shortages:
Option Explicit Sub AllocateReceipts() 'Clear the previous output range Application.ScreenUpdating = False Dim wsOutput As Worksheet Set wsOutput = Worksheets("ChallanAllocation") wsOutput.Range("A1").CurrentRegion.Offset(1).ClearContents 'Sort each table by id and date Dim loCha As ListObject, loRec As ListObject Set loCha = Worksheets("Challan").ListObjects("Challan") Set loRec = Worksheets("Receipts").ListObjects("Receipts") Call SortTable(loCha, 1, 3) Call SortTable(loRec, 1, 3) 'Load each table into an array Dim cha As Variant, rec As Variant cha = loCha.DataBodyRange.Value rec = loRec.DataBodyRange.Value 'Allocate receipts to challan amounts Dim arr(1 To 1, 1 To 6) As Variant, id As Variant, rowId As Long, i As Long, j As Long, k As Double rowId = 2 For i = 1 To UBound(rec, 1) ' check for new receipt id If rec(i, 1) <> id Then ' reset id and running total id = rec(i, 1) k = 0 End If ' validate receipt amount If rec(i, 4) > 0 Then ' loop through challan array until receipt amount has been exhausted For j = 1 To UBound(cha, 1) ' only allocate challan amounts with matching ids If cha(j, 1) = id And cha(j, 4) > 0 Then ' copy the transaction details to an array arr(1, 1) = rec(i, 1) '<-------------------------------------- ID arr(1, 2) = rec(i, 3) '<-------------------------------------- Receipt Date arr(1, 3) = IIf(cha(j, 4) < rec(i, 4), cha(j, 4), rec(i, 4)) ' Amount arr(1, 4) = arr(1, 3) + k '<---------------------------------- Running Total arr(1, 5) = cha(j, 2) '<-------------------------------------- CIN arr(1, 6) = cha(j, 3) '<-------------------------------------- CIN Date ' write the transaction array to the next output row wsOutput.Cells(rowId, 1).Resize(, 6).Value = arr rowId = rowId + 1 ' update the running total k = arr(1, 4) ' reduce the current amounts by the transaction amount cha(j, 4) = cha(j, 4) - arr(1, 3) rec(i, 4) = rec(i, 4) - arr(1, 3) If rec(i, 4) = 0 Then Exit For End If Next j ' verify receipt amount has been exhausted If rec(i, 4) > 0 Then ' record the shortage arr(1, 1) = rec(i, 1) '<--- ID arr(1, 2) = rec(i, 3) '<--- Receipt Date arr(1, 3) = rec(i, 4) '<--- Amount arr(1, 4) = arr(1, 3) + k ' Running Total arr(1, 5) = "Short" '<----- CIN arr(1, 6) = Null '<-------- CIN Date wsOutput.Cells(rowId, 1).Resize(, 6).Value = arr rowId = rowId + 1 k = arr(1, 4) End If End If Next i Application.ScreenUpdating = True End Sub Private Sub SortTable(table As ListObject, field1 As Long, field2 As Long) With table.Sort.SortFields .Clear .Add2 Key:=table.ListColumns(field1).Range, Order:=xlAscending .Add2 Key:=table.ListColumns(field2).Range, Order:=xlAscending End With With table.Sort .Header = xlYes .MatchCase = False .Orientation = xlTopToBottom .SortMethod = xlPinYin .Apply End With End Sub
Let me know how it goes. Cheers!
- PeterBartholomew1Silver Contributor
The attached attempts FIFO allocation using Excel Dynamic Array formulas.
The 'FIFOλ' was intended to allocate input costs to outputs and, at present, assigns #N/A to the 'cost' of inputs that have yet to be purchased.
- PeterBartholomew1Silver Contributor
Image and formula:
= LET( ID, "A01", outputID, FILTER(Receipts[Receipt Date], Receipts[ID]=ID), outputQty, FILTER(Receipts[Amount], Receipts[ID]=ID), inputID, FILTER(Challan[CIN Date], Challan[ID]=ID), inputQty, FILTER(Challan[Amount], Challan[ID]=ID), resultArr, FIFOλ(outputID,outputQty,inputID,inputQty,SIGN(inputQty),2), resultArr )
FIFOλ stacks all input and output events in order of accumulated quantity into a single array and sorts by accumulated quantity. With 365 insider the output can be switched between a basic table format and crosstab using PIVOTBY.
FIFOλ(OutputID, OutQty, InputID, InpuQty, PriceIn, [format]) =LET( inpID, EXPAND(VSTACK(InputID, "On order"), , 2, ""), cumInp, HSTACK(VSTACK(0, SCAN(0, InputQty, ADDλ)), priceIn), outID, CHOOSECOLS(EXPAND(VSTACK(OutputID, "Stock"), , 2, ""), 2, 1), cumOut, EXPAND(VSTACK(0, SCAN(0, OutQty, ADDλ)), , 2, ""), transact, SORT(VSTACK(HSTACK(inpID, cumInp), HSTACK(outID, cumOut)), 3), inpRef, Filldownλ(TAKE(inpID, 1, 1), CHOOSECOLS(transact, 1)), outRef, Filldownλ(TAKE(outID, 1, -1), CHOOSECOLS(transact, 2)), price, Filldownλ(TAKE(priceIn, 1, 1), CHOOSECOLS(transact, 4)), cumAmt, CHOOSECOLS(transact, 3), transAmt, DROP(cumAmt, 1) - cumAmt, transactTbl, DROP(DROP(HSTACK(inpRef, outRef, transAmt, price), 1), -1), inpHdr, CHOOSECOLS(transactTbl, 1), outHdr, CHOOSECOLS(transactTbl, 2), qty, CHOOSECOLS(transactTbl, 3), uCost, CHOOSECOLS(transactTbl, 4), SWITCH( format, 0, VSTACK({"Input ID", "Output ID", "Quantity", "Unit cost"}, transactTbl), 1, VSTACK( {"Output ID", "Quantity", "Cost"}, GROUPBY(outHdr, HSTACK(qty, qty * uCost), SUM, , 0) ), 2, PIVOTBY(outHdr, inpHdr, qty * uCost, SUM, , 0), 3, PIVOTBY(outHdr, inpHdr, HSTACK(qty, qty * uCost), SUM, , 0, , 0) ) ); Filldownλ(init, list) =SCAN(init, list, REPLACEBLANKλ);
- KanwalNo1Iron Contributor
Thanks a Lot Sir!
First of all, I must admire the elegancy with which you create these Dynamic Array Formulae. Just magic, to say the least. I have been trying to do some stuff myself but still find at loss to get a grip on the LAMBDA function. It seems I need to invest some serious time learning these DAFs.
I must admit that the solution is not best suited to the result I wanted. But it is definitely helping me in unentangling the LAMBDA mysteries. So, a BIG Thank You to you!
I am also trying to find something suitable for such scenarios, other than VBA solution, just in the pursuit of learning the DAF. Will definitely need your help in very near future.
Regards
Kanwaljit