diff --git a/src/Core/Enums/TransactionType.cs b/src/Core/Enums/TransactionType.cs new file mode 100644 index 0000000000..40ec69f31e --- /dev/null +++ b/src/Core/Enums/TransactionType.cs @@ -0,0 +1,10 @@ +namespace Bit.Core.Enums +{ + public enum TransactionType : byte + { + Charge = 0, + Credit = 1, + PromotionalCredit = 2, + ReferralCredit = 3 + } +} diff --git a/src/Core/Models/Table/Transaction.cs b/src/Core/Models/Table/Transaction.cs new file mode 100644 index 0000000000..2e6e6c6d91 --- /dev/null +++ b/src/Core/Models/Table/Transaction.cs @@ -0,0 +1,27 @@ +using System; +using Bit.Core.Enums; +using Bit.Core.Utilities; + +namespace Bit.Core.Models.Table +{ + public class Transaction : ITableObject + { + public Guid Id { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public TransactionType Type { get; set; } + public decimal Amount { get; set; } + public bool? Refunded { get; set; } + public decimal? RefundedAmount { get; set; } + public string Details { get; set; } + public PaymentMethodType? PaymentMethodType { get; set; } + public GatewayType? Gateway { get; set; } + public string GatewayId { get; set; } + public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; + + public void SetNewId() + { + Id = CoreHelpers.GenerateComb(); + } + } +} diff --git a/src/Core/Repositories/ITransactionRepository.cs b/src/Core/Repositories/ITransactionRepository.cs new file mode 100644 index 0000000000..d1248cbe86 --- /dev/null +++ b/src/Core/Repositories/ITransactionRepository.cs @@ -0,0 +1,13 @@ +using System; +using Bit.Core.Models.Table; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Bit.Core.Repositories +{ + public interface ITransactionRepository : IRepository + { + Task> GetManyByUserIdAsync(Guid userId); + Task> GetManyByOrganizationIdAsync(Guid organizationId); + } +} diff --git a/src/Core/Repositories/SqlServer/TransactionRepository.cs b/src/Core/Repositories/SqlServer/TransactionRepository.cs new file mode 100644 index 0000000000..0299c8288b --- /dev/null +++ b/src/Core/Repositories/SqlServer/TransactionRepository.cs @@ -0,0 +1,48 @@ +using System; +using Bit.Core.Models.Table; +using System.Collections.Generic; +using System.Threading.Tasks; +using Dapper; +using System.Data; +using System.Data.SqlClient; +using System.Linq; + +namespace Bit.Core.Repositories.SqlServer +{ + public class TransactionRepository : Repository, ITransactionRepository + { + public TransactionRepository(GlobalSettings globalSettings) + : this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) + { } + + public TransactionRepository(string connectionString, string readOnlyConnectionString) + : base(connectionString, readOnlyConnectionString) + { } + + public async Task> GetManyByUserIdAsync(Guid userId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[Transaction_ReadByUserId]", + new { UserId = userId }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + + public async Task> GetManyByOrganizationIdAsync(Guid organizationId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[Transaction_ReadByOrganizationId]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + } +} diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index 6dac10bb0c..2de1ba284d 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -52,6 +52,7 @@ namespace Bit.Core.Utilities services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); } if(globalSettings.SelfHosted) diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 95bfe03d42..34fa0f76da 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -240,5 +240,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Transaction_Create.sql b/src/Sql/dbo/Stored Procedures/Transaction_Create.sql new file mode 100644 index 0000000000..0f9efba271 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Transaction_Create.sql @@ -0,0 +1,48 @@ +CREATE PROCEDURE [dbo].[Transaction_Create] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Amount MONEY, + @Refunded BIT, + @RefundedAmount MONEY, + @Details NVARCHAR(100), + @PaymentMethodType TINYINT, + @Gateway TINYINT, + @GatewayId VARCHAR(50), + @CreationDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Transaction] + ( + [Id], + [UserId], + [OrganizationId], + [Type], + [Amount], + [Refunded], + [RefundedAmount], + [Details], + [PaymentMethodType], + [Gateway], + [GatewayId], + [CreationDate] + ) + VALUES + ( + @Id, + @UserId, + @OrganizationId, + @Type, + @Amount, + @Refunded, + @RefundedAmount, + @Details, + @PaymentMethodType, + @Gateway, + @GatewayId, + @CreationDate + ) +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Transaction_DeleteById.sql b/src/Sql/dbo/Stored Procedures/Transaction_DeleteById.sql new file mode 100644 index 0000000000..9bf87ef995 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Transaction_DeleteById.sql @@ -0,0 +1,12 @@ +CREATE PROCEDURE [dbo].[Transaction_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DELETE + FROM + [dbo].[Transaction] + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Transaction_ReadById.sql b/src/Sql/dbo/Stored Procedures/Transaction_ReadById.sql new file mode 100644 index 0000000000..c4426ebc27 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Transaction_ReadById.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[Transaction_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[TransactionView] + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Transaction_ReadByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/Transaction_ReadByOrganizationId.sql new file mode 100644 index 0000000000..d15c7603e1 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Transaction_ReadByOrganizationId.sql @@ -0,0 +1,14 @@ +CREATE PROCEDURE [dbo].[Transaction_ReadByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[TransactionView] + WHERE + [UserId] = NULL + AND [OrganizationId] = @OrganizationId +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Transaction_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/Transaction_ReadByUserId.sql new file mode 100644 index 0000000000..5a76fd8d07 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Transaction_ReadByUserId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[Transaction_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[TransactionView] + WHERE + [UserId] = @UserId +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Transaction_Update.sql b/src/Sql/dbo/Stored Procedures/Transaction_Update.sql new file mode 100644 index 0000000000..fc2ac67659 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Transaction_Update.sql @@ -0,0 +1,34 @@ +CREATE PROCEDURE [dbo].[Transaction_Update] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Amount MONEY, + @Refunded BIT, + @RefundedAmount MONEY, + @Details NVARCHAR(100), + @PaymentMethodType TINYINT, + @Gateway TINYINT, + @GatewayId VARCHAR(50), + @CreationDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[Transaction] + SET + [UserId] = @UserId, + [OrganizationId] = @OrganizationId, + [Type] = @Type, + [Amount] = @Amount, + [Refunded] = @Refunded, + [RefundedAmount] = @RefundedAmount, + [Details] = @Details, + [PaymentMethodType] = @PaymentMethodType, + [Gateway] = @Gateway, + [GatewayId] = @GatewayId, + [CreationDate] = @CreationDate + WHERE + [Id] = @Id +END \ No newline at end of file diff --git a/src/Sql/dbo/Tables/Transaction.sql b/src/Sql/dbo/Tables/Transaction.sql new file mode 100644 index 0000000000..6f30cf6b39 --- /dev/null +++ b/src/Sql/dbo/Tables/Transaction.sql @@ -0,0 +1,28 @@ +CREATE TABLE [dbo].[Transaction] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [UserId] UNIQUEIDENTIFIER NULL, + [OrganizationId] UNIQUEIDENTIFIER NULL, + [Type] TINYINT NOT NULL, + [Amount] MONEY NOT NULL, + [Refunded] BIT NULL, + [RefundedAmount] MONEY NULL, + [Details] NVARCHAR(100) NULL, + [PaymentMethodType] TINYINT NULL, + [Gateway] TINYINT NULL, + [GatewayId] VARCHAR(50) NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + CONSTRAINT [PK_Transaction] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_Transaction_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE, + CONSTRAINT [FK_Transaction_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE +); + + +GO +CREATE NONCLUSTERED INDEX [IX_Transaction_Gateway_GatewayId] + ON [dbo].[Transaction]([Gateway] ASC, [GatewayId] ASC); + + +GO +CREATE NONCLUSTERED INDEX [IX_Transaction_UserId_OrganizationId_CreationDate] + ON [dbo].[Transaction]([UserId] ASC, [OrganizationId] ASC, [CreationDate] ASC); + diff --git a/src/Sql/dbo/Views/TransactionView.sql b/src/Sql/dbo/Views/TransactionView.sql new file mode 100644 index 0000000000..e81127d10d --- /dev/null +++ b/src/Sql/dbo/Views/TransactionView.sql @@ -0,0 +1,6 @@ +CREATE VIEW [dbo].[TransactionView] +AS +SELECT + * +FROM + [dbo].[Transaction] diff --git a/util/Setup/DbScripts/2019-01-31_00_Transactions.sql b/util/Setup/DbScripts/2019-01-31_00_Transactions.sql new file mode 100644 index 0000000000..6e864c80f0 --- /dev/null +++ b/util/Setup/DbScripts/2019-01-31_00_Transactions.sql @@ -0,0 +1,224 @@ +IF OBJECT_ID('[dbo].[Transaction]') IS NULL +BEGIN + CREATE TABLE [dbo].[Transaction] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [UserId] UNIQUEIDENTIFIER NULL, + [OrganizationId] UNIQUEIDENTIFIER NULL, + [Type] TINYINT NOT NULL, + [Amount] MONEY NOT NULL, + [Refunded] BIT NULL, + [RefundedAmount] MONEY NULL, + [Details] NVARCHAR(100) NULL, + [PaymentMethodType] TINYINT NULL, + [Gateway] TINYINT NULL, + [GatewayId] VARCHAR(50) NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + CONSTRAINT [PK_Transaction] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_Transaction_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE, + CONSTRAINT [FK_Transaction_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE + ); + + CREATE NONCLUSTERED INDEX [IX_Transaction_Gateway_GatewayId] + ON [dbo].[Transaction]([Gateway] ASC, [GatewayId] ASC); + + + CREATE NONCLUSTERED INDEX [IX_Transaction_UserId_OrganizationId_CreationDate] + ON [dbo].[Transaction]([UserId] ASC, [OrganizationId] ASC, [CreationDate] ASC); +END +GO + +IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'TransactionView') +BEGIN + DROP VIEW [dbo].[TransactionView] +END +GO + +CREATE VIEW [dbo].[TransactionView] +AS +SELECT + * +FROM + [dbo].[Transaction] +GO + +IF OBJECT_ID('[dbo].[Transaction_Create]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Transaction_Create] +END +GO + +CREATE PROCEDURE [dbo].[Transaction_Create] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Amount MONEY, + @Refunded BIT, + @RefundedAmount MONEY, + @Details NVARCHAR(100), + @PaymentMethodType TINYINT, + @Gateway TINYINT, + @GatewayId VARCHAR(50), + @CreationDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[Transaction] + ( + [Id], + [UserId], + [OrganizationId], + [Type], + [Amount], + [Refunded], + [RefundedAmount], + [Details], + [PaymentMethodType], + [Gateway], + [GatewayId], + [CreationDate] + ) + VALUES + ( + @Id, + @UserId, + @OrganizationId, + @Type, + @Amount, + @Refunded, + @RefundedAmount, + @Details, + @PaymentMethodType, + @Gateway, + @GatewayId, + @CreationDate + ) +END +GO + +IF OBJECT_ID('[dbo].[Transaction_DeleteById]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Transaction_DeleteById] +END +GO + +CREATE PROCEDURE [dbo].[Transaction_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + DELETE + FROM + [dbo].[Transaction] + WHERE + [Id] = @Id +END +GO + +IF OBJECT_ID('[dbo].[Transaction_ReadById]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Transaction_ReadById] +END +GO + +CREATE PROCEDURE [dbo].[Transaction_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[TransactionView] + WHERE + [Id] = @Id +END +GO + +IF OBJECT_ID('[dbo].[Transaction_ReadByOrganizationId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Transaction_ReadByOrganizationId] +END +GO + +CREATE PROCEDURE [dbo].[Transaction_ReadByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[TransactionView] + WHERE + [UserId] = NULL + AND [OrganizationId] = @OrganizationId +END +GO + +IF OBJECT_ID('[dbo].[Transaction_ReadByUserId]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Transaction_ReadByUserId] +END +GO + +CREATE PROCEDURE [dbo].[Transaction_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[TransactionView] + WHERE + [UserId] = @UserId +END +GO + +IF OBJECT_ID('[dbo].[Transaction_Update]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[Transaction_Update] +END +GO + +CREATE PROCEDURE [dbo].[Transaction_Update] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @Type TINYINT, + @Amount MONEY, + @Refunded BIT, + @RefundedAmount MONEY, + @Details NVARCHAR(100), + @PaymentMethodType TINYINT, + @Gateway TINYINT, + @GatewayId VARCHAR(50), + @CreationDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[Transaction] + SET + [UserId] = @UserId, + [OrganizationId] = @OrganizationId, + [Type] = @Type, + [Amount] = @Amount, + [Refunded] = @Refunded, + [RefundedAmount] = @RefundedAmount, + [Details] = @Details, + [PaymentMethodType] = @PaymentMethodType, + [Gateway] = @Gateway, + [GatewayId] = @GatewayId, + [CreationDate] = @CreationDate + WHERE + [Id] = @Id +END +GO diff --git a/util/Setup/Setup.csproj b/util/Setup/Setup.csproj index d48a268697..d8a77b63e1 100644 --- a/util/Setup/Setup.csproj +++ b/util/Setup/Setup.csproj @@ -16,6 +16,7 @@ +