Untitled

 avatar
unknown
plain_text
2 months ago
12 kB
7
Indexable
var ACWAPP = ACWAPP || {};
var Sdk = window.Sdk || {};

ACWAPP.JoinAwardReview = (function () {

  /* =========================
     CONFIG
  ========================= */
  const WEBR_CTRL_NAME = "WebResource_JoinAwardReview";
  const RELATED_SUBGRID_NAME = "RelatedCaseRef";
  const RELATIONSHIP_NAME = "acwapp_acwapp_abusecases_acwapp_abusecases";

  const CASE_LOGICAL = "acwapp_abusecases";
  const ALLEG_LOGICAL = "acwapp_suspectedabuse";

  /* =========================
     FILTER VALUES
  ========================= */
  const CASECATEGORY_YES = 100000000;
  const CASESTATUS_AWARD = 100000005;

  /* =========================
     CASE FIELDS
  ========================= */
  const CASE_ID = "acwapp_abusecasesid";
  const CASE_NUMBER_FIELD = "acwapp_name";
  const CASE_STAGE_FIELD = "acwapp_casestatus";
  const CASE_CATEGORY_FIELD = "acwapp_casecategory";
  const CASE_PROPERTY_LOOKUP_VALUE = "_acwapp_propertycodemainflat_value";
  const CASE_COT_FIELD = "acwapp_commencementoftenancydate";
  const CASE_ESTATE_LOOKUP_VALUE = "_acwapp_estate_value";
  const CASE_SOURCE_OF_ALLEGATION = "acwapp_sourceofallegation";

  /* =========================
     ALLEGATION FIELDS
  ========================= */
  const ALLEG_CASE_LOOKUP_VALUE = "_acwapp_abusecase_value";
  const ALLEG_TYPE_FIELD = "acwapp_typeofsuspectedabuse";
  const ALLEG_REFER_TO_MOT = "acwapp_refertomot";
  const ALLEG_RESULT_FIELD = "acwapp_investigationresult";
  const ALLEG_RESULT_MOT_FIELD = "acwapp_suggestedinvestigationresult2";
  const ALLEG_SUGGESTED_ACTION_FIELD = "acwapp_suggestedtenancyactions";
  const ALLEG_STATUS_FIELD = "acwapp_allegationstatus";
  const ALLEG_ACTIONS_FOR_SUGGESTED_TENANCY = "acwapp_actionsforsuggestedtenancy";
  const ALLEG_REASON_NO_TENANCY_ACTION = "acwapp_reasonofnotenancyactionrequired";

  /* =========================
     OPTION SET VALUES
  ========================= */
  const REFER_TO_MOT = {
    NO: 100000000,
    YES: 100000001
  };

  /* =========================
     ASSOCIATE REQUEST
  ========================= */
  Sdk.AssociateRequest = function (target, relatedEntities, relationship) {
    this.target = target;
    this.relatedEntities = relatedEntities;
    this.relationship = relationship;
  };

  Sdk.AssociateRequest.prototype.getMetadata = function () {
    return {
      boundParameter: null,
      parameterTypes: {
        target: { typeName: "mscrm.crmbaseentity", structuralProperty: 5 },
        relatedEntities: {
          typeName: "Collection(mscrm.crmbaseentity)",
          structuralProperty: 4
        },
        relationship: { typeName: "Edm.String", structuralProperty: 1 }
      },
      operationType: 2,
      operationName: "Associate"
    };
  };

  /* =========================
     HELPERS
  ========================= */
  function stripBraces(id) {
    return (id || "").replace(/[{}]/g, "");
  }

  function escapeHtml(s) {
    return (s ?? "").toString()
      .replace(/&/g, "&")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;");
  }

  function fmt(e, f) {
    return e?.[`${f}@OData.Community.Display.V1.FormattedValue`] ?? e?.[f] ?? "";
  }

  function fmtLookup(e, f) {
    return e?.[`${f}@OData.Community.Display.V1.FormattedValue`] ?? "";
  }

  function formatDate(d) {
    if (!d) return "";
    const dt = new Date(d);
    return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, "0")}-${String(dt.getDate()).padStart(2, "0")}`;
  }

  function dayRangeUtc(dateValue) {
    const start = new Date(dateValue);
    start.setUTCHours(0, 0, 0, 0);
    const end = new Date(start);
    end.setUTCDate(start.getUTCDate() + 1);
    return { start, end };
  }

  async function getRoot(formContext) {
    const ctrl = formContext.getControl(WEBR_CTRL_NAME);
    const win = await ctrl.getContentWindow();
    return win.document.getElementById("root");
  }

  function wait(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /* =========================
     READ RELATED SUBGRID IDS
  ========================= */
  function getRelatedCaseIds(formContext) {
    const set = new Set();
    const grid = formContext.getControl(RELATED_SUBGRID_NAME);
    if (!grid || !grid.getGrid) return set;

    try {
      grid.getGrid().getRows().forEach(r => {
        const id = stripBraces(r.getData().getEntity().getId());
        if (id) set.add(id);
      });
    } catch (error) {
      console.error("Failed to read related subgrid rows:", error);
    }

    return set;
  }

  /* =========================
     TABLE RENDER
  ========================= */
  function renderTable(root, rows, formContext, relatedSet) {
    root.innerHTML = `
      <table style="width:100%;border-collapse:collapse">
        <thead>
          <tr style="background:#f5f5f5">
            <th></th>
            <th>Case Number</th>
            <th>Estate</th>
            <th>Allegation</th>
            <th>Property Code</th>
            <th>COT</th>
            <th>Case Stage</th>
            <th>Allegation Result</th>
            <th>Suggested Action</th>
            <th>Allegation Status</th>
            <th>Actions for Suggested Tenancy</th>
            <th>Reason of No Tenancy Action Required</th>
          </tr>
        </thead>
        <tbody>
          ${rows.map(r => {
            const added = relatedSet.has(stripBraces(r.caseId));
            return `
              <tr data-id="${r.caseId}">
                <td>
                  <button class="add-btn" ${added ? "disabled" : ""}>
                    ${added ? "Added" : "Add"}
                  </button>
                </td>
                <td>${escapeHtml(r.caseNumber)}</td>
                <td>${escapeHtml(r.estate)}</td>
                <td>${escapeHtml(r.allegation)}</td>
                <td>${escapeHtml(r.property)}</td>
                <td>${escapeHtml(r.cot)}</td>
                <td>${escapeHtml(r.stage)}</td>
                <td>${escapeHtml(r.result)}</td>
                <td>${escapeHtml(r.action)}</td>
                <td>${escapeHtml(r.allegStatus)}</td>
                <td>${escapeHtml(r.actionsForSuggestedTenancy)}</td>
                <td>${escapeHtml(r.reasonNoTenancyAction)}</td>
              </tr>
            `;
          }).join("")}
        </tbody>
      </table>
    `;

    root.querySelectorAll(".add-btn:not([disabled])").forEach(btn => {
      btn.onclick = async (e) => {
        e.stopPropagation();

        const tr = btn.closest("tr");
        const relatedId = stripBraces(tr.dataset.id);
        const parentId = stripBraces(formContext.data.entity.getId());

        btn.disabled = true;
        btn.textContent = "Adding...";

        try {
          await Xrm.WebApi.online.execute(
            new Sdk.AssociateRequest(
              { entityType: CASE_LOGICAL, id: parentId },
              [{ entityType: CASE_LOGICAL, id: relatedId }],
              RELATIONSHIP_NAME
            )
          );

          btn.textContent = "Added";
          relatedSet.add(relatedId);

          const subgrid = formContext.getControl(RELATED_SUBGRID_NAME);
          if (subgrid) {
            subgrid.refresh();
          }

          await wait(1000);
          await render({ getFormContext: () => formContext });

        } catch (error) {
          console.error("Associate failed:", error);
          btn.disabled = false;
          btn.textContent = "Add";

          await Xrm.Navigation.openAlertDialog({
            text: error?.message || "Failed to add related abuse case."
          });
        }
      };
    });

    root.querySelectorAll("tbody tr").forEach(tr => {
      tr.style.cursor = "pointer";
      tr.addEventListener("click", () => {
        const id = stripBraces(tr.dataset.id);
        if (!id) return;
        Xrm.Navigation.openForm({ entityName: CASE_LOGICAL, entityId: id });
      });
    });
  }

  /* =========================
     MAIN
  ========================= */
  async function render(executionContext) {
    try {
      const formContext = executionContext.getFormContext();
      const root = await getRoot(formContext);

      root.innerHTML = "Loading Join Award Review…";

      const parentId = stripBraces(formContext.data.entity.getId());

      const parent = await Xrm.WebApi.retrieveRecord(
        CASE_LOGICAL,
        parentId,
        `?$select=${CASE_COT_FIELD},${CASE_PROPERTY_LOOKUP_VALUE}`
      );

      const { start, end } = dayRangeUtc(parent[CASE_COT_FIELD]);
      const propId = stripBraces(parent[CASE_PROPERTY_LOOKUP_VALUE]);

      const cases = (await Xrm.WebApi.retrieveMultipleRecords(
        CASE_LOGICAL,
        `?$select=${CASE_ID},
                  ${CASE_NUMBER_FIELD},
                  ${CASE_STAGE_FIELD},
                  ${CASE_COT_FIELD},
                  ${CASE_ESTATE_LOOKUP_VALUE},
                  ${CASE_PROPERTY_LOOKUP_VALUE},
                  ${CASE_SOURCE_OF_ALLEGATION}` +
        `&$filter=${CASE_PROPERTY_LOOKUP_VALUE} eq ${propId}` +
        ` and ${CASE_COT_FIELD} ge ${start.toISOString()}` +
        ` and ${CASE_COT_FIELD} lt ${end.toISOString()}` +
        ` and ${CASE_CATEGORY_FIELD} eq ${CASECATEGORY_YES}` +
        ` and ${CASE_STAGE_FIELD} le ${CASESTATUS_AWARD}`
      )).entities;

      const caseMap = {};
      cases.forEach(c => {
        caseMap[c[CASE_ID]] = c;
      });

      let allegs = [];
      if (cases.length > 0) {
        allegs = (await Xrm.WebApi.retrieveMultipleRecords(
          ALLEG_LOGICAL,
          `?$select=${ALLEG_CASE_LOOKUP_VALUE},
                    ${ALLEG_TYPE_FIELD},
                    ${ALLEG_REFER_TO_MOT},
                    ${ALLEG_RESULT_FIELD},
                    ${ALLEG_RESULT_MOT_FIELD},
                    ${ALLEG_SUGGESTED_ACTION_FIELD},
                    ${ALLEG_STATUS_FIELD},
                    ${ALLEG_ACTIONS_FOR_SUGGESTED_TENANCY},
                    ${ALLEG_REASON_NO_TENANCY_ACTION}` +
          `&$filter=${cases.map(c => `${ALLEG_CASE_LOOKUP_VALUE} eq ${c[CASE_ID]}`).join(" or ")}`
        )).entities;
      }

      const rows = allegs.map(a => {
        const c = caseMap[a[ALLEG_CASE_LOOKUP_VALUE]];
        if (!c) return null;

        const source = c[CASE_SOURCE_OF_ALLEGATION];
        const referToMot = a[ALLEG_REFER_TO_MOT] === REFER_TO_MOT.YES;

        let resultValue = "";

        if (source === 100000002 || source === 100000003) {
          resultValue = fmt(a, ALLEG_RESULT_MOT_FIELD);
        } else if (source === 100000000 || source === 100000001) {
          if (!referToMot) {
            resultValue = fmt(a, ALLEG_RESULT_FIELD);
          } else {
            resultValue = fmt(a, ALLEG_RESULT_MOT_FIELD);
          }
        }

        return {
          caseId: c[CASE_ID],
          caseNumber: fmt(c, CASE_NUMBER_FIELD),
          estate: fmtLookup(c, CASE_ESTATE_LOOKUP_VALUE),
          property: fmtLookup(c, CASE_PROPERTY_LOOKUP_VALUE),
          cot: formatDate(c[CASE_COT_FIELD]),
          stage: fmt(c, CASE_STAGE_FIELD),
          allegation: fmt(a, ALLEG_TYPE_FIELD),
          result: resultValue,
          action: fmt(a, ALLEG_SUGGESTED_ACTION_FIELD),
          allegStatus: fmt(a, ALLEG_STATUS_FIELD),
          actionsForSuggestedTenancy: fmt(a, ALLEG_ACTIONS_FOR_SUGGESTED_TENANCY),
          reasonNoTenancyAction: fmt(a, ALLEG_REASON_NO_TENANCY_ACTION)
        };
      }).filter(Boolean);

      renderTable(root, rows, formContext, getRelatedCaseIds(formContext));

    } catch (error) {
      console.error("Join Award Review render failed:", error);

      try {
        const formContext = executionContext.getFormContext();
        const root = await getRoot(formContext);
        root.innerHTML = `<div style="color:red;">Failed to load Join Award Review.</div>`;
      } catch {}
    }
  }

  return { render };
})();
Editor is loading...
Leave a Comment