Nacherfassung an alten Kettengliedern, Filter fuer die Spielerliste

Beim Durcharbeiten von Spieler A stellt sich heraus, dass er schon vor drei
Redesigns bei einem Gruppen-CC von Spieler B dabei war. Das ging bisher nicht:
"Einem inaktiven CC kann niemand beitreten" - richtig fuer den laufenden
Betrieb, falsch fuers Nacherfassen. Dort geht es nicht um jetzt, sondern um
eine Geschichte, die laengst gelaufen ist.

An einem stillgelegten CC wird deshalb nachgetragen statt beigetreten: mit
Zeitraum, und die Guthaben-Angabe ist freigestellt, weil die Buchung im Konto
stehen kann. Wer weiter unten in derselben Kette schon steht, kommt auch an
einem laufenden CC ohne neue Buchung dazu - sein Platz wurde einmal bezahlt,
und ein Redesign tauscht ihn aus. Fuer alle anderen bleibt die Angabe Pflicht.
Damit vier Kettenglieder nicht vier Klicks bedeuten, traegt ein Aufruf einen
Spieler ab einem Glied in die ganze weitere Kette ein; gebucht wird einmal.

Ein Test spielt den ganzen Fall durch: B fertig, A nachgetragen, C steigt in
der Mitte ein und wieder aus, D kommt erst am Ende dazu - und am Schluss sieht
jeder seine eigene Geschichte.

Leere Import-Vorschauen blieben in der Liste stehen, sobald alle Zeilen beim
Spieler gebucht waren. Bestaetigen liess sich so eine Huelle nicht ("nichts zu
buchen"), sie war nur im Weg. Jetzt gilt sie als erledigt - sie ist es ja.
Auch der Bestand wird beim naechsten Aufruf der Liste abgeraeumt.

Und die Spielerliste laesst sich eingrenzen: nur mit aktivem CC, nur mit
ausschliesslich stillgelegten, nur Beteiligte eines Gruppen-CCs, nur ohne
jeden CC, nur Problemfaelle. Sortiert wird nach Namen, nach der Zahl der
CC-Eintraege oder nach Anlage.
This commit is contained in:
2026-09-21 16:17:00 +02:00
parent 2527f6a240
commit fe89032c03
13 changed files with 747 additions and 87 deletions
+72 -2
View File
@@ -277,14 +277,14 @@ describe('Problemfälle', () => {
});
it('listet sie auf Wunsch für sich', async () => {
const offen = await players.searchPlayers('', 100, 0, true);
const offen = await players.searchPlayers('', 100, 0, 'problems');
assert.ok(offen.total >= 3);
assert.equal(
offen.items.every((p) => p.isProblem),
true,
);
const alle = await players.searchPlayers('', 100, 0, false);
const alle = await players.searchPlayers('', 100, 0, 'all');
assert.ok(alle.total > offen.total, 'ohne Filter sind auch die unauffälligen dabei');
});
});
@@ -415,3 +415,73 @@ describe('Ersteller ohne Spieler', () => {
);
});
});
describe('Spielerliste eingrenzen und sortieren', () => {
before(async () => {
// Ein Gruppen-CC, ein stillgelegter CC und ein Spieler ohne alles.
const wirt = await players.createPlayer(
{ displayName: 'Filter-Wirt', steamId: '76561198000040001' },
ctx,
);
const gast = await players.createPlayer(
{ displayName: 'Filter-Gast', steamId: '76561198000040002' },
ctx,
);
for (const id of [wirt.id, gast.id]) {
await db.prisma.creditLedger.create({
data: { playerId: id, kind: 'CC', amount: 1, source: 'CC_PACKAGE' },
});
}
const gruppe = await characters.createCharacter(wirt.id, { name: 'Filter-Runde' }, ctx);
await characters.addMember(gruppe.character.id, gast.id, { useKind: 'CC' }, ctx);
const einsam = await players.createPlayer(
{ displayName: 'Filter-Einsam', steamId: '76561198000040003' },
ctx,
);
await db.prisma.creditLedger.create({
data: { playerId: einsam.id, kind: 'CC', amount: 1, source: 'CC_PACKAGE' },
});
const stillgelegt = await characters.createCharacter(einsam.id, { name: 'Ruht' }, ctx);
await characters.deactivateCharacter(stillgelegt.character.id, 'PLAYER_INACTIVITY', ctx);
await players.createPlayer(
{ displayName: 'Filter-Leer', steamId: '76561198000040004' },
ctx,
);
});
const namen = async (filter: Parameters<typeof players.searchPlayers>[3]) =>
(await players.searchPlayers('Filter-', 100, 0, filter)).items.map((p) => p.displayName).sort();
it('findet die Beteiligten eines Gruppen-CCs', async () => {
assert.deepEqual(await namen('group'), ['Filter-Gast', 'Filter-Wirt']);
});
it('findet, wer einen aktiven CC hat', async () => {
assert.deepEqual(await namen('active'), ['Filter-Gast', 'Filter-Wirt']);
});
it('findet, wer nur noch stillgelegte CCs hat', async () => {
assert.deepEqual(
await namen('onlyInactive'),
['Filter-Einsam'],
'wer gar keinen CC hat, gehört nicht dazu',
);
});
it('findet, wer gar keinen CC hat', async () => {
assert.deepEqual(await namen('none'), ['Filter-Leer']);
});
it('sortiert nach der Zahl der CC-Einträge', async () => {
const liste = await players.searchPlayers('Filter-', 100, 0, 'all', 'characters');
const zahlen = liste.items.map((p) => p.characterCount);
assert.deepEqual(
[...zahlen].sort((a, b) => b - a),
zahlen,
'die längste Liste steht oben',
);
});
});