Attribut d'action multi-Documents vers un autre objet avec attribut document simple

Request description

Bonjour,

Je cherche à déposer via une action plusieurs documents via un champ DocumentSet multi-doc (attribut d’action) et de les répartir dans des enregistrements distincts d’un autre objet Content, où chaque enregistrement contiendra un attribut document unique.

Je rencontre des difficultés à mettre cela en œuvre,avec la doc j’ai réussi à faire le cas nominal d’un docSimple vers un objet avec attribut document simple


mais le cas avec un attribut d’action multi-document vers un attribut document simple est compliqué . Pourriez-vous m’indiquer la meilleure façon de procéder pour atteindre cet objectif ?

J’ai essayé de voir au niveau des logs ce que getDocuments() me recupere, il me recupere bien les 2 documents mais sans ID ainsi que 4 autre documents non selectionner avec leurs ID, je dois donc mal utiliser cette méthode

Y-a-il une autre méthode ou un code générique pour ce cas générale.

Merci d’avance pour votre aide sur ce point.

Cordialement,

Steps to reproduce

This request concerns an up-to-date Simplicité instance
and these are the steps to reproduce it:

1.Code action java

public String addDocumentWithLanguage(Action action) {

    String lang = getGrant().getLang();

    ObjectField multiDocField = action.getConfirmField(lang, "DocumentSetContent");
    AppLog.info(getClass(), "addDocumentWithLanguage", "multiDocField: " + multiDocField , getGrant());

    DocumentDB multiDocs = multiDocField.getDocument(); //BON
    
    
    List<DocumentDB> docs = multiDocs != null ? multiDocs.getDocuments() : null;
    
	AppLog.info(getClass(), "addDocumentWithLanguage", "Nombre de documents récupérés: " + (docs != null ? docs.size() : 0), getGrant());
	
	if (docs != null) {
    for (DocumentDB doc : docs) {
        AppLog.info(getClass(), "addDocumentWithLanguage", "Document path: " + doc.getPath(), getGrant());
        AppLog.info(getClass(), "addDocumentWithLanguage", "Document rowId: " + doc.getId(), getGrant());
	        }
	    }
   	return null;
}

2.Log associé dans technical information

Technical information

Instance /health
[Platform]
Status=OK
Version=6.1.2
BuiltOn=2024-08-05 20:47
Git=6.1/467048f75ed27520b8cf7eca0ba57eeb2e0e1a0f
Encoding=UTF-8
EndpointIP=100.88.207.56
EndpointURL=http://lbc-77449-app-f4787f7fc-p9r27:8080
TimeZone=Europe/Paris
SystemDate=2024-08-27 17:42:20```
Simplicité logs
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document rowId: 0
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document path: DOC-MULTI-2.rtf
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document rowId: 0
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document path: DOC-MULTI1.rtf
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document rowId: 18835
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document path: LbcLegalText/DocumentSetContent/0/50/CGU_ID_Connect_FR.rtf
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document rowId: 18858
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document path: LbcLegalText/DocumentSetContent/0/50/CGU_ID_Connect_FR.rtf
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document rowId: 19054
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document path: LbcLegalText/DocumentSetContent/0/50/LEBONN.rtf
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document rowId: 19106
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Document path: LbcLegalText/DocumentSetContent/0/50/LEBONN.rtf
2024-08-27 17:35:38,208|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: Nombre de documents récupérés: 6
2024-08-27 17:35:38,206|SIMPLICITE|INFO||http://lbc-77449-app-f4787f7fc-p9r27:8080||INFO|p124722|com.simplicite.objects.RenaultLBC.LbcLegalText|addDocumentWithLanguage||Evénement: multiDocField: {"precision":0,"rendering":"MBOX","label":"Document","type":17,"fullinput":"DocumentSetContent","filter":"%","input":"DocumentSetContent","default":"","size":11,"name":"DocumentSetContent","id":"8925","value":"","oldvalue":""}```
Browser logs
---paste content of the **relevant** browser-side logs---
Other relevant information

----E.g. type of deployment, browser vendor and version, etc.----

Bonjour,

Merci pour votre retour.
Très étrange en effet, nous n’avons pas de cas d’usage de multi-docs dans un attribut d’action.
A mon avis, ce n’est pas bien traité, il y a un problème de “remise à zéro” du champ pour ne recevoir que les documents postés par l’action.

Nous allons investiguer ce point.

Bonjour Francois, merci pour ce retour rapide,

petit élément de réponse,les documents qui étaient non sectionnée était dans la liste des documents de l’attribut ( relié à l’attribut d’action) , après suppression je n’ai plus eu ce problème.

Le nouveau problème que je rencontre est le suivant, lorsque je log le DocumentDB ainsi que la liste des documents docs, j’ai bien mes 2 documents, mais je ne retrouve ni l’id ni la size pour continuer les règlages au niveau du back-end, si cela peut aider.

Je ne reproduis pas le problème.

Etes vous sur de n’utiliser le champ “DocumentSetContent” uniquement dans l’action ?
Il faut créer des attributs dédiés à l’action pour éviter toute collision avec d’autre code qui les utiliserait.

Chaque document posté est temporaire, il faut donc les récupérer et les détruire une fois utilisés :

if (doc!=null) {
	// Uploaded tmp file from UI
	java.io.File file = doc.getUploadFile();
	AppLog.info(getClass(), "myAction", "DOCUMENT FILE " + file, getGrant());
	// do something with the document...
	// You must remove the file from /tmp directory when used
	file.delete();
}

