I have a canvas expense system connected to two SharePoint lists: Expenses and expense line items.
Expenses Table
SharePointID | Name | Amount |
1 | Expense1 | 1000 |
2 | Expense2 | 2000 |
3 | Expense3 | 3000 |
Expenses Items Table
SharePointID | Category | Amount | Date | ExpenseID |
1 | Food | 500 | 1 | |
2 | Drinks | 500 | 1 | |
3 | Airfare | 3000 | 2 |
The ExpenseID is created when submitting the form to relate the items. My flow;
I have a formula that unfortunately duplicates the items that previously existed in the collection when patching back to sharepoint.
here is my formula;
ForAll(ColRelatedItems,
<Checks the new empty row created is not uploaded and the expenseID is empty>
If(!IsBlank(Title) && IsBlank(LookUp(ExpenseRelatedItems, ExpensesID = GalleryExpensesDetail.Selected.ID)),
Patch(ExpenseRelatedItems,Defaults(ExpenseRelatedItems),{Title:Title,Amount:Amount,ExpensesID:GalleryExpensesDetails.Selected.ID}),
<Checks the new empty row created is not uploaded and the expenseID exists>
If( !IsBlank(Title) && !IsBlank(LookUp(ExpensesItems, ExpensesID = GalleryExpensesDetail.Selected.ID)), Patch(Title:Title,Amount:Amount,ExpensesID:GalleryExpensesDetails.Selected.ID})
)));
Not sure what I could be doing wrong with the formula
Solved! Go to Solution.
Keep in mind that doing a Patch on a collection using "ThisItem" can lead to issues. It is not what you expect. There is not direct link of a gallery row to a collection row by itself. Patch has to try and look up what is in the "ThisItem" record to then patch that record in the collection.
Let me explain.
So, let's say you have two records in your collection and they are shown in a gallery:
The two records: {Title: "", Value: 0}, {Title: "", Value: 0}
Now, let's say you have a means to edit the Title in your gallery and you edit the second row in the gallery and then issue a : Patch(myCollection, ThisItem, {Title: TextInput.Text})
This will cause a problem because it will patch the first record of the collection...not the second. This is because ThisItem is a record and that record is {Title: "", Value: 0} which is the same as the first record!
You cannot rely on "ThisItem" to be the underlying record!!
So, the solution I provided you accounts for all of that.
This was very specifically accounted for in the OnChange action formula I provided:
If(!IsBlank(ThisItem.ID),
UpdateIf(yourCollection, ID=ThisItem.ID, {Changed: true})
)
Everything is based on the ID. This is the key to everything (thus the primary key)
If you fill your collection with existing records with a formula such as:
ClearCollect(myCollection, Filter(myData, columnX=value))
Then every record will have an ID. That is essentially all we care about - except for the initial display of values - like if there was a Title textinput, we would set the Default of that to: ThisItem.Title But after that, we don't care about .Title any longer. The TextInput now contains the value that we are interested in.
With the OnChange formula above on the TextInput control, it will very specifically look at the ID column. If it exists, it will update THAT record with a Changed column set to true. That is it. We don't care about trying to capture all the values in the gallery because we will get them later. Doing so is just adding more work.
Finally, in the Patch function:
Patch(ExpenseRelatedItems,
ForAll(
Filter(yourCollection, Changed || IsBlank(ID)) As _item,
With(LookUp(GalleryExpensesDetails.AllItem, ID=_item.ID),
{ID: ID,
Title: TitleTextInputControl.Text,
Amount: Value(AmountTextInputControl.Text),
TravelExpensesID: GalleryExpensesDetails.Selected.ID
}
)
)
)
We are very specifically filtering for any record that has a Changed column that is true. OR any record that has no ID.
We then put all of that together in a table (The ForAll) with records that correspond to the datasource record needed. We supply the ID of the record from the gallery...if there is one, then the Patch will update that record in the datasource, if not (blank), then it will create one.
This can all be done in an OnSuccess action of a form and can reference any LastSubmit record data that was submitted in the form submit.
Hopefully that all makes sense.
Your formula has some errors in it...but what is patchstuff??
Hi, It's formula inside the patch for the columns to be updates. {Total : Total,...}
How is it defined? Your formula is not defining it.
I advise you to create a lookup column in Expenses Items to Expenses (selecting ID for "In this column" field)!
Uhmm not sure what that means, but I've updated my formula to show the patch example
Hi, I do have it, The expensesID.
It seems to be working only that it's duplicating the existing items.
Ah, see I thought you were copying and pasting your exact formula...now I see you were just shorthanding it in your post.
So my suggestion is to ditch the collection and work from your gallery where you already have the data. And also to use the ForAll as the function it was designed to be (it is not a For Loop!)
Your formula would be more like this:
Patch(ExpenseRelatedItems,
ForAll(
Filter(GalleryExpensesDetails.AllItem, !IsBlank(TitleInputControl.Text)),
{ID: ID,
Title:Title,
Amount:Amount,
TravelExpensesID: GalleryExpensesDetails.Selected.ID
}
)
)
The point of the above being that your gallery is already populated by the records from the datasource and it will have the correct ID needed.
Patch is smart enough to know when to create and when to update. If you supply an ID, then it will update. If the ID is blank, then it will create.
The above creates a table of records to create and/or update (that's the purpose of the ForAll) and then provides that table to Patch to act on the records.
Thanks for this insight.
Just to clarify, I have two Galleries.
1. Has the expenseHeaderDetails(GalleryExpenseDetails) - when an item on this is selected, I create a collection that pulls all the related expense line items with the expenseID.
2. The second gallery(GallerGrid), gets populated by the just pulled-in data ( The expense line items), this allows for the addition of new items that come without the expenseid and any updates on the existing ones.
so I push this data back to SP using the formula, hence I reference the first gallery's ID as the expenseID in the patch.
So don't get caught up in the collection trap! You will need a collection only for the purpose of being able to add records to a gallery. Primarily, the only thing the collection needs to contain is the primary keys for the records (or blank if new).
Your formula to update and change records should base itself on the gallery - not the collection. Otherwise you get into the wasted time of doing OnChange actions and such to update the collection...You need not waste the effort. The gallery will have the values you want.
The only one suggestion is to use the OnChange actions to "tag" your record in your collection as changed. This way you can filter just the changed and new records for your patch function.
If(!IsBlank(ThisItem.ID),
UpdateIf(yourCollection, ID=ThisItem.ID, {Changed: true})
)
The formula mentioned would do what you need from that. Based on what you have mentioned though, the following changes would/should be made:
Patch(ExpenseRelatedItems,
ForAll(
Filter(yourCollection, IsChanged || IsBlank(ID)) As _item,
With(LookUp(GalleryExpensesDetails.AllItem, ID=_item.ID),
{ID: ID,
Title: TitleTextInputControl.Text,
Amount: Value(AmountTextInputControl.Text),
TravelExpensesID: GalleryExpensesDetails.Selected.ID
}
)
)
)
That would perform the action you need.
User | Count |
---|---|
256 | |
110 | |
97 | |
51 | |
39 |