feat(queens): verdrängte Königin bekommt ihren Endstatus automatisch
Entscheidung Inhaber: Zieht eine neue Königin ein, wird die bisherige „Ersetzt“; Vereinigen und Auflösen eines Volks setzen dessen Königin auf „Ersetzt“, ein Winterverlust auf „Eingegangen“. Nur aus einem verfügbaren Status (ACTIVE, RESERVE, MATED, UNMATED), über updateStatusIn in derselben Transaktion mit Archivdatum und Audit (QueensService.retireIn). Ein von Hand gesetzter Endstatus eines Volks fragt weiter über den Aufräum- Vorschlag nach. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
f9e03a1695
commit
81927d629a
10 files changed
+205
-4
No files matched your search
@@ -143,6 +143,11 @@ describe('QueensService', () => {
|
||||
];
|
||||
expect(historyArg.data.queenId).toBe('queen-1');
|
||||
expect(historyArg.data.colonyId).toBe('colony-1');
|
||||
// The displaced queen leaves for the archive as REPLACED.
|
||||
expect(tx.queen.update).toHaveBeenCalledWith({
|
||||
where: { id: 'queen-old' },
|
||||
data: { status: 'REPLACED', deactivatedAt: expect.any(Date) as Date },
|
||||
});
|
||||
});
|
||||
|
||||
it('assignColony records QueenAssigned with previousColonyId and calls neither webhooks.dispatch nor calendarSync', async () => {
|
||||
@@ -434,6 +439,29 @@ describe('QueensService', () => {
|
||||
expect(lineage.descendants[0].daughters).toEqual([]);
|
||||
});
|
||||
|
||||
describe('retireIn', () => {
|
||||
it('sets the status of a queen still recorded as available, stamps deactivatedAt and audits', async () => {
|
||||
tx.queen.findUnique.mockResolvedValue({ ...queen, deactivatedAt: null });
|
||||
|
||||
await service.retireIn(tx as never, 'queen-1', 'REPLACED', 'user-1');
|
||||
|
||||
expect(tx.queen.update).toHaveBeenCalledWith({
|
||||
where: { id: 'queen-1' },
|
||||
data: { status: 'REPLACED', deactivatedAt: expect.any(Date) as Date },
|
||||
});
|
||||
expect(auditLog.record).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('never overwrites a status that already takes her out of use', async () => {
|
||||
tx.queen.findUnique.mockResolvedValue({ ...queen, status: 'SOLD' });
|
||||
|
||||
await service.retireIn(tx as never, 'queen-1', 'REPLACED', 'user-1');
|
||||
|
||||
expect(tx.queen.update).not.toHaveBeenCalled();
|
||||
expect(auditLog.record).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateStatusIn', () => {
|
||||
it('locks the queen, reads it afterwards, sets the status and audits in the same transaction', async () => {
|
||||
tx.queen.findUnique.mockResolvedValue(queen);
|
||||
|
||||
@@ -232,6 +232,28 @@ export class QueensService {
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retires a queen who just left her colony with it (displaced by a new
|
||||
* queen, colony merged, dissolved or died): sets `status` through
|
||||
* updateStatusIn while she is still recorded as available, so she moves to
|
||||
* the archive. A status that already takes her out of use stays. The
|
||||
* caller has released her from the colony first.
|
||||
*/
|
||||
async retireIn(
|
||||
tx: Prisma.TransactionClient,
|
||||
id: string,
|
||||
status: QueenStatus,
|
||||
actingUserId: string,
|
||||
): Promise<void> {
|
||||
assertTransactionClient(tx, 'QueensService.retireIn');
|
||||
await lockRowForUpdate(tx, 'Queen', id);
|
||||
const queen = await tx.queen.findUnique({ where: { id } });
|
||||
if (!queen || QUEEN_NO_LONGER_AVAILABLE_STATUSES.includes(queen.status)) {
|
||||
return;
|
||||
}
|
||||
await this.updateStatusIn(tx, id, status, actingUserId);
|
||||
}
|
||||
|
||||
async assignColony(
|
||||
id: string,
|
||||
dto: AssignColonyDto,
|
||||
@@ -321,6 +343,14 @@ export class QueensService {
|
||||
status: colony.status === 'QUEENLESS' ? 'ACTIVE' : undefined,
|
||||
},
|
||||
});
|
||||
if (colony.currentQueenId) {
|
||||
await this.retireIn(
|
||||
tx,
|
||||
colony.currentQueenId,
|
||||
'REPLACED',
|
||||
actingUserId,
|
||||
);
|
||||
}
|
||||
const history = await tx.queenColonyHistory.create({
|
||||
data: {
|
||||
queenId: queen.id,
|
||||
|
||||
Reference in new issue
Block a user