mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 16:03:02 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 280e55bbc9 | |||
| e84060cdf7 | |||
| fbc85b05a1 |
@@ -17,5 +17,5 @@
|
||||
"src/nest/plugins/runtime/plugin-sdk.ts": 1093,
|
||||
"src/nest/reservations/reservations.service.ts": 1237,
|
||||
"src/nest/trips/trips.service.ts": 1006,
|
||||
"src/nest/vacay/vacay.service.ts": 1416
|
||||
"src/nest/vacay/vacay.service.ts": 1412
|
||||
}
|
||||
|
||||
@@ -69,3 +69,23 @@ export function shiftIsoDate(isoDate: string, days: number): string {
|
||||
d.setUTCDate(d.getUTCDate() + days);
|
||||
return d.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
export const CACHE_TTL = 24 * 60 * 60 * 1000;
|
||||
export const FETCH_TIMEOUT_MS = 10_000;
|
||||
// A country's holidays for one year are a few kilobytes; two megabytes means the
|
||||
// provider is misbehaving and we would rather report the usual error than buffer it.
|
||||
export const MAX_HOLIDAY_BYTES = 2 * 1024 * 1024;
|
||||
// Both providers key on a 4-digit year and an ISO 3166-1 alpha-2 code. Anything
|
||||
// else has no business reaching the URL path.
|
||||
export const YEAR_RE = /^\d{4}$/;
|
||||
export const COUNTRY_RE = /^[A-Za-z]{2}$/;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color palette for auto-assign
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const COLORS = [
|
||||
'#6366f1', '#ec4899', '#14b8a6', '#8b5cf6', '#ef4444',
|
||||
'#3b82f6', '#22c55e', '#06b6d4', '#f43f5e', '#a855f7',
|
||||
'#10b981', '#0ea5e9', '#64748b', '#be185d', '#0d9488',
|
||||
];
|
||||
|
||||
@@ -30,7 +30,7 @@ import { SchoolHolidayRegions } from '../../db/entities/SchoolHolidayRegions.ent
|
||||
import type { SchoolHolidayRegionsRepository } from '../../db/repositories/SchoolHolidayRegions.repository';
|
||||
import {
|
||||
daysAlwaysInMonth, isBlockedWeekend, normalizeFraction, normalizeKind, normalizeYearType, pad2, parseAsUtcMillis,
|
||||
shiftIsoDate, windowEndYear,
|
||||
shiftIsoDate, windowEndYear, CACHE_TTL, COLORS, COUNTRY_RE, FETCH_TIMEOUT_MS, MAX_HOLIDAY_BYTES, YEAR_RE,
|
||||
} from './vacay.helpers';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -125,26 +125,6 @@ export interface VacayShare {
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
const CACHE_TTL = 24 * 60 * 60 * 1000;
|
||||
const FETCH_TIMEOUT_MS = 10_000;
|
||||
// A country's holidays for one year are a few kilobytes; two megabytes means the
|
||||
// provider is misbehaving and we would rather report the usual error than buffer it.
|
||||
const MAX_HOLIDAY_BYTES = 2 * 1024 * 1024;
|
||||
// Both providers key on a 4-digit year and an ISO 3166-1 alpha-2 code. Anything
|
||||
// else has no business reaching the URL path.
|
||||
const YEAR_RE = /^\d{4}$/;
|
||||
const COUNTRY_RE = /^[A-Za-z]{2}$/;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color palette for auto-assign
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const COLORS = [
|
||||
'#6366f1', '#ec4899', '#14b8a6', '#8b5cf6', '#ef4444',
|
||||
'#3b82f6', '#22c55e', '#06b6d4', '#f43f5e', '#a855f7',
|
||||
'#10b981', '#0ea5e9', '#64748b', '#be185d', '#0d9488',
|
||||
];
|
||||
|
||||
/**
|
||||
* Vacay domain service — owns the vacay SQL, now through the ten vacay
|
||||
* repositories (+ `SchoolHolidayRegionsRepository`'s cross-domain read,
|
||||
@@ -1150,16 +1130,29 @@ export class VacayService {
|
||||
// Removing a stray entry on a blocked day stays possible; keeping one
|
||||
// there (converted in place) does not.
|
||||
if (weekendBlocked) return { error: 'weekend_blocked' };
|
||||
await this.entries.updateFractionKind(existing.id, frac, knd);
|
||||
await this.uow.transactional(async () => {
|
||||
await this.entries.updateFractionKind(existing.id, frac, knd);
|
||||
await this.ensureYearOf(planId, userId, date);
|
||||
});
|
||||
await this.notifyPlanUsers(planId, socketId);
|
||||
return { action: 'updated', fraction: frac, kind: knd };
|
||||
}
|
||||
if (weekendBlocked) return { error: 'weekend_blocked' };
|
||||
await this.entries.insertEntry(planId, userId, date, '', frac, knd);
|
||||
await this.uow.transactional(async () => {
|
||||
await this.entries.insertEntry(planId, userId, date, '', frac, knd);
|
||||
await this.ensureYearOf(planId, userId, date);
|
||||
});
|
||||
await this.notifyPlanUsers(planId, socketId);
|
||||
return { action: 'added', fraction: frac, kind: knd };
|
||||
}
|
||||
|
||||
/** A day written for a year the plan has not opened adds that year, or the calendar never shows it (#737 picks the period). */
|
||||
private async ensureYearOf(planId: number, userId: number, date: string): Promise<void> {
|
||||
const [y, m, d] = date.split('-').map(Number);
|
||||
const year = await this.currentPeriodYear(userId, new Date(y, m - 1, d));
|
||||
if (!(await this.years.exists(planId, year))) await this.addYear(planId, year, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* A company holiday on or off, whole or half (#2439). The same click again clears
|
||||
* it; the other size converts it in place, the way a vacation entry toggles. A
|
||||
@@ -1179,6 +1172,8 @@ export class VacayService {
|
||||
await this.companyHolidays.insertHoliday(planId, date, note || '', frac); // VC120
|
||||
}
|
||||
await this.makeRoomForCompanyHoliday(planId, date, frac);
|
||||
const owner = await this.plans.findOwnerId(planId);
|
||||
if (owner) await this.ensureYearOf(planId, owner.owner_id, date);
|
||||
return { action: existing ? 'updated' : 'added', fraction: frac };
|
||||
});
|
||||
await this.notifyPlanUsers(planId, socketId);
|
||||
|
||||
@@ -602,14 +602,22 @@ const vacayCases: ParityCase[] = [
|
||||
needs: ['vacay'],
|
||||
args: () => ({ date: '2027-06-01' }),
|
||||
rest: { method: 'post', path: () => `${VACAY}/entries/toggle`, body: () => ({ date: '2027-06-01' }) },
|
||||
expect: [{ event: 'vacay:update', user: 'actor' }],
|
||||
// 2027 is not a year of the plan yet, so the day opens it too.
|
||||
expect: [
|
||||
{ event: 'vacay:settings', user: 'actor' },
|
||||
{ event: 'vacay:update', user: 'actor' },
|
||||
],
|
||||
},
|
||||
{
|
||||
tool: 'toggle_company_holiday',
|
||||
needs: ['vacay'],
|
||||
args: () => ({ date: '2027-06-02' }),
|
||||
rest: { method: 'post', path: () => `${VACAY}/entries/company-holiday`, body: () => ({ date: '2027-06-02' }) },
|
||||
expect: [{ event: 'vacay:update', user: 'actor' }],
|
||||
// 2027 is not a year of the plan yet, so the day opens it too.
|
||||
expect: [
|
||||
{ event: 'vacay:settings', user: 'actor' },
|
||||
{ event: 'vacay:update', user: 'actor' },
|
||||
],
|
||||
},
|
||||
{
|
||||
tool: 'update_vacay_stats',
|
||||
|
||||
@@ -1875,3 +1875,38 @@ describe('quirk fixes', () => {
|
||||
expect(await storedFields(VacayYears, { plan: plan.id, year: year + 1 }, ['id'])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('a day written for a year the plan has not opened', () => {
|
||||
it('VACAY-SVC-079: adds that year with the member allowance, so the day shows in the calendar', async () => {
|
||||
const { user, plan } = await setupUserWithPlan();
|
||||
await allowWeekends(plan.id);
|
||||
expect(await svc.listYears(plan.id)).not.toContain(2031);
|
||||
|
||||
const result = await svc.toggleEntry(user.id, plan.id, '2031-01-06', 1, 'vacation');
|
||||
|
||||
expect(result.action).toBe('added');
|
||||
expect(await svc.listYears(plan.id)).toContain(2031);
|
||||
expect(await storedRow(VacayUserYears, { user: user.id, plan: plan.id, year: 2031 })).toBeDefined();
|
||||
});
|
||||
|
||||
it('VACAY-SVC-080: taking the day away again keeps the year it opened', async () => {
|
||||
const { user, plan } = await setupUserWithPlan();
|
||||
await allowWeekends(plan.id);
|
||||
await svc.toggleEntry(user.id, plan.id, '2032-03-02', 1, 'vacation');
|
||||
await svc.deleteYear(plan.id, 2032, undefined);
|
||||
const before = await svc.listYears(plan.id);
|
||||
|
||||
await svc.toggleEntry(user.id, plan.id, '2032-03-02', 1, 'vacation');
|
||||
await svc.toggleEntry(user.id, plan.id, '2032-03-02', 1, 'vacation');
|
||||
|
||||
expect(await svc.listYears(plan.id)).toEqual([...before, 2032].sort((a, b) => a - b));
|
||||
});
|
||||
|
||||
it('VACAY-SVC-081: a company holiday in a year not opened yet adds that year too', async () => {
|
||||
const { plan } = await setupUserWithPlan();
|
||||
|
||||
await svc.toggleCompanyHoliday(plan.id, '2033-12-25', 'Christmas', undefined);
|
||||
|
||||
expect(await svc.listYears(plan.id)).toContain(2033);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user