Problème notifications automatique

Actuellement en train de finaliser la configuration d’un dolibarr version 20 (installé sur une VM debian 12, avec php 8.2), je suis confronté à un problème concernant le module Notification par email. En effet, je l’ai configuré pour que des notifications mail partent à différentes étapes, cependant, j’ai remarqué quelques comportements étranges :

  • Je reçois toutes les notifications mails dans les bonnes boites mail, mais certaines n’ont pas de pièces jointes alors qu’elles sont configurées pour être accompagnées de leurs pièces jointes.
  • Lorsque je valide une proposition commerciale cliente ou une commande cliente, une notification mail part mais concernant le brouillon de la proposition ou commande (avant l’ajout des produits) et sans pièce jointe.
  • Lors des commandes fournisseur aucunes des notifications mail configuré n’est envoyé

Je vous donne un exemple plus détaillé pour illustrer :

Lorsque je crée une proposition commerciale (cliente) cela me crée un brouillon, lors de mon dernier test elle s’appelait : (PROV48)

Lorsque je clique sur valider dolibarr me prévient que la proposition s’appellera désormais PR2504-0005 et qu’une notification va être envoyé. La notification est bien envoyée et reçu, mais concerne (PROV48), sans pièce jointe (alors que le modèle précise de joindre le pdf en pièce jointe) et avec un lien de signature pour (PROV48) qui n’a pas encore de produit et est donc indiqué 0 euros et impossible de signer/accepter. Si je fais modifier puis valider (à nouveau) alors le mail de notification part et concerne cette fois PR2504-0005 avec le lien de signature qui fonctionne (bon prix, bonne référence de proposition et signature possible). Je peux même modifier et valider à nouveau en ajoutant/enlevant des produits dans la proposition et chaque fois les modifications sont prises en compte dans la notification mail. D’où ma question : pourquoi le premier mail de notifications part-elle à l’état de brouillon (sans la prise en compte de l’ajout de produit et la génération de pdf et de lien de signature) ? Comment éviter cela ?

Une fois la proposition commerciale signée (via le lien de signature), la proposition est considérée comme accepté et une notification mail part avec la proposition en pièces jointe, mais non signées

J’ai un problème similaire à l’étape de commande (cliente). C’est à dire qu’elle part aussi à l’état de brouillon et sans pièce jointe

J’ai monté progressivement la version de mon installation dolibarr jusqu’à la version 20.0.4, espérant que cela résoudrait mon problème mais cela n’a rien changé.

Quelques info concernant ma configuration mail :

Si quelqu’un a une idée, je suis preneur. Pour ma part j’envisage de passer à la version 21 (en croisant les doigts) ou d’aller trifouiller dans le code de dolibarr pour essayer de modifier les trigger (mais bon, je suis pas développeur).
Merci d’avance.

Bonjour,

Je suis moi-même confronté à ce problème. En gros, les événements déclenchant l’envoi des notifications sont envoyés AVANT la (re)création des documents.
Ainsi, les pièces-jointes intégrées dans les emails sont à l’état précédant la mise à jour réalisée sur les fiches métier.

Y a pas vraiment de solution, si ce n’est de se retrousser les manches et de modifier le code soi-même :confused:

– EDIT

