mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(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:
@@ -85,27 +85,37 @@ class ActionSummary(object):
|
||||
table_id = root_name(table_id)
|
||||
col_id = root_name(col_id)
|
||||
|
||||
def update_action(filtered_row_ids, delta_index):
|
||||
values = [column_delta[r][delta_index] for r in filtered_row_ids]
|
||||
return actions.BulkUpdateRecord(table_id, filtered_row_ids, {col_id: values}).simplify()
|
||||
|
||||
if not defunct:
|
||||
row_ids = self.filter_out_gone_rows(table_id, full_row_ids)
|
||||
if row_ids:
|
||||
values = [column_delta[r][1] for r in row_ids]
|
||||
out_stored.append(actions.BulkUpdateRecord(table_id, row_ids, {col_id: values}).simplify())
|
||||
row_ids_after = self.filter_out_gone_rows(table_id, full_row_ids)
|
||||
if row_ids_after:
|
||||
out_stored.append(update_action(row_ids_after, 1))
|
||||
|
||||
if self.is_created(table_id, col_id) and not defunct:
|
||||
# A newly-create column, and not replacing a defunct one. Don't generate undo actions.
|
||||
pass
|
||||
else:
|
||||
row_ids = self.filter_out_new_rows(table_id, full_row_ids)
|
||||
if row_ids:
|
||||
values = [column_delta[r][0] for r in row_ids]
|
||||
undo_action = actions.BulkUpdateRecord(table_id, row_ids, {col_id: values}).simplify()
|
||||
if defunct:
|
||||
# If we deleted the column (or its containing table), then during undo, the updates for it
|
||||
# should come after it's re-added. So we need to insert the undos *before*.
|
||||
out_undo.insert(0, undo_action)
|
||||
else:
|
||||
out_undo.append(undo_action)
|
||||
# A newly-created column, and not replacing a defunct one. Don't generate undo actions.
|
||||
return
|
||||
|
||||
## Maybe add one or two undo update actions for rows that existed before the change.
|
||||
row_ids_before = self.filter_out_new_rows(table_id, full_row_ids)
|
||||
|
||||
if defunct:
|
||||
preserved_row_ids = []
|
||||
else:
|
||||
preserved_row_ids = self.filter_out_gone_rows(table_id, row_ids_before)
|
||||
|
||||
preserved_row_ids_set = set(preserved_row_ids)
|
||||
defunct_row_ids = [r for r in row_ids_before if r not in preserved_row_ids_set]
|
||||
|
||||
if preserved_row_ids:
|
||||
out_undo.append(update_action(preserved_row_ids, 0))
|
||||
|
||||
if defunct_row_ids:
|
||||
# Updates for deleted rows/columns/tables should come after they're re-added.
|
||||
# So we need to insert the undos *before*.
|
||||
out_undo.insert(0, update_action(defunct_row_ids, 0))
|
||||
|
||||
def _forTable(self, table_id):
|
||||
return self._tables.get(table_id) or self._tables.setdefault(table_id, TableDelta())
|
||||
|
||||
Reference in New Issue
Block a user