${description_of_issue}
+ +Submitted via Formstack | Form ID: ${FormID} | Submission ID: ${UniqueID} | Received: ${receivedAt}
` + : `No description provided
+ +Submitted via Formstack | Form ID: ${FormID} | Submission ID: ${UniqueID} | Received: ${receivedAt}
`; + + // Get Zammad configuration from environment + const zammadUrl = process.env.ZAMMAD_URL || 'http://zammad-nginx:8080'; + const zammadToken = process.env.ZAMMAD_API_TOKEN; + + if (!zammadToken) { + logger.error('ZAMMAD_API_TOKEN environment variable is not configured'); + throw new Error('ZAMMAD_API_TOKEN is required'); + } + + const zammad = Zammad({ token: zammadToken }, zammadUrl); + + try { + // Get or create user based on contact info + // Priority: signal_number > phone_number > email_address + let customer; + + // Try to find existing user by phone or email + if (signal_number || phone_number) { + const phoneToSearch = signal_number || phone_number; + customer = await getUser(zammad, phoneToSearch); + if (customer) { + logger.info({ customerId: customer.id, method: 'phone' }, 'Found existing user by phone'); + } + } + + if (!customer && email_address) { + // Search by email if phone search didn't work + const emailResults = await zammad.user.search(`email:${email_address}`); + if (emailResults.length > 0) { + customer = emailResults[0]; + logger.info({ customerId: customer.id, method: 'email' }, 'Found existing user by email'); + } + } + + if (!customer) { + // Create new user with all available contact information + logger.info('Creating new user from form submission'); + customer = await zammad.user.create({ + firstname: name?.first || '', + lastname: name?.last || '', + email: email_address || `${UniqueID}@formstack.local`, + phone: signal_number || phone_number || '', + note: `User created from Formstack submission ${UniqueID}`, + }); + } + + logger.info({ + customerId: customer.id, + customerEmail: customer.email, + customerPhone: customer.phone, + }, 'Customer identified/created'); + + // Build address parts + const streetAddress = address?.address || ''; + const cityValue = address?.city || ''; + const stateValue = address?.state || ''; + const zipValue = address?.zip || ''; + + // Create the ticket with custom fields mapped to Zammad attributes + // Following the pattern from ngo-isac-uploader where all form data + // goes into structured fields rather than HTML body + const ticket = await zammad.ticket.create({ + title, + group: "Users", // Default group - you may want to make this configurable + customer_id: customer.id, + + // Custom fields - these will be populated in Zammad's ticket attributes + // NOTE: 'organization', 'formstack_form_id', 'formstack_submission_id' + // fields could not be created due to naming conflicts, so metadata + // is included in the ticket body instead + signal_number: signal_number || undefined, + type_of_help_requested: type_of_help_requested || undefined, + type_of_organization: type_of_organization || undefined, + urgency_level: urgency_level || undefined, + city: cityValue || undefined, + us_state: stateValue || undefined, + zip_code: zipValue || undefined, + street_address: streetAddress || undefined, + preferred_contact_method: preferred_contact_method || undefined, + available_times: available_times_for_contact || undefined, + where_heard: how_did_you_hear_about_us || undefined, + preferred_language: preferred_language || undefined, + + // Article with just the description + article: { + body, + subject: title, + content_type: "text/html", + type: "note", + }, + }); + + logger.info({ + ticketId: ticket.id, + customerId: customer.id, + formId: FormID, + submissionId: UniqueID, + }, 'Zammad ticket created successfully'); + + } catch (error: any) { + logger.error({ + error: error.message, + stack: error.stack, + output: error.output, + formId: FormID, + submissionId: UniqueID, + }, 'Failed to create Zammad ticket'); + throw error; + } +}; + +export default createTicketFromFormTask; diff --git a/apps/bridge-worker/tasks/leafcutter/import-label-studio.ts b/apps/bridge-worker/tasks/leafcutter/import-label-studio.ts index cb1857e..4f50066 100644 --- a/apps/bridge-worker/tasks/leafcutter/import-label-studio.ts +++ b/apps/bridge-worker/tasks/leafcutter/import-label-studio.ts @@ -36,7 +36,6 @@ const getZammadTickets = async ( { headers }, ); const tickets: any = await rawTickets.json(); - console.log({ tickets }); if (!tickets || tickets.length === 0) { return [shouldContinue, docs]; } @@ -49,23 +48,9 @@ const getZammadTickets = async ( shouldContinue = true; if (source_closed_at <= minUpdatedTimestamp) { - console.log(`Skipping ticket`, { - source_id, - source_updated_at, - source_closed_at, - minUpdatedTimestamp, - }); continue; } - - console.log(`Processing ticket`, { - source_id, - source_updated_at, - source_closed_at, - minUpdatedTimestamp, - }); - - const rawArticles = await fetch( + const rawArticles = await fetch( `${zammadApiUrl}/ticket_articles/by_ticket/${source_id}`, { headers }, ); @@ -178,8 +163,6 @@ const sendToLabelStudio = async (tickets: FormattedZammadTicket[]) => { body: JSON.stringify([ticket]), }); const importResult = await res.json(); - - console.log(JSON.stringify(importResult, undefined, 2)); } }; */ @@ -201,7 +184,6 @@ const importLabelStudioTask = async (): Promise