(core) Fix undo error for automatically removed rows, especially in summary tables

Summary:
Fixes a bug noted here: https://grist.slack.com/archives/C069RUP71/p1662564341132349

This bug could happen quite easily as follows:

1. Have a formula in a summary table such as `$group.amount`. Typically there's also a `SUM` but that's not essential.
2. Find a group with nonzero values of `amount`.
3. Delete all rows in that group in the source table. Typically that just means one row in a lonely group.
4. The summary table row is automatically deleted.
5. Try to undo. This raises an error about trying to update a non-existent summary table row.

I tried to account for this undo problem in https://phab.getgrist.com/D3489 by not saving the updated value for `$group` when it was found to be empty. The reason this was insufficient is that `$group.amount` is immediately invalidated anyway when the source row(s) are deleted (I think because that's just how dependency relations involving references work) *and* the calculated value of `$group.amount` changes even if `$group` doesn't. For example, `$group.amount` may have previously been `[100, 200]`. After deleting the rows, `$group.amount` becomes `[0, 0]`. Keeping `$group` unchanged prevents `$group.amount` from just being `[]`, but deleting the source rows means that the amounts become the numeric default `0` which is still a change. This change in value is then noted which leads to saving an undo action to update the summary table record. All this happens in step 3 above, and the summary record is only deleted after that point.

This diff removes that special handling for `group` and instead adds a more general fix to `action_summary.py`. This inserts undo actions for deleted rows at the beginning of the undo list rather than at the end, which was already done for deleted tables and columns.

Test Plan: Python tests

Reviewers: dsagal

Reviewed By: dsagal

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D3626
This commit is contained in:
Alex Hall
2022-09-09 20:41:31 +02:00
parent c9a81ea7ea
commit 56624c4a95
7 changed files with 113 additions and 42 deletions

View File

@@ -503,6 +503,28 @@ class Address:
]
})
# Add a new source record which creates a new summary row,
# then delete the source row which implicitly deletes the summary row.
# Overall this is a no-op, but it tests a specific undo-related bugfix.
self.add_record(source_tbl_name, city="Nowhere", state="??", amount=666)
out_actions = self.remove_record(source_tbl_name, 34)
self.assertOutActions(out_actions, {
'calc': [],
'direct': [True, False],
'stored': [
['RemoveRecord', source_tbl_name, 34],
['RemoveRecord', summary_tbl_name, 12],
],
'undo': [
['UpdateRecord', summary_tbl_name, 12, {'group': ['L', 34]}],
['UpdateRecord', summary_tbl_name, 12, {'count': 1}],
['UpdateRecord', summary_tbl_name, 12, {'amount': 666.0}],
['AddRecord', source_tbl_name, 34, {'amount': 666.0, 'city': 'Nowhere', 'state': '??'}],
['AddRecord', summary_tbl_name, 12,
{'city': 'Nowhere', 'group': ['L'], 'state': '??'}],
],
})
# Verify the resulting data after all the updates.
self.assertTableData(summary_tbl_name, cols="subset", data=[
[ "id", "city", "state", "count", "amount" ],