Implemented the Organization Chart feature to create customer organizational

This commit is contained in:
2026-10-26 18:15:32 -03:00
parent bf48ed1dcb
commit 3229ce9c41
41 changed files with 4099 additions and 63 deletions

View File

@@ -0,0 +1,105 @@
function createFusionAccountContact({ environment, getToken, logRequestStart, logRequestSuccess, logRequestFailure, readResponseBodyForDebug }) {
async function requestQueryAccountContact(accountId, keywords, isActive = () => true, contactPartyId = null) {
if (!/^\d+$/.test(String(accountId)) || !Number.isSafeInteger(Number(accountId))) throw new Error("The customer's PartyId is unavailable.");
if (contactPartyId != null && !/^\d+$/.test(String(contactPartyId))) throw new Error("The contact's PartyId is unavailable.");
if ((!String(keywords || "").trim() && contactPartyId == null) || !isActive()) return [];
const token = await getToken();
if (!isActive()) return [];
const url = "https://eeho.fa.us2.oraclecloud.com/crmRestApi/searchResources/latest/custom-actions/queries";
const fields = ["PartyName", "PrimaryEmail.EmailAddress", "JobTitle", "PrimaryAccount.PartyName", "PartyId", "PartyNumber"];
const payload = { aggregationResults: false, skipInValidFields: true, skipHiddenFromUIFields: true,
language: "en", onlyData: true, keywords, entity: "Contact", fields, keywordsFields: fields,
q: { op: "$and", criteria: [{ op: "$or", innerQuery: true, criteria: [{ attribute: "PrimaryAccount.PartyId", op: "$eq", value: Number(accountId) }] }], innerQuery: true },
sort: [{ attribute: "_score", direction: "descending" }], limit: 15, offset: 0 };
if (contactPartyId != null) {
payload.keywords = "";
payload.q.criteria.push({ attribute: "PartyId", op: "$eq", value: String(contactPartyId) });
}
const headers = { Accept: "application/json", "Content-Type": "application/json", Authorization: `Bearer ${token}`, Preference: "transient", Origin: "https://eeho.fa.us2.oraclecloud.com" };
const entry = logRequestStart({ name: "requestQueryAccountContact", method: "POST", url, headers, body: JSON.stringify(payload, null, 2) });
try {
const response = await environment.fetch.call(environment.window, url, { method: "POST", credentials: "include", headers, body: JSON.stringify(payload) });
logRequestSuccess(entry, response, await readResponseBodyForDebug(response));
if (!response.ok) throw new Error(`requestQueryAccountContact failed with status ${response.status}.`);
const data = await response.json();
if (!data || !Array.isArray(data.items)) throw new Error("requestQueryAccountContact returned an invalid response.");
if (!isActive()) return [];
const contacts = new Map();
for (const item of data.items) if (item?.PartyId != null && typeof item.PartyName === "string" && item.PartyName.trim()) {
contacts.set(String(item.PartyId), { ...item, PartyId: String(item.PartyId) });
}
return [...contacts.values()];
} catch (error) { logRequestFailure(entry, error.message); throw error; }
}
async function requestPersistContact(account, values) {
if (!/^\d+$/.test(String(account?.PartyId)) || !String(account?.PartyNumber || "").trim()) throw new Error("The customer's PartyId or PartyNumber is unavailable.");
const phone = (number, country) => {
if (!number && !country) return "";
if (!number || !country) throw new Error("Select a country code and enter a phone number for each phone provided.");
const digits = number.replace(/[\s().-]/g, "");
if (!/^\d+$/.test(digits)) throw new Error("Enter the phone number using digits only, without the country code.");
return country + "-" + digits;
};
const payload = {
FirstName: values.firstName.trim(), LastName: values.lastName.trim(),
AccountPartyId: String(account.PartyId), AccountPartyNumber: String(account.PartyNumber),
JobTitle: values.jobTitle.trim(), Type: "ZCA_CONTACT",
EmailAddress: values.email.trim(),
RawWorkPhoneNumber: phone(values.workPhone.trim(), values.workCountry), WorkPhoneCountryCode: values.workCountry,
WorkPhoneExtension: values.workExtension.trim(), MobileCountryCode: values.mobileCountry,
RawMobileNumber: phone(values.mobilePhone.trim(), values.mobileCountry)
};
if (!payload.FirstName || !payload.LastName || !payload.EmailAddress) throw new Error("First Name, Last Name and Email Address are required.");
for (const key of Object.keys(payload)) if (payload[key] === "") delete payload[key];
for (const [choice, allow, deny] of [
[values.emailConsent, "DoContactEmailFlag", "DoNotContactEmailFlag"],
[values.workConsent, "DoCallWorkPhoneFlag", "DoNotCallWorkPhoneFlag"],
[values.mobileConsent, "DoCallMobilePhoneFlag", "DoNotCallMobileFlag"]
]) {
if (choice === "allow" || choice === "deny") {
payload[allow] = choice === "allow";
payload[deny] = choice === "deny";
}
}
const body = JSON.stringify(payload);
const token = await getToken();
const url = "https://eeho.fa.us2.oraclecloud.com/crmRestApi/rest/rv:e75ce5f9-a69f-4c6a-8591-6b06a538829e/en/11.13.18.05:9/contacts?onlyData=true";
const headers = { Accept: "application/json", "Content-Type": "application/json", Authorization: `Bearer ${token}`, Origin: "https://eeho.fa.us2.oraclecloud.com" };
const entry = logRequestStart({ name: "requestPersistContact", method: "POST", url, headers, body: JSON.stringify(payload, null, 2) });
try {
const response = await environment.fetch.call(environment.window, url, { method: "POST", credentials: "include", headers, body });
logRequestSuccess(entry, response, await readResponseBodyForDebug(response));
if (!response.ok) throw new Error(`requestPersistContact failed with status ${response.status}.`);
const text = await response.text();
try { return text ? JSON.parse(text) : null; } catch { return null; }
} catch (error) { logRequestFailure(entry, error.message); throw error; }
}
async function requestOpportunitiesByContact(partyNumber, offset = 0, context = {}) {
const baseUrl = "https://eeho.fa.us2.oraclecloud.com/crmRestApi/rest/rv:e75ce5f9-a69f-4c6a-8591-6b06a538829e/en/11.13.18.05:9/opportunities";
let entry;
try {
if (!String(partyNumber || "").trim() && context.resolvePartyNumber) partyNumber = await context.resolvePartyNumber();
partyNumber = String(partyNumber || "").trim();
if (!/^[A-Za-z0-9]{1,100}$/.test(partyNumber)) throw new Error("The person's PartyNumber is missing or invalid. The opportunities HTTP request was not sent.");
if (context.isActive && !context.isActive()) throw new Error("Opportunities lookup cancelled because its dialog was closed. HTTP request was not sent.");
const token = await getToken();
const fields = "SalesStageId,KeyContactId,EffectiveDate,WinProb,CurrencyCode,Revenue,SalesStage,Name,ManageRestrictedProfileDataFlag,OptyId,OptyNumber,ManageRevenueDataFlag,StatusCode";
const query = encodeURIComponent("(OpportunityContact.ContactPartyNumber='" + partyNumber + "')").replace(/[()']/g, character => "%" + character.charCodeAt(0).toString(16).toUpperCase());
const url = baseUrl + "?onlyData=true&q=" + query + "&totalResults=true&fields=" + encodeURIComponent(fields) + "&orderBy=LastUpdateDate%3Adesc&limit=5&offset=" + offset;
const headers = { Accept: "application/json", "Content-Type": "application/json", Authorization: `Bearer ${token}`, Preference: "transient", Origin: "https://eeho.fa.us2.oraclecloud.com" };
entry = logRequestStart({ name: "requestOpportunitiesByContact", method: "GET", url, headers,
metadata: { contactPartyId: context.contactPartyId || "", contactName: context.contactName || "", partyNumber, offset, httpRequestSent: true } });
const response = await environment.fetch.call(environment.window, url, { method: "GET", credentials: "include", headers });
logRequestSuccess(entry, response, await readResponseBodyForDebug(response));
if (!response.ok) throw new Error(`requestOpportunitiesByContact failed with status ${response.status}.`);
const data = await response.json();
if (!Array.isArray(data?.items)) throw new Error("requestOpportunitiesByContact returned an invalid response.");
return data;
} catch (error) {
if (!entry) entry = logRequestStart({ name: "resolveContactForOpportunities", method: "LOCAL", url: "(HTTP request not sent)",
metadata: { contactPartyId: context.contactPartyId || "", contactName: context.contactName || "", offset, httpRequestSent: false, targetRequest: "requestOpportunitiesByContact" } });
logRequestFailure(entry, error.message); throw error;
}
}
return { requestQueryAccountContact, requestPersistContact, requestOpportunitiesByContact };
}