diff --git a/src/Api/AdminConsole/Models/Response/BaseProfileOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/BaseProfileOrganizationResponseModel.cs index 92aef5315482..76705a35ada1 100644 --- a/src/Api/AdminConsole/Models/Response/BaseProfileOrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/BaseProfileOrganizationResponseModel.cs @@ -52,6 +52,7 @@ protected BaseProfileOrganizationResponseModel( UseDisableSMAdsForUsers = organizationDetails.UseDisableSMAdsForUsers; UsePasswordManager = organizationDetails.UsePasswordManager; UseMyItems = organizationDetails.UseMyItems; + ExemptFromBillingAutomation = organizationDetails.ExemptFromBillingAutomation; SelfHost = organizationDetails.SelfHost; Seats = organizationDetails.Seats; MaxCollections = organizationDetails.MaxCollections; @@ -106,6 +107,7 @@ protected BaseProfileOrganizationResponseModel( public bool UseDisableSMAdsForUsers { get; set; } public bool UsePhishingBlocker { get; set; } public bool UseMyItems { get; set; } + public bool ExemptFromBillingAutomation { get; set; } public bool SelfHost { get; set; } public int? Seats { get; set; } public short? MaxCollections { get; set; } diff --git a/src/Core/AdminConsole/Models/Data/IProfileOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/IProfileOrganizationDetails.cs index 3552cdd35812..46e4b3ce939b 100644 --- a/src/Core/AdminConsole/Models/Data/IProfileOrganizationDetails.cs +++ b/src/Core/AdminConsole/Models/Data/IProfileOrganizationDetails.cs @@ -57,4 +57,5 @@ public interface IProfileOrganizationDetails bool UsePhishingBlocker { get; set; } bool UseMyItems { get; set; } + bool ExemptFromBillingAutomation { get; set; } } diff --git a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs index 9c86d234dc3e..49843e3051b2 100644 --- a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs @@ -73,4 +73,5 @@ public class OrganizationUserOrganizationDetails : IProfileOrganizationDetails public bool UseDisableSMAdsForUsers { get; set; } public bool UsePhishingBlocker { get; set; } public bool UseMyItems { get; set; } + public bool ExemptFromBillingAutomation { get; set; } } diff --git a/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs index fbf7322f4aa1..a2d749d5de8e 100644 --- a/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs @@ -59,4 +59,5 @@ public class ProviderUserOrganizationDetails : IProfileOrganizationDetails public bool UseDisableSMAdsForUsers { get; set; } public bool UsePhishingBlocker { get; set; } public bool UseMyItems { get; set; } + public bool ExemptFromBillingAutomation { get; set; } } diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs index a6014e15f1c3..24249e690210 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs @@ -78,7 +78,8 @@ from os in os_g.DefaultIfEmpty() UseDisableSMAdsForUsers = o.UseDisableSmAdsForUsers, UsePhishingBlocker = o.UsePhishingBlocker, UseMyItems = o.UseMyItems, - RevocationReason = ou.RevocationReason + RevocationReason = ou.RevocationReason, + ExemptFromBillingAutomation = o.ExemptFromBillingAutomation }; return query; } diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs index 62fa9949f335..b98245f1b041 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs @@ -63,7 +63,8 @@ from ss in ss_g.DefaultIfEmpty() SsoConfig = x.ss.Data, UseDisableSMAdsForUsers = x.o.UseDisableSmAdsForUsers, UsePhishingBlocker = x.o.UsePhishingBlocker, - UseMyItems = x.o.UseMyItems + UseMyItems = x.o.UseMyItems, + ExemptFromBillingAutomation = x.o.ExemptFromBillingAutomation }); } } diff --git a/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql b/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql index c7754b8ca3bd..986462e67a24 100644 --- a/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql +++ b/src/Sql/dbo/Views/OrganizationUserOrganizationDetailsView.sql @@ -59,7 +59,8 @@ SELECT O.[UsePhishingBlocker], O.[UseDisableSmAdsForUsers], O.[UseMyItems], - OU.[RevocationReason] + OU.[RevocationReason], + O.[ExemptFromBillingAutomation] FROM [dbo].[OrganizationUser] OU LEFT JOIN diff --git a/src/Sql/dbo/Views/ProviderUserProviderOrganizationDetailsView.sql b/src/Sql/dbo/Views/ProviderUserProviderOrganizationDetailsView.sql index e9adeced7078..afb86c487e6e 100644 --- a/src/Sql/dbo/Views/ProviderUserProviderOrganizationDetailsView.sql +++ b/src/Sql/dbo/Views/ProviderUserProviderOrganizationDetailsView.sql @@ -47,7 +47,8 @@ SELECT SS.[Data] SsoConfig, O.[UsePhishingBlocker], O.[UseDisableSmAdsForUsers], - O.[UseMyItems] + O.[UseMyItems], + O.[ExemptFromBillingAutomation] FROM [dbo].[ProviderUser] PU INNER JOIN diff --git a/test/Api.Test/AdminConsole/Models/Response/ProfileOrganizationResponseModelTests.cs b/test/Api.Test/AdminConsole/Models/Response/ProfileOrganizationResponseModelTests.cs index 56190947e5bd..09586e7dc6a2 100644 --- a/test/Api.Test/AdminConsole/Models/Response/ProfileOrganizationResponseModelTests.cs +++ b/test/Api.Test/AdminConsole/Models/Response/ProfileOrganizationResponseModelTests.cs @@ -53,6 +53,7 @@ public void Constructor_ShouldPopulatePropertiesCorrectly(Organization organizat UseOrganizationDomains = organization.UseOrganizationDomains, UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies, UseAutomaticUserConfirmation = organization.UseAutomaticUserConfirmation, + ExemptFromBillingAutomation = organization.ExemptFromBillingAutomation, SelfHost = organization.SelfHost, Seats = organization.Seats, MaxCollections = organization.MaxCollections, @@ -148,5 +149,6 @@ public void Constructor_ShouldPopulatePropertiesCorrectly(Organization organizat Assert.Equal(organizationDetails.FamilySponsorshipValidUntil, result.FamilySponsorshipValidUntil); Assert.True(result.IsAdminInitiated); Assert.False(result.FamilySponsorshipAvailable); + Assert.Equal(organization.ExemptFromBillingAutomation, result.ExemptFromBillingAutomation); } } diff --git a/test/Api.Test/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModelTests.cs b/test/Api.Test/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModelTests.cs index 2cb0f1273819..84b3181cf27f 100644 --- a/test/Api.Test/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModelTests.cs +++ b/test/Api.Test/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModelTests.cs @@ -50,6 +50,7 @@ public void Constructor_ShouldPopulatePropertiesCorrectly(Organization organizat UseOrganizationDomains = organization.UseOrganizationDomains, UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies, UseAutomaticUserConfirmation = organization.UseAutomaticUserConfirmation, + ExemptFromBillingAutomation = organization.ExemptFromBillingAutomation, SelfHost = organization.SelfHost, Seats = organization.Seats, MaxCollections = organization.MaxCollections, @@ -127,5 +128,6 @@ public void Constructor_ShouldPopulatePropertiesCorrectly(Organization organizat Assert.False(result.Permissions.ManageUsers); Assert.False(result.ResetPasswordEnrolled); Assert.False(result.AccessSecretsManager); + Assert.Equal(organization.ExemptFromBillingAutomation, result.ExemptFromBillingAutomation); } } diff --git a/util/Migrator/DbScripts/2026-04-23_00_AddExemptFromBillingAutomationToProviderUserView.sql b/util/Migrator/DbScripts/2026-04-23_00_AddExemptFromBillingAutomationToProviderUserView.sql new file mode 100644 index 000000000000..598a644bc6ac --- /dev/null +++ b/util/Migrator/DbScripts/2026-04-23_00_AddExemptFromBillingAutomationToProviderUserView.sql @@ -0,0 +1,141 @@ +CREATE OR ALTER VIEW [dbo].[ProviderUserProviderOrganizationDetailsView] +AS +SELECT + PU.[UserId], + PO.[OrganizationId], + O.[Name], + O.[Enabled], + O.[UsePolicies], + O.[UseSso], + O.[UseKeyConnector], + O.[UseScim], + O.[UseGroups], + O.[UseDirectory], + O.[UseEvents], + O.[UseTotp], + O.[Use2fa], + O.[UseApi], + O.[UseResetPassword], + O.[UseSecretsManager], + O.[UsePasswordManager], + O.[SelfHost], + O.[UsersGetPremium], + O.[UseCustomPermissions], + O.[Seats], + O.[MaxCollections], + COALESCE(O.[MaxStorageGbIncreased], O.[MaxStorageGb]) AS [MaxStorageGb], + O.[Identifier], + PO.[Key], + O.[PublicKey], + O.[PrivateKey], + PU.[Status], + PU.[Type], + PO.[ProviderId], + PU.[Id] ProviderUserId, + P.[Name] ProviderName, + O.[PlanType], + O.[LimitCollectionCreation], + O.[LimitCollectionDeletion], + O.[AllowAdminAccessToAllCollectionItems], + O.[UseRiskInsights], + O.[UseAdminSponsoredFamilies], + P.[Type] ProviderType, + O.[LimitItemDeletion], + O.[UseOrganizationDomains], + O.[UseAutomaticUserConfirmation], + SS.[Enabled] SsoEnabled, + SS.[Data] SsoConfig, + O.[UsePhishingBlocker], + O.[UseDisableSmAdsForUsers], + O.[UseMyItems], + O.[ExemptFromBillingAutomation] +FROM + [dbo].[ProviderUser] PU +INNER JOIN + [dbo].[ProviderOrganization] PO ON PO.[ProviderId] = PU.[ProviderId] +INNER JOIN + [dbo].[Organization] O ON O.[Id] = PO.[OrganizationId] +INNER JOIN + [dbo].[Provider] P ON P.[Id] = PU.[ProviderId] +LEFT JOIN + [dbo].[SsoConfig] SS ON SS.[OrganizationId] = O.[Id] +GO + +CREATE OR ALTER VIEW [dbo].[OrganizationUserOrganizationDetailsView] +AS +SELECT + OU.[UserId], + OU.[OrganizationId], + OU.[Id] OrganizationUserId, + O.[Name], + O.[Enabled], + O.[PlanType], + O.[UsePolicies], + O.[UseSso], + O.[UseKeyConnector], + O.[UseScim], + O.[UseGroups], + O.[UseDirectory], + O.[UseEvents], + O.[UseTotp], + O.[Use2fa], + O.[UseApi], + O.[UseResetPassword], + O.[SelfHost], + O.[UsersGetPremium], + O.[UseCustomPermissions], + O.[UseSecretsManager], + O.[Seats], + O.[MaxCollections], + COALESCE(O.[MaxStorageGbIncreased], O.[MaxStorageGb]) AS [MaxStorageGb], + O.[Identifier], + OU.[Key], + OU.[ResetPasswordKey], + O.[PublicKey], + O.[PrivateKey], + OU.[Status], + OU.[Type], + SU.[ExternalId] SsoExternalId, + OU.[Permissions], + PO.[ProviderId], + P.[Name] ProviderName, + P.[Type] ProviderType, + SS.[Enabled] SsoEnabled, + SS.[Data] SsoConfig, + OS.[FriendlyName] FamilySponsorshipFriendlyName, + OS.[LastSyncDate] FamilySponsorshipLastSyncDate, + OS.[ToDelete] FamilySponsorshipToDelete, + OS.[ValidUntil] FamilySponsorshipValidUntil, + OU.[AccessSecretsManager], + O.[UsePasswordManager], + O.[SmSeats], + O.[SmServiceAccounts], + O.[LimitCollectionCreation], + O.[LimitCollectionDeletion], + O.[AllowAdminAccessToAllCollectionItems], + O.[UseRiskInsights], + O.[LimitItemDeletion], + O.[UseAdminSponsoredFamilies], + O.[UseOrganizationDomains], + OS.[IsAdminInitiated], + O.[UseAutomaticUserConfirmation], + O.[UsePhishingBlocker], + O.[UseDisableSmAdsForUsers], + O.[UseMyItems], + OU.[RevocationReason], + O.[ExemptFromBillingAutomation] +FROM + [dbo].[OrganizationUser] OU +LEFT JOIN + [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId] +LEFT JOIN + [dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId] +LEFT JOIN + [dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id] +LEFT JOIN + [dbo].[Provider] P ON P.[Id] = PO.[ProviderId] +LEFT JOIN + [dbo].[SsoConfig] SS ON SS.[OrganizationId] = OU.[OrganizationId] +LEFT JOIN + [dbo].[OrganizationSponsorship] OS ON OS.[SponsoringOrganizationUserID] = OU.[Id] +GO