La solution que j’ai employée pour palier à ce problème a été de créer un module dans lequel, pour chaque événément associé à une notification (ex: ORDER_VALIDATE) :

  • créer un nouvelle notification MY_XXX_XXX (ex: MY_ORDER_VALIDATE) ;
  • déclarer cette notification dans le hook notifsupported du fichier class/actions_xxxxx.class.php de votre module :
  function notifsupported($parameters, &$object, &$action, $hookmanager) {
    $this->results = array(
      'arrayofnotifsupported' => [
        'MY_ORDER_VALIDATE',
      ]
    );
    return 0;
  }
  • associer l’envoi de l’email à la nouvelle notification ainsi créée ;
  • créer une fonction chargée d’être exécutée lors de l’émission de l’événement surchargé (ici: ORDER_VALIDATE) dans le fichier core/triggers/core/triggers/interface_99_modXXXX_XXXXTriggers.class.php :
   // [...]

  public function runtrigger($action, $object, $user, $langs, $conf) {
    if (!isModEnabled('xxx')) {
      return 0; // If module is not enabled, we do nothing
    }

    $result = 0;
    if ('ORDER_VALIDATE' == $action) {
      $result = $this->processOrderValidate($object, $user, $langs, $conf);
    }
    return $result;
  }

  private function processOrderValidate($order, $user, $langs, $conf) {
    // prepare document
    $templateused = $order->model_pdf;
    if (0 >= $order->fetch($order->id) || 0 >= $order->generateDocument($templateused, $langs)) {
      dol_syslog('Cannot generate order document: '.$order->error, LOG_ERR);
      return 1;
    }

    // raise the event that'll send the email
    $tmp = $order->element;
    $order->element = 'order';
    $order->call_trigger('MY_ORDER_VALIDATE', $user);
    $order->element = $tmp;

    return 0;
  }

Ici, on a donc un trigger sur ORDER_VALIDATE qui va se charger de lancer la génération du document associé à la commande. Une fois celle-ci réalisée, il se charge de lever un nouvel événement MY_ORDER_VALIDATE qui sera intercepté par le module de notification qui pourra alors faire son boulot.

C’est un peu moche étant donné que le document sera en tout généré 2 fois (par notre méthode, et aussi par le code Dolibarr suite à la gestion du trigger ORDER_VALIDATE)…

Mais on obtient ce que l’on veut.

Merci beaucoup pour ta réponse très complète. Je vais passer à la version 21, au cas où cela suffirait à résoudre le problème. Dans le cas contraire, j’essaierais ta solution.

A priori la v21 ne solutionne pas ce problème.
J’ai le même soucis sur l’envoi de factures acquittées aux clients (une fois qu’ils ont réglé en ligne). Et je suis en v21.
J’avais d’ailleurs ouvert un thread à ce sujet.

Oui, en effet, je suis passé à la version 21, et même à la v21.0.1, et le problème demeure. Je vais devoir attendre que mon collègue développeur revienne pour créer un module dédié à résoudre ce problème.

ou mieux proposer une PR pour améliorer le coeur :slight_smile:

Bonjour,

C’est moi le collègue développeur :grinning_face:

Le problème est surtout lié à l’ordonnancement des tâches.
L’appel au trigger PROPAL_VALIDATE de la méthode est fait avant le $this->db->commit().

De plus le pdf est également généré après.

J’ai donc fait un module qui écoute les hooks :
‹ propalcard ›,
‹ ordercard ›,
‹ invoicecard ›,
‹ expeditioncard ›,
‹ ordersuppliercard ›
et qui surcharge la méthode de validation de chaque classe.

Exemple avec Propal :

require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';

/**
 * MyPropal class - Extended Propal with corrected notification timing
 */
