Paramètres & Montants du Séjour
Rapport de Répartition
| Logement & Voyageur |
Canal | Payé Voyageur |
Virement Reçu |
Base Com. |
Com. HT |
Ménage HT |
Autres Frais HT & Motif |
TVA (20%) |
Com. Abritel |
Facture Conciergerie |
Net Proprio |
Actions |
|---|
Ajouter / Modifier un Bien
Applique automatiquement vos tarifs lors de la saisie
Catalogue de vos Logements
Modifiez vos tarifs en direct ou synchronisez vos nouveaux biens Hostaway sans écraser l'existant
Session Utilisateur & Niveau d'Accès
Gérez votre connexion Firebase Auth
Seul vous (en tant que Super-Admin) voyez cet encadré et pouvez autoriser une nouvelle adresse e-mail à devenir Administrateur.
Identité de la Conciergerie & TVA
Informations imprimées sur vos factures PDF
Base Cloud Temps Réel (Firebase)
Projet pré-configuré : conciegerie-split
Chaque utilisateur connecté possède automatiquement son propre espace lié à son email.
Liaison Google Sheets
Ajoute chaque réservation dans votre feuille Google Sheets
Voir le code Google Apps Script à coller dans votre Sheet
function doPost(e) { return traiterRequete(e); }
function doGet(e) { return traiterRequete(e); }
function normText(s) {
return String(s || "").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
}
function traiterRequete(e) {
var lock = LockService.getScriptLock();
try { lock.waitLock(10000); } catch (err) {}
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
if (sheet.getLastRow() === 0) {
sheet.appendRow(["Date Saisie", "Logement", "Voyageur", "Dates Séjour", "Plateforme", "Total Payé Voyageur", "Versement Plateforme", "Base Commissionnable", "Taux Com (%)", "Commission HT", "Ménage Facturé HT", "Autres Frais HT", "Motif Frais", "TVA (20%)", "Com. Abritel", "Total Facturé Conciergerie", "Net Final Propriétaire", "ID Réservation", "ID Hostaway"]);
sheet.getRange(1, 1, 1, 19).setFontWeight("bold").setBackground("#113D48").setFontColor("#FFFFFF");
}
var rawData = (e && e.parameter && e.parameter.payload) ? e.parameter.payload : (e && e.postData && e.postData.contents ? e.postData.contents : null);
if (!rawData) {
var cleaned = nettoyerLignesVides(sheet);
return ContentService.createTextOutput("PRET : Script v3 actif (avec suppression auto) ! Lignes vides nettoyees : " + cleaned).setMimeType(ContentService.MimeType.TEXT);
}
var payload = JSON.parse(rawData);
var urlAction = (e && e.parameter && e.parameter.action) ? e.parameter.action : null;
var action = urlAction || ((payload && !Array.isArray(payload) && payload.action) ? payload.action : "add");
var items = (payload && !Array.isArray(payload) && payload.items) ? payload.items : (Array.isArray(payload) ? payload : [payload]);
// Nettoie toujours les éventuelles lignes à 0 créées par l'ancien script
nettoyerLignesVides(sheet);
if (action === "delete" || (items.length > 0 && items[0] && items[0]._isDelete === true)) {
var deleted = supprimerLignesSheet(sheet, items);
return ContentService.createTextOutput("SUCCES : " + deleted + " ligne(s) supprimee(s) de Google Sheets !").setMimeType(ContentService.MimeType.TEXT);
}
if (action === "update") {
supprimerLignesSheet(sheet, items);
}
var added = 0;
items.forEach(function(r) {
if (!r || r.action === "delete" || r._isDelete) return;
var prop = String(r.propertyName || "").trim();
var guest = String(r.guestName || "").trim();
var tot = Number(r.totalPaid) || 0;
var payout = Number(r.platformPayout) || 0;
if (!prop && !guest && tot === 0 && payout === 0) return;
// Évite aussi les doublons si la même réservation existe déjà
supprimerLignesSheet(sheet, [r]);
var isAbritel = (r.platformKey === "abritel") || (/abritel|vrbo/i.test(r.platformName || ""));
var comAbritel = isAbritel ? (Number(r.otaComm || r.reimbursedOta) || 0) : 0;
sheet.appendRow([new Date(), prop, guest, r.stayDates || "", r.platformName || "", tot, payout, Number(r.netBase) || 0, Number(r.conciergeRate) || 0, Number(r.conciergeComm) || 0, Number(r.cleaningReal) || 0, Number(r.extraFees) || 0, r.extraLabel || "", Number(r.vatAmount) || 0, comAbritel, Number(r.conciergeInvoiceTotal) || 0, Number(r.ownerFinalKeep) || 0, String(r.id || ""), String(r.hostawayId || "")]);
added++;
});
return ContentService.createTextOutput("SUCCES : " + added + " ligne(s) synchronisee(s) !").setMimeType(ContentService.MimeType.TEXT);
} finally {
try { lock.releaseLock(); } catch (err) {}
}
}
function nettoyerLignesVides(sheet) {
var lastRow = sheet.getLastRow();
if (lastRow <= 1) return 0;
var values = sheet.getRange(1, 1, lastRow, Math.max(19, sheet.getLastColumn())).getValues();
var removed = 0;
for (var i = values.length - 1; i >= 1; i--) {
var row = values[i];
var p = String(row[1] || "").trim();
var g = String(row[2] || "").trim();
var d = String(row[3] || "").trim();
var tot = Number(row[5] || 0);
var vir = Number(row[6] || 0);
if (!p && !g && !d && tot === 0 && vir === 0) {
sheet.deleteRow(i + 1);
removed++;
}
}
return removed;
}
function supprimerLignesSheet(sheet, itemsToDelete) {
var lastRow = sheet.getLastRow();
if (lastRow <= 1) return 0;
var values = sheet.getRange(1, 1, lastRow, Math.max(19, sheet.getLastColumn())).getValues();
var count = 0;
for (var i = values.length - 1; i >= 1; i--) {
var row = values[i];
var rowPropNorm = normText(row[1]);
var rowGuestNorm = normText(row[2]);
var rowDatesNorm = normText(row[3]);
var rowTotal = Number(row[5] || 0);
var rowPayout = Number(row[6] || 0);
var rowCol17 = String(row[16] || "").trim();
var rowCol18 = String(row[17] || "").trim();
var rowCol19 = String(row[18] || "").trim();
var match = itemsToDelete.some(function(it) {
if (!it) return false;
var tId = String(it.id || "").trim();
var tHwId = String(it.hostawayId || "").trim();
// 1. Correspondance par ID Réservation ou ID Hostaway
if (tId && (rowCol18 === tId || rowCol17 === tId || rowCol19 === tId)) return true;
if (tHwId && (rowCol18 === tHwId || rowCol19 === tHwId || rowCol17 === tHwId)) return true;
var tPropNorm = normText(it.propertyName);
var tGuestNorm = normText(it.guestName);
var tDatesNorm = normText(it.stayDates);
var tTotal = Number(it.totalPaid || 0);
var tPayout = Number(it.platformPayout || 0);
// 2. Correspondance par Voyageur + Dates de séjour
if (tGuestNorm && rowGuestNorm === tGuestNorm && tDatesNorm && rowDatesNorm === tDatesNorm) return true;
// 3. Correspondance souple : Voyageur + Montant Total Payé (ou Virement) pour les anciennes lignes
if (tGuestNorm && rowGuestNorm === tGuestNorm && tTotal > 0 && Math.abs(rowTotal - tTotal) < 0.05) return true;
if (tGuestNorm && rowGuestNorm === tGuestNorm && tPayout > 0 && Math.abs(rowPayout - tPayout) < 0.05) return true;
// 4. Correspondance Logement + Dates + Montant
if (tPropNorm && rowPropNorm === tPropNorm && tDatesNorm && rowDatesNorm === tDatesNorm && tTotal > 0 && Math.abs(rowTotal - tTotal) < 0.05) return true;
return false;
});
if (match) {
sheet.deleteRow(i + 1);
count++;
}
}
return count;
}
Connexion API Hostaway
Propre à votre session (vous et votre épouse pouvez utiliser 2 comptes différents)
Disponible dans votre compte Hostaway : Settings > Hostaway API.
Sauvegarde & Restauration (.JSON)
Exportez ou importez une copie complète de sécurité sur votre disque dur
Le fichier de sauvegarde contient l'intégralité de l'espace actuellement ouvert : historique des réservations, catalogue de logements, mentions légales et préférences TVA.