Source: management-system/src/frontend/components/5thIndustry/5thIndustryHelper.js

import {
  toBpmnObject,
  toBpmnXml,
  getElementsByTagName,
  getMetaData,
  setMetaData,
  getTaskConstraintMapping,
  addConstraintsToElementById,
  setUserTaskData,
} from '@proceed/bpmn-helper';
import { fifthIndustryInterface } from '@/frontend/backend-api/index.js';

import { getUpdatedTaskConstraintMapping } from '@/frontend/helpers/usertask-helper.js';
import { asyncForEach } from '@/shared-frontend-backend/helpers/javascriptHelpers.js';

export async function remove5thIndustryDataFromXml(data) {
  const bpmnObj = await toBpmnObject(data.bpmn);

  const processes = getElementsByTagName(bpmnObj, 'bpmn:Process');

  await asyncForEach(processes, async (process) => {
    const meta = await getMetaData(bpmnObj, process.id);

    // if there is no plan set on the bpmn we don't expect there to be other 5i Data
    if (!meta['_5i-Inspection-Plan-ID'] && !meta['_5i-Inspection-Plan-Template-ID']) {
      return;
    }

    // reset 5thIndustry data on the process object
    await setMetaData(bpmnObj, process.id, {
      '_5i-Inspection-Plan-ID': undefined,
      '_5i-Inspection-Plan-Template-ID': undefined,
      '_5i-Inspection-Plan-Title': undefined,
      '_5i-API-Address': undefined,
      '_5i-Application-Address': undefined,
    });

    // reset 5thIndustry data on all user tasks
    const userTasks = getElementsByTagName(process, 'bpmn:UserTask');

    await asyncForEach(userTasks, async (userTask) => {
      await setMetaData(bpmnObj, userTask.id, {
        '_5i-Assembly-Group-ID': undefined,
        '_5i-Assembly-Group-Name': undefined,
        '_5i-Manufacturing-Step-ID': undefined,
        '_5i-Manufacturing-Step-Name': undefined,
        '_5i-Inspection-Order-ID': undefined,
        '_5i-Inspection-Order-Code': undefined,
        '_5i-Inspection-Order-Shortdescription': undefined,
      });
    });
  });

  data.bpmn = await toBpmnXml(bpmnObj);
  return data;
}

export async function createPlan(info, templateId, bpmn) {
  const newPlan = await fifthIndustryInterface.createInspectionPlan(info, templateId);

  let newBpmn;
  if (bpmn) {
    newBpmn = await mapUserTasks(newPlan, bpmn);
  }

  return { newPlanId: newPlan._id, newBpmn };
}

/**
 * Tries to adjust the ids of the assembly groups, manufacturing steps and inspection orders that were assigned to user tasks
 *
 * This is necessary since the ids change when a plan is created from a template
 *
 * @param {Object} newPlan the newly created plan
 */
async function mapUserTasks(newPlan, bpmn) {
  // we first have to make sure that all assembly groups and all manufacturing steps inside a single assembly group are uniquely named
  // this is necessary since the names are the only thing that can be used to match them to their counterparts in the template
  // if they were not unique we couldn't know which one is supposed to be the one to map to
  const bpmnObj = await toBpmnObject(bpmn);
  const userTasks = await getElementsByTagName(bpmnObj, 'bpmn:UserTask');

  for (const userTask of userTasks) {
    const meta = await getMetaData(bpmnObj, userTask.id);

    if (meta['_5i-Assembly-Group-Name']) {
      const newMeta = getNewMetaData(
        newPlan.assemblyGroup,
        meta['_5i-Assembly-Group-Name'],
        meta['_5i-Manufacturing-Step-Name'],
        meta['_5i-Inspection-Order-Code']
      );

      await setMetaData(bpmnObj, userTask.id, newMeta);
    }
  }

  return await toBpmnXml(bpmnObj);
}

// check if the order can be uniquely identified and if yes return the updated information (e.g. the new ids) of the group step and order
function getNewMetaData(assemblyGroups, groupName, stepName, orderCode) {
  // will be returned if the correct order can't be deduced to reset the order assigned to a user task
  let newMeta = {
    '_5i-Assembly-Group-ID': undefined,
    '_5i-Assembly-Group-Name': undefined,
    '_5i-Manufacturing-Step-ID': undefined,
    '_5i-Manufacturing-Step-Name': undefined,
    '_5i-Inspection-Order-ID': undefined,
    '_5i-Inspection-Order-Code': undefined,
    '_5i-Inspection-Order-Shortdescription': undefined,
  };

  const groups = assemblyGroups.filter((group) => {
    return group.assemblyGroupName.some(({ value }) => value === groupName);
  });

  if (groups.length !== 1) {
    return newMeta;
  }

  const steps = groups[0].manufacturingStep.filter((step) => {
    return step.manufacturingStepName.some(({ value }) => value === stepName);
  });

  if (steps.length !== 1) {
    return newMeta;
  }

  const orders = steps[0].inspectionOrders.filter((order) => {
    return order.inspectionCode.some(({ value }) => value === orderCode);
  });

  if (orders.length !== 1) {
    return newMeta;
  }

  let order = orders[0];

  let orderDescription;

  if (order.inspectionDescriptionShort && order.inspectionDescriptionShort.length) {
    orderDescription = order.inspectionDescriptionShort[0].value.trim();
  }

  return {
    '_5i-Assembly-Group-ID': groups[0]._id,
    '_5i-Assembly-Group-Name': groupName,
    '_5i-Manufacturing-Step-ID': steps[0]._id,
    '_5i-Manufacturing-Step-Name': stepName,
    '_5i-Inspection-Order-ID': orders[0]._id,
    '_5i-Inspection-Order-Code': orderCode,
    '_5i-Inspection-Order-Shortdescription': orderDescription,
  };
}

/**
 * Will remove trailing slash from a url if there is one
 *
 * @param {String} url the url to normalize
 */
export function normalizeUrl(url) {
  if (url.endsWith('/')) {
    url = url.slice(0, url.length - 1);
  }

  return url;
}

export async function setUserTasksTo5thIndustry(bpmn) {
  const bpmnObj = await toBpmnObject(bpmn);

  const userTasks = await getElementsByTagName(bpmnObj, 'bpmn:UserTask');
  const constraintMapping = await getTaskConstraintMapping(bpmnObj);

  for (const userTask of userTasks) {
    // make sure to remove html specific constraints
    const newConstraints = getUpdatedTaskConstraintMapping(constraintMapping[userTask.id], '');
    await addConstraintsToElementById(bpmnObj, userTask.id, newConstraints);
    // remove fileName attribute and set implementation to 5thIndustry
    await setUserTaskData(bpmnObj, userTask.id, undefined, '5thIndustry');
  }

  return await toBpmnXml(bpmnObj);
}