Enhance staffing management in school simulation by introducing API endpoints for hiring staff and assigning subjects. Implement payroll cap validation to ensure hiring and subject assignments do not exceed the allocated budget. Update the simulation options to include a monthly payroll cap and revise related classes to support new staffing functionalities. Enhance documentation to reflect these changes and update tests to validate the new features.
This commit is contained in:
@@ -342,10 +342,7 @@ internal sealed class SchoolWorker
|
||||
{
|
||||
while (_mailbox.Reader.TryRead(out var orphan))
|
||||
{
|
||||
if (orphan is WorkerCommand.GetPerson getPerson)
|
||||
{
|
||||
getPerson.Result.TrySetResult(new PersonCardResult(null, PersonLookupError.UnknownSchool));
|
||||
}
|
||||
CompleteOrphan(orphan);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -393,14 +390,23 @@ internal sealed class SchoolWorker
|
||||
? new PersonCardResult(null, PersonLookupError.UnknownPerson)
|
||||
: new PersonCardResult(card, PersonLookupError.None));
|
||||
break;
|
||||
|
||||
case WorkerCommand.HireStaff hire:
|
||||
hire.Result.TrySetResult(ApplyHire(school, hire.PersonId, hire.Position));
|
||||
break;
|
||||
|
||||
case WorkerCommand.AssignSubject assign:
|
||||
assign.Result.TrySetResult(ApplyAssign(school, assign.PersonId, assign.Subject));
|
||||
break;
|
||||
|
||||
case WorkerCommand.UnassignSubject unassign:
|
||||
unassign.Result.TrySetResult(ApplyUnassign(school, unassign.PersonId, unassign.Subject));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (command is WorkerCommand.GetPerson failed)
|
||||
{
|
||||
failed.Result.TrySetException(ex);
|
||||
}
|
||||
FailCommand(command, ex);
|
||||
|
||||
_logger.LogError(
|
||||
ex,
|
||||
@@ -421,6 +427,76 @@ internal sealed class SchoolWorker
|
||||
}
|
||||
}
|
||||
|
||||
private static void CompleteOrphan(WorkerCommand command)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case WorkerCommand.GetPerson getPerson:
|
||||
getPerson.Result.TrySetResult(new PersonCardResult(null, PersonLookupError.UnknownSchool));
|
||||
break;
|
||||
case WorkerCommand.HireStaff hire:
|
||||
hire.Result.TrySetResult(Staffing.UnknownSchool());
|
||||
break;
|
||||
case WorkerCommand.AssignSubject assign:
|
||||
assign.Result.TrySetResult(Staffing.UnknownSchool());
|
||||
break;
|
||||
case WorkerCommand.UnassignSubject unassign:
|
||||
unassign.Result.TrySetResult(Staffing.UnknownSchool());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FailCommand(WorkerCommand command, Exception exception)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case WorkerCommand.GetPerson getPerson:
|
||||
getPerson.Result.TrySetException(exception);
|
||||
break;
|
||||
case WorkerCommand.HireStaff hire:
|
||||
hire.Result.TrySetException(exception);
|
||||
break;
|
||||
case WorkerCommand.AssignSubject assign:
|
||||
assign.Result.TrySetException(exception);
|
||||
break;
|
||||
case WorkerCommand.UnassignSubject unassign:
|
||||
unassign.Result.TrySetException(exception);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private StaffingOutcome ApplyHire(School school, string personId, string position) =>
|
||||
ApplyStaffingChange(school, (catalog, roster, pool) =>
|
||||
Staffing.Hire(catalog, school.Map, roster, pool, personId, position, _options.MonthlyPayrollCap));
|
||||
|
||||
private StaffingOutcome ApplyAssign(School school, string personId, string subject) =>
|
||||
ApplyStaffingChange(school, (catalog, roster, pool) =>
|
||||
Staffing.AssignSubject(catalog, roster, pool, personId, subject, _options.MonthlyPayrollCap));
|
||||
|
||||
private StaffingOutcome ApplyUnassign(School school, string personId, string subject) =>
|
||||
ApplyStaffingChange(school, (catalog, roster, pool) =>
|
||||
Staffing.UnassignSubject(catalog, roster, pool, personId, subject, _options.MonthlyPayrollCap));
|
||||
|
||||
private StaffingOutcome ApplyStaffingChange(
|
||||
School school,
|
||||
Func<DefCatalog, Roster, ApplicantPool, StaffingOutcome> apply)
|
||||
{
|
||||
if (school.Roster is null || school.Applicants is null || school.Catalog is null)
|
||||
{
|
||||
return Staffing.UnknownSchool();
|
||||
}
|
||||
|
||||
var outcome = apply(school.Catalog, school.Roster, school.Applicants);
|
||||
if (outcome.Error == StaffingError.None)
|
||||
{
|
||||
school.ApplyStaffing(outcome.Roster, outcome.Pool);
|
||||
PersistPeople();
|
||||
PublishSnapshot();
|
||||
}
|
||||
|
||||
return outcome;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes pause/speed changes, at most once per <see cref="SimulationOptions.MinSaveInterval"/>.
|
||||
/// A single click still lands within that window; a burst collapses into one write.
|
||||
@@ -459,7 +535,7 @@ internal sealed class SchoolWorker
|
||||
|
||||
/// <summary>
|
||||
/// Writes the composition file. Not called from the 30-second clock save — the roster and
|
||||
/// applicant pool change on create, weekly refresh and yearly intake, not every tick.
|
||||
/// applicant pool change on create, hire, weekly refresh and yearly intake, not every tick.
|
||||
/// </summary>
|
||||
private void PersistPeople()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user