cf

Attention, les documents de l’action ne sont pas en mémoire (heap java) mais bien stockés dans le répertoire /tmp de la plateforme. A vous d’utiliser les API qui manipulent des fichiers par stream buffer pour éviter de saturer la mémoire lorsque vous allez stocker le fichier ailleurs (il faut éviter de monter le fichier en mémoire genre en byte array).

N’hésiter pas à demander du support si vous n’y arrivez pas.

Bonjour,

Oui le champ “DocumentSetContent” n’a été créer que pour être dans cette attribut d’action.

Merci je vais ajouter ce code pour cloisonner cette partie. Concernant l’ID des documents dois-je faire autre chose pour pouvoir les Récuperer ? Lorsque je log les id sont null pour les documents importés.

Je vais voir la documentation concernant la dernière partie de la réponse, merci :slight_smile:

Il n’y a pas d’ID car le document de l’action n’est pas en base mais juste uploadé dans /tmp.
Il faut récupérer le fichier via doc.getUploadFile(), et le supprimer une fois utilisé pour vos besoins.

1 Like

ok petite remarque niveau syntaxique :

  • il est conseillé de prefixer un attribut par le nom de l’objet ou de l’action, ou a minima par le trigramme du module.
  • de commencer par une minuscule pour un attribut et une majuscule pour un nom d’objet

exemple : myappLegalConfirmMultiDocs

Ceci pour éviter tout conflit avec d’autres projets qui seraient hébergés sur la même instance.

Bonjour François,

Merci encore pour tes retours rapides et conseils sur les bonnes pratiques de typage. :slight_smile:

J’ai finalement pu résoudre le problème en utilisant les méthodes setFieldRef() et setObjectRef() de la documentation JAVA. Un document doit obligatoirement avoir un ID après avoir été associé à l’objet. J’ai donc utilisé ces méthodes pour modifier le comportement de doc() afin que tout soit correctement enregistré dans l’objetContent et dans l’attribut ContentDoc, sans avoir à affecter directement l’attribut lié à l’action (lbcMultiDocumentConfirm).

Après avoir appliqué ces changements, les documents sont bien créés dans la table Content pour chaque enregistrement, et les associations sont correctement établies dans la table de croisement.

Ci-dessous, je partage le code correspondant. Le cas d’utilisation général de cette action est de permettre, via un attribut document-multiple, d’enregistrer ses documents dans un autre objet avec un document par attribut document-simple.

public String addDocumentWithLanguage(Action action) {

		String legalTextId = getRowId();
    	String lang = getGrant().getLang();
    	
		ObjectField documentField = action.getConfirmField(lang,"lbcMultiDocumentConfirm");
		DocumentDB documentContent = documentField != null ? documentField.getDocument() : null;
	    List<DocumentDB> docs = documentContent != null ? documentContent.getDocuments() : null;


		if (docs != null) {
        for (DocumentDB doc : docs) {
        	if (doc!=null) {

		    doc.setFieldRef("ContentDoc");
		    doc.setObjectRef("LbcContent");

	        ObjectDB content = getGrant().getTmpObject("LbcContent");
	        content.resetValues(true);
	        content.create();
	        content.setFieldValue("ContentId", content.getRowId()); 
	        content.setFieldValue("ContentDoc", doc); 
	        content.setFieldValue("ContentLanguage", "FRA"); // Francais 
	        content.save();
	
	        ObjectDB legalTextContent = getGrant().getTmpObject("LbcLegalTextContent");
	        legalTextContent.resetValues(true);
	        legalTextContent.create();
	        legalTextContent.setFieldValue("LegalTextIdContent", legalTextId);
	        legalTextContent.setFieldValue("LegalTextContentId", content.getRowId());
	        legalTextContent.save();
        }}}
    	return null;

}

.

Ok si ça fonctionne, mais le code cache des erreurs

  • création sans données/clé fonctionnelle puis modification, ce qui semble impossible sauf à avoir un problème de modèle (objet sans champ obligatoire ni clé fonctionnelle)
  • pas de validation avant de faire le save pour controler les données, et surtout passer par les hooks pre/postValidate éventuels
  • code non thread-safe : si un utilisateur ou un job asynchrone dans cette session utilise la même instance tmp, il faut synchroniser l’usage de l’instance

Exemple de code correct :

ObjectDB legalTextContent = getGrant().getTmpObject("LbcLegalTextContent");
synchronized (legalTextContent.getLock()) {
  try {
    // prepare the creation with row_id=0
    legalTextContent.resetValues(true, ObjectField.DEFAULT_ROW_ID);
    // legalTextContent.create(); A retirer car on ne peut pas créer un objet sans sa clé fonctionnelle unique, il y a surement des erreurs en SQL sur la user key
    // Set values
    legalTextContent.setFieldValue("LegalTextIdContent", legalTextId);
    legalTextContent.setFieldValue("LegalTextContentId", content.getRowId());
    // Save = helper pre/postValidate + preSave + pre/postCreate + postSave
    legalTextContent.getTool().validateAndSave(); 
  }
  catch (ValidateException | SaveException e) {
	return "Save error: " + e.getMessage();
  }
}
1 Like

This topic was automatically closed 60 minutes after the last reply. New replies are no longer allowed.