department-of-veterans-affairs/vets-website

View on GitHub
src/applications/ivc-champva/10-10D/config/submitTransformer.js

Summary

Maintainability
B
5 hrs
Test Coverage
import { transformForSubmit as formsSystemTransformForSubmit } from 'platform/forms-system/src/js/helpers';
import { REQUIRED_FILES, OPTIONAL_FILES } from './constants';
import {
  adjustYearString,
  concatStreets,
  getAgeInYears,
} from '../../shared/utilities';

// Rearranges date string from YYYY-MM-DD to MM-DD-YYYY
function fmtDate(date) {
  return date?.length && date.length === 10
    ? `${date.slice(5)}-${date.slice(0, 4)}`
    : date;
}

// Simplify a relationship object from (potentially) multiple nested objects to a single string
function transformRelationship(obj) {
  if (typeof obj === 'string') return obj;
  let rel = obj?.relationshipToVeteran;
  // Check if relationshipToVeteran is an object
  if (typeof obj?.relationshipToVeteran === 'object') {
    // Iterate over keys in relationshipToVeteran object
    Object.keys(obj?.relationshipToVeteran).forEach(key => {
      // edge case for certifier relationship (generated by a checkboxgroup):
      if (obj?.relationshipToVeteran[key] === true) {
        rel = key; // e.g., `'spouse': true` yields `'spouse'`
      }
    });
  }
  // Check if otherRelationshipToVeteran is a string
  if (typeof obj?.otherRelationshipToVeteran === 'string') {
    rel = obj?.otherRelationshipToVeteran;
  }

  return rel;
}

// For each applicant, adjust organization of the object, add supporting documents array
function transformApplicants(applicants) {
  const applicantsPostTransform = [];
  applicants.forEach(app => {
    let transformedApp = {
      ...app,
      ssnOrTin: app.applicantSSN?.ssn ?? '',
      vetRelationship: transformRelationship(
        app.applicantRelationshipToSponsor || 'NA',
      ),
      // Grab any file upload properties from this applicant and combine into a
      // supporting documents array:
      applicantSupportingDocuments: Object.keys({
        ...REQUIRED_FILES,
        ...OPTIONAL_FILES,
      })
        .filter(k => k.includes('applicant')) // Ignore sponsor files
        .map(f => app?.[f]) // Grab the upload obj from top-level in applicant
        .filter(el => el), // Drop any undefineds/nulls
    };
    transformedApp = adjustYearString(transformedApp);
    transformedApp.applicantAddress = concatStreets(
      transformedApp.applicantAddress,
    );
    applicantsPostTransform.push(transformedApp);
  });
  return applicantsPostTransform;
}

// Since the certifier data may be the sponsor's or a third party, this maps
// the sponsor's info into the certifier property names for simplicity on BE
function parseCertifier(transformedData) {
  return {
    date: new Date().toJSON().slice(0, 10),
    firstName: transformedData.veteransFullName.first || '',
    lastName: transformedData.veteransFullName.last || '',
    middleInitial: transformedData?.veteransFullName?.middle || '',
    phoneNumber: transformedData?.sponsorPhone || '',
    relationship: 'sponsor',
    streetAddress: transformedData?.sponsorAddress?.streetCombined || '',
    city: transformedData?.sponsorAddress?.city || '',
    state: transformedData?.sponsorAddress?.state || '',
    postalCode: transformedData?.sponsorAddress?.postalCode || '',
  };
}

// Set up the `primaryContactInfo` for the backend notification API service.
function getPrimaryContact(data) {
  // If a certification name is present, third party signer is the certifier
  const useCert =
    data?.certification?.firstName && data?.certification?.firstName !== '';

  // Either the first applicant with an email address, or the first applicant
  const primaryApp =
    data?.applicants?.filter(
      app => app.applicantEmailAddress && app.applicantEmailAddress.length > 0,
    )[0] ?? data?.applicants?.[0];

  return {
    name: {
      first:
        (useCert
          ? data?.certification?.firstName
          : primaryApp?.applicantName?.first) ?? false,
      last:
        (useCert
          ? data?.certification?.lastName
          : primaryApp?.applicantName?.last) ?? false,
    },
    email:
      (useCert
        ? data?.certification?.email
        : primaryApp?.applicantEmailAddress) ?? false,
    phone:
      (useCert
        ? data?.certification?.phoneNumber
        : primaryApp?.applicantPhone) ?? false,
  };
}

