fix: save posMasterHistory null
All checks were successful
Build & Deploy on Dev / build (push) Successful in 1m19s
All checks were successful
Build & Deploy on Dev / build (push) Successful in 1m19s
This commit is contained in:
parent
ed70999eac
commit
82ecf2cb81
1 changed files with 302 additions and 283 deletions
|
|
@ -7952,312 +7952,331 @@ 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 = {
|
||||
orgRoot: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
orgChild1: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
orgChild2: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
orgChild3: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
orgChild4: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
};
|
||||
|
||||
// Track sync statistics for organization nodes
|
||||
const orgSyncStats: Record<string, { deleted: number; updated: number; inserted: number }> =
|
||||
{};
|
||||
if (orgRootCurrent) {
|
||||
const allMappings: AllOrgMappings = {
|
||||
orgRoot: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
orgChild1: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
orgChild2: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
orgChild3: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
orgChild4: { byAncestorDNA: new Map(), byDraftId: new Map() },
|
||||
};
|
||||
|
||||
// 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,
|
||||
allMappings,
|
||||
orgRootDraft?.id,
|
||||
orgRootCurrent?.id,
|
||||
);
|
||||
allMappings.orgRoot = orgRootResult.mapping;
|
||||
orgSyncStats.orgRoot = orgRootResult.counts;
|
||||
// Track sync statistics for organization nodes
|
||||
const orgSyncStats: Record<string, { deleted: number; updated: number; inserted: number }> =
|
||||
{};
|
||||
|
||||
// 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;
|
||||
// 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,
|
||||
allMappings,
|
||||
orgRootDraft?.id,
|
||||
orgRootCurrent?.id,
|
||||
);
|
||||
allMappings.orgRoot = orgRootResult.mapping;
|
||||
orgSyncStats.orgRoot = orgRootResult.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;
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
|
||||
// Part 2: Sync position data using new org IDs from Part 1
|
||||
// 2.1 Clear current_holderId for affected positions (keep existing logic)
|
||||
// Get draft organization IDs under the given rootDnaId to find positions to clear
|
||||
const draftOrgIds = {
|
||||
orgRoot: [...allMappings.orgRoot.byDraftId.keys()],
|
||||
orgChild1: [...allMappings.orgChild1.byDraftId.keys()],
|
||||
orgChild2: [...allMappings.orgChild2.byDraftId.keys()],
|
||||
orgChild3: [...allMappings.orgChild3.byDraftId.keys()],
|
||||
orgChild4: [...allMappings.orgChild4.byDraftId.keys()],
|
||||
};
|
||||
// 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;
|
||||
|
||||
// Get draft positions that belong to any org under the rootDnaId
|
||||
const posMasterDraft = await this.posMasterRepository.find({
|
||||
where: [
|
||||
{ orgRevisionId: drafRevisionId, orgRootId: In(draftOrgIds.orgRoot) },
|
||||
{ orgRevisionId: drafRevisionId, orgChild1Id: In(draftOrgIds.orgChild1) },
|
||||
{ orgRevisionId: drafRevisionId, orgChild2Id: In(draftOrgIds.orgChild2) },
|
||||
{ orgRevisionId: drafRevisionId, orgChild3Id: In(draftOrgIds.orgChild3) },
|
||||
{ orgRevisionId: drafRevisionId, orgChild4Id: In(draftOrgIds.orgChild4) },
|
||||
],
|
||||
});
|
||||
// Part 2: Sync position data using new org IDs from Part 1
|
||||
// 2.1 Clear current_holderId for affected positions (keep existing logic)
|
||||
// Get draft organization IDs under the given rootDnaId to find positions to clear
|
||||
const draftOrgIds = {
|
||||
orgRoot: [...allMappings.orgRoot.byDraftId.keys()],
|
||||
orgChild1: [...allMappings.orgChild1.byDraftId.keys()],
|
||||
orgChild2: [...allMappings.orgChild2.byDraftId.keys()],
|
||||
orgChild3: [...allMappings.orgChild3.byDraftId.keys()],
|
||||
orgChild4: [...allMappings.orgChild4.byDraftId.keys()],
|
||||
};
|
||||
|
||||
if (posMasterDraft.length <= 0)
|
||||
return new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งในโครงสร้างร่าง");
|
||||
|
||||
// Clear current_holderId for positions that will have new holders
|
||||
const nextHolderIds = posMasterDraft
|
||||
.filter((x) => x.next_holderId != null)
|
||||
.map((x) => x.next_holderId) as string[];
|
||||
|
||||
if (nextHolderIds.length > 0) {
|
||||
// FIX: Fetch positions first before updating (to avoid race condition)
|
||||
const posMastersToUpdate = await queryRunner.manager.find(PosMaster, {
|
||||
where: {
|
||||
orgRevisionId: currentRevisionId,
|
||||
current_holderId: In(nextHolderIds),
|
||||
},
|
||||
// Get draft positions that belong to any org under the rootDnaId
|
||||
const posMasterDraft = await this.posMasterRepository.find({
|
||||
where: [
|
||||
{ orgRevisionId: drafRevisionId, orgRootId: In(draftOrgIds.orgRoot) },
|
||||
{ orgRevisionId: drafRevisionId, orgChild1Id: In(draftOrgIds.orgChild1) },
|
||||
{ orgRevisionId: drafRevisionId, orgChild2Id: In(draftOrgIds.orgChild2) },
|
||||
{ orgRevisionId: drafRevisionId, orgChild3Id: In(draftOrgIds.orgChild3) },
|
||||
{ orgRevisionId: drafRevisionId, orgChild4Id: In(draftOrgIds.orgChild4) },
|
||||
],
|
||||
});
|
||||
|
||||
// Save history BEFORE clearing current_holderId
|
||||
const historyOps = posMastersToUpdate.map((pos) => ({
|
||||
posMasterDnaId: pos.ancestorDNA,
|
||||
profileId: null,
|
||||
pm: null,
|
||||
}));
|
||||
await BatchSavePosMasterHistoryOfficer(queryRunner, historyOps);
|
||||
if (posMasterDraft.length <= 0)
|
||||
return new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งในโครงสร้างร่าง");
|
||||
|
||||
// Now clear current_holderId
|
||||
await queryRunner.manager.update(
|
||||
PosMaster,
|
||||
{
|
||||
orgRevisionId: currentRevisionId,
|
||||
current_holderId: In(nextHolderIds),
|
||||
},
|
||||
{ current_holderId: null, isSit: false },
|
||||
);
|
||||
}
|
||||
// Clear current_holderId for positions that will have new holders
|
||||
const nextHolderIds = posMasterDraft
|
||||
.filter((x) => x.next_holderId != null)
|
||||
.map((x) => x.next_holderId) as string[];
|
||||
|
||||
// 2.2 Fetch current positions for comparison
|
||||
// Get current organization IDs from the mappings
|
||||
const currentOrgIds = {
|
||||
orgRoot: [...allMappings.orgRoot.byDraftId.values()],
|
||||
orgChild1: [...allMappings.orgChild1.byDraftId.values()],
|
||||
orgChild2: [...allMappings.orgChild2.byDraftId.values()],
|
||||
orgChild3: [...allMappings.orgChild3.byDraftId.values()],
|
||||
orgChild4: [...allMappings.orgChild4.byDraftId.values()],
|
||||
};
|
||||
|
||||
const posMasterCurrent = await this.posMasterRepository.find({
|
||||
where: [
|
||||
{ orgRevisionId: currentRevisionId, orgRootId: In(currentOrgIds.orgRoot) },
|
||||
{ orgRevisionId: currentRevisionId, orgChild1Id: In(currentOrgIds.orgChild1) },
|
||||
{ orgRevisionId: currentRevisionId, orgChild2Id: In(currentOrgIds.orgChild2) },
|
||||
{ orgRevisionId: currentRevisionId, orgChild3Id: In(currentOrgIds.orgChild3) },
|
||||
{ orgRevisionId: currentRevisionId, orgChild4Id: In(currentOrgIds.orgChild4) },
|
||||
],
|
||||
});
|
||||
|
||||
// Build lookup map
|
||||
const currentByDNA = new Map(posMasterCurrent.map((p) => [p.ancestorDNA, p]));
|
||||
|
||||
// 2.3 Batch DELETE: positions in current but not in draft
|
||||
const toDelete = posMasterCurrent.filter(
|
||||
(curr) => !posMasterDraft.some((d) => d.ancestorDNA === curr.ancestorDNA),
|
||||
);
|
||||
|
||||
if (toDelete.length > 0) {
|
||||
const toDeleteIds = toDelete.map((p) => p.id);
|
||||
|
||||
// Cascade delete positions first
|
||||
await queryRunner.manager.delete(Position, { posMasterId: In(toDeleteIds) });
|
||||
|
||||
// Then delete posMaster records
|
||||
await queryRunner.manager.delete(PosMaster, toDeleteIds);
|
||||
|
||||
const deleteHistoryOps = toDelete.map((pos) => ({
|
||||
posMasterDnaId: pos.ancestorDNA,
|
||||
profileId: null,
|
||||
pm: null,
|
||||
}));
|
||||
await BatchSavePosMasterHistoryOfficer(queryRunner, deleteHistoryOps);
|
||||
}
|
||||
|
||||
// 2.4 Process draft positions (UPDATE or INSERT)
|
||||
const toUpdate: PosMaster[] = [];
|
||||
const toInsert: any[] = [];
|
||||
|
||||
// Track draft PosMaster ID to current PosMaster ID mapping for position sync
|
||||
// Type: Map<draftPosMasterId, [currentPosMasterId, nextHolderId]>
|
||||
const posMasterMapping: Map<string, [string, string | null | undefined]> = new Map();
|
||||
|
||||
for (const draftPos of posMasterDraft) {
|
||||
const current = currentByDNA.get(draftPos.ancestorDNA);
|
||||
|
||||
// Map organization IDs using new IDs from Part 1
|
||||
const orgRootId = this.resolveOrgId(draftPos.orgRootId ?? null, allMappings.orgRoot);
|
||||
const orgChild1Id = this.resolveOrgId(draftPos.orgChild1Id ?? null, allMappings.orgChild1);
|
||||
const orgChild2Id = this.resolveOrgId(draftPos.orgChild2Id ?? null, allMappings.orgChild2);
|
||||
const orgChild3Id = this.resolveOrgId(draftPos.orgChild3Id ?? null, allMappings.orgChild3);
|
||||
const orgChild4Id = this.resolveOrgId(draftPos.orgChild4Id ?? null, allMappings.orgChild4);
|
||||
|
||||
if (current) {
|
||||
// UPDATE existing position
|
||||
Object.assign(current, {
|
||||
createdAt: draftPos.createdAt,
|
||||
createdUserId: draftPos.createdUserId,
|
||||
createdFullName: draftPos.createdFullName,
|
||||
lastUpdatedAt: new Date(),
|
||||
lastUpdateUserId: request.user.sub,
|
||||
lastUpdateFullName: request.user.name,
|
||||
posMasterNoPrefix: draftPos.posMasterNoPrefix,
|
||||
posMasterNoSuffix: draftPos.posMasterNoSuffix,
|
||||
posMasterNo: draftPos.posMasterNo,
|
||||
posMasterOrder: draftPos.posMasterOrder,
|
||||
orgRootId,
|
||||
orgChild1Id,
|
||||
orgChild2Id,
|
||||
orgChild3Id,
|
||||
orgChild4Id,
|
||||
current_holderId: draftPos.next_holderId,
|
||||
isSit: draftPos.isSit,
|
||||
reason: draftPos.reason,
|
||||
isDirector: draftPos.isDirector,
|
||||
isStaff: draftPos.isStaff,
|
||||
positionSign: draftPos.positionSign,
|
||||
statusReport: "DONE",
|
||||
isCondition: draftPos.isCondition,
|
||||
conditionReason: draftPos.conditionReason,
|
||||
});
|
||||
toUpdate.push(current);
|
||||
|
||||
if (draftPos.next_holderId === null) {
|
||||
await SavePosMasterHistoryOfficer(queryRunner, draftPos.ancestorDNA, null, null);
|
||||
}
|
||||
|
||||
// Track mapping for position sync
|
||||
posMasterMapping.set(draftPos.id, [current.id, draftPos.next_holderId]);
|
||||
} else {
|
||||
// INSERT new position
|
||||
const newPosMaster = queryRunner.manager.create(PosMaster, {
|
||||
...draftPos,
|
||||
id: undefined,
|
||||
orgRevisionId: currentRevisionId,
|
||||
orgRootId,
|
||||
orgChild1Id,
|
||||
orgChild2Id,
|
||||
orgChild3Id,
|
||||
orgChild4Id,
|
||||
current_holderId: draftPos.next_holderId,
|
||||
statusReport: "DONE",
|
||||
if (nextHolderIds.length > 0) {
|
||||
// FIX: Fetch positions first before updating (to avoid race condition)
|
||||
const posMastersToUpdate = await queryRunner.manager.find(PosMaster, {
|
||||
where: {
|
||||
orgRevisionId: currentRevisionId,
|
||||
current_holderId: In(nextHolderIds),
|
||||
},
|
||||
});
|
||||
|
||||
toInsert.push(newPosMaster);
|
||||
// Save history BEFORE clearing current_holderId
|
||||
const historyOps = posMastersToUpdate
|
||||
.filter((x) => x.orgRootId != orgRootCurrent?.id)
|
||||
.map((pos) => ({
|
||||
posMasterDnaId: pos.ancestorDNA,
|
||||
profileId: null,
|
||||
pm: null,
|
||||
}));
|
||||
await BatchSavePosMasterHistoryOfficer(queryRunner, historyOps);
|
||||
|
||||
// Now clear current_holderId
|
||||
await queryRunner.manager.update(
|
||||
PosMaster,
|
||||
{
|
||||
orgRevisionId: currentRevisionId,
|
||||
current_holderId: In(nextHolderIds),
|
||||
},
|
||||
{ current_holderId: null, isSit: false },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Batch save updates and inserts
|
||||
if (toUpdate.length > 0) {
|
||||
await queryRunner.manager.save(toUpdate);
|
||||
}
|
||||
if (toInsert.length > 0) {
|
||||
const saved = await queryRunner.manager.save(toInsert);
|
||||
// 2.2 Fetch current positions for comparison
|
||||
// Get current organization IDs from the mappings
|
||||
const currentOrgIds = {
|
||||
orgRoot: [...allMappings.orgRoot.byDraftId.values()],
|
||||
orgChild1: [...allMappings.orgChild1.byDraftId.values()],
|
||||
orgChild2: [...allMappings.orgChild2.byDraftId.values()],
|
||||
orgChild3: [...allMappings.orgChild3.byDraftId.values()],
|
||||
orgChild4: [...allMappings.orgChild4.byDraftId.values()],
|
||||
};
|
||||
|
||||
// Track mapping for newly inserted posMasters
|
||||
// saved is an array, map each to its draft ID
|
||||
if (Array.isArray(saved)) {
|
||||
for (let i = 0; i < saved.length; i++) {
|
||||
const draftPos = posMasterDraft.filter((d) => !currentByDNA.has(d.ancestorDNA))[i];
|
||||
if (draftPos && saved[i]) {
|
||||
posMasterMapping.set(draftPos.id, [saved[i].id, draftPos.next_holderId]);
|
||||
const posMasterCurrent = await this.posMasterRepository.find({
|
||||
where: [
|
||||
{ orgRevisionId: currentRevisionId, orgRootId: In(currentOrgIds.orgRoot) },
|
||||
{ orgRevisionId: currentRevisionId, orgChild1Id: In(currentOrgIds.orgChild1) },
|
||||
{ orgRevisionId: currentRevisionId, orgChild2Id: In(currentOrgIds.orgChild2) },
|
||||
{ orgRevisionId: currentRevisionId, orgChild3Id: In(currentOrgIds.orgChild3) },
|
||||
{ orgRevisionId: currentRevisionId, orgChild4Id: In(currentOrgIds.orgChild4) },
|
||||
],
|
||||
});
|
||||
|
||||
// Build lookup map
|
||||
const currentByDNA = new Map(posMasterCurrent.map((p) => [p.ancestorDNA, p]));
|
||||
|
||||
// 2.3 Batch DELETE: positions in current but not in draft
|
||||
const toDelete = posMasterCurrent.filter(
|
||||
(curr) => !posMasterDraft.some((d) => d.ancestorDNA === curr.ancestorDNA),
|
||||
);
|
||||
|
||||
if (toDelete.length > 0) {
|
||||
const toDeleteIds = toDelete.map((p) => p.id);
|
||||
|
||||
// Cascade delete positions first
|
||||
await queryRunner.manager.delete(Position, { posMasterId: In(toDeleteIds) });
|
||||
|
||||
// Then delete posMaster records
|
||||
await queryRunner.manager.delete(PosMaster, toDeleteIds);
|
||||
|
||||
const deleteHistoryOps = toDelete.map((pos) => ({
|
||||
posMasterDnaId: pos.ancestorDNA,
|
||||
profileId: null,
|
||||
pm: null,
|
||||
}));
|
||||
await BatchSavePosMasterHistoryOfficer(queryRunner, deleteHistoryOps);
|
||||
}
|
||||
|
||||
// 2.4 Process draft positions (UPDATE or INSERT)
|
||||
const toUpdate: PosMaster[] = [];
|
||||
const toInsert: any[] = [];
|
||||
|
||||
// Track draft PosMaster ID to current PosMaster ID mapping for position sync
|
||||
// Type: Map<draftPosMasterId, [currentPosMasterId, nextHolderId]>
|
||||
const posMasterMapping: Map<string, [string, string | null | undefined]> = new Map();
|
||||
|
||||
for (const draftPos of posMasterDraft) {
|
||||
const current = currentByDNA.get(draftPos.ancestorDNA);
|
||||
|
||||
// Map organization IDs using new IDs from Part 1
|
||||
const orgRootId = this.resolveOrgId(draftPos.orgRootId ?? null, allMappings.orgRoot);
|
||||
const orgChild1Id = this.resolveOrgId(
|
||||
draftPos.orgChild1Id ?? null,
|
||||
allMappings.orgChild1,
|
||||
);
|
||||
const orgChild2Id = this.resolveOrgId(
|
||||
draftPos.orgChild2Id ?? null,
|
||||
allMappings.orgChild2,
|
||||
);
|
||||
const orgChild3Id = this.resolveOrgId(
|
||||
draftPos.orgChild3Id ?? null,
|
||||
allMappings.orgChild3,
|
||||
);
|
||||
const orgChild4Id = this.resolveOrgId(
|
||||
draftPos.orgChild4Id ?? null,
|
||||
allMappings.orgChild4,
|
||||
);
|
||||
|
||||
if (current) {
|
||||
// UPDATE existing position
|
||||
Object.assign(current, {
|
||||
createdAt: draftPos.createdAt,
|
||||
createdUserId: draftPos.createdUserId,
|
||||
createdFullName: draftPos.createdFullName,
|
||||
lastUpdatedAt: new Date(),
|
||||
lastUpdateUserId: request.user.sub,
|
||||
lastUpdateFullName: request.user.name,
|
||||
posMasterNoPrefix: draftPos.posMasterNoPrefix,
|
||||
posMasterNoSuffix: draftPos.posMasterNoSuffix,
|
||||
posMasterNo: draftPos.posMasterNo,
|
||||
posMasterOrder: draftPos.posMasterOrder,
|
||||
orgRootId,
|
||||
orgChild1Id,
|
||||
orgChild2Id,
|
||||
orgChild3Id,
|
||||
orgChild4Id,
|
||||
current_holderId: draftPos.next_holderId,
|
||||
isSit: draftPos.isSit,
|
||||
reason: draftPos.reason,
|
||||
isDirector: draftPos.isDirector,
|
||||
isStaff: draftPos.isStaff,
|
||||
positionSign: draftPos.positionSign,
|
||||
statusReport: "DONE",
|
||||
isCondition: draftPos.isCondition,
|
||||
conditionReason: draftPos.conditionReason,
|
||||
});
|
||||
toUpdate.push(current);
|
||||
|
||||
if (draftPos.next_holderId === null) {
|
||||
await SavePosMasterHistoryOfficer(queryRunner, draftPos.ancestorDNA, null, null);
|
||||
}
|
||||
|
||||
// Track mapping for position sync
|
||||
posMasterMapping.set(draftPos.id, [current.id, draftPos.next_holderId]);
|
||||
} else {
|
||||
// INSERT new position
|
||||
const newPosMaster = queryRunner.manager.create(PosMaster, {
|
||||
...draftPos,
|
||||
id: undefined,
|
||||
orgRevisionId: currentRevisionId,
|
||||
orgRootId,
|
||||
orgChild1Id,
|
||||
orgChild2Id,
|
||||
orgChild3Id,
|
||||
orgChild4Id,
|
||||
current_holderId: draftPos.next_holderId,
|
||||
statusReport: "DONE",
|
||||
});
|
||||
|
||||
toInsert.push(newPosMaster);
|
||||
}
|
||||
}
|
||||
|
||||
// Batch save updates and inserts
|
||||
if (toUpdate.length > 0) {
|
||||
await queryRunner.manager.save(toUpdate);
|
||||
}
|
||||
if (toInsert.length > 0) {
|
||||
const saved = await queryRunner.manager.save(toInsert);
|
||||
|
||||
// Track mapping for newly inserted posMasters
|
||||
// saved is an array, map each to its draft ID
|
||||
if (Array.isArray(saved)) {
|
||||
for (let i = 0; i < saved.length; i++) {
|
||||
const draftPos = posMasterDraft.filter((d) => !currentByDNA.has(d.ancestorDNA))[i];
|
||||
if (draftPos && saved[i]) {
|
||||
posMasterMapping.set(draftPos.id, [saved[i].id, draftPos.next_holderId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2.5 Sync positions table for all affected posMasters (BATCH operation for performance)
|
||||
const positionSyncStats = await this.syncAllPositionsBatch(
|
||||
queryRunner,
|
||||
posMasterMapping,
|
||||
drafRevisionId,
|
||||
currentRevisionId,
|
||||
);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 2.5 Sync positions table for all affected posMasters (BATCH operation for performance)
|
||||
const positionSyncStats = await this.syncAllPositionsBatch(
|
||||
queryRunner,
|
||||
posMasterMapping,
|
||||
drafRevisionId,
|
||||
currentRevisionId,
|
||||
);
|
||||
|
||||
// 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);
|
||||
return new HttpSuccess({});
|
||||
} catch (error) {
|
||||
console.error("Error moving draft to current:", error);
|
||||
await queryRunner.rollbackTransaction();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue