add org chart by root id
This commit is contained in:
parent
2ec55789a7
commit
554e3f2e22
1 changed files with 328 additions and 1 deletions
|
|
@ -3353,7 +3353,13 @@ export class OrganizationController extends Controller {
|
||||||
"orgChild3",
|
"orgChild3",
|
||||||
"orgChild4",
|
"orgChild4",
|
||||||
],
|
],
|
||||||
order: { posMasterOrder: "ASC" },
|
order: {
|
||||||
|
orgRoot: {
|
||||||
|
orgRootOrder: "ASC",
|
||||||
|
},
|
||||||
|
posMasterOrder: "ASC",
|
||||||
|
posMasterNo: "ASC",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// แยกข้อมูลด้วย JavaScript แทนการใช้ database queries หลาย ๆ ครั้ง
|
// แยกข้อมูลด้วย JavaScript แทนการใช้ database queries หลาย ๆ ครั้ง
|
||||||
|
|
@ -3883,6 +3889,327 @@ export class OrganizationController extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API Organizational Chart
|
||||||
|
*
|
||||||
|
* @summary Organizational Chart
|
||||||
|
*
|
||||||
|
* @param {string} revisionId Id revison
|
||||||
|
*/
|
||||||
|
@Get("org-chart/{revisionId}/{rootId}")
|
||||||
|
async orgChartByRoot(@Path() revisionId: string, @Path() rootId: string) {
|
||||||
|
// get revision data
|
||||||
|
const data = await this.orgRevisionRepository.findOne({ where: { id: revisionId } });
|
||||||
|
if (!data) {
|
||||||
|
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้าง");
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrgInfo = { shortName: string; name: string; id: string };
|
||||||
|
|
||||||
|
let fieldId: "current_holderId" | "next_holderId" = "current_holderId";
|
||||||
|
let relations: string[] =
|
||||||
|
rootId === "root"
|
||||||
|
? ["current_holder", "orgRoot"]
|
||||||
|
: ["current_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"];
|
||||||
|
if (data.orgRevisionIsCurrent === false && data.orgRevisionIsDraft === true) {
|
||||||
|
fieldId = "next_holderId";
|
||||||
|
relations =
|
||||||
|
rootId === "root"
|
||||||
|
? ["next_holder", "orgRoot"]
|
||||||
|
: ["next_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"];
|
||||||
|
}
|
||||||
|
|
||||||
|
const where =
|
||||||
|
rootId === "root"
|
||||||
|
? { orgRevisionId: data.id, posMasterNo: 1 }
|
||||||
|
: { orgRevisionId: data.id, orgRootId: rootId };
|
||||||
|
|
||||||
|
const allPosMasters = await this.posMasterRepository.find({
|
||||||
|
where,
|
||||||
|
relations,
|
||||||
|
order: {
|
||||||
|
orgRoot: { orgRootOrder: "ASC" },
|
||||||
|
posMasterOrder: "ASC",
|
||||||
|
posMasterNo: "ASC",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Split positions by level
|
||||||
|
const posMasterRoot = allPosMasters.filter((item) => item.orgChild1Id === null);
|
||||||
|
const posMasterChild1 =
|
||||||
|
rootId !== "root"
|
||||||
|
? allPosMasters.filter((item) => item.orgChild2Id === null && item.orgChild1Id !== null)
|
||||||
|
: [];
|
||||||
|
const posMasterChild2 =
|
||||||
|
rootId !== "root"
|
||||||
|
? allPosMasters.filter((item) => item.orgChild3Id === null && item.orgChild2Id !== null)
|
||||||
|
: [];
|
||||||
|
const posMasterChild3 =
|
||||||
|
rootId !== "root"
|
||||||
|
? allPosMasters.filter((item) => item.orgChild4Id === null && item.orgChild3Id !== null)
|
||||||
|
: [];
|
||||||
|
const posMasterChild4 =
|
||||||
|
rootId !== "root" ? allPosMasters.filter((item) => item.orgChild4Id !== null) : [];
|
||||||
|
|
||||||
|
// Find the minimum posMasterNo for each orgRootId
|
||||||
|
const minPosMasterNoByOrgRootId = new Map<string, number>();
|
||||||
|
posMasterRoot.forEach((x) => {
|
||||||
|
const orgRootId = x.orgRootId ?? "";
|
||||||
|
const currentMin = minPosMasterNoByOrgRootId.get(orgRootId);
|
||||||
|
if (
|
||||||
|
(currentMin === undefined || x.posMasterNo < currentMin) &&
|
||||||
|
(x as any)[fieldId] != null &&
|
||||||
|
x.isDirector
|
||||||
|
) {
|
||||||
|
minPosMasterNoByOrgRootId.set(orgRootId, x.posMasterNo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Utility to group by key
|
||||||
|
function groupBy<T>(
|
||||||
|
arr: T[],
|
||||||
|
keyFn: (item: T) => string | number | undefined | null,
|
||||||
|
): Map<string, T[]> {
|
||||||
|
const map = new Map<string, T[]>();
|
||||||
|
for (const item of arr) {
|
||||||
|
let key = keyFn(item);
|
||||||
|
if (key === undefined || key === null) key = "";
|
||||||
|
key = String(key);
|
||||||
|
if (!map.has(key)) map.set(key, []);
|
||||||
|
map.get(key)!.push(item);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build lookup maps
|
||||||
|
const rootByOrgRootId = groupBy(
|
||||||
|
posMasterRoot.filter(
|
||||||
|
(item) =>
|
||||||
|
((item as any)[fieldId] && !item.isDirector) ||
|
||||||
|
(item.isDirector &&
|
||||||
|
minPosMasterNoByOrgRootId.get(item.orgRootId ?? "") !== item.posMasterNo),
|
||||||
|
),
|
||||||
|
(item) => item.orgRootId ?? "",
|
||||||
|
);
|
||||||
|
const child1ByOrgRootId = groupBy(
|
||||||
|
posMasterChild1.filter((item) => item.isDirector),
|
||||||
|
(item) => item.orgRootId ?? "",
|
||||||
|
);
|
||||||
|
const child1ByOrgChild1Id = groupBy(
|
||||||
|
posMasterChild1.filter((item) => (item as any)[fieldId] && !item.isDirector),
|
||||||
|
(item) => item.orgChild1Id ?? "",
|
||||||
|
);
|
||||||
|
const child2ByOrgChild1Id = groupBy(
|
||||||
|
posMasterChild2.filter((item) => item.isDirector),
|
||||||
|
(item) => item.orgChild1Id ?? "",
|
||||||
|
);
|
||||||
|
const child2ByOrgChild2Id = groupBy(
|
||||||
|
posMasterChild2.filter((item) => (item as any)[fieldId] && !item.isDirector),
|
||||||
|
(item) => item.orgChild2Id ?? "",
|
||||||
|
);
|
||||||
|
const child3ByOrgChild2Id = groupBy(
|
||||||
|
posMasterChild3.filter((item) => item.isDirector),
|
||||||
|
(item) => item.orgChild2Id ?? "",
|
||||||
|
);
|
||||||
|
const child3ByOrgChild3Id = groupBy(
|
||||||
|
posMasterChild3.filter((item) => (item as any)[fieldId] && !item.isDirector),
|
||||||
|
(item) => item.orgChild3Id ?? "",
|
||||||
|
);
|
||||||
|
const child4ByOrgChild3Id = groupBy(
|
||||||
|
posMasterChild4.filter((item) => item.isDirector),
|
||||||
|
(item) => item.orgChild3Id ?? "",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Helper to create node
|
||||||
|
function createNode(item: any, level: number, orgInfo: OrgInfo): any {
|
||||||
|
const holder = fieldId === "current_holderId" ? item.current_holder : item.next_holder;
|
||||||
|
return {
|
||||||
|
level,
|
||||||
|
personID: holder?.id ?? "",
|
||||||
|
name: holder ? `${holder.firstName} ${holder.lastName}` : "ว่าง",
|
||||||
|
avatar:
|
||||||
|
holder?.avatar && holder?.avatarName ? `${holder.avatar}/${holder.avatarName}` : null,
|
||||||
|
positionName: holder?.position ?? "",
|
||||||
|
positionNum: `${orgInfo.shortName} ${item.posMasterNo}`,
|
||||||
|
positionNumInt: item.posMasterNo,
|
||||||
|
departmentName: orgInfo.name,
|
||||||
|
organizationId: orgInfo.id,
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursive builder for children
|
||||||
|
function buildChildren(level: number, parent: any, orgInfo: OrgInfo): any[] {
|
||||||
|
if (level === 2) {
|
||||||
|
// Level 2: child1 non-directors and child2 directors
|
||||||
|
const children: any[] = [];
|
||||||
|
// Root non-directors
|
||||||
|
(
|
||||||
|
rootByOrgRootId
|
||||||
|
.get(parent.orgRootId)
|
||||||
|
?.filter(
|
||||||
|
(x00) =>
|
||||||
|
!x00.isDirector ||
|
||||||
|
(x00.isDirector &&
|
||||||
|
minPosMasterNoByOrgRootId.get(x00.orgRootId ?? "") !== x00.posMasterNo),
|
||||||
|
) || []
|
||||||
|
).forEach((x00) => {
|
||||||
|
children.push(
|
||||||
|
createNode(x00, 2, {
|
||||||
|
shortName: x00.orgRoot.orgRootShortName,
|
||||||
|
name: x00.orgRoot.orgRootName,
|
||||||
|
id: x00.orgRoot.id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (rootId !== "root") {
|
||||||
|
// Child1 directors
|
||||||
|
(child1ByOrgRootId.get(parent.orgRootId) || []).forEach((x1) => {
|
||||||
|
const childLevel2 = buildChildren(3, x1, {
|
||||||
|
shortName: x1.orgChild1.orgChild1ShortName,
|
||||||
|
name: x1.orgChild1.orgChild1Name,
|
||||||
|
id: x1.orgChild1.id,
|
||||||
|
});
|
||||||
|
const node = createNode(x1, 2, {
|
||||||
|
shortName: x1.orgChild1.orgChild1ShortName,
|
||||||
|
name: x1.orgChild1.orgChild1Name,
|
||||||
|
id: x1.orgChild1.id,
|
||||||
|
});
|
||||||
|
node.children = childLevel2;
|
||||||
|
children.push(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
} else if (level === 3) {
|
||||||
|
// Level 3: child1 non-directors and child2 directors
|
||||||
|
const children: any[] = [];
|
||||||
|
(child1ByOrgChild1Id.get(parent.orgChild1Id) || []).forEach((x11) => {
|
||||||
|
children.push(
|
||||||
|
createNode(x11, 3, {
|
||||||
|
shortName: x11.orgChild1.orgChild1ShortName,
|
||||||
|
name: x11.orgChild1.orgChild1Name,
|
||||||
|
id: x11.orgChild1.id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
(child2ByOrgChild1Id.get(parent.orgChild1Id) || []).forEach((x2) => {
|
||||||
|
const childLevel3 = buildChildren(4, x2, {
|
||||||
|
shortName: x2.orgChild2.orgChild2ShortName,
|
||||||
|
name: x2.orgChild2.orgChild2Name,
|
||||||
|
id: x2.orgChild2.id,
|
||||||
|
});
|
||||||
|
const node = createNode(x2, 3, {
|
||||||
|
shortName: x2.orgChild2.orgChild2ShortName,
|
||||||
|
name: x2.orgChild2.orgChild2Name,
|
||||||
|
id: x2.orgChild2.id,
|
||||||
|
});
|
||||||
|
node.children = childLevel3;
|
||||||
|
children.push(node);
|
||||||
|
});
|
||||||
|
return children;
|
||||||
|
} else if (level === 4) {
|
||||||
|
// Level 4: child2 non-directors and child3 directors
|
||||||
|
const children: any[] = [];
|
||||||
|
(child2ByOrgChild2Id.get(parent.orgChild2Id) || []).forEach((x22) => {
|
||||||
|
children.push(
|
||||||
|
createNode(x22, 4, {
|
||||||
|
shortName: x22.orgChild2.orgChild2ShortName,
|
||||||
|
name: x22.orgChild2.orgChild2Name,
|
||||||
|
id: x22.orgChild2.id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
(child3ByOrgChild2Id.get(parent.orgChild2Id) || []).forEach((x3) => {
|
||||||
|
const childLevel4 = buildChildren(5, x3, {
|
||||||
|
shortName: x3.orgChild3.orgChild3ShortName,
|
||||||
|
name: x3.orgChild3.orgChild3Name,
|
||||||
|
id: x3.orgChild3.id,
|
||||||
|
});
|
||||||
|
const node = createNode(x3, 4, {
|
||||||
|
shortName: x3.orgChild3.orgChild3ShortName,
|
||||||
|
name: x3.orgChild3.orgChild3Name,
|
||||||
|
id: x3.orgChild3.id,
|
||||||
|
});
|
||||||
|
node.children = childLevel4;
|
||||||
|
children.push(node);
|
||||||
|
});
|
||||||
|
return children;
|
||||||
|
} else if (level === 5) {
|
||||||
|
// Level 5: child3 non-directors and child4 directors
|
||||||
|
const children: any[] = [];
|
||||||
|
(child3ByOrgChild3Id.get(parent.orgChild3Id) || []).forEach((x33) => {
|
||||||
|
children.push(
|
||||||
|
createNode(x33, 5, {
|
||||||
|
shortName: x33.orgChild3.orgChild3ShortName,
|
||||||
|
name: x33.orgChild3.orgChild3Name,
|
||||||
|
id: x33.orgChild3.id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
(child4ByOrgChild3Id.get(parent.orgChild3Id) || []).forEach((x4) => {
|
||||||
|
// Level 5: child4 directors and their non-directors
|
||||||
|
const childLevel5: any[] = [];
|
||||||
|
posMasterChild4
|
||||||
|
.filter((x) => !x.isDirector && (x as any)[fieldId] && x.orgChild3Id === x4.orgChild3Id)
|
||||||
|
.forEach((x44) => {
|
||||||
|
childLevel5.push(
|
||||||
|
createNode(x44, 5, {
|
||||||
|
shortName: x44.orgChild4.orgChild4ShortName,
|
||||||
|
name: x44.orgChild4.orgChild4Name,
|
||||||
|
id: x44.orgChild4.id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const node = createNode(x4, 4, {
|
||||||
|
shortName: x4.orgChild4.orgChild4ShortName,
|
||||||
|
name: x4.orgChild4.orgChild4Name,
|
||||||
|
id: x4.orgChild4.id,
|
||||||
|
});
|
||||||
|
node.children = childLevel5;
|
||||||
|
children.push(node);
|
||||||
|
});
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build root nodes
|
||||||
|
const formattedData = posMasterRoot
|
||||||
|
.filter(
|
||||||
|
(x: any) =>
|
||||||
|
x.isDirector && minPosMasterNoByOrgRootId.get(x.orgRootId ?? "") === x.posMasterNo,
|
||||||
|
)
|
||||||
|
.map((x0) => {
|
||||||
|
const rootNode = createNode(x0, 1, {
|
||||||
|
shortName: x0.orgRoot.orgRootShortName,
|
||||||
|
name: x0.orgRoot.orgRootName,
|
||||||
|
id: x0.orgRoot.id,
|
||||||
|
});
|
||||||
|
rootNode.children = buildChildren(2, x0, {
|
||||||
|
shortName: x0.orgRoot.orgRootShortName,
|
||||||
|
name: x0.orgRoot.orgRootName,
|
||||||
|
id: x0.orgRoot.id,
|
||||||
|
});
|
||||||
|
return rootNode;
|
||||||
|
});
|
||||||
|
|
||||||
|
const formattedData_ =
|
||||||
|
rootId === "root"
|
||||||
|
? {
|
||||||
|
personID: "",
|
||||||
|
name: "",
|
||||||
|
avatar: "",
|
||||||
|
positionName: "",
|
||||||
|
positionNum: "",
|
||||||
|
positionNumInt: null,
|
||||||
|
departmentName: data.orgRevisionName,
|
||||||
|
organizationId: data.id,
|
||||||
|
children: formattedData,
|
||||||
|
}
|
||||||
|
: formattedData;
|
||||||
|
return new HttpSuccess([formattedData_]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API Organizational StructChart
|
* API Organizational StructChart
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue