Merge branch 'features/feature-add-talk-in-h2f' into releases/release-h2f

This commit is contained in:
Tiavina 2025-03-11 14:30:38 +03:00
commit be24ee9bfd
35 changed files with 514 additions and 32 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -44,7 +44,8 @@ class Bdd {
"provider_address","provider_city","fk_provider_id",
"label","fk_order_id","fk_order_item_id","quantity",
"brand","model","immatriculation","purchase_date","fk_vehicle_purchase_type_key","purchase_date",
"thanato_address","thanato_city","thanato_phone","thanato_siret","thanato_company_name");
"thanato_address","thanato_city","thanato_phone","thanato_siret","thanato_company_name",
"portal_code","alarm_code","funeral_code");
$this->whiteTable = array("client", "lieu", "trajet", "devis", "produit_devis", "facture", "produit",
"configuration", "ligne_trajet", "thanato", "article", "defunt", "article_devis",
"bibliotheque", "bijou_defunt", "obs_defunt", "hypo_defunt",
@ -265,7 +266,8 @@ class Bdd {
}
public function getLieux($idNextcloud){
$sql = "SELECT lieu.nom, lieu.adresse, lieu.latitude,lieu.longitude, lieu.user_id, lieu.id
$sql = "SELECT lieu.nom, lieu.adresse, lieu.latitude,lieu.longitude, lieu.user_id, lieu.id,
lieu.portal_code,lieu.alarm_code,lieu.funeral_code
FROM ".$this->tableprefix."lieu as lieu
ORDER BY lieu.id DESC";
return $this->execSQL($sql, array());
@ -542,8 +544,12 @@ class Bdd {
$sql = "SELECT ".$this->tableprefix."devis.id as devisid, ".$this->tableprefix."devis.version, ".$this->tableprefix."devis.comment, ".$this->tableprefix."devis.date, num,"
.$this->tableprefix."devis.id_nextcloud as didnextcloud,".$this->tableprefix."devis.id_client, id_lieu, id_thanato,"
.$this->tableprefix."defunt.id as id_defunt, ".$this->tableprefix."defunt.nom as nom_defunt,"
.$this->tableprefix."client.id as clientid, ".$this->tableprefix."client.nom, ".$this->tableprefix."client.prenom, legal_one, entreprise, telephone, mail, ".$this->tableprefix."client.adresse,"
.$this->tableprefix."client.id as clientid, ".$this->tableprefix."client.nom, "
.$this->tableprefix."client.prenom, legal_one, entreprise, telephone, mail, ".$this->tableprefix."client.adresse,"
.$this->tableprefix."lieu.nom as lieu, ".$this->tableprefix."lieu.adresse as adresse_soin ,"
.$this->tableprefix."lieu.portal_code as portal_code,"
.$this->tableprefix."lieu.alarm_code as alarm_code,"
.$this->tableprefix."lieu.funeral_code as funeral_code,"
.$this->tableprefix."thanato.nom as nom_thanato, ".$this->tableprefix."thanato.prenom as prenom_thanato
FROM ".$this->tableprefix."devis
LEFT JOIN ".$this->tableprefix."client on id_client = ".$this->tableprefix."client.id
@ -3187,4 +3193,89 @@ class Bdd {
return $response;
}
private function getDevisTalkRoomClientContent($clientPrenom){
$message = ". Pour PF: ";
$clientNameContent = "aucun";
$clientPrenomIsSet = $clientPrenom != null && $clientPrenom != "" && $clientPrenom != "-";
if($clientPrenomIsSet){
$clientNameContent = html_entity_decode($clientPrenom);
}
$message .= $clientNameContent;
return $message;
}
private function getDevisByIdWithCalendarDataAndProducts($devisId,$idNextcloud){
$devis = $this->getOneDevis($devisId,$idNextcloud);
$devis= json_decode($devis);
if(empty($devis)){
return null;
}
$devis = $devis[0];
if(isset($devis->numm) && $devis->num == null){
return null;
}
$calendarData = $this->getCalendarDataByCalendarObjectUuid($devis->num);
$devisTimeValue = VCalendarHelpers::GetStartAndEndTimeFromVCalendarString($calendarData);
$devis->startTime = $devisTimeValue["startTime"];
$devis->endTime = $devisTimeValue["endTime"];
$devisProducts = $this->getDevisProduits($devisId);
$devisProducts = json_encode($devisProducts);
$devisProducts = json_decode($devisProducts);
$devis->products = $devisProducts;
return $devis;
}
private function getDevisTalkRoomProductSectionMessage($devisProducts){
if(empty($devisProducts)){
return "";
}
$productMessage = ". ACTE A FAIRE : ";
foreach($devisProducts as $product){
$productMessage.= html_entity_decode($product->produit_reference).", ";
}
$productMessage = trim( $productMessage );
$productMessage = rtrim($productMessage,",");
return $productMessage;
}
public function getDevisTalkRoomMessage($devisId,$idNextcloud){
$devis = $this->getDevisByIdWithCalendarDataAndProducts($devisId,$idNextcloud);
if($devis == null){
return null;
}
$devisDate = new Datetime($devis->date);
$devisDate = $devisDate->format('d/m/Y');
$message = "NOUVELLE INTERVENTION: ";
$message .= html_entity_decode($devis->nom_defunt) . ' ';
$message .= 'le '.$devisDate. ' ';
$message .= 'à '.$devis->startTime. ' ';
$message .= 'à '.html_entity_decode($devis->lieu). ' '. html_entity_decode($devis->adresse_soin);
$clientMessageContent = $this->getDevisTalkRoomClientContent($devis->prenom);
$message .= $clientMessageContent;
$productMessage = $this->getDevisTalkRoomProductSectionMessage($devis->products);
$message .= $productMessage;
$comment = "aucun";
if(strtolower($devis->comment) != "commentaire" && $devis->comment != ""){
$comment = html_entity_decode($devis->comment);
}
$message .= ". COMMENTAIRES: ".$comment.". ";
$locationCodes = [
"Code portail" => $devis->portal_code,
"Code alarme" => $devis->alarm_code,
"Code funéraire" => $devis->funeral_code
];
$locationCodeMessageContent = "";
foreach($locationCodes as $label => $code){
$value = "Aucun";
if($code != null && trim($code) != ""){
$value = $code;
}
$locationCodeMessageContent .= $label. ": ".$value.". ";
}
$locationCodeMessageContent = trim($locationCodeMessageContent);
$message .= $locationCodeMessageContent;
return $message;
}
}

266
gestion/lib/Db/TalkDb.php Normal file
View File

@ -0,0 +1,266 @@
<?php
namespace OCA\Gestion\Db;
use OCP\IDBConnection;
use \Datetime;
use OCA\Gestion\Constants\BddConstant;
use Ramsey\Uuid\Uuid;
class TalkDb {
private $talkTablePrefix;
private const DEVIS_TALK_ROOM_TYPE = 1;
private const DEVIS_TALK_ROOM_ACTOR_TYPE = "users";
private const DEVIS_TALK_ROOM_VERB_COMMENT = "comment";
private const DEVIS_TALK_ROOM_VERB_INITIAL_MESSAGE = "system";
private const DEVIS_TALK_ROOM_INITIAL_MESSAGE = '{"message":"conversation_created","parameters":[]}';
private const DEVIS_TALK_ROOM_DEFAULT_MESSAGE_TYPE = "chat";
private const DEVIS_TALK_ROOM_DEFAULT_PARTICIPANT_TYPE = 1;
private const DEVIS_TALK_NOTIFICATIONS_APP_NAME ="spreed";
private $defaultTablePrefix;
private IDbConnection $pdo;
public function __construct(IDbConnection $db) {
$this->talkTablePrefix = BddConstant::DEFAULT_TABLE_PREFIX ."talk_";
$this->defaultTablePrefix = BddConstant::DEFAULT_TABLE_PREFIX;
$this->pdo = $db;
}
private function execSQL($sql, $conditions){
$stmt = $this->pdo->prepare($sql);
$stmt->execute($conditions);
$data = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$stmt->closeCursor();
return json_encode($data);
}
private function execSQLNoData($sql, $conditions){
$stmt = $this->pdo->prepare($sql);
$stmt->execute($conditions);
$stmt->closeCursor();
}
private function execSQLNoJsonReturn($sql, $conditions){
$stmt = $this->pdo->prepare($sql);
$stmt->execute($conditions);
$data = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$stmt->closeCursor();
return $data;
}
private function getDevisTalkRoomByName($name){
$sql = "SELECT *
FROM ".$this->talkTablePrefix."rooms as room
WHERE room.name = ? ORDER BY room.id DESC LIMIT 1;";
$rooms = $this->execSQLNoJsonReturn(
$sql,
[$name]);
if(!empty($rooms)){
return $rooms[0];
}
return null;
}
private function createDevisTalkRoom($name,$token){
$dateTime = new Datetime();
$dateTime = $dateTime->format(('Y-m-d H:i:s'));
$sql = "INSERT INTO `".$this->talkTablePrefix."rooms` (
`name`,
`token`,
`type`,
`last_activity`) VALUES (?,?,?,?);";
$this->execSQLNoData($sql, array(
$name,
$token,
self::DEVIS_TALK_ROOM_TYPE,
$dateTime
));
return true;
}
private function createDevisTalkRoomAttendeesByActors($actors,$roomId){
foreach($actors as $actor){
$this->createDevisTalkRoomAttendees($actor,$roomId);
}
}
private function createDevisTalkRoomAttendees($idNextCloud,$roomId){
$sql = "INSERT INTO `".$this->talkTablePrefix."attendees` (
`room_id`,
`actor_type`,
`actor_id`,
`display_name`,
`participant_type`) VALUES (?,?,?,?,?);";
$this->execSQLNoData($sql, array(
$roomId,
self::DEVIS_TALK_ROOM_ACTOR_TYPE,
$idNextCloud,
$idNextCloud,
self::DEVIS_TALK_ROOM_DEFAULT_PARTICIPANT_TYPE
));
}
private function createDevisTalkRoomMessage($actorId,$message,$verb,$roomId,$reference = null){
$datetime = new DateTime();
$datetime = $datetime->format(('Y-m-d H:i:s'));
$sql = "INSERT INTO `".$this->defaultTablePrefix."comments` (
`actor_type`,
`actor_id`,
`message`,
`verb`,
`creation_timestamp`,
`object_type`,
`object_id`,
`reference_id`
) VALUES (?,?,?,?,?,?,?,?);";
$this->execSQLNoData($sql, array(
self::DEVIS_TALK_ROOM_ACTOR_TYPE,
$actorId,
$message,
$verb,
$datetime,
self::DEVIS_TALK_ROOM_DEFAULT_MESSAGE_TYPE,
$roomId,
$reference
));
}
private function getDevisTalkRoomMessageByRoomAndMessage($roomId,$message){
$sql = "SELECT *
FROM ".$this->defaultTablePrefix."comments as comment
WHERE comment.object_id = ? AND
comment.message = ?
ORDER BY comment.id DESC
LIMIT 1;";
$messages = $this->execSQLNoJsonReturn(
$sql,
[$roomId,$message]);
if(!empty($messages)){
return $messages[0];
}
return null;
}
public function getDevisTalkRoomByNames($names){
$sql = "SELECT *
FROM ".$this->talkTablePrefix."rooms as room
WHERE room.name = ? OR room.name = ? LIMIT 1;";
$rooms = $this->execSQLNoJsonReturn(
$sql,
[$names['createdByUser'],$names['createdByAdmin']]);
if(!empty($rooms)){
return $rooms[0];
}
return null;
}
public function createDevisTalkRoomAndReturnDevisTalkRoom($idNextCloud,$token){
$roomName = '["'.$idNextCloud.'","'.BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD.'"]';
$this->createDevisTalkRoom($roomName,$token);
$room = $this->getDevisTalkRoomByName($roomName);
$attendees = [
$idNextCloud,
BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD
];
$this->createDevisTalkRoomAttendeesByActors($attendees,$room['id']);
return $room;
}
public function setDevisTalkRoomInitialMessageAndReturnMessage($roomId,$idNextCloud){
$this->createDevisTalkRoomMessage(
$idNextCloud,
self::DEVIS_TALK_ROOM_INITIAL_MESSAGE,
self::DEVIS_TALK_ROOM_VERB_INITIAL_MESSAGE,
$roomId
);
$message = $this->getDevisTalkRoomMessageByRoomAndMessage($roomId,self::DEVIS_TALK_ROOM_INITIAL_MESSAGE);
return $message;
}
public function setAttendeeLastReadMessage($roomId,$messageId,$idNextCloud){
$sql = "UPDATE ".$this->talkTablePrefix."attendees as attendee
SET
attendee.last_read_message = ?
WHERE attendee.room_id = ? AND
attendee.actor_id = ?;";
$this->execSQLNoData(
$sql,
[$messageId,$roomId,$idNextCloud]);
}
public function createDevisTalkRoomMessageAndReturnMessage($roomId,$message){
$this->createDevisTalkRoomMessage(
BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD,
$message,
self::DEVIS_TALK_ROOM_VERB_COMMENT,
$roomId,
Uuid::uuid4()->toString()
);
$message = $this->getDevisTalkRoomMessageByRoomAndMessage($roomId,$message);
return $message;
}
public function updateDevisTalkRoomLastActivity($roomId,$datetime){
$datetime = $datetime->format(('Y-m-d H:i:s'));
$sql = "UPDATE ".$this->talkTablePrefix."rooms as room
SET
room.last_activity = ?
WHERE room.id = ?;";
$this->execSQLNoData(
$sql,
[$datetime,$roomId]);
}
public function updateRoomLastMessage($roomId,$lastMessageId){
$sql = "UPDATE ".$this->talkTablePrefix."rooms as room
SET
room.last_message = ?
WHERE room.id = ?;";
$this->execSQLNoData(
$sql,
[$lastMessageId,$roomId]);
}
public function sendAttendeeNotifications($idNextCloud,$roomToken,$notificationsSubjectsParameters,$notificationsMessageParameters){
$datetimeNow = new DateTime();
$timestamp = $datetimeNow->getTimestamp();
$sql = "INSERT INTO `".$this->defaultTablePrefix."notifications` (
`app`,
`user`,
`timestamp`,
`object_type`,
`object_id`,
`subject`,
`subject_parameters`,
`message`,
`message_parameters`,
`link`,
`icon`,
`actions`
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?);";
$this->execSQLNoData($sql, array(
self::DEVIS_TALK_NOTIFICATIONS_APP_NAME,
$idNextCloud,
$timestamp,
'chat',
$roomToken,
'chat',
$notificationsSubjectsParameters,
'comment',
$notificationsMessageParameters,
'',
'',
'[]'
));
}
}

View File

@ -48,6 +48,8 @@ class GestionService {
/** @var \OCA\Gestion\Db\OrderBdd */
private $orderBdd;
private TalkService $talkService;
private $orderPdfService;
private $devisPdfService;
@ -56,12 +58,14 @@ class GestionService {
OrderBdd $orderBdd,
LoggerInterface $logger,
OrderPdfService $orderPdfService,
DevisPdfService $devisPdfService) {
DevisPdfService $devisPdfService,
TalkService $talkService) {
$this->orderBdd = $orderBdd;
$this->logger = $logger;
$this->gestionBdd = $gestionBdd;
$this->orderPdfService = $orderPdfService;
$this->devisPdfService = $devisPdfService;
$this->talkService = $talkService;
}
private function GetCalendarSummaryFromVCalendarString(string $vCalendarString): string
@ -195,6 +199,8 @@ class GestionService {
}
}
}
$devisTalkMessage = $this->gestionBdd->getDevisTalkRoomMessage($devisId,$userName);
$this->talkService->sendDevisTalkNotifications($devisTalkMessage,$userName);
$this->devisPdfService->generateDevisPdfByDevisId($devisId,BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD);
$this->devisPdfService->generateDevisPdfByDevisId($devisId,BddConstant::DEFAULT_ADMIN_ID_NEXTCLOUD);
$this->gestionBdd->createDevisTrajetFromVCalendar($devisId,$userName);
@ -313,6 +319,9 @@ class GestionService {
return true;
}
$this->UpdateDevisDataByVCalendarString($devis,$vCalendarString);
$userName = $this->GetThanatoNameFromVCalendarString($vCalendarString);
$devisTalkMessage = $this->gestionBdd->getDevisTalkRoomMessage($devis['id'],$userName);
$this->talkService->sendDevisTalkNotifications($devisTalkMessage,$userName);
}
$this->devisPdfService->generateDevisPdfByDevisId($devis['id'],BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD);
$this->devisPdfService->generateDevisPdfByDevisId($devis['id'],BddConstant::DEFAULT_ADMIN_ID_NEXTCLOUD);

View File

@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/**
* Calendar App
*
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
*
* @author Anna Larch <anna.larch@gmx.net>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Gestion\Service;
use DateTime;
use OCA\Gestion\Constants\BddConstant;
use OCA\Gestion\Db\Bdd;
use OCA\Gestion\Db\TalkDb;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
class TalkService {
/** @var TalkDb */
private $talkDb;
/** @var LoggerInterface */
private $logger;
public function __construct(
TalkDb $talkDb,
LoggerInterface $logger) {
$this->logger = $logger;
$this->talkDb = $talkDb;
}
private function getUserDevisTalkRoomNames($idNextCloud){
$roomNamesCreatedByUser = '["'.$idNextCloud.'","'.BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD.'"]';
$roomNamesCreatedByAdmin = '["'.BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD.'","'.$idNextCloud.'"]';
return [
"createdByUser" => $roomNamesCreatedByUser,
"createdByAdmin" => $roomNamesCreatedByAdmin
];
}
private function generateTalkRandomToken(){
$length = 8;
$characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
$charactersLength = strlen($characters);
$randomToken = '';
for ($i = 0; $i < $length; $i++) {
$randomToken .= $characters[rand(0, $charactersLength - 1)];
}
return $randomToken;
}
private function getNotificationsSubjectsParameters(){
return '{"userType":"users","userId":"'.BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD.'"}';
}
private function getNotificationsMessageParameters($commentId){
return '{"commentId":"'.$commentId.'"}';
}
public function sendDevisTalkNotifications(string $message,string $idNextcloud){
if($idNextcloud === BddConstant::DEFAULT_ADMIN_ID_NEXTCLOUD || $idNextcloud === BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD){
return true;
}
$roomNames = $this->getUserDevisTalkRoomNames($idNextcloud);
$room = $this->talkDb->getDevisTalkRoomByNames($roomNames);
if($room == null){
$roomToken = $this->generateTalkRandomToken();
$room = $this->talkDb->createDevisTalkRoomAndReturnDevisTalkRoom($idNextcloud,$roomToken);
$initialMessage = $this->talkDb->setDevisTalkRoomInitialMessageAndReturnMessage($room['id'],$idNextcloud);
}
else{
$roomToken = $room['token'];
}
$devisMessage = $this->talkDb->createDevisTalkRoomMessageAndReturnMessage($room['id'],$message);
$this->talkDb->updateRoomLastMessage($room['id'],$devisMessage['id']);
$this->talkDb->setAttendeeLastReadMessage($room['id'],$devisMessage['id'],BddConstant::DEFAULT_ADMIN_APP_ID_NEXTCLOUD);
//send notifications
$notificationsSubjectsParameters = $this->getNotificationsSubjectsParameters();
$notificationsMessageParameters = $this->getNotificationsMessageParameters($devisMessage['id']);
$this->talkDb->sendAttendeeNotifications($idNextcloud,$roomToken,$notificationsSubjectsParameters,$notificationsMessageParameters);
return true;
}
}

