public void DeleteCompletedAndDecrementSchedules() { var schedules = new MazakSchedules() { Schedules = new[] { new MazakScheduleRow() { Id = 1, PartName = "part1:1:1", Comment = MazakPart.CreateComment("uniq1", new [] { 1 }, false), PlanQuantity = 15, CompleteQuantity = 15, Priority = 50, Processes = { new MazakScheduleProcessRow() { MazakScheduleRowId = 1, FixedMachineFlag = 1, ProcessNumber = 1 } } }, new MazakScheduleRow() { Id = 2, PartName = "part2:1:1", Comment = MazakPart.CreateComment("uniq2", new [] { 1 }, false), PlanQuantity = 15, CompleteQuantity = 10, Priority = 50, Processes = { new MazakScheduleProcessRow() { MazakScheduleRowId = 1, FixedMachineFlag = 1, ProcessNumber = 1, ProcessMaterialQuantity = 3, ProcessExecuteQuantity = 2 } } } } }; var(actions, tokeep) = BuildMazakSchedules.RemoveCompletedAndDecrementSchedules(schedules, DecrementPriorityOnDownload: true); schedules.Schedules.First().Command = MazakWriteCommand.Delete; schedules.Schedules.Skip(1).First().Command = MazakWriteCommand.ScheduleSafeEdit; schedules.Schedules.Skip(1).First().Priority = 40; schedules.Schedules.Skip(1).First().Processes.Clear(); actions.Schedules.Should().BeEquivalentTo(schedules.Schedules); actions.Parts.Should().BeEmpty(); actions.Fixtures.Should().BeEmpty(); actions.Pallets.Should().BeEmpty(); tokeep.Should().BeEquivalentTo(new[] { "part2:1:1" }); }
public void IgnoresAlreadyExistingDecrement() { // plan 50, completed 30, 5 in proc and 15 not yet started _read.LoadSchedulesAndLoadActions().Returns(new MazakSchedulesAndLoadActions() { Schedules = new[] { new MazakScheduleRow() { Id = 15, Comment = MazakPart.CreateComment("uuuu", new[] { 1 }, manual: false), PartName = "pppp:1", PlanQuantity = 50, CompleteQuantity = 30, Processes = new List <MazakScheduleProcessRow> { new MazakScheduleProcessRow() { MazakScheduleRowId = 15, FixQuantity = 1, ProcessNumber = 1, ProcessMaterialQuantity = 15, ProcessExecuteQuantity = 5 } } } } }); var j = new JobPlan("uuuu", 1); j.PartName = "pppp"; j.SetPlannedCyclesOnFirstProcess(path: 1, numCycles: 50); _jobDB.AddJobs(new NewJobs() { Jobs = new List <JobPlan> { j } }, null); var now = DateTime.UtcNow.AddHours(-1); _jobDB.AddNewDecrement(new[] { new JobDB.NewDecrementQuantity() { JobUnique = "uuuu", Part = "pppp", Quantity = 3 } }, now); _decr.Decrement(); _write.Schedules.Should().BeNull(); _jobDB.LoadDecrementsForJob("uuuu").Should().BeEquivalentTo(new[] { new InProcessJobDecrement() { DecrementId = 0, TimeUTC = now, Quantity = 3 } }); }
public void SinglePathSingleProc() { // plan 50, completed 30, 5 in proc and 15 not yet started _read.LoadSchedulesAndLoadActions().Returns(new MazakSchedulesAndLoadActions() { Schedules = new[] { new MazakScheduleRow() { Id = 15, Comment = MazakPart.CreateComment("uuuu", new[] { 1 }, false), PartName = "pppp:1", PlanQuantity = 50, CompleteQuantity = 30, Processes = new List <MazakScheduleProcessRow> { new MazakScheduleProcessRow() { MazakScheduleRowId = 15, FixQuantity = 1, ProcessNumber = 1, ProcessMaterialQuantity = 15, ProcessExecuteQuantity = 5 } } } } }); var j = new JobPlan("uuuu", 1); j.PartName = "pppp"; j.SetPlannedCyclesOnFirstProcess(path: 1, numCycles: 50); _jobDB.AddJobs(new NewJobs() { Jobs = new List <JobPlan> { j } }, null); var now = DateTime.UtcNow; _decr.Decrement(now); _write.Schedules.Count.Should().Be(1); var sch = _write.Schedules[0]; sch.Id.Should().Be(15); sch.PlanQuantity.Should().Be(35); sch.Processes.Should().BeEmpty(); _jobDB.LoadDecrementsForJob("uuuu").Should().BeEquivalentTo(new[] { new InProcessJobDecrement() { DecrementId = 0, TimeUTC = now, Quantity = 50 - 35 } }); }
public void IgnoresManualSchedule() { // plan 50, completed 30, 5 in proc and 15 not yet started _read.LoadSchedulesAndLoadActions().Returns(new MazakSchedulesAndLoadActions() { Schedules = new[] { new MazakScheduleRow() { Id = 15, Comment = MazakPart.CreateComment("uuuu", new[] { 1 }, manual: true), PartName = "pppp:1", PlanQuantity = 50, CompleteQuantity = 30, Processes = new List <MazakScheduleProcessRow> { new MazakScheduleProcessRow() { MazakScheduleRowId = 15, FixQuantity = 1, ProcessNumber = 1, ProcessMaterialQuantity = 15, ProcessExecuteQuantity = 5 } } } } }); var j = new JobPlan("uuuu", 1); j.PartName = "pppp"; j.SetPlannedCyclesOnFirstProcess(path: 1, numCycles: 50); _jobDB.AddJobs(new NewJobs() { Jobs = new List <JobPlan> { j } }, null); _decr.Decrement(); _write.Schedules.Should().BeNull(); _jobDB.LoadDecrementsForJob("uuuu").Should().BeEmpty(); }
public void AddsSchedules() { //basic job var job1 = new JobPlan("uniq1", 2, new int[] { 2, 2 }); job1.PartName = "part1"; job1.SetPathGroup(1, 1, 1); job1.SetPathGroup(1, 2, 2); job1.SetPathGroup(2, 1, 1); job1.SetPathGroup(2, 2, 2); job1.SetSimulatedStartingTimeUTC(1, 1, new DateTime(2018, 1, 9, 8, 7, 6, DateTimeKind.Local).ToUniversalTime()); job1.SetSimulatedStartingTimeUTC(1, 2, new DateTime(2018, 1, 2, 3, 4, 5, DateTimeKind.Local).ToUniversalTime()); job1.SetPlannedCyclesOnFirstProcess(1, 51); job1.SetPlannedCyclesOnFirstProcess(2, 41); job1.SetFixtureFace(1, 1, "fixA", 1); job1.SetFixtureFace(1, 2, "fixA", 1); job1.SetFixtureFace(2, 1, "fixA", 2); job1.SetFixtureFace(2, 2, "fixA", 2); //one with an input queue var job2 = new JobPlan("uniq2", 2, new int[] { 2, 2 }); job2.PartName = "part2"; job2.SetPathGroup(1, 1, 1); job2.SetPathGroup(1, 2, 2); job2.SetPathGroup(2, 1, 1); job2.SetPathGroup(2, 2, 2); job2.SetSimulatedStartingTimeUTC(1, 1, new DateTime(2018, 2, 9, 8, 7, 6, DateTimeKind.Local).ToUniversalTime()); job2.SetSimulatedStartingTimeUTC(1, 2, new DateTime(2018, 2, 2, 3, 4, 5, DateTimeKind.Local).ToUniversalTime()); job2.SetPlannedCyclesOnFirstProcess(1, 12); job2.SetPlannedCyclesOnFirstProcess(2, 42); job2.SetInputQueue(1, 1, "aaa"); job2.SetInputQueue(1, 2, "bbb"); job2.SetFixtureFace(1, 1, "fixA", 2); // conflicts with both job1 paths job2.SetFixtureFace(2, 2, "fixB", 2); // no conflicts //a schedule which already exists var job3 = new JobPlan("uniq3", 2, new int[] { 1, 1 }); job3.PartName = "part3"; job3.SetSimulatedStartingTimeUTC(1, 1, new DateTime(2018, 3, 9, 8, 7, 6, DateTimeKind.Local).ToUniversalTime()); job3.SetPlannedCyclesOnFirstProcess(1, 23); // all the parts, plus a schedule for uniq3 var curData = new MazakSchedulesPartsPallets() { Parts = new[] { new MazakPartRow() { PartName = "part1:6:1", Comment = MazakPart.CreateComment("uniq1", new[] { 1, 1 }, false) }, new MazakPartRow() { PartName = "part1:6:2", Comment = MazakPart.CreateComment("uniq1", new[] { 2, 2 }, false) }, new MazakPartRow() { PartName = "part2:6:1", Comment = MazakPart.CreateComment("uniq2", new[] { 1, 1 }, false) }, new MazakPartRow() { PartName = "part2:6:2", Comment = MazakPart.CreateComment("uniq2", new[] { 2, 2 }, false) }, new MazakPartRow() { PartName = "part3:6:1", Comment = MazakPart.CreateComment("uniq3", new[] { 1, 1 }, false) } }, Schedules = new[] { new MazakScheduleRow() { Id = 1, PartName = "part3:6:1", Comment = MazakPart.CreateComment("uniq3", new[] { 1, 1 }, false) } } }; var actions = BuildMazakSchedules.AddSchedules(curData, new[] { job1, job2, job3 }, true); actions.Parts.Should().BeEmpty(); actions.Fixtures.Should().BeEmpty(); actions.Pallets.Should().BeEmpty(); actions.Schedules.Should().BeEquivalentTo(new[] { //uniq1 new MazakScheduleRow() { Command = MazakWriteCommand.Add, Id = 2, PartName = "part1:6:1", Comment = MazakPart.CreateComment("uniq1", new[] { 1, 1 }, false), PlanQuantity = 51, Priority = 91 + 1, // one earlier conflict DueDate = new DateTime(2018, 1, 9, 0, 0, 0, DateTimeKind.Local), Processes = { new MazakScheduleProcessRow() { MazakScheduleRowId = 2, ProcessNumber = 1, ProcessMaterialQuantity = 51 }, new MazakScheduleProcessRow() { MazakScheduleRowId = 2, ProcessNumber = 2, ProcessMaterialQuantity = 0 }, } }, new MazakScheduleRow() { Command = MazakWriteCommand.Add, Id = 3, PartName = "part1:6:2", Comment = MazakPart.CreateComment("uniq1", new[] { 2, 2 }, false), PlanQuantity = 41, Priority = 91, DueDate = new DateTime(2018, 1, 2, 0, 0, 0, DateTimeKind.Local), Processes = { new MazakScheduleProcessRow() { MazakScheduleRowId = 3, ProcessNumber = 1, ProcessMaterialQuantity = 41 }, new MazakScheduleProcessRow() { MazakScheduleRowId = 3, ProcessNumber = 2, ProcessMaterialQuantity = 0 }, } }, //uniq2 new MazakScheduleRow() { Command = MazakWriteCommand.Add, Id = 4, PartName = "part2:6:1", Comment = MazakPart.CreateComment("uniq2", new[] { 1, 1 }, false), PlanQuantity = 12, Priority = 91 + 2, // conflicts with 2 earlier DueDate = new DateTime(2018, 2, 9, 0, 0, 0, DateTimeKind.Local), Processes = { new MazakScheduleProcessRow() { MazakScheduleRowId = 4, ProcessNumber = 1, ProcessMaterialQuantity = 0 // no material, input queue }, new MazakScheduleProcessRow() { MazakScheduleRowId = 4, ProcessNumber = 2, ProcessMaterialQuantity = 0 }, } }, new MazakScheduleRow() { Command = MazakWriteCommand.Add, Id = 5, PartName = "part2:6:2", Comment = MazakPart.CreateComment("uniq2", new[] { 2, 2 }, false), PlanQuantity = 42, Priority = 91, DueDate = new DateTime(2018, 2, 2, 0, 0, 0, DateTimeKind.Local), Processes = { new MazakScheduleProcessRow() { MazakScheduleRowId = 5, ProcessNumber = 1, ProcessMaterialQuantity = 0 //no material, input queue }, new MazakScheduleProcessRow() { MazakScheduleRowId = 5, ProcessNumber = 2, ProcessMaterialQuantity = 0 }, } } }); }
public WriteJobsSpec() { var logConn = new Microsoft.Data.Sqlite.SqliteConnection("Data Source=:memory:"); logConn.Open(); _logDB = new JobLogDB(new FMSSettings(), logConn); _logDB.CreateTables(firstSerialOnEmpty: null); var jobConn = new Microsoft.Data.Sqlite.SqliteConnection("Data Source=:memory:"); jobConn.Open(); _jobDB = new JobDB(jobConn); _jobDB.CreateTables(); _writeMock = new WriteMock(); _readMock = Substitute.For <IReadDataAccess>(); _readMock.MazakType.Returns(MazakDbType.MazakSmooth); _readMock.LoadAllData().Returns(new MazakAllData() { Schedules = new[] { // a completed schedule, should be deleted new MazakScheduleRow() { Id = 1, PartName = "part1:1:1", Comment = MazakPart.CreateComment("uniq1", new [] { 1 }, false), PlanQuantity = 15, CompleteQuantity = 15, Priority = 50, Processes = { new MazakScheduleProcessRow() { MazakScheduleRowId = 1, FixedMachineFlag = 1, ProcessNumber = 1 } } }, // a non-completed schedule, should be decremented new MazakScheduleRow() { Id = 2, PartName = "part2:1:1", Comment = MazakPart.CreateComment("uniq2", new [] { 1 }, false), PlanQuantity = 15, CompleteQuantity = 10, Priority = 50, Processes = { new MazakScheduleProcessRow() { MazakScheduleRowId = 1, FixedMachineFlag = 1, ProcessNumber = 1, ProcessMaterialQuantity = 3, ProcessExecuteQuantity = 2 } } }, }, Parts = new[] { // should be deleted, since corresponding schedule is deleted new MazakPartRow() { PartName = "part1:1:1", Comment = MazakPart.CreateComment("uniq1", new[] { 1 }, false), Processes = { new MazakPartProcessRow() { PartName = "part1:1:1", ProcessNumber = 1, FixQuantity = 5, Fixture = "fixtoremove" } } }, //should be kept, since schedule is kept new MazakPartRow() { PartName = "part2:1:1", Comment = MazakPart.CreateComment("uniq2", new[] { 1 }, false), Processes = { new MazakPartProcessRow() { PartName = "part2:1:1", ProcessNumber = 1, FixQuantity = 2, Fixture = "fixtokeep" } } }, }, Fixtures = new[] { new MazakFixtureRow() { FixtureName = "fixtoremove", Comment = "Insight" }, new MazakFixtureRow() { FixtureName = "fixtokeep", Comment = "Insight" } }, Pallets = new[] { new MazakPalletRow() { PalletNumber = 5, Fixture = "fixtoremove" }, new MazakPalletRow() { PalletNumber = 6, Fixture = "fixtokeep" } }, PalletSubStatuses = Enumerable.Empty <MazakPalletSubStatusRow>(), PalletPositions = Enumerable.Empty <MazakPalletPositionRow>(), LoadActions = Enumerable.Empty <LoadAction>(), MainPrograms = Enumerable.Concat( (new[] { "1001", "1002", "1003", "1004", "1005" }).Select(p => new MazakProgramRow() { MainProgram = p, Comment = "" }), new[] { new MazakProgramRow() { MainProgram = System.IO.Path.Combine("theprogdir", "prog-bbb-1_rev2.EIA"), Comment = "Insight:2:prog-bbb-1" }, new MazakProgramRow() { MainProgram = System.IO.Path.Combine("theprogdir", "prog-bbb-1_rev3.EIA"), Comment = "Insight:3:prog-bbb-1" } } ) }); _readMock.LoadSchedulesPartsPallets().Returns(x => new MazakSchedulesPartsPallets() { Schedules = Enumerable.Empty <MazakScheduleRow>(), Parts = _writeMock.AddParts.Parts, Pallets = _writeMock.AddParts.Pallets, PalletSubStatuses = Enumerable.Empty <MazakPalletSubStatusRow>(), PalletPositions = Enumerable.Empty <MazakPalletPositionRow>(), LoadActions = Enumerable.Empty <LoadAction>(), MainPrograms = (new[] { "1001", "1002", "1003", "1004", "1005" }).Select(p => new MazakProgramRow() { MainProgram = p, Comment = "" }), }); _settings = new FMSSettings(); _settings.Queues["castings"] = new QueueSize(); _settings.Queues["queueAAA"] = new QueueSize(); _settings.Queues["queueBBB"] = new QueueSize(); _settings.Queues["queueCCC"] = new QueueSize(); _writeJobs = new WriteJobs( _writeMock, _readMock, Substitute.For <IHoldManagement>(), _jobDB, _logDB, _settings, check: false, useStarting: true, progDir: "theprogdir"); jsonSettings = new JsonSerializerSettings(); jsonSettings.Converters.Add(new BlackMaple.MachineFramework.TimespanConverter()); jsonSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); jsonSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; jsonSettings.Formatting = Formatting.Indented; }
public void MultplePathsAndProcs() { // path 1: plan 50, complete 30, 5 in-proc #1, 3 in-proc #2, 2 material proc #2, 0 material proc1 (has input queue). 10 un-started parts // path 2: plan 25, complete 3, 2 in-proc #1, 4 in-proc #2, 3 material proc #2, 25 - 3 - 2 - 4 - 3 = 13 un-started parts _read.LoadSchedulesAndLoadActions().Returns(new MazakSchedulesAndLoadActions() { Schedules = new[] { new MazakScheduleRow() { Id = 15, Comment = MazakPart.CreateComment("uuuu", new[] { 1, 2 }, false), PartName = "pppp:1", PlanQuantity = 50, CompleteQuantity = 30, Processes = new List <MazakScheduleProcessRow> { new MazakScheduleProcessRow() { MazakScheduleRowId = 15, FixQuantity = 1, ProcessNumber = 1, ProcessMaterialQuantity = 0, ProcessExecuteQuantity = 5 }, new MazakScheduleProcessRow() { MazakScheduleRowId = 15, FixQuantity = 1, ProcessNumber = 2, ProcessMaterialQuantity = 2, ProcessExecuteQuantity = 3 } } }, new MazakScheduleRow() { Id = 16, Comment = MazakPart.CreateComment("uuuu", new[] { 2, 1 }, false), PartName = "pppp:1", PlanQuantity = 25, CompleteQuantity = 3, Processes = new List <MazakScheduleProcessRow> { new MazakScheduleProcessRow() { MazakScheduleRowId = 16, FixQuantity = 1, ProcessNumber = 1, ProcessMaterialQuantity = 0, ProcessExecuteQuantity = 2 }, new MazakScheduleProcessRow() { MazakScheduleRowId = 16, FixQuantity = 1, ProcessNumber = 2, ProcessMaterialQuantity = 3, ProcessExecuteQuantity = 4 } } } } }); var j = new JobPlan("uuuu", 2, new[] { 2, 2 }); j.PartName = "pppp"; j.SetPlannedCyclesOnFirstProcess(path: 1, numCycles: 50); j.SetPlannedCyclesOnFirstProcess(path: 2, numCycles: 25); j.SetPathGroup(process: 1, path: 1, pgroup: 1); j.SetPathGroup(process: 2, path: 2, pgroup: 1); j.SetPathGroup(process: 1, path: 2, pgroup: 2); j.SetPathGroup(process: 2, path: 1, pgroup: 2); j.SetInputQueue(process: 1, path: 1, queue: "castings"); j.SetInputQueue(process: 1, path: 2, queue: "castings"); _jobDB.AddJobs(new NewJobs() { Jobs = new List <JobPlan> { j } }, null); var now = DateTime.UtcNow; _decr.Decrement(now); _write.Schedules.Count.Should().Be(2); _write.Schedules[0].Id.Should().Be(15); _write.Schedules[0].PlanQuantity.Should().Be(50 - 10); _write.Schedules[1].Id.Should().Be(16); _write.Schedules[1].PlanQuantity.Should().Be(25 - 13); _jobDB.LoadDecrementsForJob("uuuu").Should().BeEquivalentTo(new[] { new InProcessJobDecrement() { DecrementId = 0, TimeUTC = now, Quantity = 10 + 13 } }); }
public void LoadInProcess() { // plan 50, completed 30, 5 in proc and 15 not yet started. BUT, one is being loaded at the load station _read.LoadSchedulesAndLoadActions().Returns(new MazakSchedulesAndLoadActions() { Schedules = new[] { new MazakScheduleRow() { Id = 15, Comment = MazakPart.CreateComment("uuuu", new[] { 1 }, false), PartName = "pppp:1", PlanQuantity = 50, CompleteQuantity = 30, Processes = new List <MazakScheduleProcessRow> { new MazakScheduleProcessRow() { MazakScheduleRowId = 15, FixQuantity = 1, ProcessNumber = 1, ProcessMaterialQuantity = 15, ProcessExecuteQuantity = 5 } } } }, LoadActions = new[] { new LoadAction() { LoadStation = 1, LoadEvent = true, // load Unique = "uuuu", Part = "pppp", Process = 1, Path = 1, Qty = 1 }, new LoadAction() { LoadStation = 1, LoadEvent = false, // unload, should be ignored Unique = "uuuu", Part = "pppp", Process = 1, Path = 1, Qty = 1 }, new LoadAction() { LoadStation = 2, LoadEvent = true, // load of different part Unique = "uuuu2", Part = "pppp", Process = 1, Path = 1, Qty = 1 } } }); var j = new JobPlan("uuuu", 1); j.PartName = "pppp"; j.SetPlannedCyclesOnFirstProcess(path: 1, numCycles: 50); _jobDB.AddJobs(new NewJobs() { Jobs = new List <JobPlan> { j } }, null); var now = DateTime.UtcNow; _decr.Decrement(now); _write.Schedules.Count.Should().Be(1); _write.Schedules[0].PlanQuantity.Should().Be(36); _jobDB.LoadDecrementsForJob("uuuu").Should().BeEquivalentTo(new[] { new InProcessJobDecrement() { DecrementId = 0, TimeUTC = now, Quantity = 50 - 36 } }); }
public void IncludesNotCopiedJobs() { // uuuu plan 50, completed 30, 5 in proc and 15 not yet started // vvvv not copied so not returned from LoadSchedulesAndLoadActions _read.LoadSchedulesAndLoadActions().Returns(new MazakSchedulesAndLoadActions() { Schedules = new[] { new MazakScheduleRow() { Id = 15, Comment = MazakPart.CreateComment("uuuu", new[] { 1 }, false), PartName = "pppp:1", PlanQuantity = 50, CompleteQuantity = 30, Processes = new List <MazakScheduleProcessRow> { new MazakScheduleProcessRow() { MazakScheduleRowId = 15, FixQuantity = 1, ProcessNumber = 1, ProcessMaterialQuantity = 15, ProcessExecuteQuantity = 5 } } } } }); var now = DateTime.UtcNow; var uuuu = new JobPlan("uuuu", 1); uuuu.PartName = "pppp"; uuuu.SetPlannedCyclesOnFirstProcess(path: 1, numCycles: 50); uuuu.RouteStartingTimeUTC = now.AddHours(-12); uuuu.RouteEndingTimeUTC = now.AddHours(12); uuuu.JobCopiedToSystem = true; var vvvv = new JobPlan("vvvv", 1, new[] { 2 }); vvvv.PartName = "oooo"; vvvv.JobCopiedToSystem = false; vvvv.SetPlannedCyclesOnFirstProcess(path: 1, numCycles: 4); vvvv.SetPlannedCyclesOnFirstProcess(path: 2, numCycles: 7); vvvv.RouteStartingTimeUTC = now.AddHours(-12); vvvv.RouteEndingTimeUTC = now.AddHours(12); _jobDB.AddJobs(new NewJobs() { Jobs = new List <JobPlan> { uuuu, vvvv } }, null); _jobDB.LoadJobsNotCopiedToSystem(now.AddHours(-12), now.AddHours(12), includeDecremented: false).Jobs.Select(j => j.UniqueStr) .Should().BeEquivalentTo(new[] { "vvvv" }); _decr.Decrement(now); _write.Schedules.Count.Should().Be(1); var sch = _write.Schedules[0]; sch.Id.Should().Be(15); sch.PlanQuantity.Should().Be(35); sch.Processes.Should().BeEmpty(); _jobDB.LoadDecrementsForJob("uuuu").Should().BeEquivalentTo(new[] { new InProcessJobDecrement() { DecrementId = 0, TimeUTC = now, Quantity = 50 - 35 } }); _jobDB.LoadDecrementsForJob("vvvv").Should().BeEquivalentTo(new[] { new InProcessJobDecrement() { DecrementId = 0, TimeUTC = now, Quantity = 4 + 7 } }); _jobDB.LoadDecrementQuantitiesAfter(now.AddHours(-12)).Should().BeEquivalentTo(new[] { new JobAndDecrementQuantity() { DecrementId = 0, JobUnique = "uuuu", Part = "pppp", Quantity = 50 - 35, TimeUTC = now }, new JobAndDecrementQuantity() { DecrementId = 0, JobUnique = "vvvv", Part = "oooo", Quantity = 4 + 7, TimeUTC = now } }); _jobDB.LoadJobsNotCopiedToSystem(now.AddHours(-12), now.AddHours(12), includeDecremented: false).Jobs .Should().BeEmpty(); }