Génération d'un échéancier de plusieurs lignes

Bonjour,

Dans l’écran « Echéancier de paiement » (ImmoPaySchedule), l’utilisateur doit saisir un montant, une date de paiement ainsi qu’un déclencheur (énuméré).

Si ce dernier est différent de « Abonnement », on insère une ligne avec les informations renseignées, sinon, on doit générer plusieurs lignes (en fonction de la périodicité qui se trouve dans son contrat)
J’ai essayé de coder cela dans le postCreate de l’objet :

	if (trigger == 10) {      // Abonnement
		if (type == 10) {     // Contrat
			amount = amountCont/freqCont;
			ps_amount.setValue(amount);
			for (var i = 0; i < freqCont; i++) {
				ps_ExpectDate = Tool.shiftMonths(ps_ExpectDate,12/freqCont);					
				console.error("NewDate = " + i + " " + ps_ExpectDate);			
				console.error("amount = " + i + " " + amount);			
				var o = this.getGrant().getTmpObject("ImmoPaySchedule");
				o.setRowId(ObjectField.DEFAULT_ROW_ID);
//				o.resetValues(true);	
				o.setFieldValue("payScheduleTrigger",trigger);
				o.setFieldValue("payScheduleExpectDate", ps_ExpectDate);		
				o.setFieldValue("paySchedulePerccentage", perccentage);
				o.setFieldValue("payScheduleAmount",amount);		
				o.setFieldValue("payScheduleType", type);
				o.setFieldValue("payScheduleImmoContractId", this.getFieldValue("payScheduleImmoContractId"));		
				o.setFieldValue("contractId", conId);	
				new BusinessObjectTool(o).validateAndSave(); 
			}

J’obtiens l’erreur suivante :

Des lignes (incomplètes) pourtant ont été insérées :

Echeancier%20Liste
Pourriez-vous me dire svp qu’elle est la meilleure façon de coder cela ?

PS 1 : La clé fonctionnelle de cet objet est alimentée par une valeur par défaut :

[EXPR:Tool.format("ECHEA-%05d", Long.valueOf([ROWID]))]

PS 2 : Quand j’exécute “o.resetValues(true)”, le traitement tourne en boucle.

Merci d’avance pour votre aide.
Abed.

En première lecture:

Retirez ça:

o.setRowId(ObjectField.DEFAULT_ROW_ID);

Et remettez ça (avec le default row ID explicite si un de vos champs est calculé avec le row ID):

o.resetValues(true, ObjectField.DEFAULT_ROW_ID);	

Je ne comprends pas bien ce que vous voulez dire par “le traitement tourne en boucle” mais clairement le resetValues n’est en aucun cas facultatif pour une création de records d’objet par code.

Ensuite pour optimiser ne mettez pas le this.getGrant().getTmpObject("ImmoPaySchedule") et le new BusinessObjectTool(o) dans la boucle mais avant la boucle.

Merci @david pour votre retour. Mais j’ai toujours des pb avec cette génération d’échéancier.

J’ai bien mis dans la boucle o.resetValues(true, ObjectField.DEFAULT_ROW_ID);

Quand je mets le new BusinessObjectTool(o) avant la boucle, j’ai l’erreur de validation des attributs obligatoire :

Quand je le mets dedans, j’obtiens l’erreur :

Quand je le mets après, j’obtiens l’erreur :

On voit dans la console qu’au début (partie the_ajax_ImmoPaySchedule) , la boucle se passe bien, mais que seulement la dernière occurrence est insérée (comme s’il est en train de récrir avec le même rowid).

En plus, au lieu de s’arrêter là, on voit dans la log qu’il y a ensuite une partie tmp_ImmoPaySchedule, où on dirait que les valeurs de i et du freqCont sont perdues dans les deux derniers cas (dedans et après la boucle). Du coup sa tourne jusqu’à l’erreur HTTP 405. (environ 360 lignes!)


La première ligne, est celle des valeurs des attributs renseignés et que je voudrais pas qu’il les insère puisqu’on est dans le cas “Abonnement”.
La deuxième ligne est correcte.
A partir de la troisième et jusqu’à la fin : Lignes erronées (date, montant,…)

Merci de mettre votre code, sans ça je ne peux pas vous aider

Le voici :

ImmoPaySchedule.postCreate = function() {
	var amountCont    = this.getField("contractAmountTtc").getDouble(0);
	var amountWork    = this.getField("workAmountTtc").getDouble(0);
	var amountContAss = this.getField("contractAssAmountTtc").getDouble(0);
	var freqCont      = this.getField("contractPayFreq").getValue();
	var freqContAss   = this.getField("contractAssPayFreq").getValue();
	var type          = this.getField("payScheduleType").getValue();
	var trigger       = this.getField("payScheduleTrigger").getValue();	
	var perccentage   = this.getField("paySchedulePerccentage").getDouble(0);	
	var ps_amount     = this.getField("payScheduleAmount");	
	var ps_ExpectDate = this.getField("payScheduleExpectDate").getValue();	
	var conId		  = this.getField("contractId").getValue();	
	var amount = 0 ;

	if (trigger == 10) {      // Abonnement
		if (type == 10) {     // Contrat
			amount = amountCont/freqCont;
			ps_amount.setValue(amount);
			var o = this.getGrant().getTmpObject("ImmoPaySchedule");
			for (var i = 0; i < freqCont; i++) {
				ps_ExpectDate = Tool.shiftMonths(ps_ExpectDate,12/freqCont);					
				console.error("freqCont = " + i + " " + freqCont);							
				console.error("NewDate = " + i + " " + ps_ExpectDate);			
				console.error("amount = " + i + " " + amount);			
				o.resetValues(true, ObjectField.DEFAULT_ROW_ID);	
				o.setFieldValue("payScheduleTrigger",trigger);
				o.setFieldValue("payScheduleExpectDate", ps_ExpectDate);		
				o.setFieldValue("paySchedulePerccentage", perccentage);
				o.setFieldValue("payScheduleAmount",amount);		
				o.setFieldValue("payScheduleType", type);
				o.setFieldValue("payScheduleImmoContractId", this.getFieldValue("payScheduleImmoContractId"));		
				o.setFieldValue("contractId", conId);	
			}
			new BusinessObjectTool(o).validateAndSave(); 						
		} else {				// Contrat d'aasurance
			amount = amountContAss/freqContAss;
			ps_amount.setValue(amount)}
	} else {                	// Différent d'abonnement
			if (type == 10) {	// Contrat
				amount = amountCont*(perccentage/100);
				ps_amount.setValue(amount)}               
			if (type == 20) {	// Travaux
				amount = amountWork*(perccentage/100);
				ps_amount.setValue(amount)}
			if (type == 30) {	// Contrat d'aasurance
				amount = amountContAss*(perccentage/100);
				ps_amount.setValue(amount)}
		  }
};

Le save en dehors de la boucle n’est sans doute pas ce que vous voulez.

Votre code devrait ressembler à ça:

var obj = this.getGrant().getTmpObject(...);
var objt = new BusinessObjectTool(obj);
for (...) {
    try {
        obj.resetValues(true, ObjectField.DEFAULT_ROW_ID);
        obj.setFieldValue(..., ...);
        ...
        objt.validateAndSave();
    } catch(e) {
        console.error(e);
    }
}

Bien entendu un tel code suppose que les records créés dans la boucle n’existent pas sinon forcément il y aura des erreurs de clé fonctionnelle en doublon.

Donc si votre boucle contient potentiellement aussi des updates il faut commencer par faire une recherche pour récupérer le record s’il existe ou sinon le reseter, genre:

var obj = this.getGrant().getTmpObject(...);
var objt = new BusinessObjectTool(obj);
for (...) {
    try {
        obj.resetFilters();
        // Set functional key fields' filters
        obj.getField(...).setFilter(...);
        ...
        var rows = objt.search(false);
        if (rows.size() == 0) {
            obj.resetValues(true, ObjectField.DEFAULT_ROW_ID);
            // Set functional key fields' values
            obj.setFieldValue(..., ...);
        } else
            obj.setValues(rows.get(0));
        // Set non functional key fields' values
        obj.setFieldValue(..., ...);
        ...
        objt.validateAndSave();
    } catch(e) {
        console.error(e);
    }
}

J’ai toujours le même problème.

J’ai utilisé le premier code puisqu’il n’y aura que des nouvelles lignes à insérer. j’ai juste remplacé le resetFilters par resetValues :

			var obj	= this.getGrant().getTmpObject("ImmoPaySchedule");
			var objt = new BusinessObjectTool(obj);
			for (var i = 0; i < freqCont; i++) {
				try {
					obj.resetValues(true, ObjectField.DEFAULT_ROW_ID);
					ps_ExpectDate = Tool.shiftMonths(ps_ExpectDate,12/freqCont);					
					console.error("freqCont = " + i + " " + freqCont);							
					console.error("NewDate = " + i + " " + ps_ExpectDate);			
					console.error("amount = " + i + " " + amount);			
					obj.resetValues(true, ObjectField.DEFAULT_ROW_ID);	
					obj.setFieldValue("payScheduleTrigger",trigger);
					obj.setFieldValue("payScheduleExpectDate", ps_ExpectDate);		
					obj.setFieldValue("paySchedulePerccentage", perccentage);
					obj.setFieldValue("payScheduleAmount",amount);		
					obj.setFieldValue("payScheduleType", type);
					obj.setFieldValue("payScheduleImmoContractId", fk_Contract);		
					obj.setFieldValue("contractId", contraId);	
					objt.validateAndSave();
				} catch(e) {
					console.error(e);
				}
			}			

Dans la console :

Et dans l’objet :

Echeancier%20erreur%20nouveau%20code%202

Oui il y avait effectivement une coquille dans mes exemples j’ai corrigé en resetValues(true, ObjectField.DEFAULT_ROW_ID)

Dans votre code pourquoi utilisez vous console.error et pas console.log (ou console.info) pour vos traces de debug ?

Et pourquoi faites vous 2 fois un obj.resetValues(true, ObjectField.DEFAULT_ROW_ID); ? Ca ne sert à rien.

Pour le reste je ne vois pas de pb dans ce que vous avez posté: je ne vois pas de traces d’erreur (à part vos traces de debug qui ne sont pas des erreurs) et je vois bien des records créés.

Expliquez moi en quoi vous considérez qu’il y a un pb ?

Pour les console.error, c’est juste pour que ça apparaisse en rouge quand je consulte la console (phase dev) ensuite c’est retiré.
Pour le double resetValues, je l’avais vu, j’ai retiré le deuxième, le problème reste le même.

Le problème est que je cherche à générer 4 lignes dans un échéancier (trimestrielle), or comme vous voyez, il généré plus de 300 lignes avant de planter en HTTP 405.

En plus, parmi les lignes qui sont insérés, il n’y a que la deuxième qui est juste (bonne date et bon montant). La première ne devrait pas s’ajouter, les autres sont erronées et en tout je ne doit avoir que 4 lignes.

Dans les lignes rouges, on voit bien que la valeur de freqCont = 0 alors qu’elle devrait être à 4.
Pareil pour le compteur i qui apparaît à 0 tout le temps dans cette nouvelle version du code.

J’ai vidé l’objet, et j’ai relancé la génération de l’échéancier trimestriel, qui est censé contenir que 4 lignes, et je me retrouve avec plus de 600 lignes :

De quel type est votre variable freqCont ?
Si ce n’est pas un entier (mais par exemple un string) forcément ça bouclera à l’infini…

la valriable freqCont est alimenté depuis l’attribut : var freqCont = this.getField("contractPayFreq").getValue();

Qui lui est un énuméré :

Dans ma dernière copie d’écran de la console, on voit que cette variable contenait bien un ‘4’ au début, mais qu’elle a été remplacé par ‘12’ (valeur par défaut de l’attribut). En plus, le i de la boucle reste à 0 (toujours dans la console).

C’est comme si à chaque ValidateAndSave, il repasse par ce postCreate et il réinitialise toutes les variables, et donc, on tourne à l’infini.

ObjectField.getValue renvoie un string. Vous devez utiliser Tool.parseInt pour en faire un vrai entier ("4"et 4 ce n’est pas la même chose, même quand on utilise un language de script loosely typed, et si vous utiliser une valeur string comme borne dans une boucle ça fera n’importe quoi, il n’y a rien de specifique à Simplicité ici)

J’ ai utilisé parseInt dans la valorisation de la variable freqCont :
var freqCont = Tool.parseInt(this.getField("contractPayFreq").getValue());
Le problème pour moi reste le même :

Le i ne s’incrémente pas, et la valeur de freqCont est réinitialisée à 12 (au lieu de 4)

C’est forcément un pb dans votre code, une boucle for ça marche parfaitement.

Forcément une boucle for marche très bien (j’ai déjà eu l’occasion de l’utiliser), mais là, c’est une boucle for qui est dans le postCreate et surtout avec un ValidateAndSave. Ce n’est peut-être pas possible. Non ? (ça se morde la queue ?)

Le pb est forcément dans ce qui se passe dans votre boucle (peut être un pb de scoping de vos variables ou dans le genre). Il y a plein de choses que vous pouvez changer pour tester à commencer par le nom de vos variables.

J’ai fait en sorte que les noms de variables soient unique et différent des noms des attributs. Qu’est ce que vous préconisez ?

Je pense que j’ai trouvé une solution, c’est de sortir cette génération d’échéancier du postCreate.
J’ai ajouté une nouvelle action et j’ai mis le même code dedans. Et ça a l’air de marcher.
Je continue de tester…

Je n’avais pas noté que c’était le même objet qui est utilisé dans votre boucle. Vous vous retrouvez donc dans une logique recursive (le postCreate fait des créations qui rappellent forcement le postCreate etc) et forcément ça part en boucle et rapidement ça fait n’importe quoi si vous ne mettez pas une condition de fin à votre récursion.