class MyPropal extends Propal
{
    /**
     * Validate a proposal - CORRECTED VERSION
     * This version ensures that notifications are sent AFTER the proposal is saved
     * and has its definitive reference number
     *
     * @param  User   $user      User making validation
     * @param  int    $notrigger 1=Does not execute triggers, 0= execute triggers
     * @return int                Return integer <0 if KO, >0 if OK
     */
    public function valid($user, $notrigger = 0)
    {
      global $conf, $langs;

		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';

		$error = 0;

		// Protection
		if ($this->status == self::STATUS_VALIDATED) {
			dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
			return 0;
		}

		if (!((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'creer'))
		|| (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate')))) {
			$this->error = 'ErrorPermissionDenied';
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
			return -1;
		}

		$now = dol_now();

		$this->db->begin();

		// Numbering module definition
		$soc = new Societe($this->db);
		$soc->fetch($this->socid);

		// Define new ref
		if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
			$num = $this->getNextNumRef($soc);
		} else {
			$num = $this->ref;
		}
		$this->newref = dol_sanitizeFileName($num);

		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
		$sql .= " SET ref = '".$this->db->escape($num)."',";
		$sql .= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".((int) $user->id);
		$sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = ".self::STATUS_DRAFT;

		dol_syslog(get_class($this)."::valid", LOG_DEBUG);
		$resql = $this->db->query($sql);
		if (!$resql) {
			dol_print_error($this->db);
			$error++;
		}

		if (!$error) {
			$this->oldref = $this->ref;

			// Rename directory if dir was a temporary ref
			if (preg_match('/^[\(]?PROV/i', $this->ref)) {
				// Now we rename also files into index
				$sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'propale/".$this->db->escape($this->newref)."'";
				$sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
				$resql = $this->db->query($sql);
				if (!$resql) {
					$error++;
					$this->error = $this->db->lasterror();
				}
				$sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'propale/".$this->db->escape($this->newref)."'";
				$sql .= " WHERE filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
				$resql = $this->db->query($sql);
				if (!$resql) {
					$error++;
					$this->error = $this->db->lasterror();
				}

				// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
				$oldref = dol_sanitizeFileName($this->ref);
				$newref = dol_sanitizeFileName($num);
				$dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref;
				$dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref;
				if (!$error && file_exists($dirsource)) {
					dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
					if (@rename($dirsource, $dirdest)) {
						dol_syslog("Rename ok");
						// Rename docs starting with $oldref with $newref
						$listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
						foreach ($listoffiles as $fileentry) {
							$dirsource = $fileentry['name'];
							$dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
							$dirsource = $fileentry['path'].'/'.$dirsource;
							$dirdest = $fileentry['path'].'/'.$dirdest;
							@rename($dirsource, $dirdest);
						}
					}
				}
			}

			$this->ref = $num;
			$this->statut = self::STATUS_VALIDATED;
			$this->status = self::STATUS_VALIDATED;
			$this->user_validation_id = $user->id;
			$this->datev = $now;
			$this->date_validation = $now;

			if (!$error) {
				$this->db->commit();

				// Trigger calls
				if(!$notrigger) {
					$this->db->begin();
					// Call trigger
					$result = $this->call_trigger('PROPAL_VALIDATE', $user);
					if ($result < 0) {
						$error++;
					}
					// End call triggers
					if (!$error) {
						$this->db->commit();
						return 1;
					}
					else {
						$this->db->rollback();
						return -1;
					}
				}
				return 1;
			} else {
				$this->db->rollback();
				return -1;
			}
			
		} else {
			$this->db->rollback();
			return -1;
		}
    }
}

Mon module se charge aussi de capter le hook ‹ notifsupported › pour aller générer le doc si la notification associée est configurée pour joindre le document. Pour cela j’ai créé une classe MyNotify qui étend Notify et contient plusieurs méthodes de vérifications / génération pour plusieurs type de doc. Je n’ai fais que ceux dont j’ai besoin mais le principe peut s’adapter à tous.

D’après ce que j’ai compris, je ne peux pas faire un PR avec toutes mes modifs. Il faudrait que j’en fasse plusieurs.
J’ai créé une issue sur github à ce sujet : 34357

Bonjour,

J’ai aussi été confronté à ce comportement lors du développement d’un module de notification par SMS à validation de propal, commande etc.
La primo validation renvoyait toujours la ref en PROVxxx.

Pour y remédier j’ai dû écrire cette petite méthode dans le fichier trigger du module

    /**
     * Get object final reference (newref during validation, ref otherwise)
     * This solves the PROVxxx issue during validation triggers
     *
     * @param Object $object Business object
     * @return string        Final reference
     */
    private function getObjectFinalRef($object)
    {
        // During validation, newref contains the final reference
        if (!empty($object->newref) && $object->newref !== $object->ref) {
            dol_syslog('DoliSMS trigger: Using newref [' . $object->newref . '] instead of ref [' . $object->ref . ']', LOG_DEBUG);
            return $object->newref;
        }
        
        // Fallback to regular ref
        return $object->ref ?? '';
    }

Cette solution ne répond qu’au problème de la référence, dans le cas d’une notification par SMS le problème du fichier joint ne se posait pas :slightly_smiling_face: