Cascade copy inopérant sur un objet spécifique

Request description

Bonjour,

Nous avons un objet avec des liens configurés en Cascade copy = Yes, mais la copie des liens ne se fait pas quand on copie l’objet.

En revanche, si je copie le parent de mon objet, ce dernier se duplique bien avec ses liens.

Je n’ai rien de particulier dans les logs (même en debug), et j’ai désactivé tous les hook sans résultat.

Je n’ai pas le souci sur d’autres objets, donc il y a quelque chose de spécifique à celui ci que je n’arrive pas à identifier.
Auriez vous d’autres pistes d’analyse à me conseiller ? Des traces à mettre quelque part ?

Merci d’avance pour votre aide car je sèche …
Emmanuelle

Technical information

Instance /health
[Platform]
Status=OK
Version=5.3.54
BuiltOn=2024-11-04 16:13

Bonjour,

Il faudrait clarifier le modèle logique et les clés foncitonnelles.

C’est l’idée.

  • La copie cascade des liens est une recopie des objets liées ayant l’objet parent en clé fonctionnelle. Sinon c’est impossible de copier N objets qui serait en doublon.

  • Pour copier un objet enfant, c’est juste la FK vers le parent qui doit être recopiable. La notion de copie cascade ne sert pas.

D’accord et merci de ton retour rapide, voici la clé fonctionnelle sur un des liens qui ne se copie pas

image

Je peux regarder autre chose au niveau modèle ?

Si la clé fonctionnelle de l’objet lié n’est que la FK du parent, tu ne peux avoir qu’un seul objet rattaché au parent. Il doit manquer un autre champ en clé pour avoir une relation 0,n en plus de la FK au parent.

Ensuite tu peux debugger :

  • les links définis sur la parent initUpdate : getLinks() / link.isCascadeCopy()
  • les pre/postSelect(id, copy) for copy=true sur les enfants (panel instance) + setValue de la FK au parent + validate/save

Si la FK n’est pas clé fonctionnelle il y a un warning dans les logs.

Oui pardon, voici l’autre clé

Je vais regarder dans les classes que tu cites, merci beaucoup.

J’ai donc bien isCascadeCopy = true pour mon link.

Et pour le pre et post Select du lien, j’y passe à l’affichage de l’objet à copier mais ni lors du Copy, ni lors du Save de la copie.

com.simplicite.objects.RCIB.RciAppsubRspent|preSelect||Event: preSelect rowId 1421 copy false
com.simplicite.objects.RCIB.RciAppsubRspent|postSelect||Event: postSelect rowId 1421 copy false
com.simplicite.objects.RCIB.RciAppsubRspent|preSelect||Event: preSelect rowId 1421 copy false
com.simplicite.objects.RCIB.RciAppsubRspent|postSelect||Event: postSelect rowId 1421 copy false

Sur cette partie je n’ai pas compris ce que je dois faire ? :grimacing:

Oui à l’affichage, ce n’est pas une copie, mais ça passe par les hooks
preSearch / preSelect/postSelect… par ligne / et postSearch final

Si “le select for copy” n’est pas appelé, il faut regarder le search de l’instance panel fille qui précède les select et qui a surement une search-spec ou un filtrage (LOG_SQL_USER=yes si pas clair dans le code).

preValidate/postSave… si le select for copy est ok, Simplicité lui change de parent et enregistre/create. Bref rien de nouveau, Simplicité passe par la couche logique comme tout le monde pour manipuler les objets en copie.

Tu peux ajouter des parent.isCopied() pour bypasser des règles de filtrage dans ton code lors d’une recopie d’un objet fils.

Bonjour François,

En fait j’ai l’impression qu’il n’y a aucun search sur les liens de mon objet source, on dirait qu’il ne détecte pas qu’ils sont en cascade copie.
J’ai mis des traces dans le preSearch et je n’ai rien, avec ou sans search spec. Ca s’arrête après le postCreate de mon objet copié.

Est-ce que je peux tracer la façon dont il sélectionne les liens en cascade copie ?

Merci pour ton aide, je suis à cours d’idées !
Emmanuelle

Bonjour,

Voici le pseudo-code d’une recopie cascade :

// Already copied (thru recursion) ?
if (h.containsKey(key))
	return;

// All links
for (Link link : obj.getLinks()) {
	// Hook copiable ?
	if (!link.isCascadeCopy())
		continue;

	ObjectDB child = g.getPanelObject(link.getObjectName(), link.getFieldName());
	child.resetFilters();
	ObjectField fk = child.getField(link.getFieldName());

	// Belongs to functional key ?
	if (!fk.isFunctId())
		// warning
		continue;

	// List all children with the parent context
	fk.setFilter(parentId);
	child.setParentObject(obj);
	child.setParentObjectRefField(link.getFieldName());
	List<String[]> rows = child.search(false);

	for (String[] row : rows) {
		// Select for copy
		if (!child.select(row[0], true))
			continue;

		// Update the foreign key to copied item
		fk.setValue(newId);

		// populate FK
		// reset non copiable fields
		// copy documents...

		// Validate and save

		// Recursion
		copyCascad(child, child.getRowId(), row[0], h);
	}
}

Si le search n’est pas appelé c’est à priori :

  • le getLinks de l’objet qui ne contient pas ce lien ? pb héritage, droits ?
  • isCascadeCopy = false
  • isFunctId() = false
1 Like

Est-il possible qu’on ne rentre pas du tout dans ce code ? C’est à dire qu’on ne détecte pas qu’on est sur une action de Copie ?

Ai-je un moyen de voir dans les logs SQL le select sur les Link de l’objet ?

La méthode est appelée en Création si l’objet est copiable / hook canCopy() = true.

donc au preCreate de l’objet père copié, tu peux tester :

  • canCopy()
  • ce que contient getLinks() et leur isCascadeCopy()

ensuite pour tracer les SQL physiques c’est via LOG_SQL_USER=yes

J’ai peut-être une piste, j’ai mis cette trace dans le preCreate de la classe commune à tous les objets.

AppLog.info("Entering precreate for " + getName() + " can copy " + canCopy() + " is copy " + isCopied(), getGrant());

Sur un objet où le cascade copy fonctionne, j’ai isCopied = true, mais sur mon objet qui pose problème, j’ai isCopied = false.
Est-ce possible que je fasse quelque chose qui change le contexte ? Est-ce que ça peut influer sur le cascade copy ?

canCopy est true dans tous les cas et j’ai bien mes links avec isCascade copy = true donc pas de souci de ce côté.

Je me demandais plutôt si je pouvais voir les logs système, c’est à dire les SELECT sur m_objfield etc … car même en activant les LOG_SQL_SYSTEM j’ai l’impression que je n’ai pas ce niveau de détail.

Aurais-tu un getTargetObject pour rediriger vers les objets hérités ?
Dans ce cas il faut penser à bien forwarder l’id copié.

exemple de getTargetObject sur les Vues qui ont de héritiers :

@Override
public String[] getTargetObject(String rowId, String[] row) {
	if (isBatchInstance())
		return null;

	if (isCopied())
		rowId = getCopyId(); // Propagate the copy Id (not "0")
	else if (rowId.equals(ObjectField.DEFAULT_ROW_ID))
		return null; // No redirection at creation

	if (row==null && select(rowId))
		row = getValues();

	String target = null;
	if (row!=null) {
		String type = getFieldValue("viw_type", row);
		switch (type)
		{
		case com.simplicite.util.View.TYPE_SIMPLE:
			target = "ViewSimple";
			break;
		case com.simplicite.util.View.TYPE_OBJECT:
			target = "ViewObject";
			break;
		case com.simplicite.util.View.TYPE_DOMAIN:
			target = "ViewDomain";
			break;
		case com.simplicite.util.View.TYPE_HOME:
			target = "ViewHome";
			break;
		case com.simplicite.util.View.TYPE_DASHBOARD:
			target = "ViewDashboard";
			break;
		}
	}

	return target!=null
	? new String[] { target, "the_ajax_"+target, rowId }
	: null;
}

ou autre exemple dans la doc :

https://docs.simplicite.io/lesson/docs/core/objects/businessobject-code-hooks#redirections

Non pas de getTargetObject sur cet objet.
isCopied change entre initCopy et initUpdate mais je ne vois rien d’intéressant dans les traces entre les deux :-/

RciAppSub|initCopy for RciAppSub is copy true
RciAppSub|initUpdate||Event: Entering initUpdate for RciAppSub is copy false

Et même sur l’objet ascendant ? en général c’est lui qui pilote quel héritier afficher.

c’est normal.
Une copy ou une création ne passe jamais par initUpdate. mais par l’initCopy et initCreate. on appelle souvent l’initCreate dans l’initCopy pour appliquer les mêmes règles.

Tu peux forcer l’id à copier par code setCopyId(id) mais ça c’est déjà fait par le “select for copy” select(id, true) sauf si tu travailles sur une instance à part qui fait autre chose.

As-tu du code pour pré-créer le record, puis qui ouvre le formulaire en mise à jour sans passer par l’écran de recopie ?

Non j’ai fait tout le code et pas de getTargetObject.
Mon modop est assez basique, j’ai un objet avec deux objets liés en cascade copie


Je passe par l’action Copy

Les valeurs liées ne sont pas encore là à l’affichage de l’écran de copie ce qui semble normal

Mais pareil après le save


Dans le preSelect de celui en pillbox où j’avais mis des traces, je vois que Copy = false

Et peut-être une révélation, je perds le copy ID entre le initCopy et le preSave.
J’ai codé en dur un setCopyId avec mon ID d’origine et cette fois j’ai bien les objets liés.
Plus qu’à trouver où je perds cet ID en route …

RciAppSub|initCopy for RciAppSub isCOpied true rowid 0 copy id 3151||Event: BEGIN
RciAppSub|initUpdate||Event: Entering initUpdate for RciAppSub can copy true is copy false
RciAppSub|preSave||Event: Entering preSave for RciAppSub rowid 0
RciAppSub|preSave||Event: Entering preSave for RciAppSub can copy true is copy false copy id null

PS : il semble en revanche que je passe dans le initUpdate alors que ce n’est pas sensé être le cas ?

Bonjour,

Il y a peut-être du code qui refait un select(id) simple quelque part sur l’instance et qui du coup retire le copyId ? Il faut mettre le plus de code en commentaire et voir si ça corrige, puis décommenter progressivement pour identifier ce qui fait ce reset.

Ou sinon un effet indésirable du lien N,N présenté en pillbox ? Essaye de le remettre en affichage de liste temporairement pour voir.

J’ai commenté tout le code sans résultat, en revanche j’ai identifié le moment où je perds le copy ID, c’est quand je change une clé fonctionnelle de mon objet avant d’enregistrer (pour ne pas avoir de duplicate).
J’ai fait un setValue de cette valeur dans le preValidate pour ne pas avoir à cliquer sur la loupe et select la clé, mon cascade copy fonctionne.

Maintenant je debug pour comprendre ce qui fait disparaître mon copy ID au moment du Select.

François, je pense que le problème n’est pas spécifique à mon objet.
Je reproduis ailleurs :

  • copy
  • cliquer sur la loupe d’un champ référence, Close même sans sélectionner
  • save

→ le cascade copy ne fonctionne pas