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",
|
||||
"orgChild4",
|
||||
],
|
||||
order: { posMasterOrder: "ASC" },
|
||||
order: {
|
||||
orgRoot: {
|
||||
orgRootOrder: "ASC",
|
||||
},
|
||||
posMasterOrder: "ASC",
|
||||
posMasterNo: "ASC",
|
||||
},
|
||||
});
|
||||
|
||||
// แยกข้อมูลด้วย 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
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue