diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 02e35abf..83e5719c 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -7865,7 +7865,6 @@ export class OrganizationController extends Controller { ]); if (!orgRootDraft) return new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้างร่าง"); - // Part 1: Differential sync of organization structure (bottom-up) // Build mapping incrementally as we process each level const allMappings: AllOrgMappings = { @@ -7876,60 +7875,80 @@ export class OrganizationController extends Controller { orgChild4: { byAncestorDNA: new Map(), byDraftId: new Map() }, }; - // Process from bottom (Child4) to top (Root) to handle foreign key constraints - // Child4 (leaf nodes - no children depending on them) - allMappings.orgChild4 = await this.syncOrgLevel( - queryRunner, - OrgChild4, - this.child4Repository, - drafRevisionId, - currentRevisionId, - rootDnaId, - allMappings, - ); + // Track sync statistics for organization nodes + const orgSyncStats: Record = + {}; - // Child3 - allMappings.orgChild3 = await this.syncOrgLevel( - queryRunner, - OrgChild3, - this.child3Repository, - drafRevisionId, - currentRevisionId, - rootDnaId, - allMappings, - ); - - // Child2 - allMappings.orgChild2 = await this.syncOrgLevel( - queryRunner, - OrgChild2, - this.child2Repository, - drafRevisionId, - currentRevisionId, - rootDnaId, - allMappings, - ); - - // Child1 - allMappings.orgChild1 = await this.syncOrgLevel( - queryRunner, - OrgChild1, - this.child1Repository, - drafRevisionId, - currentRevisionId, - rootDnaId, - allMappings, - ); - - // OrgRoot (root level - no parent mapping needed) - allMappings.orgRoot = await this.syncOrgLevel( + // Process from top (Root) to bottom (Child4) to handle foreign key constraints + // OrgRoot (sync first - no parent dependencies) + const orgRootResult = await this.syncOrgLevel( queryRunner, OrgRoot, this.orgRootRepository, drafRevisionId, currentRevisionId, - rootDnaId, + allMappings, + orgRootDraft?.id, + orgRootCurrent?.id, ); + allMappings.orgRoot = orgRootResult.mapping; + orgSyncStats.orgRoot = orgRootResult.counts; + + // Child1 (parent OrgRoot already synced) + const child1Result = await this.syncOrgLevel( + queryRunner, + OrgChild1, + this.child1Repository, + drafRevisionId, + currentRevisionId, + allMappings, + orgRootDraft?.id, + orgRootCurrent?.id, + ); + allMappings.orgChild1 = child1Result.mapping; + orgSyncStats.orgChild1 = child1Result.counts; + + // Child2 (parents OrgRoot and Child1 already synced) + const child2Result = await this.syncOrgLevel( + queryRunner, + OrgChild2, + this.child2Repository, + drafRevisionId, + currentRevisionId, + allMappings, + orgRootDraft?.id, + orgRootCurrent?.id, + ); + allMappings.orgChild2 = child2Result.mapping; + orgSyncStats.orgChild2 = child2Result.counts; + + // Child3 (parents OrgRoot, Child1, Child2 already synced) + const child3Result = await this.syncOrgLevel( + queryRunner, + OrgChild3, + this.child3Repository, + drafRevisionId, + currentRevisionId, + allMappings, + orgRootDraft?.id, + orgRootCurrent?.id, + ); + allMappings.orgChild3 = child3Result.mapping; + orgSyncStats.orgChild3 = child3Result.counts; + + // Child4 (parents OrgRoot, Child1, Child2, Child3 already synced) + const child4Result = await this.syncOrgLevel( + queryRunner, + OrgChild4, + this.child4Repository, + drafRevisionId, + currentRevisionId, + allMappings, + orgRootDraft?.id, + orgRootCurrent?.id, + ); + allMappings.orgChild4 = child4Result.mapping; + orgSyncStats.orgChild4 = child4Result.counts; // Part 2: Sync position data using new org IDs from Part 1 // 2.1 Clear current_holderId for affected positions (keep existing logic) @@ -8097,17 +8116,44 @@ export class OrganizationController extends Controller { } // 2.5 Sync positions table for all affected posMasters + const positionSyncStats: { deleted: number; updated: number; inserted: number } = { + deleted: 0, + updated: 0, + inserted: 0, + }; for (const [draftPosMasterId, currentPosMasterId] of posMasterMapping) { - await this.syncPositionsForPosMaster( + const stats = await this.syncPositionsForPosMaster( queryRunner, draftPosMasterId, currentPosMasterId, drafRevisionId, currentRevisionId, ); + positionSyncStats.deleted += stats.deleted; + positionSyncStats.updated += stats.updated; + positionSyncStats.inserted += stats.inserted; } + // Build comprehensive summary + const summary = { + message: "ย้ายโครงสร้างสำเร็จ", + organization: { + orgRoot: orgSyncStats.orgRoot, + orgChild1: orgSyncStats.orgChild1, + orgChild2: orgSyncStats.orgChild2, + orgChild3: orgSyncStats.orgChild3, + orgChild4: orgSyncStats.orgChild4, + }, + positionMaster: { + deleted: toDelete.length, + updated: toUpdate.length, + inserted: toInsert.length, + }, + position: positionSyncStats, + }; + await queryRunner.commitTransaction(); + return new HttpSuccess(summary); } catch (error) { console.error("Error moving draft to current:", error); await queryRunner.rollbackTransaction(); @@ -8161,23 +8207,49 @@ export class OrganizationController extends Controller { repository: any, draftRevisionId: string, currentRevisionId: string, - rootDnaId: string, parentMappings?: AllOrgMappings, - ): Promise { + draftOrgRootId?: string, + currentOrgRootId?: string, + ): Promise<{ + mapping: OrgIdMapping; + counts: { deleted: number; updated: number; inserted: number }; + }> { // 1. Fetch draft and current nodes under the given rootDnaId + // Build WHERE condition for draft nodes + const draftWhere: any = { + orgRevisionId: draftRevisionId, + }; + if ( + draftOrgRootId && + (entityClass === OrgChild1 || + entityClass === OrgChild2 || + entityClass === OrgChild3 || + entityClass === OrgChild4) + ) { + draftWhere.orgRootId = draftOrgRootId; + } else if (entityClass === OrgRoot) { + draftWhere.id = draftOrgRootId; + } + + // Build WHERE condition for current nodes + const currentWhere: any = { + orgRevisionId: currentRevisionId, + }; + if ( + currentOrgRootId && + (entityClass === OrgChild1 || + entityClass === OrgChild2 || + entityClass === OrgChild3 || + entityClass === OrgChild4) + ) { + currentWhere.orgRootId = currentOrgRootId; + } else if (entityClass === OrgRoot) { + currentWhere.id = currentOrgRootId; + } + const [draftNodes, currentNodes] = await Promise.all([ - repository.find({ - where: { - orgRevisionId: draftRevisionId, - ancestorDNA: Like(`${rootDnaId}%`), - }, - }), - repository.find({ - where: { - orgRevisionId: currentRevisionId, - ancestorDNA: Like(`${rootDnaId}%`), - }, - }), + repository.find({ where: draftWhere }), + repository.find({ where: currentWhere }), ]); // 2. Build lookup maps for efficient matching by ancestorDNA @@ -8270,37 +8342,71 @@ export class OrganizationController extends Controller { }); // Map parent IDs based on entity level - if (entityClass === OrgChild1 && draft.orgRootId && parentMappings) { - newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); - } else if (entityClass === OrgChild2) { - if (draft.orgRootId && parentMappings) { + if (entityClass === OrgChild1 && draft.orgRootId) { + if (draft.orgRootId === draftOrgRootId) { + newNode.orgRootId = currentOrgRootId; + } else if (parentMappings) { newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); } + } else if (entityClass === OrgChild2) { + if (draft.orgRootId) { + if (draft.orgRootId === draftOrgRootId) { + newNode.orgRootId = currentOrgRootId; + } else if (parentMappings) { + newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); + } + } if (draft.orgChild1Id && parentMappings) { - newNode.orgChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); + const mappedChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); + if (mappedChild1Id) { + newNode.orgChild1Id = mappedChild1Id; + } } } else if (entityClass === OrgChild3) { - if (draft.orgRootId && parentMappings) { - newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); + if (draft.orgRootId) { + if (draft.orgRootId === draftOrgRootId) { + newNode.orgRootId = currentOrgRootId; + } else if (parentMappings) { + newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); + } } if (draft.orgChild1Id && parentMappings) { - newNode.orgChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); + const mappedChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); + if (mappedChild1Id) { + newNode.orgChild1Id = mappedChild1Id; + } } if (draft.orgChild2Id && parentMappings) { - newNode.orgChild2Id = parentMappings.orgChild2.byDraftId.get(draft.orgChild2Id); + const mappedChild2Id = parentMappings.orgChild2.byDraftId.get(draft.orgChild2Id); + if (mappedChild2Id) { + newNode.orgChild2Id = mappedChild2Id; + } } } else if (entityClass === OrgChild4) { - if (draft.orgRootId && parentMappings) { - newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); + if (draft.orgRootId) { + if (draft.orgRootId === draftOrgRootId) { + newNode.orgRootId = currentOrgRootId; + } else if (parentMappings) { + newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); + } } if (draft.orgChild1Id && parentMappings) { - newNode.orgChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); + const mappedChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); + if (mappedChild1Id) { + newNode.orgChild1Id = mappedChild1Id; + } } if (draft.orgChild2Id && parentMappings) { - newNode.orgChild2Id = parentMappings.orgChild2.byDraftId.get(draft.orgChild2Id); + const mappedChild2Id = parentMappings.orgChild2.byDraftId.get(draft.orgChild2Id); + if (mappedChild2Id) { + newNode.orgChild2Id = mappedChild2Id; + } } if (draft.orgChild3Id && parentMappings) { - newNode.orgChild3Id = parentMappings.orgChild3.byDraftId.get(draft.orgChild3Id); + const mappedChild3Id = parentMappings.orgChild3.byDraftId.get(draft.orgChild3Id); + if (mappedChild3Id) { + newNode.orgChild3Id = mappedChild3Id; + } } } @@ -8310,7 +8416,14 @@ export class OrganizationController extends Controller { mapping.byDraftId.set(draft.id, saved.id); } - return mapping; + return { + mapping, + counts: { + deleted: toDelete.length, + updated: toUpdate.length, + inserted: toInsert.length, + }, + }; } /** @@ -8323,7 +8436,7 @@ export class OrganizationController extends Controller { currentPosMasterId: string, draftRevisionId: string, currentRevisionId: string, - ): Promise { + ): Promise<{ deleted: number; updated: number; inserted: number }> { // Fetch draft and current positions for this posMaster const [draftPositions, currentPositions] = await Promise.all([ queryRunner.manager.find(Position, { @@ -8347,7 +8460,7 @@ export class OrganizationController extends Controller { currentPositions.map((p: any) => p.id), ); } - return; + return { deleted: currentPositions.length, updated: 0, inserted: 0 }; } // Build maps for tracking @@ -8365,6 +8478,8 @@ export class OrganizationController extends Controller { } // UPDATE and INSERT + let updatedCount = 0; + let insertedCount = 0; for (const draftPos of draftPositions) { const current: any = currentByOrderNo.get(draftPos.orderNo); @@ -8380,6 +8495,7 @@ export class OrganizationController extends Controller { positionArea: draftPos.positionArea, isSpecial: draftPos.isSpecial, }); + updatedCount++; } else { // INSERT new position const newPosition = queryRunner.manager.create(Position, { @@ -8388,7 +8504,10 @@ export class OrganizationController extends Controller { posMasterId: currentPosMasterId, }); await queryRunner.manager.save(newPosition); + insertedCount++; } } + + return { deleted: toDelete.length, updated: updatedCount, inserted: insertedCount }; } }