View File

@ -0,0 +1,4 @@
ALTER TABLE oc_gestion_lieu
ADD COLUMN portal_code VARCHAR(255) DEFAULT NULL,
ADD COLUMN alarm_code VARCHAR(255) DEFAULT NULL,
ADD COLUMN funeral_code VARCHAR(255) DEFAULT NULL;

View File

@ -14,6 +14,9 @@ export class Lieu {
this.adresse = ((myresp.adresse == null || myresp.adresse.length === 0) ? '-' : myresp.adresse);
this.latitude = ((myresp.latitude == null || myresp.latitude.length === 0) ? '-' : myresp.latitude);
this.longitude = ((myresp.longitude == null || myresp.longitude.length === 0) ? '-' : myresp.longitude);
this.portalCode = myresp.portal_code != null && myresp.portal_code.length > 0 ? myresp.portal_code : "-";
this.alarmCode = myresp.alarm_code != null && myresp.alarm_code.length > 0 ? myresp.alarm_code : "-";
this.funeralCode = myresp.funeral_code != null && myresp.funeral_code.length > 0 ? myresp.funeral_code : "-";
}
/**undefined
@ -26,6 +29,9 @@ export class Lieu {
'<div class="editable" data-table="lieu" data-column="adresse" data-id="' + this.id + '" style="display:inline">' + this.adresse + '</div>',
'<div class="editable" data-table="lieu" data-column="latitude" data-id="' + this.id + '" style="display:inline">' + this.latitude + '</div>',
'<div class="editable" data-table="lieu" data-column="longitude" data-id="' + this.id + '" style="display:inline">' + this.longitude + '</div>',
'<div class="editable" data-table="lieu" data-column="portal_code" data-id="' + this.id + '">' + this.portalCode + '</div>',
'<div class="editable" data-table="lieu" data-column="alarm_code" data-id="' + this.id + '">' + this.alarmCode + '</div>',
'<div class="editable" data-table="lieu" data-column="funeral_code" data-id="' + this.id + '">' + this.funeralCode + '</div>',
'<div data-modifier="lieu" data-id=' + this.id + ' data-table="lieu" style="display:inline-block;margin-right:0px;" class="deleteItem icon-delete"></div>'
];
return myrow;

View File

@ -21,6 +21,9 @@
<th>Adresse</th>
<th>Latitude</th>
<th>Longitude</th>
<th><?php p($l->t('Code portail'));?></th>
<th><?php p($l->t('Code alarme'));?></th>
<th><?php p($l->t('Code funéraire'));?></th>
<th><?php p($l->t('Actions'));?></th>
</tr>
</thead>