Skip to content

Commit dd6f1d9

Browse files
renemadsenclaude
andcommitted
fix: use email-based user lookup and read phone numbers from SDK Workers
Replace fragile name-matching (site.Name vs FirstName+LastName) with email-based lookup (worker.Email ↔ Users.Email) across all 3 services. Read PhoneNumber from SDK Workers table where it's actually stored, not from Angular Users table where it was always NULL. Fixes: 8 occurrences in TimeSettingService, TimePlanningWorkingHoursService, and TimePlanningPlanningService. Adds integration tests verifying phone numbers flow through GetAvailableSitesByCurrentUser correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 232da3e commit dd6f1d9

4 files changed

Lines changed: 438 additions & 27 deletions

File tree

Lines changed: 385 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
using System.Linq;
2+
using System.Threading.Tasks;
3+
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.Logging;
5+
using Microting.eForm.Infrastructure.Constants;
6+
using Microting.eFormApi.BasePn.Abstractions;
7+
using Microting.eFormApi.BasePn.Infrastructure.Helpers.PluginDbOptions;
8+
using TimePlanning.Pn.Infrastructure.Models.Settings;
9+
using AssignedSiteEntity = Microting.TimePlanningBase.Infrastructure.Data.Entities.AssignedSite;
10+
using NSubstitute;
11+
using NUnit.Framework;
12+
using TimePlanning.Pn.Services.TimePlanningLocalizationService;
13+
using TimePlanning.Pn.Services.TimePlanningSettingService;
14+
15+
namespace TimePlanning.Pn.Test;
16+
17+
[TestFixture]
18+
public class SettingsServicePhoneNumberTests : TestBaseSetup
19+
{
20+
private ISettingService _settingsService;
21+
private IUserService _userService;
22+
private ITimePlanningLocalizationService _localizationService;
23+
private IEFormCoreService _coreService;
24+
private IPluginDbOptions<TimePlanningBaseSettings> _options;
25+
26+
[SetUp]
27+
public async Task SetUp()
28+
{
29+
await base.Setup();
30+
_userService = Substitute.For<IUserService>();
31+
_userService.UserId.Returns(1);
32+
33+
_localizationService = Substitute.For<ITimePlanningLocalizationService>();
34+
_localizationService.GetString(Arg.Any<string>()).Returns(x => x[0]?.ToString());
35+
36+
_coreService = Substitute.For<IEFormCoreService>();
37+
var core = await GetCore();
38+
_coreService.GetCore().Returns(core);
39+
40+
_options = Substitute.For<IPluginDbOptions<TimePlanningBaseSettings>>();
41+
_options.Value.Returns(new TimePlanningBaseSettings
42+
{
43+
AutoBreakCalculationActive = "0",
44+
DayOfPayment = 20,
45+
GpsEnabled = "0",
46+
SnapshotEnabled = "0"
47+
});
48+
49+
_settingsService = new TimeSettingService(
50+
_options,
51+
TimePlanningPnDbContext,
52+
Substitute.For<ILogger<TimeSettingService>>(),
53+
_userService,
54+
_localizationService,
55+
null,
56+
_coreService);
57+
}
58+
59+
[Test]
60+
public async Task GetAvailableSitesByCurrentUser_ReturnsPhoneNumbers_FromSdkWorkers()
61+
{
62+
// Arrange — create 3 SDK sites, site workers, and workers with varying phone numbers
63+
var core = await _coreService.GetCore();
64+
var sdkDbContext = core.DbContextHelper.GetDbContext();
65+
66+
// Worker 1: has phone number
67+
var site1 = new Microting.eForm.Infrastructure.Data.Entities.Site
68+
{
69+
Name = "Site Alice",
70+
MicrotingUid = 100
71+
};
72+
await site1.Create(sdkDbContext);
73+
74+
var worker1 = new Microting.eForm.Infrastructure.Data.Entities.Worker
75+
{
76+
FirstName = "Alice",
77+
LastName = "Smith",
78+
Email = "alice@example.com",
79+
PhoneNumber = "+4512345678",
80+
MicrotingUid = 1001
81+
};
82+
await worker1.Create(sdkDbContext);
83+
84+
var siteWorker1 = new Microting.eForm.Infrastructure.Data.Entities.SiteWorker
85+
{
86+
SiteId = site1.Id,
87+
WorkerId = worker1.Id,
88+
MicrotingUid = 2001
89+
};
90+
await siteWorker1.Create(sdkDbContext);
91+
92+
// Worker 2: no phone number (null)
93+
var site2 = new Microting.eForm.Infrastructure.Data.Entities.Site
94+
{
95+
Name = "Site Bob",
96+
MicrotingUid = 200
97+
};
98+
await site2.Create(sdkDbContext);
99+
100+
var worker2 = new Microting.eForm.Infrastructure.Data.Entities.Worker
101+
{
102+
FirstName = "Bob",
103+
LastName = "Jones",
104+
Email = "bob@example.com",
105+
PhoneNumber = null,
106+
MicrotingUid = 1002
107+
};
108+
await worker2.Create(sdkDbContext);
109+
110+
var siteWorker2 = new Microting.eForm.Infrastructure.Data.Entities.SiteWorker
111+
{
112+
SiteId = site2.Id,
113+
WorkerId = worker2.Id,
114+
MicrotingUid = 2002
115+
};
116+
await siteWorker2.Create(sdkDbContext);
117+
118+
// Worker 3: has different phone number
119+
var site3 = new Microting.eForm.Infrastructure.Data.Entities.Site
120+
{
121+
Name = "Site Carol",
122+
MicrotingUid = 300
123+
};
124+
await site3.Create(sdkDbContext);
125+
126+
var worker3 = new Microting.eForm.Infrastructure.Data.Entities.Worker
127+
{
128+
FirstName = "Carol",
129+
LastName = "Lee",
130+
Email = "carol@example.com",
131+
PhoneNumber = "+4587654321",
132+
MicrotingUid = 1003
133+
};
134+
await worker3.Create(sdkDbContext);
135+
136+
var siteWorker3 = new Microting.eForm.Infrastructure.Data.Entities.SiteWorker
137+
{
138+
SiteId = site3.Id,
139+
WorkerId = worker3.Id,
140+
MicrotingUid = 2003
141+
};
142+
await siteWorker3.Create(sdkDbContext);
143+
144+
// Create units (required by the service)
145+
var unit1 = new Microting.eForm.Infrastructure.Data.Entities.Unit
146+
{
147+
SiteId = site1.Id,
148+
MicrotingUid = 3001,
149+
CustomerNo = 1
150+
};
151+
await unit1.Create(sdkDbContext);
152+
153+
var unit2 = new Microting.eForm.Infrastructure.Data.Entities.Unit
154+
{
155+
SiteId = site2.Id,
156+
MicrotingUid = 3002,
157+
CustomerNo = 2
158+
};
159+
await unit2.Create(sdkDbContext);
160+
161+
var unit3 = new Microting.eForm.Infrastructure.Data.Entities.Unit
162+
{
163+
SiteId = site3.Id,
164+
MicrotingUid = 3003,
165+
CustomerNo = 3
166+
};
167+
await unit3.Create(sdkDbContext);
168+
169+
// Create languages
170+
var language = await sdkDbContext.Languages.FirstOrDefaultAsync();
171+
if (language == null)
172+
{
173+
language = new Microting.eForm.Infrastructure.Data.Entities.Language
174+
{
175+
LanguageCode = "da",
176+
Name = "Danish"
177+
};
178+
await language.Create(sdkDbContext);
179+
}
180+
181+
// Assign language to sites
182+
site1.LanguageId = language.Id;
183+
await site1.Update(sdkDbContext);
184+
site2.LanguageId = language.Id;
185+
await site2.Update(sdkDbContext);
186+
site3.LanguageId = language.Id;
187+
await site3.Update(sdkDbContext);
188+
189+
// Create AssignedSites in plugin DB (links to SDK sites via MicrotingUid)
190+
var assignedSite1 = new AssignedSiteEntity
191+
{
192+
SiteId = 100, // matches site1.MicrotingUid
193+
CreatedByUserId = 1,
194+
UpdatedByUserId = 1
195+
};
196+
await assignedSite1.Create(TimePlanningPnDbContext);
197+
198+
var assignedSite2 = new AssignedSiteEntity
199+
{
200+
SiteId = 200, // matches site2.MicrotingUid
201+
CreatedByUserId = 1,
202+
UpdatedByUserId = 1
203+
};
204+
await assignedSite2.Create(TimePlanningPnDbContext);
205+
206+
var assignedSite3 = new AssignedSiteEntity
207+
{
208+
SiteId = 300, // matches site3.MicrotingUid
209+
CreatedByUserId = 1,
210+
UpdatedByUserId = 1
211+
};
212+
await assignedSite3.Create(TimePlanningPnDbContext);
213+
214+
// Act
215+
var result = await _settingsService.GetAvailableSitesByCurrentUser();
216+
217+
// Assert
218+
Assert.That(result.Success, Is.True);
219+
Assert.That(result.Model, Is.Not.Null);
220+
Assert.That(result.Model.Count, Is.EqualTo(3));
221+
222+
var alice = result.Model.First(x => x.FirstName == "Alice");
223+
Assert.That(alice.PhoneNumber, Is.EqualTo("+4512345678"));
224+
Assert.That(alice.LastName, Is.EqualTo("Smith"));
225+
Assert.That(alice.Email, Is.EqualTo("alice@example.com"));
226+
227+
var bob = result.Model.First(x => x.FirstName == "Bob");
228+
Assert.That(bob.PhoneNumber, Is.EqualTo(""));
229+
Assert.That(bob.LastName, Is.EqualTo("Jones"));
230+
231+
var carol = result.Model.First(x => x.FirstName == "Carol");
232+
Assert.That(carol.PhoneNumber, Is.EqualTo("+4587654321"));
233+
Assert.That(carol.LastName, Is.EqualTo("Lee"));
234+
}
235+
236+
[Test]
237+
public async Task GetAvailableSitesByCurrentUser_PhoneComesFromWorker_NotFromUser()
238+
{
239+
// This test verifies that even when an Angular User exists with a DIFFERENT phone number,
240+
// the phone number returned comes from the SDK Worker, not the User.
241+
var core = await _coreService.GetCore();
242+
var sdkDbContext = core.DbContextHelper.GetDbContext();
243+
244+
var site = new Microting.eForm.Infrastructure.Data.Entities.Site
245+
{
246+
Name = "Site Dave",
247+
MicrotingUid = 400
248+
};
249+
await site.Create(sdkDbContext);
250+
251+
var worker = new Microting.eForm.Infrastructure.Data.Entities.Worker
252+
{
253+
FirstName = "Dave",
254+
LastName = "Brown",
255+
Email = "dave@example.com",
256+
PhoneNumber = "+45WorkerPhone",
257+
MicrotingUid = 1004
258+
};
259+
await worker.Create(sdkDbContext);
260+
261+
var siteWorker = new Microting.eForm.Infrastructure.Data.Entities.SiteWorker
262+
{
263+
SiteId = site.Id,
264+
WorkerId = worker.Id,
265+
MicrotingUid = 2004
266+
};
267+
await siteWorker.Create(sdkDbContext);
268+
269+
var unit = new Microting.eForm.Infrastructure.Data.Entities.Unit
270+
{
271+
SiteId = site.Id,
272+
MicrotingUid = 3004,
273+
CustomerNo = 4
274+
};
275+
await unit.Create(sdkDbContext);
276+
277+
var language = await sdkDbContext.Languages.FirstOrDefaultAsync();
278+
if (language == null)
279+
{
280+
language = new Microting.eForm.Infrastructure.Data.Entities.Language
281+
{
282+
LanguageCode = "da",
283+
Name = "Danish"
284+
};
285+
await language.Create(sdkDbContext);
286+
}
287+
site.LanguageId = language.Id;
288+
await site.Update(sdkDbContext);
289+
290+
var assignedSite = new AssignedSiteEntity
291+
{
292+
SiteId = 400,
293+
CreatedByUserId = 1,
294+
UpdatedByUserId = 1
295+
};
296+
await assignedSite.Create(TimePlanningPnDbContext);
297+
298+
// Note: baseDbContext is null in this test setup, so no Angular User lookup occurs.
299+
// The key assertion is that PhoneNumber comes from worker.PhoneNumber.
300+
301+
// Act
302+
var result = await _settingsService.GetAvailableSitesByCurrentUser();
303+
304+
// Assert
305+
Assert.That(result.Success, Is.True);
306+
Assert.That(result.Model.Count, Is.EqualTo(1));
307+
308+
var dave = result.Model.First();
309+
Assert.That(dave.PhoneNumber, Is.EqualTo("+45WorkerPhone"));
310+
Assert.That(dave.FirstName, Is.EqualTo("Dave"));
311+
Assert.That(dave.LastName, Is.EqualTo("Brown"));
312+
Assert.That(dave.Email, Is.EqualTo("dave@example.com"));
313+
}
314+
315+
[Test]
316+
public async Task GetAvailableSitesByCurrentUser_ExcludesResignedSites()
317+
{
318+
// Verify resigned sites are excluded from the result
319+
var core = await _coreService.GetCore();
320+
var sdkDbContext = core.DbContextHelper.GetDbContext();
321+
322+
var site = new Microting.eForm.Infrastructure.Data.Entities.Site
323+
{
324+
Name = "Site Resigned",
325+
MicrotingUid = 500
326+
};
327+
await site.Create(sdkDbContext);
328+
329+
var worker = new Microting.eForm.Infrastructure.Data.Entities.Worker
330+
{
331+
FirstName = "Eve",
332+
LastName = "Wilson",
333+
Email = "eve@example.com",
334+
PhoneNumber = "+4511111111",
335+
MicrotingUid = 1005
336+
};
337+
await worker.Create(sdkDbContext);
338+
339+
var siteWorker = new Microting.eForm.Infrastructure.Data.Entities.SiteWorker
340+
{
341+
SiteId = site.Id,
342+
WorkerId = worker.Id,
343+
MicrotingUid = 2005
344+
};
345+
await siteWorker.Create(sdkDbContext);
346+
347+
var unit = new Microting.eForm.Infrastructure.Data.Entities.Unit
348+
{
349+
SiteId = site.Id,
350+
MicrotingUid = 3005,
351+
CustomerNo = 5
352+
};
353+
await unit.Create(sdkDbContext);
354+
355+
var language = await sdkDbContext.Languages.FirstOrDefaultAsync();
356+
if (language == null)
357+
{
358+
language = new Microting.eForm.Infrastructure.Data.Entities.Language
359+
{
360+
LanguageCode = "da",
361+
Name = "Danish"
362+
};
363+
await language.Create(sdkDbContext);
364+
}
365+
site.LanguageId = language.Id;
366+
await site.Update(sdkDbContext);
367+
368+
// Create a resigned assigned site
369+
var assignedSite = new AssignedSiteEntity
370+
{
371+
SiteId = 500,
372+
Resigned = true,
373+
CreatedByUserId = 1,
374+
UpdatedByUserId = 1
375+
};
376+
await assignedSite.Create(TimePlanningPnDbContext);
377+
378+
// Act
379+
var result = await _settingsService.GetAvailableSitesByCurrentUser();
380+
381+
// Assert
382+
Assert.That(result.Success, Is.True);
383+
Assert.That(result.Model.Count, Is.EqualTo(0));
384+
}
385+
}

0 commit comments

Comments
 (0)