export default function transformForSubmit(formConfig, form) {
  const transformedData = JSON.parse(
    formsSystemTransformForSubmit(formConfig, form),
  );

  if (transformedData.sponsorAddress)
    transformedData.sponsorAddress = concatStreets(
      transformedData.sponsorAddress,
    );

  if (transformedData.certifierAddress)
    transformedData.certifierAddress = concatStreets(
      transformedData.certifierAddress,
    );

  const dataPostTransform = {
    veteran: {
      fullName: transformedData?.veteransFullName || {},
      ssnOrTin: transformedData?.ssn?.ssn || '',
      vaClaimNumber: transformedData?.ssn?.vaFileNumber || '',
      dateOfBirth: fmtDate(transformedData?.sponsorDob) || '',
      phoneNumber: transformedData?.sponsorPhone || '',
      address: transformedData?.sponsorAddress || {},
      sponsorIsDeceased: transformedData?.sponsorIsDeceased,
      dateOfDeath: fmtDate(transformedData?.sponsorDOD) || '',
      // Find the first applicant with a date of marriage to sponsor
      dateOfMarriage:
        fmtDate(
          transformedData?.applicants?.find(
            a =>
              a?.applicantRelationshipToSponsor?.relationshipToVeteran ===
                'spouse' && a?.dateOfMarriageToSponsor !== undefined,
          )?.dateOfMarriageToSponsor,
        ) || '',
      isActiveServiceDeath: transformedData?.sponsorDeathConditions,
    },
    applicants: transformApplicants(transformedData.applicants ?? []),
    certification: {
      date: fmtDate(new Date().toISOString().replace(/T.*/, '')) || '',
      lastName: transformedData?.certifierName?.last || '',
      middleInitial: transformedData?.certifierName?.middle || '',
      firstName: transformedData?.certifierName?.first || '',
      phoneNumber: transformedData?.certifierPhone || '',
      relationship: transformRelationship(
        transformedData?.certifierRelationship,
      ),
      streetAddress: transformedData?.certifierAddress?.streetCombined || '',
      city: transformedData?.certifierAddress?.city || '',
      state: transformedData?.certifierAddress?.state || '',
      postalCode: transformedData?.certifierAddress?.postalCode || '',
    },
    supportingDocs: [],
    // Include everything we originally received
    rawData: transformedData,
  };

  // Fill in certification data with sponsor info as needed
  if (form.data.certifierRole === 'sponsor')
    dataPostTransform.certification = { ...parseCertifier(transformedData) };

  // Flatten supporting docs for all applicants to a single array
  const supDocs = [];
  dataPostTransform.applicants.forEach(app => {
    if (app.applicantSupportingDocuments.length > 0) {
      app.applicantSupportingDocuments.forEach(doc => {
        if (doc !== undefined && doc !== null) {
          // doc is an array of files for a given input (e.g., insurance cards).
          // For clarity's sake, add applicant's name onto each file object:
          const files = doc.map(file => ({
            ...file,
            applicantName: app.applicantName,
          }));
          supDocs.push(...files);
        }
      });
    }
  });

  // Set a top-level boolean indicating if any applicants are over 65
  dataPostTransform.hasApplicantOver65 = dataPostTransform.applicants.some(
    app => getAgeInYears(app.applicantDob) >= 65,
  );

  dataPostTransform.supportingDocs = dataPostTransform.supportingDocs
    .flat()
    .concat(supDocs);
  dataPostTransform.certifierRole = transformedData.certifierRole;
  dataPostTransform.statementOfTruthSignature =
    transformedData.statementOfTruthSignature;
  // `primaryContactInfo` is who BE callback API emails if there's a notification event
  dataPostTransform.primaryContactInfo = getPrimaryContact(dataPostTransform);
  return JSON.stringify({
    ...dataPostTransform,
    formNumber: formConfig.formId,
  });
}