list only my billing codes
Some checks failed
Dotnet build and test / build (pull_request) Failing after 11m23s
Some checks failed
Dotnet build and test / build (pull_request) Failing after 11m23s
This commit is contained in:
parent
7c565d3af9
commit
f7e1f4a736
3 changed files with 225 additions and 90 deletions
|
|
@ -7,6 +7,7 @@ using Yavsc.Api.Test.Fixtures;
|
||||||
using Yavsc.Helpers;
|
using Yavsc.Helpers;
|
||||||
using Yavsc.Models;
|
using Yavsc.Models;
|
||||||
using Yavsc.Models.Billing;
|
using Yavsc.Models.Billing;
|
||||||
|
using Yavsc.Models.Haircut;
|
||||||
using Yavsc.Models.Workflow;
|
using Yavsc.Models.Workflow;
|
||||||
using Yavsc.Tests.Shared;
|
using Yavsc.Tests.Shared;
|
||||||
|
|
||||||
|
|
@ -154,6 +155,74 @@ VALUES
|
||||||
Assert.DoesNotContain(payload!, item => string.IsNullOrWhiteSpace(item.BillingCode));
|
Assert.DoesNotContain(payload!, item => string.IsNullOrWhiteSpace(item.BillingCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetProviderOngoingCommands_returns_haircut_and_grouped_haircut_requests()
|
||||||
|
{
|
||||||
|
WorkflowHelpers.ConfigureBillingService();
|
||||||
|
_fixture.ResetAndSeedHaircutGraph();
|
||||||
|
|
||||||
|
using (var scope = _fixture.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||||
|
db.UserActivities.Add(new UserActivity
|
||||||
|
{
|
||||||
|
UserId = "alice",
|
||||||
|
DoesCode = "brush",
|
||||||
|
Weight = 50,
|
||||||
|
});
|
||||||
|
db.UserActivities.Add(new UserActivity
|
||||||
|
{
|
||||||
|
UserId = "alice",
|
||||||
|
DoesCode = "mbrush",
|
||||||
|
Weight = 50,
|
||||||
|
});
|
||||||
|
db.CommandForm.Add(new CommandForm
|
||||||
|
{
|
||||||
|
ActivityCode = "brush",
|
||||||
|
ActionName = BillingCodes.Brush,
|
||||||
|
Title = "Brush",
|
||||||
|
});
|
||||||
|
db.CommandForm.Add(new CommandForm
|
||||||
|
{
|
||||||
|
ActivityCode = "mbrush",
|
||||||
|
ActionName = BillingCodes.MBrush,
|
||||||
|
Title = "MBrush",
|
||||||
|
});
|
||||||
|
db.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
using var http = NewClient();
|
||||||
|
|
||||||
|
var response = await http.GetAsync("/api/v1/bill/provider/ongoing", TestContext.Current.CancellationToken);
|
||||||
|
var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
|
Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}");
|
||||||
|
|
||||||
|
var payload = await response.Content.ReadFromJsonAsync<List<ProviderOngoingCommandDto>>(TestContext.Current.CancellationToken);
|
||||||
|
Assert.NotNull(payload);
|
||||||
|
Assert.Contains(payload!, item => item.BillingCode == BillingCodes.Brush && item.PerformerId == "alice");
|
||||||
|
Assert.Contains(payload!, item => item.BillingCode == BillingCodes.MBrush && item.PerformerId == "alice");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetProviderOngoingCommands_excludes_requests_outside_performer_declared_activities()
|
||||||
|
{
|
||||||
|
WorkflowHelpers.ConfigureBillingService();
|
||||||
|
_fixture.ResetAndSeedHaircutGraph();
|
||||||
|
|
||||||
|
using var http = NewClient();
|
||||||
|
|
||||||
|
var response = await http.GetAsync("/api/v1/bill/provider/ongoing", TestContext.Current.CancellationToken);
|
||||||
|
var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
|
Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}");
|
||||||
|
|
||||||
|
var payload = await response.Content.ReadFromJsonAsync<List<ProviderOngoingCommandDto>>(TestContext.Current.CancellationToken);
|
||||||
|
Assert.NotNull(payload);
|
||||||
|
Assert.DoesNotContain(payload!, item => item.BillingCode == BillingCodes.Brush);
|
||||||
|
Assert.DoesNotContain(payload!, item => item.BillingCode == BillingCodes.MBrush);
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class ProviderOngoingCommandDto
|
private sealed class ProviderOngoingCommandDto
|
||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
using System.Runtime.Loader;
|
|
||||||
using Yavsc.Controllers;
|
using Yavsc.Controllers;
|
||||||
using Yavsc.Interfaces.Workflow;
|
using Yavsc.Interfaces.Workflow;
|
||||||
using Yavsc.Models;
|
using Yavsc.Models;
|
||||||
|
|
@ -22,6 +22,7 @@ public sealed class ApiWebServerFixture : WebHostFixture
|
||||||
{
|
{
|
||||||
private const string DbProviderEnvVar = "YAVSC_API_TEST_DB_PROVIDER";
|
private const string DbProviderEnvVar = "YAVSC_API_TEST_DB_PROVIDER";
|
||||||
private const string NpgsqlAdminConnectionEnvVar = "YAVSC_API_TEST_NPGSQL_ADMIN_CONNECTION";
|
private const string NpgsqlAdminConnectionEnvVar = "YAVSC_API_TEST_NPGSQL_ADMIN_CONNECTION";
|
||||||
|
private const string DedicatedNpgsqlDatabaseName = "yavscTestDb";
|
||||||
private const string DefaultDevelopmentConnectionString = "Server=localhost;Port=5432;Database=yavscdev;Username=yavscdev;Password=8*5idas;Include Error Detail=true";
|
private const string DefaultDevelopmentConnectionString = "Server=localhost;Port=5432;Database=yavscdev;Username=yavscdev;Password=8*5idas;Include Error Detail=true";
|
||||||
|
|
||||||
protected override int HttpsPort => 5104;
|
protected override int HttpsPort => 5104;
|
||||||
|
|
@ -30,9 +31,6 @@ public sealed class ApiWebServerFixture : WebHostFixture
|
||||||
private static readonly object _sqliteLock = new();
|
private static readonly object _sqliteLock = new();
|
||||||
private static readonly object _npgsqlLock = new();
|
private static readonly object _npgsqlLock = new();
|
||||||
private static string? _sharedNpgsqlConnectionString;
|
private static string? _sharedNpgsqlConnectionString;
|
||||||
private static string? _sharedNpgsqlAdminConnectionString;
|
|
||||||
private static string? _sharedNpgsqlDatabaseName;
|
|
||||||
private static bool _npgsqlCleanupRegistered;
|
|
||||||
|
|
||||||
protected override WebApplication BuildApp(WebApplicationBuilder builder)
|
protected override WebApplication BuildApp(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
|
|
@ -126,15 +124,22 @@ public sealed class ApiWebServerFixture : WebHostFixture
|
||||||
}
|
}
|
||||||
|
|
||||||
var adminConnectionString = BuildAdminConnectionString();
|
var adminConnectionString = BuildAdminConnectionString();
|
||||||
var databaseName = $"yavsc_api_test_{Guid.NewGuid():N}";
|
var databaseName = DedicatedNpgsqlDatabaseName;
|
||||||
|
|
||||||
using (var adminConnection = new NpgsqlConnection(adminConnectionString))
|
using (var adminConnection = new NpgsqlConnection(adminConnectionString))
|
||||||
{
|
{
|
||||||
adminConnection.Open();
|
adminConnection.Open();
|
||||||
|
using var existsCommand = adminConnection.CreateCommand();
|
||||||
|
existsCommand.CommandText = "SELECT 1 FROM pg_database WHERE datname = @databaseName";
|
||||||
|
existsCommand.Parameters.AddWithValue("databaseName", databaseName);
|
||||||
|
|
||||||
|
if (existsCommand.ExecuteScalar() is null)
|
||||||
|
{
|
||||||
using var createCommand = adminConnection.CreateCommand();
|
using var createCommand = adminConnection.CreateCommand();
|
||||||
createCommand.CommandText = $"CREATE DATABASE \"{databaseName}\"";
|
createCommand.CommandText = $"CREATE DATABASE \"{databaseName}\"";
|
||||||
createCommand.ExecuteNonQuery();
|
createCommand.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var testConnectionBuilder = new NpgsqlConnectionStringBuilder(adminConnectionString)
|
var testConnectionBuilder = new NpgsqlConnectionStringBuilder(adminConnectionString)
|
||||||
{
|
{
|
||||||
|
|
@ -143,10 +148,7 @@ public sealed class ApiWebServerFixture : WebHostFixture
|
||||||
IncludeErrorDetail = true
|
IncludeErrorDetail = true
|
||||||
};
|
};
|
||||||
|
|
||||||
_sharedNpgsqlAdminConnectionString = adminConnectionString;
|
|
||||||
_sharedNpgsqlDatabaseName = databaseName;
|
|
||||||
_sharedNpgsqlConnectionString = testConnectionBuilder.ToString();
|
_sharedNpgsqlConnectionString = testConnectionBuilder.ToString();
|
||||||
RegisterNpgsqlCleanup();
|
|
||||||
return _sharedNpgsqlConnectionString;
|
return _sharedNpgsqlConnectionString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -176,61 +178,6 @@ public sealed class ApiWebServerFixture : WebHostFixture
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RegisterNpgsqlCleanup()
|
|
||||||
{
|
|
||||||
if (_npgsqlCleanupRegistered)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppDomain.CurrentDomain.ProcessExit += (_, __) => DropTemporaryNpgsqlDatabase();
|
|
||||||
AssemblyLoadContext.Default.Unloading += _ => DropTemporaryNpgsqlDatabase();
|
|
||||||
_npgsqlCleanupRegistered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DropTemporaryNpgsqlDatabase()
|
|
||||||
{
|
|
||||||
lock (_npgsqlLock)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(_sharedNpgsqlDatabaseName)
|
|
||||||
|| string.IsNullOrWhiteSpace(_sharedNpgsqlAdminConnectionString))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var adminConnection = new NpgsqlConnection(_sharedNpgsqlAdminConnectionString);
|
|
||||||
adminConnection.Open();
|
|
||||||
|
|
||||||
using (var terminateCommand = adminConnection.CreateCommand())
|
|
||||||
{
|
|
||||||
terminateCommand.CommandText = @"
|
|
||||||
SELECT pg_terminate_backend(pid)
|
|
||||||
FROM pg_stat_activity
|
|
||||||
WHERE datname = @databaseName
|
|
||||||
AND pid <> pg_backend_pid();";
|
|
||||||
terminateCommand.Parameters.AddWithValue("databaseName", _sharedNpgsqlDatabaseName);
|
|
||||||
terminateCommand.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
using var dropCommand = adminConnection.CreateCommand();
|
|
||||||
dropCommand.CommandText = $"DROP DATABASE IF EXISTS \"{_sharedNpgsqlDatabaseName}\"";
|
|
||||||
dropCommand.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Best-effort cleanup only.
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_sharedNpgsqlConnectionString = null;
|
|
||||||
_sharedNpgsqlAdminConnectionString = null;
|
|
||||||
_sharedNpgsqlDatabaseName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class NoopMessageSender : IYavscMessageSender
|
private sealed class NoopMessageSender : IYavscMessageSender
|
||||||
{
|
{
|
||||||
public Task<MessageWithPayloadResponse> NotifyBookQueryAsync(IEnumerable<string> connectionIds, RdvQueryEvent ev)
|
public Task<MessageWithPayloadResponse> NotifyBookQueryAsync(IEnumerable<string> connectionIds, RdvQueryEvent ev)
|
||||||
|
|
@ -251,7 +198,7 @@ WHERE datname = @databaseName
|
||||||
using var scope = Services.CreateScope();
|
using var scope = Services.CreateScope();
|
||||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||||
|
|
||||||
db.Database.EnsureDeleted();
|
ResetDatabase(db);
|
||||||
db.Database.EnsureCreated();
|
db.Database.EnsureCreated();
|
||||||
|
|
||||||
var user = new ApplicationUser
|
var user = new ApplicationUser
|
||||||
|
|
@ -352,6 +299,94 @@ WHERE datname = @databaseName
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ResetDatabase(ApplicationDbContext db)
|
||||||
|
{
|
||||||
|
if (UseNpgsqlProvider())
|
||||||
|
{
|
||||||
|
db.Set<UserActivity>().RemoveRange(db.Set<UserActivity>());
|
||||||
|
db.Set<Activity>().RemoveRange(db.Set<Activity>());
|
||||||
|
db.Set<PerformerProfile>().RemoveRange(db.Set<PerformerProfile>());
|
||||||
|
db.Set<ApplicationUser>().RemoveRange(db.Set<ApplicationUser>());
|
||||||
|
db.Set<Location>().RemoveRange(db.Set<Location>());
|
||||||
|
db.Set<RdvQuery>().RemoveRange(db.Set<RdvQuery>());
|
||||||
|
db.Set<HairCutQuery>().RemoveRange(db.Set<HairCutQuery>());
|
||||||
|
db.Set<HairMultiCutQuery>().RemoveRange(db.Set<HairMultiCutQuery>());
|
||||||
|
db.Set<HairPrestation>().RemoveRange(db.Set<HairPrestation>());
|
||||||
|
db.Set<HairPrestationCollectionItem>().RemoveRange(db.Set<HairPrestationCollectionItem>());
|
||||||
|
|
||||||
|
db.SaveChanges();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Database.EnsureDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static IReadOnlyList<IEntityType> GetDeletionOrder(IModel model)
|
||||||
|
{
|
||||||
|
var entityTypes = model
|
||||||
|
.GetEntityTypes()
|
||||||
|
.Where(et =>
|
||||||
|
et.ClrType is not null &&
|
||||||
|
!et.IsOwned() &&
|
||||||
|
et.FindPrimaryKey() is not null)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var included = new HashSet<IEntityType>(entityTypes);
|
||||||
|
var dependencies = new Dictionary<IEntityType, HashSet<IEntityType>>();
|
||||||
|
|
||||||
|
foreach (var entityType in entityTypes)
|
||||||
|
{
|
||||||
|
var principals = entityType
|
||||||
|
.GetForeignKeys()
|
||||||
|
.Where(fk => !fk.IsOwnership)
|
||||||
|
.Select(fk => fk.PrincipalEntityType)
|
||||||
|
.Where(included.Contains)
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
|
dependencies[entityType] = principals;
|
||||||
|
}
|
||||||
|
|
||||||
|
var queue = new Queue<IEntityType>(
|
||||||
|
dependencies.Where(kvp => kvp.Value.Count == 0).Select(kvp => kvp.Key));
|
||||||
|
|
||||||
|
var order = new List<IEntityType>(entityTypes.Length);
|
||||||
|
|
||||||
|
while (queue.Count > 0)
|
||||||
|
{
|
||||||
|
var current = queue.Dequeue();
|
||||||
|
if (!order.Contains(current))
|
||||||
|
{
|
||||||
|
order.Add(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var kvp in dependencies)
|
||||||
|
{
|
||||||
|
if (!kvp.Value.Remove(current) || kvp.Value.Count != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!order.Contains(kvp.Key) && !queue.Contains(kvp.Key))
|
||||||
|
{
|
||||||
|
queue.Enqueue(kvp.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If cycles remain (rare), append unresolved types last and rely on DB cascades.
|
||||||
|
foreach (var entityType in entityTypes)
|
||||||
|
{
|
||||||
|
if (!order.Contains(entityType))
|
||||||
|
{
|
||||||
|
order.Add(entityType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
public void ResetAndSeedRdvQueryGraph()
|
public void ResetAndSeedRdvQueryGraph()
|
||||||
{
|
{
|
||||||
ResetAndSeedActivityGraph();
|
ResetAndSeedActivityGraph();
|
||||||
|
|
|
||||||
|
|
@ -123,34 +123,65 @@ namespace Yavsc.ApiControllers
|
||||||
WorkflowHelpers.ConfigureBillingService();
|
WorkflowHelpers.ConfigureBillingService();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query known derived types explicitly so legacy rows with
|
var allowedActivityCodes = dbContext.UserActivities
|
||||||
// invalid/empty discriminator values are naturally ignored.
|
|
||||||
var rdvCommands = dbContext.Set<RdvQuery>()
|
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(q => q.PerformerId == uid)
|
.Where(a => a.UserId == uid)
|
||||||
.Where(q => q.Status == QueryStatus.Inserted
|
.Select(a => a.DoesCode)
|
||||||
|| q.Status == QueryStatus.Accepted
|
.Distinct()
|
||||||
|| q.Status == QueryStatus.InProgress)
|
|
||||||
.Cast<NominativeServiceCommand>()
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var hairCommands = dbContext.Set<HairCutQuery>()
|
if (allowedActivityCodes.Count == 0)
|
||||||
.AsNoTracking()
|
{
|
||||||
.Where(q => q.PerformerId == uid)
|
return Ok(Array.Empty<object>());
|
||||||
.Where(q => q.Status == QueryStatus.Inserted
|
}
|
||||||
|| q.Status == QueryStatus.Accepted
|
|
||||||
|| q.Status == QueryStatus.InProgress)
|
|
||||||
.Cast<NominativeServiceCommand>()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var hairMultiCommands = dbContext.Set<HairMultiCutQuery>()
|
var allowedBillingCodes = dbContext.CommandForm
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(form => allowedActivityCodes.Contains(form.ActivityCode))
|
||||||
|
.Select(form => form.ActionName)
|
||||||
|
.Where(actionName => !string.IsNullOrWhiteSpace(actionName))
|
||||||
|
.Distinct()
|
||||||
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
var fallbackToActivityFilteringOnly = allowedBillingCodes.Count == 0;
|
||||||
|
|
||||||
|
// Query only the command types allowed by the performer's declared
|
||||||
|
// activities; this avoids touching unrelated legacy slices.
|
||||||
|
var rdvCommands = fallbackToActivityFilteringOnly || allowedBillingCodes.Contains(BillingCodes.Rdv)
|
||||||
|
? dbContext.Set<RdvQuery>()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(q => q.PerformerId == uid)
|
.Where(q => q.PerformerId == uid)
|
||||||
|
.Where(q => allowedActivityCodes.Contains(q.ActivityCode))
|
||||||
.Where(q => q.Status == QueryStatus.Inserted
|
.Where(q => q.Status == QueryStatus.Inserted
|
||||||
|| q.Status == QueryStatus.Accepted
|
|| q.Status == QueryStatus.Accepted
|
||||||
|| q.Status == QueryStatus.InProgress)
|
|| q.Status == QueryStatus.InProgress)
|
||||||
.Cast<NominativeServiceCommand>()
|
.Cast<NominativeServiceCommand>()
|
||||||
.ToList();
|
.ToList()
|
||||||
|
: new List<NominativeServiceCommand>();
|
||||||
|
|
||||||
|
var hairCommands = fallbackToActivityFilteringOnly || allowedBillingCodes.Contains(BillingCodes.Brush)
|
||||||
|
? dbContext.Set<HairCutQuery>()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(q => q.PerformerId == uid)
|
||||||
|
.Where(q => allowedActivityCodes.Contains(q.ActivityCode))
|
||||||
|
.Where(q => q.Status == QueryStatus.Inserted
|
||||||
|
|| q.Status == QueryStatus.Accepted
|
||||||
|
|| q.Status == QueryStatus.InProgress)
|
||||||
|
.Cast<NominativeServiceCommand>()
|
||||||
|
.ToList()
|
||||||
|
: new List<NominativeServiceCommand>();
|
||||||
|
|
||||||
|
var hairMultiCommands = fallbackToActivityFilteringOnly || allowedBillingCodes.Contains(BillingCodes.MBrush)
|
||||||
|
? dbContext.Set<HairMultiCutQuery>()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(q => q.PerformerId == uid)
|
||||||
|
.Where(q => allowedActivityCodes.Contains(q.ActivityCode))
|
||||||
|
.Where(q => q.Status == QueryStatus.Inserted
|
||||||
|
|| q.Status == QueryStatus.Accepted
|
||||||
|
|| q.Status == QueryStatus.InProgress)
|
||||||
|
.Cast<NominativeServiceCommand>()
|
||||||
|
.ToList()
|
||||||
|
: new List<NominativeServiceCommand>();
|
||||||
|
|
||||||
var commands = rdvCommands
|
var commands = rdvCommands
|
||||||
.Concat(hairCommands)
|
.Concat(hairCommands)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue