major
#29731
TL Views: <persist-transient> copies the draft's empty values over the creation defaults; a sequence number or creation date computed at commit is lost
Problem
PersistTransientAction (<persist-transient>, #29675) creates the persistent object with CopyOperation.initial() and no filter, so every value of the transient draft is copied, empty ones included (InitialOperation.copyValues; CopyOperationImpl.accept answers true for every part).
A transient draft has no value for a default the model computes only for a persistent object: TLFactory.setupDefaultValues runs with forUI for a transient object, and then SequenceDefaultProvider and DefaultByExpression with in-transaction="true" answer null. The persistent object is created with the right defaults, and the copy then overwrites them with the draft's null.
Seen in the TL Development app: a ticket created through <persist-transient container="project" reference="tickets"/> would lose its per-project number (a SequenceDefaultProvider with ContextAwareSequenceName) and its createDate (DefaultByExpression in-transaction="true"); a comment its createDate. The app therefore keeps its script chain for these two creates:
t = $obj.copy(context: $p, transient: false, filter: part -> value -> ctx -> $value != null); $p.add(`tl.dev:Project#tickets`, $t);
Expected
<persist-transient> keeps the creation defaults of the persistent object where the draft has no value.
Test
A type with a SequenceDefaultProvider attribute and a DefaultByExpression in-transaction="true" attribute; create a draft with <create-transient>, persist it with <persist-transient>: the persistent object carries the sequence number and the computed default.
Lösung
- Ein DefaultProvider erklärt über isComputedInTransaction(), ob sein Default nur in der Transaktion berechnet werden kann, die das Objekt anlegt (SequenceDefaultProvider, NumberHandlerDefaultProvider, FolderDefaultProvider, DefaultByExpression mit in-transaction="true"). Der Parameter createForUI von createDefault entfällt: Ob ein Default für die Oberfläche bzw. ein transientes Objekt berechnet wird, entscheidet das Framework anhand dieser Erklärung (TLFactory.setupDefaultValues, ObjectCreation). Ein Provider kann seiner eigenen Erklärung damit nicht widersprechen.
- CopyOperation erhält den Schalter skipTransactionDefaults: Attribute, deren Default in der Transaktion berechnet wird, werden nicht in eine persistente Kopie übernommen; das neu angelegte Objekt behält seinen Default. Der Schalter wirkt zusätzlich zu einem gesetzten Filter. Eine transiente Kopie erhält solche Defaults nie und übernimmt daher auch mit dem Schalter die Werte des Originals.
- TL-Script: $draft.copy(transient: false, skipTransactionDefaults: true). Ohne das Argument kopiert copy() wie bisher alle Werte.
- <persist-transient> legt das Objekt immer mit skipTransactionDefaults an.
Migration
DefaultProvider.createDefault(Object context, TLStructuredTypePart attribute, boolean createForUI) hat den Parameter createForUI verloren. Eigene DefaultProvider-Implementierungen entfernen den Parameter. Eine Implementierung, die für createForUI == true null geliefert hat, weil ihr Default nur beim Anlegen in der Transaktion berechnet werden kann, überschreibt stattdessen isComputedInTransaction() mit true.