Si vous êtes amené à travailler avec Commerce Funds un jour, vous voudrez sans doute, à un moment donné, utiliser Rules pour profiter de la fléxibilité qu'il offre. Notamment pour vous permettre de créer des formulaires personnalisés.
Petite piqûre de rappel, Rules vous permets de déclancher des évènements de manière conditionnels. C'est à dire, dans notre cas, que vous pouvez par example déclancher l'exécution d'une transaction après qu'un utilisateur ait soumit une nouvelle page, et c'est ce que nous allons voir ici.
On va donc s'intéresser ici à la création d'un formulaire personnalisé pour déclancher une transaction de type "Escrow payment". Dans un premier temps, on va créer un type de contenu "escrow payment" qui nous servira de support pour collecter les données de notre transaction.
Il nous faudra deux champs obligatoires: le montant de la transaction et le destinataire qui recevra l'argent envoyé. Pour le champ "montant" on utilisera un champ de type "Price" et pour le destinataire une "entity reference". Voir ci-dessous:
A cette étape, l'utilisateur peut remplir un formulaire en indiquant un montant et un destinataire du montant, mais aucun transfer d'argent ne sera effectué. C'est là que Rules entre en jeu. Pour exécuter la transaction il va nous falloir créer une Rules sur l'énèvement "After saving a new content item" avec les conditions et actions détaillés plus bas. Voici ce qu'on obtiendra à la fin:
Conditions
Pour que Rules puisse voir les champs de notre type de contenu "escrow payment" on va devoir lui indiquer qu'on veut s'intéresser à ce type de contenu spécifique. On va donc ajouter une condition "Entity is of bundle" avec les paramètres ci-dessous:
- entity = data_selector : node,
- type = value : node,
- bundle = value : content_type_machine_name (ici, escrow_payement).
En faisant ça on va voir apparaitre tous les champs de notre types de contenu dans les actions de Rules, comme par exemple "Create a new transaction", et c'est ce que l'on va faire.
Actions
Notre action se déroulera en 3 parties:
- On crée la transaction en indiquant les différentes informations requises remplis par l'utilisateur lorsqu'il a remplis le formulaire "escrow payment".
- On la sauvegarde dans la base de donnée.
- On l'execute pour que les différentes balances utilisateurs soient mise à jours respectivement.
1. Une transaction est bien plus compliquée que simplement indiquer la valeur du montant et du destinataire. C'est un objet (au sens programmatique) contenant différentes informations tells que la date et l'heure à laquelle la transaction a été effectuée, la devise utilisée, l'expéditeur etc. Pour créer une transaction il faudra fournir à Rules toutes ces informations. On va donc créer une action "Create a new transaction" avec les paramètres suivants:
- Transaction type = value : escrow (Commerce Funds fournit 6 types de transactions qu'on peut utiliser ici: deposit, transfer, escrow, payment, withdrawal_request, conversion)
- Issuer = data_selector : node.uid.target_id (c'est l'auteur du noeud, le compte utilisateur ayant remplis le formulaire)
- Recipient = data_selector : node.field_to_user.target_id (le destinaire, le compte utilisateur indiqué dans field_to_user)
- Payment method = value : internal
- Brut amount = data_selector : node.field_amount.number (le montant entré par l'utilisateur)
- Net amount = value : empty (laisser vide car Commerce Funds le remplira pour nous après calcul des frais appliqués)
- Fee = value : empty||value. Ici on peut le laisser vide si on veut utiliser la configuration utilisé dans Commerce Funds ou mettre une valeur pour la remplacer.
- Currency = data_selector: node.field_amount.currency_code (la devise utilisé par le champ "montant")
- Status = value : Canceled (sensible à la casse. 3 possibilités: Pending, Completed, Cancelled).
Si vous avez configuré un champs pour que l'utilisateur puisse laissé un message avec la transaction, on pourra l'implémenter en ajoutant deux actions de type "set a data value". C'est un peu compliqué mais malheureusement Rules n'en est qu'à sa seconde milestome et j'espère que ça sera plus simple dans le futur.
En premier, "set a data value":
- Data = data_selector : entity.notes (c'est la note laissé par l'utilisateur côté transaction)
- Value = data_selector : node.body.value (on séléctionne le champs "body.value")
Le problème quand on fait ça c'est qu'on indique simplement le contenu de la note dans la transaction et pas son format. C'est à dire que si la note contient du HTML il sera retourné comme "plain text" ce qu'on ne veut pas. Le meilleur moyen de résoudre ce problème est d'ajouter une nouvelle action "set a data value" avec le format.
En second, "set a data value":
- Data = data_selector : entity.notes.format
- Value = data_selector : node.body.format
Maintenant que notre transaction est prête, il nous faut la sauvegarder dans la base de donnée.
2. On ajoute une action "Save entity":
- Entity = data_selector : entity (Transaction) (Par exemple commerce_funds_transaction_created)
- Force saving = 1 (pour forcer la sauvegarde, 0 sinon)
A cette étape, on a créé une entité transaction, on l'a sauvegardé dans la base de donnée, mais on a toujours pas réellement executé la transaction. Aucune balance utilisateur n'a été mise à jour.
3. Ajouter une action "Perform transaction":
- Transaction = data_selector : entity (Transaction) (Par exemple commerce_funds_transaction_created)
Cette dernière action mettra à jours les balances utilisateurs, débitant l'emmeteur et créditant le destinataire.
Validation du formulaire
Il nous reste tout de même un problème à résoudre, c'est la validation du formulaire. Que ce passe-t-il si un utilisateur veut transférer un montant supérieur à ce qu'il a de disponible sur son compte?
On va devoir implémenter des "hook validations" dans un module personnalisé. Si vous ne savez pas comment créer une module personnalisé pour Drupal, je vous renvois à la documentation que vous trouverez ici.
Commerce Funds implémente deux "constraints validations". On va utiliser ces deux constraints pour les appliquer sur les champs de notre formulaire personnalisé. Il va nous falloir pour cela utiliser une hook_entity_bundle_field_info_alter().
Dans le fichier module_name.module ajouter les lignes suivantes:
/**
* Implements hook_entity_bundle_field_info_alter().
*
* Alter custom form for validation.
*/
function MODULE_NAME_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
if ($entity_type->id() == 'node' && $bundle == 'CONTENT_TYPE_MACHINE_NAME') {
if (!empty($fields['FIELD_AMOUNT_MACHINE_NAME'])) {
$fields['FIELD_AMOUNT_MACHINE_NAME']->addConstraint('NetAmountBelowBalance'); // Add the validation set "NetAmountBelowBalance"
}
if (!empty($fields['FIELD_RECIPIENT_MACHINE_NAME'])) {
$fields['FIELD_RECIPIENT_MACHINE_NAME']->addConstraint('IssuerEqualsCurrentUser'); // Add the validation set "IssuerEqualsCurrentUser"
}
}
}
On va devoir remplacer tout ce qui est en majuscule par la configuration établie dans notre formulaire. Dans le cas de ce tutoriel ça sera:
/**
* Implements hook_entity_bundle_field_info_alter().
*
* Alter custom form for validation.
*/
function MODULE_NAME_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
if ($entity_type->id() == 'node' && $bundle == 'escrow_payment') {
if (!empty($fields['field_amount'])) {
$fields['field_amount']->addConstraint('NetAmountBelowBalance');
}
if (!empty($fields['field_to_user'])) {
$fields['field_to_user']->addConstraint('IssuerEqualsCurrentUser');
}
}
}
Si vous avez plusieurs types de contenus pour chaque type de transaction vous pouvez simplement ajouter le bloc de code entier incluant le premier "If" (if bundle == "OTHER_CONTENT_TYPE") et le configurer avec les informations de votre formulaire personnalisé.
En faisant ça on obtiendra:
Et voilà, on vient de voir comment utiliser Rules avec Commerce Funds pour utiliser des formulaires personnalisés plutôt que ceux fournit par le module. Si vous avez des questions n'hésitez pas à les mettre en commentaires.