mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 06:56:35 +00:00
fix(budget): stop receipts deleting files, and share the upload path
Attaching a receipt no longer lets the budget domain delete one. Trashing the file was reachable from the REST route, the MCP tool and the plugin RPC, none of which check file_delete, and a receipt id is any file on the trip, so anyone who could edit an expense could trash a document they were not allowed to touch. A concurrent edit could do it by accident: the client sends the full set of receipt ids from a snapshot taken when the form opened, so saving an expense someone else had just attached a file to moved that file to the trash. Removing a receipt now removes the link and nothing else. The link row is treated as shared, because it is. One row can tie a file to a place and to an expense at once, so the sync clears only the receipt half and leaves the rest, the foreign key is SET NULL rather than CASCADE, and a receipt already in the trash keeps its link so restoring it comes back attached. The upload and its rollback moved into one module both shells call. It was copied into six places, and all six rolled back with the permanent-delete route, which only accepts a file that is already in the trash and therefore answered 404 every time: a failed save left every uploaded file on the trip, attached to the expense, and the next attempt uploaded another copy. It now trashes first, keeps the pending files for the retry, and says so when a file could not be removed instead of swallowing it. Escape in the receipt viewer no longer closes the expense form behind it, the download fallback uses a real accent class rather than a utility that does not exist, and the upload indicator is actually switched on. Tests: the receipt link behaviour on both service paths, the four new branches in the files service, the upload module, and the viewer.
This commit is contained in:
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'لا توجد إيصالات مرفقة',
|
||||
'costs.deleteReceipt': 'حذف الإيصال',
|
||||
'costs.viewReceipt': 'عرض الإيصال',
|
||||
'costs.receiptLeftBehind': 'فشل الحفظ، وما زال هناك {count} إيصالات مرفوعة. احذفها من تبويب الملفات.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Nenhum comprovante anexado',
|
||||
'costs.deleteReceipt': 'Remover comprovante',
|
||||
'costs.viewReceipt': 'Ver comprovante',
|
||||
'costs.receiptLeftBehind': 'Falha ao salvar, e {count} recibos enviados ainda estão lá. Remova-os na aba Arquivos.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,6 +153,7 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Cap rebut adjunt',
|
||||
'costs.deleteReceipt': 'Eliminar rebut',
|
||||
'costs.viewReceipt': 'Veure rebut',
|
||||
'costs.receiptLeftBehind': 'No es pot desar i encara hi ha {count} rebuts pujats. Elimina aquests fitxers a la pestanya Fitxers.',
|
||||
};
|
||||
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Žádné připojené účtenky',
|
||||
'costs.deleteReceipt': 'Odebrat účtenku',
|
||||
'costs.viewReceipt': 'Zobrazit účtenku',
|
||||
'costs.receiptLeftBehind': 'Uložení selhalo, {count} nahraných účtenek tam stále je. Odstraňte je v záložce Soubory.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Keine Belege angehängt',
|
||||
'costs.deleteReceipt': 'Beleg entfernen',
|
||||
'costs.viewReceipt': 'Beleg ansehen',
|
||||
'costs.receiptLeftBehind': 'Speichern fehlgeschlagen, {count} hochgeladene Belege liegen noch da. Im Dateien-Tab entfernen.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,6 +153,7 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'No receipts attached',
|
||||
'costs.deleteReceipt': 'Remove receipt',
|
||||
'costs.viewReceipt': 'View receipt',
|
||||
'costs.receiptLeftBehind': 'Save failed, and {count} uploaded receipts are still there. Remove them in the Files tab.',
|
||||
};
|
||||
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'No hay recibos adjuntos',
|
||||
'costs.deleteReceipt': 'Eliminar recibo',
|
||||
'costs.viewReceipt': 'Ver recibo',
|
||||
'costs.receiptLeftBehind': 'Error al guardar y quedan {count} recibos subidos. Elimínalos en la pestaña Archivos.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Aucun reçu joint',
|
||||
'costs.deleteReceipt': 'Supprimer le reçu',
|
||||
'costs.viewReceipt': 'Voir le reçu',
|
||||
'costs.receiptLeftBehind': 'Échec de la sauvegarde, {count} reçus téléversés sont toujours là. Supprimez-les dans les Fichiers.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -154,5 +154,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Δεν υπάρχουν συνημμένες αποδείξεις',
|
||||
'costs.deleteReceipt': 'Αφαίρεση απόδειξης',
|
||||
'costs.viewReceipt': 'Προβολή απόδειξης',
|
||||
'costs.receiptLeftBehind': 'Η αποθήκευση απέτυχε και {count} ανεβασμένες αποδείξεις παραμένουν. Διαγράψτε τες στην καρτέλα Αρχεία.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Nincsenek csatolt nyugták',
|
||||
'costs.deleteReceipt': 'Nyugta eltávolítása',
|
||||
'costs.viewReceipt': 'Nyugta megtekintése',
|
||||
'costs.receiptLeftBehind': 'A mentés nem sikerült, és {count} feltöltött blokk még ott van. Törölje őket a Fájlok fülön.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Tidak ada kuitansi terlampir',
|
||||
'costs.deleteReceipt': 'Hapus kuitansi',
|
||||
'costs.viewReceipt': 'Lihat kuitansi',
|
||||
'costs.receiptLeftBehind': 'Gagal menyimpan, dan {count} struk yang diunggah masih ada. Hapus di tab Berkas.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Nessuna ricevuta allegata',
|
||||
'costs.deleteReceipt': 'Rimuovi ricevuta',
|
||||
'costs.viewReceipt': 'Visualizza ricevuta',
|
||||
'costs.receiptLeftBehind': 'Salvataggio non riuscito e {count} ricevute caricate sono ancora lì. Rimuovile nella scheda File.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': '添付されたレシートはありません',
|
||||
'costs.deleteReceipt': 'レシートを削除',
|
||||
'costs.viewReceipt': 'レシートを表示',
|
||||
'costs.receiptLeftBehind': '保存に失敗し、アップロード済みの領収書が {count} 件残っています。ファイルタブで削除してください。',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': '첨부된 영수증 없음',
|
||||
'costs.deleteReceipt': '영수증 삭제',
|
||||
'costs.viewReceipt': '영수증 보기',
|
||||
'costs.receiptLeftBehind': '저장에 실패했고 업로드된 영수증 {count}개가 남아 있습니다. 파일 탭에서 삭제하세요.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Geen bonnetjes bijgevoegd',
|
||||
'costs.deleteReceipt': 'Bonnetje verwijderen',
|
||||
'costs.viewReceipt': 'Bonnetje bekijken',
|
||||
'costs.receiptLeftBehind': 'Opslaan mislukt en er staan nog {count} geüploade bonnen. Verwijder ze op het tabblad Bestanden.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Brak załączonych paragonów',
|
||||
'costs.deleteReceipt': 'Usuń paragon',
|
||||
'costs.viewReceipt': 'Zobacz paragon',
|
||||
'costs.receiptLeftBehind': 'Zapis nie powiódł się, a {count} przesłanych paragonów wciąż tam jest. Usuń je w zakładce Pliki.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Нет прикрепленных чеков',
|
||||
'costs.deleteReceipt': 'Удалить чек',
|
||||
'costs.viewReceipt': 'Просмотреть чек',
|
||||
'costs.receiptLeftBehind': 'Не удалось сохранить, {count} загруженных чеков остались. Удалите их на вкладке «Файлы».',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Inga bifogade kvitton',
|
||||
'costs.deleteReceipt': 'Ta bort kvitto',
|
||||
'costs.viewReceipt': 'Visa kvitto',
|
||||
'costs.receiptLeftBehind': 'Sparandet misslyckades och {count} uppladdade kvitton finns kvar. Ta bort dem under Filer.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Ekli fiş yok',
|
||||
'costs.deleteReceipt': 'Fişi kaldır',
|
||||
'costs.viewReceipt': 'Fişi görüntüle',
|
||||
'costs.receiptLeftBehind': 'Kaydetme başarısız oldu ve yüklenen {count} fiş hâlâ duruyor. Dosyalar sekmesinden silin.',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,5 +153,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Немає доданих чеків',
|
||||
'costs.deleteReceipt': 'Видалити чек',
|
||||
'costs.viewReceipt': 'Переглянути чек',
|
||||
'costs.receiptLeftBehind': 'Не вдалося зберегти, {count} завантажених чеків залишилися. Видаліть їх на вкладці «Файли».',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -153,6 +153,7 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': 'Chưa đính kèm hóa đơn nào',
|
||||
'costs.deleteReceipt': 'Xóa hóa đơn',
|
||||
'costs.viewReceipt': 'Xem hóa đơn',
|
||||
'costs.receiptLeftBehind': 'Lưu thất bại và {count} biên lai đã tải lên vẫn còn. Hãy xoá chúng trong tab Tệp.',
|
||||
};
|
||||
|
||||
export default budget;
|
||||
|
||||
@@ -152,5 +152,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': '未附加收據',
|
||||
'costs.deleteReceipt': '刪除收據',
|
||||
'costs.viewReceipt': '查看收據',
|
||||
'costs.receiptLeftBehind': '儲存失敗,還有 {count} 張已上傳的收據。請到「檔案」分頁刪除。',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
@@ -152,5 +152,6 @@ const budget: TranslationStrings = {
|
||||
'costs.noReceipts': '未附加收据',
|
||||
'costs.deleteReceipt': '删除收据',
|
||||
'costs.viewReceipt': '查看收据',
|
||||
'costs.receiptLeftBehind': '保存失败,还有 {count} 个已上传的收据。请在“文件”标签页中删除。',
|
||||
};
|
||||
export default budget;
|
||||
|
||||
Reference in New Issue
Block a user