fix: tolerate same-day pickup profile check
Allow configured stores to ignore a visible same-day profile pickup when booking a future pickup slot, starting with store 33875. Keep the default profile check behavior unchanged for all other stores.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
fix: keep background jobs alive
|
fix: tolerate same-day pickup profile check
|
||||||
|
|
||||||
Add a background job orchestrator that restores profile jobs from saved credentials,
|
Allow configured stores to ignore a visible same-day profile pickup when booking
|
||||||
keeps pickup and store-watch schedules alive without UI logins, and exposes job
|
a future pickup slot, starting with store 33875. Keep the default profile check
|
||||||
status via health/admin endpoints.
|
behavior unchanged for all other stores.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const { ensureSession, withSessionRetry } = require('./sessionRefresh');
|
|||||||
const maintenanceMode = require('./maintenanceMode');
|
const maintenanceMode = require('./maintenanceMode');
|
||||||
const { computeReminderSchedule } = require('./journalReminderUtils');
|
const { computeReminderSchedule } = require('./journalReminderUtils');
|
||||||
const { startStoreMessageWatcher } = require('./storeMessageWatcher');
|
const { startStoreMessageWatcher } = require('./storeMessageWatcher');
|
||||||
|
const { getBlockingProfilePickup } = require('./profileCheckTolerance');
|
||||||
|
|
||||||
function wait(ms) {
|
function wait(ms) {
|
||||||
if (!ms || ms <= 0) {
|
if (!ms || ms <= 0) {
|
||||||
@@ -959,7 +960,7 @@ async function checkEntry(sessionId, entry, settings) {
|
|||||||
() => foodsharingClient.fetchPickups(entry.id, session.cookieHeader, session),
|
() => foodsharingClient.fetchPickups(entry.id, session.cookieHeader, session),
|
||||||
{ label: 'fetchPickups' }
|
{ label: 'fetchPickups' }
|
||||||
);
|
);
|
||||||
let hasProfileId = false;
|
const profilePickups = [];
|
||||||
let availablePickup = null;
|
let availablePickup = null;
|
||||||
|
|
||||||
const desiredWeekday = entry.desiredWeekday ? weekdayMap[entry.desiredWeekday] || entry.desiredWeekday : null;
|
const desiredWeekday = entry.desiredWeekday ? weekdayMap[entry.desiredWeekday] || entry.desiredWeekday : null;
|
||||||
@@ -970,7 +971,7 @@ async function checkEntry(sessionId, entry, settings) {
|
|||||||
entry.checkProfileId &&
|
entry.checkProfileId &&
|
||||||
pickup.occupiedSlots?.some((slot) => String(slot.profile?.id) === String(session.profile.id))
|
pickup.occupiedSlots?.some((slot) => String(slot.profile?.id) === String(session.profile.id))
|
||||||
) {
|
) {
|
||||||
hasProfileId = true;
|
profilePickups.push(pickup);
|
||||||
}
|
}
|
||||||
if (!matchesDesiredDate(pickupDate, entry.desiredDate, entry.desiredDateRange)) {
|
if (!matchesDesiredDate(pickupDate, entry.desiredDate, entry.desiredDateRange)) {
|
||||||
return;
|
return;
|
||||||
@@ -983,20 +984,35 @@ async function checkEntry(sessionId, entry, settings) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (entry.checkProfileId && hasProfileId) {
|
if (!availablePickup) {
|
||||||
|
console.log(
|
||||||
|
`[INFO] Kein freier Slot für ${entry.label || entry.id} in dieser Runde gefunden. Profil bereits eingetragen: ${
|
||||||
|
profilePickups.length > 0 ? 'ja' : 'nein'
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockingProfilePickup = entry.checkProfileId
|
||||||
|
? getBlockingProfilePickup({
|
||||||
|
storeId: entry.id,
|
||||||
|
profilePickups,
|
||||||
|
targetPickup: availablePickup
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (entry.checkProfileId && blockingProfilePickup) {
|
||||||
console.log(
|
console.log(
|
||||||
`[INFO] Profil bereits in einem Slot für ${entry.label || entry.id} eingetragen – überspringe Buchung.`
|
`[INFO] Profil bereits in einem Slot für ${entry.label || entry.id} eingetragen – überspringe Buchung.`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!availablePickup) {
|
if (entry.checkProfileId && profilePickups.length > 0 && !blockingProfilePickup) {
|
||||||
console.log(
|
console.log(
|
||||||
`[INFO] Kein freier Slot für ${entry.label || entry.id} in dieser Runde gefunden. Profil bereits eingetragen: ${
|
`[INFO] Heutige Profileintragung für ${entry.label || entry.id} wird toleriert; ` +
|
||||||
hasProfileId ? 'ja' : 'nein'
|
`Zieltermin: ${new Date(availablePickup.date).toLocaleString('de-DE')}.`
|
||||||
}`
|
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldIgnoreSlot(entry, availablePickup, settings)) {
|
if (shouldIgnoreSlot(entry, availablePickup, settings)) {
|
||||||
@@ -1004,7 +1020,7 @@ async function checkEntry(sessionId, entry, settings) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entry.checkProfileId || !hasProfileId) {
|
if (!entry.checkProfileId || !blockingProfilePickup) {
|
||||||
await processBooking(session, entry, availablePickup);
|
await processBooking(session, entry, availablePickup);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
39
services/profileCheckTolerance.js
Normal file
39
services/profileCheckTolerance.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
const TIME_ZONE = 'Europe/Berlin';
|
||||||
|
const TODAY_PROFILE_CHECK_TOLERANCE_STORE_IDS = new Set(['33875']);
|
||||||
|
|
||||||
|
function getDateKeyInTimeZone(date, timeZone = TIME_ZONE) {
|
||||||
|
const parsed = date instanceof Date ? date : new Date(date);
|
||||||
|
if (Number.isNaN(parsed.getTime())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const formatter = new Intl.DateTimeFormat('en-CA', {
|
||||||
|
timeZone,
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit'
|
||||||
|
});
|
||||||
|
return formatter.format(parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBlockingProfilePickup({ storeId, profilePickups = [], targetPickup, now = new Date() }) {
|
||||||
|
const pickUpsWithDate = profilePickups.filter((pickup) => pickup?.date);
|
||||||
|
if (pickUpsWithDate.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!TODAY_PROFILE_CHECK_TOLERANCE_STORE_IDS.has(String(storeId))) {
|
||||||
|
return pickUpsWithDate[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const todayKey = getDateKeyInTimeZone(now);
|
||||||
|
const targetKey = getDateKeyInTimeZone(targetPickup?.date);
|
||||||
|
if (!todayKey || !targetKey || targetKey === todayKey) {
|
||||||
|
return pickUpsWithDate[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return pickUpsWithDate.find((pickup) => getDateKeyInTimeZone(pickup.date) !== todayKey) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
TODAY_PROFILE_CHECK_TOLERANCE_STORE_IDS,
|
||||||
|
getBlockingProfilePickup
|
||||||
|
};
|
||||||
67
src/utils/pickupProfileTolerance.test.js
Normal file
67
src/utils/pickupProfileTolerance.test.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
const { getBlockingProfilePickup } = require('../../services/profileCheckTolerance');
|
||||||
|
|
||||||
|
const NOW = new Date('2026-07-16T10:00:00+02:00');
|
||||||
|
|
||||||
|
function pickup(date) {
|
||||||
|
return { date };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('pickup profile check tolerance', () => {
|
||||||
|
test('blocks today profile pickup for regular stores', () => {
|
||||||
|
const blocking = getBlockingProfilePickup({
|
||||||
|
storeId: '44975',
|
||||||
|
profilePickups: [pickup('2026-07-16T17:00:00+02:00')],
|
||||||
|
targetPickup: pickup('2026-07-17T17:00:00+02:00'),
|
||||||
|
now: NOW
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(blocking).toEqual(pickup('2026-07-16T17:00:00+02:00'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('allows future target for tolerated store when profile pickup is today', () => {
|
||||||
|
const blocking = getBlockingProfilePickup({
|
||||||
|
storeId: '33875',
|
||||||
|
profilePickups: [pickup('2026-07-16T17:00:00+02:00')],
|
||||||
|
targetPickup: pickup('2026-07-17T17:00:00+02:00'),
|
||||||
|
now: NOW
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(blocking).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('blocks same-day target for tolerated store', () => {
|
||||||
|
const blocking = getBlockingProfilePickup({
|
||||||
|
storeId: '33875',
|
||||||
|
profilePickups: [pickup('2026-07-16T12:00:00+02:00')],
|
||||||
|
targetPickup: pickup('2026-07-16T18:00:00+02:00'),
|
||||||
|
now: NOW
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(blocking).toEqual(pickup('2026-07-16T12:00:00+02:00'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('blocks non-today profile pickups for tolerated store', () => {
|
||||||
|
const blocking = getBlockingProfilePickup({
|
||||||
|
storeId: '33875',
|
||||||
|
profilePickups: [pickup('2026-07-17T17:00:00+02:00')],
|
||||||
|
targetPickup: pickup('2026-07-18T17:00:00+02:00'),
|
||||||
|
now: NOW
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(blocking).toEqual(pickup('2026-07-17T17:00:00+02:00'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('blocks when any profile pickup is outside the tolerated day', () => {
|
||||||
|
const blocking = getBlockingProfilePickup({
|
||||||
|
storeId: '33875',
|
||||||
|
profilePickups: [
|
||||||
|
pickup('2026-07-16T17:00:00+02:00'),
|
||||||
|
pickup('2026-07-18T17:00:00+02:00')
|
||||||
|
],
|
||||||
|
targetPickup: pickup('2026-07-19T17:00:00+02:00'),
|
||||||
|
now: NOW
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(blocking).toEqual(pickup('2026-07-18T17:00:00+02:00'));
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user