// src/commands/woConfirmed.js import botClient from '../integrations/webex/botClient.js'; import { addWorkOrderNote, getWorkOrderStatus, updateWorkOrderStatus, } from '../integrations/serviceChannel/client.js'; import db from '../db/mappings.js'; import { logger } from '../utils/logger.js'; import { getCompletedOperationsRoomId } from '../utils/servchanRoomTitle.js'; import { resolveDisplayName, resolveWoRoomContext } from '../utils/woRoomContext.js'; import { getWoCloseout, saveWoCloseout } from '../services/woCloseoutService.js'; function normalizeExtended(value) { return (value || '').trim().toUpperCase(); } function isTerminalExtended(extendedRaw) { const extended = normalizeExtended(extendedRaw); if (extended === 'CONFIRMED' || extended === 'CANCELLED') return true; return extended === 'NO CHARGE' || extended.includes('NO CHARGE'); } function buildConfirmationText(displayName) { return `${displayName} has confirmed this WO resolution verified and approves closing it out.`; } function isScStatusLockedError(err) { const msg = String(err?.message || err).toLowerCase(); return msg.includes('921') || msg.includes('invoiced') || msg.includes('status'); } export async function handleWoConfirmed(bot, trigger) { logger('wo:confirmed', 'HANDLER ENTERED'); const roomId = trigger.message?.roomId; const isGroup = trigger.message?.roomType === 'group'; const ctx = await resolveWoRoomContext({ db, botClient, roomId, isGroup }); if (!ctx.ok) { await bot.say(ctx.error); return; } const { workOrderId: mappedWorkOrderId, parsed } = ctx; const opsRoomId = getCompletedOperationsRoomId(); if (!opsRoomId) { logger('wo:confirmed', 'COMPLETED_OPERATIONS_ROOM_ID not configured', 'warn'); await bot.say('The close-out ops space is not configured on this bot (`COMPLETED_OPERATIONS_ROOM_ID`).'); return; } const existingCloseout = await getWoCloseout(db, mappedWorkOrderId); if (existingCloseout) { await bot.say( `Close-out was already recorded for this work order on ${new Date(existingCloseout.confirmedAt).toLocaleDateString('en-US')} by **${existingCloseout.confirmedBy}**.` ); return; } const woStatus = await getWorkOrderStatus(mappedWorkOrderId); if (!woStatus) { await bot.say('Could not load this work order from ServiceChannel. Please try again.'); return; } const primary = (woStatus.primaryStatus || '').trim().toUpperCase(); if (primary !== 'COMPLETED') { await bot.say( `This command applies only to **COMPLETED** work orders. Current status: **${woStatus.primaryStatus || 'unknown'}**${woStatus.extendedStatus ? ` / ${woStatus.extendedStatus}` : ''}.` ); return; } if (isTerminalExtended(woStatus.extendedStatus)) { await bot.say( `This work order is already in a terminal status (**${woStatus.extendedStatus}**). No action needed.` ); return; } const displayName = await resolveDisplayName(botClient, trigger.personId); const confirmationText = buildConfirmationText(displayName); const scNote = `${displayName} confirmed WO resolution verified and approves closing it out.`; let scStatusUpdated = false; try { await updateWorkOrderStatus(mappedWorkOrderId, { primary: 'COMPLETED', extended: 'CONFIRMED', note: scNote, }); scStatusUpdated = true; logger( 'wo:confirmed', `Set WO ${mappedWorkOrderId} to COMPLETED/CONFIRMED by ${displayName}` ); } catch (err) { logger( 'wo:confirmed', `SC status update failed for WO ${mappedWorkOrderId}: ${err.message} — falling back to note-only close-out`, 'warn' ); try { await addWorkOrderNote(mappedWorkOrderId, scNote); logger( 'wo:confirmed', `Posted close-out note to SC WO ${mappedWorkOrderId} by ${displayName} (status API blocked)` ); } catch (noteErr) { logger('wo:confirmed', `Failed to post SC note for WO ${mappedWorkOrderId}: ${noteErr.message}`, 'error'); const hint = isScStatusLockedError(err) ? ' ServiceChannel does not allow CONFIRMED after invoice on this work order.' : ''; await bot.say( `Could not set ServiceChannel status to CONFIRMED and adding a work order note also failed:${hint} ${noteErr.message}` ); return; } } try { await saveWoCloseout(db, { workOrderId: mappedWorkOrderId, confirmedBy: displayName, scStatusUpdated, noteText: scNote, }); } catch (err) { logger('wo:confirmed', `Failed to save close-out record for WO ${mappedWorkOrderId}: ${err.message}`, 'error'); } const opsMessage = `${parsed.headerLine}\n${confirmationText}`; try { await botClient.sendMarkdown(opsRoomId, opsMessage); logger( 'wo:confirmed', `Posted close-out confirmation for WO-${parsed.woNumber} to ops room ${opsRoomId} by ${displayName}` ); } catch (err) { logger('wo:confirmed', `Failed to post to ops room: ${err.message}`, 'error'); await bot.say( scStatusUpdated ? `ServiceChannel status was updated to CONFIRMED, but notifying the ops space failed: ${err.message}` : `Close-out was noted on the work order in ServiceChannel, but notifying the ops space failed: ${err.message}` ); return; } if (!scStatusUpdated) { await bot.say( 'ServiceChannel would not allow CONFIRMED on this work order — close-out noted on the WO, recorded in ServChan, and ops notified. The SC webhook should post the note here shortly.' ); } }