Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 23 additions & 24 deletions backend/routes/api/seasons.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,31 +673,17 @@ router.post(
? null
: await getGateDetail(season.publishableId);

const {
resolvedStatus: newStatus,
informationSvcApproved,
reservationSvcApproved,
} = await resolveSeasonApprovalState({
season,
requestedNewStatus,
oldGateDetail,
gateDetail: isWinterSeason ? null : gateDetail,
isInformationSvcApprover,
isReservationSvcApprover,
});

const shouldMarkSavedWithErrors =
newStatus !== STATUS.REQUESTED && savedWithErrors;

// Require an explanation note if the form is submitted with validation errors
if (shouldMarkSavedWithErrors && !notes.trim()) {
const error = new Error(
"Validation error: Missing explanation note for saving with errors.",
);
const { resolvedStatus, informationSvcApproved, reservationSvcApproved } =
await resolveSeasonApprovalState({
season,
requestedNewStatus,
oldGateDetail,
gateDetail: isWinterSeason ? null : gateDetail,
isInformationSvcApprover,
isReservationSvcApprover,
});

error.status = 400;
throw error;
}
const newStatus = resolvedStatus;

// If readyToPublish is null or undefined, set it to the current value
const newReadyToPublish = readyToPublish ?? season.readyToPublish;
Expand All @@ -721,6 +707,19 @@ router.post(
})
: false;

const shouldMarkSavedWithErrors =
newStatus !== STATUS.REQUESTED && savedWithErrors;

// Require an explanation note if the form is submitted with validation errors
if (shouldMarkSavedWithErrors && !notes.trim()) {
const error = new Error(
"Validation error: Missing explanation note for saving with errors.",
);

error.status = 400;
throw error;
}

// Persist the season state, dates, and related audit records to the DB
await saveSeasonData({
season,
Expand Down
53 changes: 47 additions & 6 deletions backend/utils/propagateWinterFeeDates.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,14 +493,15 @@ async function syncFeatureWinterDatesOnParkAreaSeason(
* @param {Transaction} [transaction] Optional Sequelize transaction
* @param {Object} [options] Optional propagation controls
* @param {boolean} [options.syncStateOnly=false] Sync only readyToPublish/status on derived winter seasons
* @returns {Promise<boolean | Array>} Number of Feature winter seasons updated and skipped.
* @returns {Promise<{updatedFeatures:number, skippedFeatures:number, updatedParkAreas:number, skippedParkAreas:number}>}
* Summary counts for propagated and skipped records.
*/
export default async function propagateWinterFeeDates(
seasonId,
transaction = null,
options = {},
) {
const { syncStateOnly = false } = options;
const { syncStateOnly = false, targetWinterOperatingYear = null } = options;
const sourceSeason = await getSeason(seasonId, transaction);

if (!sourceSeason) {
Expand All @@ -519,11 +520,12 @@ export default async function propagateWinterFeeDates(
}

// Winter saves target their own operating year.
// Regular operation saves target the prior winter operating year.
// Regular operation saves target the prior winter operating year by default.
const winterOperatingYear =
sourceSeason.seasonType === SEASON_TYPE.REGULAR
targetWinterOperatingYear ??
(sourceSeason.seasonType === SEASON_TYPE.REGULAR
? sourceSeason.operatingYear - 1
: sourceSeason.operatingYear;
: sourceSeason.operatingYear);

const { winterTypeId, operationTypeId } = await getDateTypeIds(transaction);

Expand All @@ -537,6 +539,18 @@ export default async function propagateWinterFeeDates(
// Do not recalculate derived Feature winter dates
// until Park winter dates are in an approved/published season.
if (!parkWinter.season) {
// For regular-season edits, a missing prior winter season should not block
// recalculation of the same-year winter season.
if (
sourceSeason.seasonType === SEASON_TYPE.REGULAR &&
targetWinterOperatingYear === null
) {
return propagateWinterFeeDates(seasonId, transaction, {
...options,
targetWinterOperatingYear: sourceSeason.operatingYear,
});
}

return {
updatedFeatures: 0,
skippedFeatures: 0,
Expand Down Expand Up @@ -723,10 +737,37 @@ export default async function propagateWinterFeeDates(
}
}

return {
const output = {
updatedFeatures,
skippedFeatures,
updatedParkAreas: updatedParkAreaIds.size,
skippedParkAreas: skippedParkAreaIds.size,
};

// For regular-season operation edits, also recalculate the same-year winter
// (where this regular year is the "previous" operation year).
if (
sourceSeason.seasonType === SEASON_TYPE.REGULAR &&
targetWinterOperatingYear === null
) {
const sameYearOutput = await propagateWinterFeeDates(
seasonId,
transaction,
{
...options,
targetWinterOperatingYear: sourceSeason.operatingYear,
},
);

return {
updatedFeatures: output.updatedFeatures + sameYearOutput.updatedFeatures,
skippedFeatures: output.skippedFeatures + sameYearOutput.skippedFeatures,
updatedParkAreas:
output.updatedParkAreas + sameYearOutput.updatedParkAreas,
skippedParkAreas:
output.skippedParkAreas + sameYearOutput.skippedParkAreas,
};
}

return output;
}