// src/integrations/serviceChannel/attachments.js import scAxios from './client.js'; import axios from 'axios'; import { extname } from 'node:path'; import { logger } from '../../utils/logger.js'; const mimeTypes = { '.pdf': 'application/pdf', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.doc': 'application/msword', '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', '.xls': 'application/vnd.ms-excel', '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', '.txt': 'text/plain', }; function getContentTypeFromFilename(filename) { if (!filename) return 'application/octet-stream'; const ext = extname(filename).toLowerCase(); return mimeTypes[ext] || 'application/octet-stream'; } // ────────────────────────────────────────────── // Download single attachment by ID // ────────────────────────────────────────────── export async function downloadAttachmentById(workOrderId, attachmentId) { const start = Date.now(); logger('servicechannel:attachment', `Downloading attachment ${attachmentId} from WO ${workOrderId}`); try { // First get metadata (to get Uri + Name) const metaRes = await scAxios.get(`/odata/workorders(${workOrderId})/attachments`, { params: { $filter: `Id eq ${attachmentId}` }, }); const atts = metaRes.data.value || []; if (atts.length === 0) { throw new Error(`Attachment ${attachmentId} not found on WO ${workOrderId}`); } const att = atts[0]; if (!att.Uri) { throw new Error(`No download URI for attachment ${attachmentId}`); } let fileName = att.Name || `attachment_${attachmentId}`; if (att.Name && !extname(att.Name)) { fileName += '.bin'; } // Download the actual file const fileRes = await axios.get(att.Uri, { responseType: 'arraybuffer' }); logger('servicechannel:attachment', `Successfully downloaded ${fileName} (${Date.now() - start} ms)`); return { success: true, fileName, buffer: Buffer.from(fileRes.data), contentType: getContentTypeFromFilename(fileName), isInvoiceCopy: !!att.IsInvoiceDigitalCopy, metadata: att, }; } catch (err) { const msg = err.response ? `${err.response?.status || 'unknown'} – ${err.message}` : err.message; logger('servicechannel:attachment', `Failed to download attachment ${attachmentId} from WO ${workOrderId}: ${msg}`, 'error'); throw new Error(`Download failed for WO ${workOrderId} / Att ${attachmentId}: ${msg}`); } } // ────────────────────────────────────────────── // Attachment Functions // ────────────────────────────────────────────── export async function listWorkOrderAttachments(workOrderId) { logger('servicechannel:client', `Listing attachments for work order ${workOrderId}`); try { const response = await scAxios.get(`/workorders/${workOrderId}/attachments`); const attachments = response.data?.value || response.data?.Attachments || response.data || []; logger('servicechannel:client', `Found ${attachments.length} attachments for WO ${workOrderId}`); return attachments; } catch (err) { logger('servicechannel:client', `Failed to list attachments for WO ${workOrderId}: ${err.message}`, 'error'); return []; } } export async function getWorkOrderAttachments(woId) { logger('servicechannel:client', `Fetching attachments for work order ${woId}`); try { const response = await scAxios.get(`/workorders/${woId}/attachments`); const attachments = response.data?.value || response.data?.Attachments || response.data || []; logger('servicechannel:client', `Found ${attachments.length} attachments for WO ${woId}`); return attachments.map(att => ({ id: att.Id || att.AttachmentId, fileName: att.FileName || att.Name || att.OriginalFileName || 'attachment', fileType: att.ContentType || att.MimeType || 'application/octet-stream', fileSize: att.FileSize ? `${(att.FileSize / 1024).toFixed(1)} KB` : 'Unknown size', uploadedDate: att.CreatedDateTime || att.UploadedOn || att.DateCreated ? new Date(att.CreatedDateTime || att.UploadedOn || att.DateCreated).toLocaleString('en-US') : 'Unknown', downloadUrl: att.DownloadUrl || att.Url || null })); } catch (err) { logger('servicechannel:client', `Error fetching attachments for WO ${woId}: ${err.message}`, 'error'); return []; } }