Push-Arbeitsblätter: deutsche Zeit, keine System-Pushes, keine Doppelten
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
531ee2a02b
commit
8d5de87ead
2 files changed
+82
-17
No files matched your search
@@ -1,4 +1,6 @@
|
|||||||
import { config } from '../config.js'
|
import { config } from '../config.js'
|
||||||
|
import { berlinDateTime } from './berlinTime.js'
|
||||||
|
import { withLocks } from './keyedLock.js'
|
||||||
import {
|
import {
|
||||||
listDocuments,
|
listDocuments,
|
||||||
getDocument,
|
getDocument,
|
||||||
@@ -11,17 +13,10 @@ const WSID_START = 100000
|
|||||||
// worksheets.details ist auf 4000 Zeichen begrenzt
|
// worksheets.details ist auf 4000 Zeichen begrenzt
|
||||||
const DETAILS_MAX_LENGTH = 3900
|
const DETAILS_MAX_LENGTH = 3900
|
||||||
|
|
||||||
function formatToday() {
|
/** Pusht das System-Konto selbst (automatische Commits) - dafuer kein Arbeitsblatt. */
|
||||||
// dd.mm.yyyy - gleiches Format wie CreateWorksheetModal der Ticketsystem-SPA
|
export function isSystemPusher(pusher) {
|
||||||
const d = new Date()
|
const name = pusher?.username || pusher?.login || ''
|
||||||
const day = String(d.getDate()).padStart(2, '0')
|
return name.toLowerCase() === config.gitea.systemUser.toLowerCase()
|
||||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
|
||||||
return `${day}.${m}.${d.getFullYear()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatTimeNow() {
|
|
||||||
const d = new Date()
|
|
||||||
return `${String(d.getHours()).padStart(2, '0')}${String(d.getMinutes()).padStart(2, '0')}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateNextWsid() {
|
async function generateNextWsid() {
|
||||||
@@ -72,9 +67,14 @@ export async function createGitPushWorksheet({
|
|||||||
commits = [],
|
commits = [],
|
||||||
pusher,
|
pusher,
|
||||||
commitSha,
|
commitSha,
|
||||||
|
pushedAt = new Date(),
|
||||||
}) {
|
}) {
|
||||||
if (!ticketId) return null
|
if (!ticketId) return null
|
||||||
|
|
||||||
|
if (isSystemPusher(pusher)) {
|
||||||
|
return { skipped: true, reason: 'system_pusher' }
|
||||||
|
}
|
||||||
|
|
||||||
let workorder
|
let workorder
|
||||||
try {
|
try {
|
||||||
workorder = await getDocument(config.collections.workorders, ticketId)
|
workorder = await getDocument(config.collections.workorders, ticketId)
|
||||||
@@ -86,6 +86,11 @@ export async function createGitPushWorksheet({
|
|||||||
const woid = workorder.woid
|
const woid = workorder.woid
|
||||||
if (!woid) return null
|
if (!woid) return null
|
||||||
|
|
||||||
|
// Gesperrt je Ticket+Commit, damit zwei gleichzeitige Zustellungen desselben Webhooks
|
||||||
|
// (Gitea liefert bei Bedarf erneut zu) nicht zweimal denselben Duplikat-Check bestehen
|
||||||
|
// und zwei Arbeitsblaetter anlegen.
|
||||||
|
const lockKey = `ws:${woid}:${(commitSha || '').slice(0, 7)}`
|
||||||
|
return withLocks([lockKey], async () => {
|
||||||
if (await gitWorksheetExists(woid, commitSha)) {
|
if (await gitWorksheetExists(woid, commitSha)) {
|
||||||
return { skipped: true, reason: 'duplicate_commit' }
|
return { skipped: true, reason: 'duplicate_commit' }
|
||||||
}
|
}
|
||||||
@@ -103,9 +108,9 @@ export async function createGitPushWorksheet({
|
|||||||
details = `${details.slice(0, DETAILS_MAX_LENGTH)}\n… (gekuerzt)`
|
details = `${details.slice(0, DETAILS_MAX_LENGTH)}\n… (gekuerzt)`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Endzeit = Push-Zeitpunkt. Startzeit bleibt leer und wird im Ticketsystem
|
// Endzeit = Push-Zeitpunkt in deutscher Zeit (MESZ/MEZ). Startzeit bleibt leer und wird
|
||||||
// nachgetragen ("Zeit nachtragen") - erst dann zaehlt die Arbeitszeit.
|
// im Ticketsystem nachgetragen ("Zeit nachtragen") - erst dann zaehlt die Arbeitszeit.
|
||||||
const today = formatToday()
|
const { date, time } = berlinDateTime(pushedAt)
|
||||||
const worksheet = await createDocument(
|
const worksheet = await createDocument(
|
||||||
config.collections.worksheets,
|
config.collections.worksheets,
|
||||||
{
|
{
|
||||||
@@ -121,10 +126,10 @@ export async function createGitPushWorksheet({
|
|||||||
oldResponseLevel: workorder.response || '',
|
oldResponseLevel: workorder.response || '',
|
||||||
newResponseLevel: workorder.response || '',
|
newResponseLevel: workorder.response || '',
|
||||||
totalTime: 0,
|
totalTime: 0,
|
||||||
startDate: today,
|
startDate: date,
|
||||||
startTime: '',
|
startTime: '',
|
||||||
endDate: today,
|
endDate: date,
|
||||||
endTime: formatTimeNow(),
|
endTime: time,
|
||||||
details,
|
details,
|
||||||
isComment: true,
|
isComment: true,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
@@ -133,4 +138,5 @@ export async function createGitPushWorksheet({
|
|||||||
)
|
)
|
||||||
|
|
||||||
return { created: true, wsid, worksheetId: worksheet.$id }
|
return { created: true, wsid, worksheetId: worksheet.$id }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { test, before, after } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { startFakeAppwrite } from './helpers/fakeAppwrite.js'
|
||||||
|
|
||||||
|
let fake
|
||||||
|
let createGitPushWorksheet
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fake = await startFakeAppwrite({
|
||||||
|
seed: {
|
||||||
|
workorders: [{ $id: 't1', woid: '100115', status: 'Assigned' }],
|
||||||
|
worksheets: [{ $id: 'w0', wsid: '100077', woid: '100001', details: '' }],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Object.assign(process.env, {
|
||||||
|
APPWRITE_ENDPOINT: fake.endpoint,
|
||||||
|
APPWRITE_API_KEY: 'test-key',
|
||||||
|
})
|
||||||
|
// config.js liest die Umgebung beim Import
|
||||||
|
;({ createGitPushWorksheet } = await import('../server/services/gitWorksheet.js'))
|
||||||
|
})
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await fake.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
const base = {
|
||||||
|
ticketId: 't1', repoFullName: 'WEBklar/D-ner-Nation', branch: 'main',
|
||||||
|
commits: [{ id: '26bc22ecf17e2e5a5a8794dcb4cf3892f802c51d', message: 'Döner Nation Website hinzufügen', author: { name: 'JUSN' } }],
|
||||||
|
pusher: { login: 'JUSN', username: 'JUSN', full_name: '' }, commitSha: '26bc22ecf17e2e5a5a8794dcb4cf3892f802c51d',
|
||||||
|
}
|
||||||
|
|
||||||
|
test('Push-Zeit in deutscher Zeit', async () => {
|
||||||
|
const r = await createGitPushWorksheet({ ...base, pushedAt: new Date('2026-09-27T14:46:34Z') })
|
||||||
|
assert.equal(r.created, true)
|
||||||
|
const ws = fake.get('worksheets', r.worksheetId)
|
||||||
|
assert.equal(ws.wsid, '100078')
|
||||||
|
assert.equal(ws.employeeName, 'JUSN')
|
||||||
|
assert.deepEqual([ws.startDate, ws.startTime, ws.endDate, ws.endTime], ['27.09.2026', '', '27.09.2026', '1646'])
|
||||||
|
assert.equal(ws.totalTime, 0); assert.equal(ws.isComment, true); assert.equal(ws.serviceType, 'GIT')
|
||||||
|
assert.match(ws.details, /Commit: 26bc22e/)
|
||||||
|
})
|
||||||
|
test('zweiter Aufruf: Duplikat', async () => {
|
||||||
|
assert.deepEqual(await createGitPushWorksheet({ ...base }), { skipped: true, reason: 'duplicate_commit' })
|
||||||
|
})
|
||||||
|
test('System-Konto erzeugt kein Arbeitsblatt', async () => {
|
||||||
|
const before = fake.all('worksheets').length
|
||||||
|
for (const name of ['webklar-system', 'WEBKLAR-SYSTEM']) {
|
||||||
|
const r = await createGitPushWorksheet({ ...base, commitSha: 'aaaaaaa1', pusher: { login: name, username: name, full_name: '' } })
|
||||||
|
assert.deepEqual(r, { skipped: true, reason: 'system_pusher' })
|
||||||
|
}
|
||||||
|
assert.equal(fake.all('worksheets').length, before)
|
||||||
|
})
|
||||||
|
test('gleichzeitige Zustellung ergibt ein Arbeitsblatt', async () => {
|
||||||
|
const args = { ...base, commitSha: 'bbbbbbb2' }
|
||||||
|
const results = await Promise.all([createGitPushWorksheet(args), createGitPushWorksheet(args)])
|
||||||
|
assert.equal(results.filter((r) => r?.created).length, 1)
|
||||||
|
assert.equal(fake.all('worksheets').filter((w) => w.details.includes('Commit: bbbbbbb')).length, 1)
|
||||||
|
})
|
||||||
Reference in new issue
Block a user