From 0565df1933cca4a8e01f65253b5505a24e30a952 Mon Sep 17 00:00:00 2001 From: abdurrohmanq Date: Fri, 10 Nov 2023 16:03:03 +0500 Subject: [PATCH 1/4] Fix errors --- MedX.sln | 8 +- src/MedX.Data/Contexts/AppDbContext.cs | 4 + ...231105130319_InitialMigration.Designer.cs} | 23 +- ....cs => 20231105130319_InitialMigration.cs} | 59 +- ...6075958_AddedDateTimeConfigure.Designer.cs | 680 ++++++++++++++++++ .../20231106075958_AddedDateTimeConfigure.cs | 35 + .../Migrations/AppDbContextModelSnapshot.cs | 21 +- src/MedX.Domain/Entities/CashDesk.cs | 6 +- src/MedX.Domain/Entities/Rooms/Room.cs | 9 +- .../DTOs/CashDesks/CashDeskBalanceDto.cs | 6 + .../DTOs/CashDesks/CashDeskCreationDto.cs | 4 +- .../DTOs/CashDesks/CashDeskResultDto.cs | 5 +- .../DTOs/Patients/PatientResultDto.cs | 13 +- .../DTOs/Payments/PaymentCreationDto.cs | 1 + .../DTOs/Payments/PaymentResultDto.cs | 1 - .../DTOs/Payments/PaymentUpdateDto.cs | 1 + .../DTOs/Rooms/RoomCreationDto.cs | 2 +- .../Interfaces/ICashDeskService.cs | 3 + src/MedX.Service/Services/AssetService.cs | 20 +- src/MedX.Service/Services/CashDeskService.cs | 62 +- .../Services/MedicalRecordService.cs | 1 + src/MedX.Service/Services/PatientService.cs | 21 +- src/MedX.Service/Services/PaymentService.cs | 41 +- src/MedX.Service/Services/RoomService.cs | 1 + .../Controllers/AffairItemsController.cs | 8 +- .../Controllers/AffairsController.cs | 4 +- .../Controllers/AppointmentsController.cs | 8 +- .../Controllers/CashDeskController.cs | 25 + .../Controllers/DoctorsController.cs | 4 +- .../Controllers/MedicalRecordsControllers.cs | 8 +- .../Controllers/PatientsController.cs | 4 +- .../Controllers/PaymentsController.cs | 8 +- .../Controllers/RoomsController.cs | 4 +- .../Controllers/TreatmentsController.cs | 4 +- src/MedX.WebApi/Dockerfile | 12 + .../Extensions/ServiceCollection.cs | 1 + src/MedX.WebApi/MedX.WebApi.csproj | 2 - src/MedX.WebApi/Program.cs | 9 +- src/MedX.WebApi/appsettings.json | 17 - .../3551c01e8f2d492796dc5b858e8ffbce.jpg | Bin 0 -> 88304 bytes .../418ab6e3235b4052bec27475a0b10421.jpg | Bin 0 -> 147038 bytes ...g => 4bd2d9e758d3423c9a5e5f9e602cced0.jpg} | Bin 362595 -> 725190 bytes .../7ccab2c9d46747078b0da11fefb11cfc.jpg | Bin 169356 -> 0 bytes .../803d66e7d15d4a899758c16342a70b6d.jpg | Bin 0 -> 126360 bytes .../c10eadc64be243f0ba9b784dd74318f7.jpeg | Bin 0 -> 5943 bytes .../c20d66cb665649e59150882d2d66aced.jpg | Bin 362595 -> 0 bytes .../ee85ab880fc64edc99560d79fbb7ce0d.jpg | Bin 0 -> 252720 bytes 47 files changed, 1019 insertions(+), 126 deletions(-) rename src/MedX.Data/Migrations/{20231001081343_InitialMig.Designer.cs => 20231105130319_InitialMigration.Designer.cs} (96%) rename src/MedX.Data/Migrations/{20231001081343_InitialMig.cs => 20231105130319_InitialMigration.cs} (96%) create mode 100644 src/MedX.Data/Migrations/20231106075958_AddedDateTimeConfigure.Designer.cs create mode 100644 src/MedX.Data/Migrations/20231106075958_AddedDateTimeConfigure.cs create mode 100644 src/MedX.Service/DTOs/CashDesks/CashDeskBalanceDto.cs create mode 100644 src/MedX.WebApi/Controllers/CashDeskController.cs create mode 100644 src/MedX.WebApi/Dockerfile create mode 100644 src/MedX.WebApi/wwwroot/Images/3551c01e8f2d492796dc5b858e8ffbce.jpg create mode 100644 src/MedX.WebApi/wwwroot/Images/418ab6e3235b4052bec27475a0b10421.jpg rename src/MedX.WebApi/wwwroot/Images/{e7f84128516649938389bbe47a792b9a.jpg => 4bd2d9e758d3423c9a5e5f9e602cced0.jpg} (50%) delete mode 100644 src/MedX.WebApi/wwwroot/Images/7ccab2c9d46747078b0da11fefb11cfc.jpg create mode 100644 src/MedX.WebApi/wwwroot/Images/803d66e7d15d4a899758c16342a70b6d.jpg create mode 100644 src/MedX.WebApi/wwwroot/Images/c10eadc64be243f0ba9b784dd74318f7.jpeg delete mode 100644 src/MedX.WebApi/wwwroot/Images/c20d66cb665649e59150882d2d66aced.jpg create mode 100644 src/MedX.WebApi/wwwroot/Images/ee85ab880fc64edc99560d79fbb7ce0d.jpg diff --git a/MedX.sln b/MedX.sln index 1bea583..23b9b95 100644 --- a/MedX.sln +++ b/MedX.sln @@ -5,13 +5,13 @@ VisualStudioVersion = 17.7.34003.232 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{681E78B2-785A-4771-B7B9-9BC2D72A8856}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MedX.WebApi", "src\MedX.WebApi\MedX.WebApi.csproj", "{2D234F20-3EA8-4541-8772-BE8B66D1D648}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MedX.WebApi", "src\MedX.WebApi\MedX.WebApi.csproj", "{2D234F20-3EA8-4541-8772-BE8B66D1D648}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MedX.Data", "src\MedX.Data\MedX.Data.csproj", "{2A5CFE2B-E743-4F9C-8BFF-416391A2F023}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MedX.Data", "src\MedX.Data\MedX.Data.csproj", "{2A5CFE2B-E743-4F9C-8BFF-416391A2F023}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MedX.Service", "src\MedX.Service\MedX.Service.csproj", "{FF5939B4-5925-40E4-8FB1-C40A6967FB9D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MedX.Service", "src\MedX.Service\MedX.Service.csproj", "{FF5939B4-5925-40E4-8FB1-C40A6967FB9D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MedX.Domain", "src\MedX.Domain\MedX.Domain.csproj", "{00ED443E-1ACF-4F62-A9AB-D976AE39327C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MedX.Domain", "src\MedX.Domain\MedX.Domain.csproj", "{00ED443E-1ACF-4F62-A9AB-D976AE39327C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/MedX.Data/Contexts/AppDbContext.cs b/src/MedX.Data/Contexts/AppDbContext.cs index 90f7586..b381bf6 100644 --- a/src/MedX.Data/Contexts/AppDbContext.cs +++ b/src/MedX.Data/Contexts/AppDbContext.cs @@ -44,6 +44,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #region Fluent API base.OnModelCreating(modelBuilder); + modelBuilder.Entity() + .Property(u => u.DateOfBirth) + .HasColumnType("timestamp"); + modelBuilder.Entity() .HasOne(d => d.Doctor) .WithMany(a => a.Appointments) diff --git a/src/MedX.Data/Migrations/20231001081343_InitialMig.Designer.cs b/src/MedX.Data/Migrations/20231105130319_InitialMigration.Designer.cs similarity index 96% rename from src/MedX.Data/Migrations/20231001081343_InitialMig.Designer.cs rename to src/MedX.Data/Migrations/20231105130319_InitialMigration.Designer.cs index 4de90dc..8fa1afe 100644 --- a/src/MedX.Data/Migrations/20231001081343_InitialMig.Designer.cs +++ b/src/MedX.Data/Migrations/20231105130319_InitialMigration.Designer.cs @@ -12,8 +12,8 @@ namespace MedX.Data.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20231001081343_InitialMig")] - partial class InitialMig + [Migration("20231105130319_InitialMigration")] + partial class InitialMigration { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -144,6 +144,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("AccountNumber") .HasColumnType("text"); + b.Property("Amount") + .HasColumnType("numeric"); + b.Property("Balance") .HasColumnType("numeric"); @@ -159,11 +162,16 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("IsIncome") .HasColumnType("boolean"); + b.Property("PaymentId") + .HasColumnType("bigint"); + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); + b.HasIndex("PaymentId"); + b.ToTable("CashDesks"); }); @@ -528,6 +536,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("Patient"); }); + modelBuilder.Entity("MedX.Domain.Entities.CashDesk", b => + { + b.HasOne("MedX.Domain.Entities.Payment", "Payment") + .WithMany() + .HasForeignKey("PaymentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Payment"); + }); + modelBuilder.Entity("MedX.Domain.Entities.Doctor", b => { b.HasOne("MedX.Domain.Entities.Assets.Asset", "Image") diff --git a/src/MedX.Data/Migrations/20231001081343_InitialMig.cs b/src/MedX.Data/Migrations/20231105130319_InitialMigration.cs similarity index 96% rename from src/MedX.Data/Migrations/20231001081343_InitialMig.cs rename to src/MedX.Data/Migrations/20231105130319_InitialMigration.cs index aac25e4..5ea1c92 100644 --- a/src/MedX.Data/Migrations/20231001081343_InitialMig.cs +++ b/src/MedX.Data/Migrations/20231105130319_InitialMigration.cs @@ -7,7 +7,7 @@ namespace MedX.Data.Migrations { /// - public partial class InitialMig : Migration + public partial class InitialMigration : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -47,25 +47,6 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_Asset", x => x.Id); }); - migrationBuilder.CreateTable( - name: "CashDesks", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Description = table.Column(type: "text", nullable: true), - Balance = table.Column(type: "numeric", nullable: false), - AccountNumber = table.Column(type: "text", nullable: true), - IsIncome = table.Column(type: "boolean", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), - IsDeleted = table.Column(type: "boolean", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CashDesks", x => x.Id); - }); - migrationBuilder.CreateTable( name: "Administrators", columns: table => new @@ -333,6 +314,33 @@ protected override void Up(MigrationBuilder migrationBuilder) onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "CashDesks", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Amount = table.Column(type: "numeric", nullable: true), + AccountNumber = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Balance = table.Column(type: "numeric", nullable: false), + IsIncome = table.Column(type: "boolean", nullable: false), + PaymentId = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + IsDeleted = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CashDesks", x => x.Id); + table.ForeignKey( + name: "FK_CashDesks_Payments_PaymentId", + column: x => x.PaymentId, + principalTable: "Payments", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateIndex( name: "IX_Administrators_ImageId", table: "Administrators", @@ -358,6 +366,11 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "Appointments", column: "PatientId"); + migrationBuilder.CreateIndex( + name: "IX_CashDesks_PaymentId", + table: "CashDesks", + column: "PaymentId"); + migrationBuilder.CreateIndex( name: "IX_Doctors_ImageId", table: "Doctors", @@ -422,15 +435,15 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "MedicalRecords"); - migrationBuilder.DropTable( - name: "Payments"); - migrationBuilder.DropTable( name: "Treatments"); migrationBuilder.DropTable( name: "Affairs"); + migrationBuilder.DropTable( + name: "Payments"); + migrationBuilder.DropTable( name: "Doctors"); diff --git a/src/MedX.Data/Migrations/20231106075958_AddedDateTimeConfigure.Designer.cs b/src/MedX.Data/Migrations/20231106075958_AddedDateTimeConfigure.Designer.cs new file mode 100644 index 0000000..3e3b888 --- /dev/null +++ b/src/MedX.Data/Migrations/20231106075958_AddedDateTimeConfigure.Designer.cs @@ -0,0 +1,680 @@ +// +using System; +using MedX.Data.Contexts; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace MedX.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20231106075958_AddedDateTimeConfigure")] + partial class AddedDateTimeConfigure + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MedX.Domain.Entities.Administrators.Administrator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountNumber") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("ImageId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("Password") + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("Administrators"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Appointments.Appointment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DoctorId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("PatientId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DoctorId"); + + b.HasIndex("PatientId"); + + b.ToTable("Appointments"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Assets.Asset", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FileName") + .HasColumnType("text"); + + b.Property("FilePath") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Asset"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.CashDesk", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountNumber") + .HasColumnType("text"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsIncome") + .HasColumnType("boolean"); + + b.Property("PaymentId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("PaymentId"); + + b.ToTable("CashDesks"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Doctor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountNumber") + .HasColumnType("text"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("ImageId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("Patronymic") + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("Professional") + .HasColumnType("text"); + + b.Property("RoomNumber") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("Doctors"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.MedicalRecords.MedicalRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Disease") + .HasColumnType("text"); + + b.Property("DoctorId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("PatientId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DoctorId"); + + b.HasIndex("PatientId"); + + b.ToTable("MedicalRecords"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Patient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountNumber") + .HasColumnType("text"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfBirth") + .HasColumnType("timestamp"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("Patronymic") + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("Pinfl") + .HasColumnType("text"); + + b.Property("RoomId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("RoomId"); + + b.ToTable("Patients"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Payment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("PatientId") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("PatientId"); + + b.ToTable("Payments"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Room", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Busy") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("ImageId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("Rooms"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Services.Affair", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("numeric"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Affairs"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Services.AffairItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AffairId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("PatientId") + .HasColumnType("bigint"); + + b.Property("Quantity") + .HasColumnType("real"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AffairId"); + + b.HasIndex("PatientId"); + + b.ToTable("AffairItems"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Treatment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DoctorId") + .HasColumnType("bigint"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("PatientId") + .HasColumnType("bigint"); + + b.Property("RoomId") + .HasColumnType("bigint"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DoctorId"); + + b.HasIndex("PatientId"); + + b.HasIndex("RoomId"); + + b.ToTable("Treatments"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Administrators.Administrator", b => + { + b.HasOne("MedX.Domain.Entities.Assets.Asset", "Image") + .WithMany() + .HasForeignKey("ImageId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Appointments.Appointment", b => + { + b.HasOne("MedX.Domain.Entities.Doctor", "Doctor") + .WithMany("Appointments") + .HasForeignKey("DoctorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MedX.Domain.Entities.Patient", "Patient") + .WithMany("Appointments") + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Doctor"); + + b.Navigation("Patient"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.CashDesk", b => + { + b.HasOne("MedX.Domain.Entities.Payment", "Payment") + .WithMany() + .HasForeignKey("PaymentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Payment"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Doctor", b => + { + b.HasOne("MedX.Domain.Entities.Assets.Asset", "Image") + .WithMany() + .HasForeignKey("ImageId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.MedicalRecords.MedicalRecord", b => + { + b.HasOne("MedX.Domain.Entities.Doctor", "Doctor") + .WithMany("MedicalRecords") + .HasForeignKey("DoctorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MedX.Domain.Entities.Patient", "Patient") + .WithMany("MedicalRecords") + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Doctor"); + + b.Navigation("Patient"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Patient", b => + { + b.HasOne("MedX.Domain.Entities.Room", null) + .WithMany("Patients") + .HasForeignKey("RoomId"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Payment", b => + { + b.HasOne("MedX.Domain.Entities.Patient", "Patient") + .WithMany("Payments") + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Patient"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Room", b => + { + b.HasOne("MedX.Domain.Entities.Assets.Asset", "Image") + .WithMany() + .HasForeignKey("ImageId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Services.AffairItem", b => + { + b.HasOne("MedX.Domain.Entities.Services.Affair", "Affair") + .WithMany() + .HasForeignKey("AffairId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MedX.Domain.Entities.Patient", "Patient") + .WithMany("AffairItems") + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Affair"); + + b.Navigation("Patient"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Treatment", b => + { + b.HasOne("MedX.Domain.Entities.Doctor", "Doctor") + .WithMany("Treatments") + .HasForeignKey("DoctorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MedX.Domain.Entities.Patient", "Patient") + .WithMany("Treatments") + .HasForeignKey("PatientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MedX.Domain.Entities.Room", "Room") + .WithMany() + .HasForeignKey("RoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Doctor"); + + b.Navigation("Patient"); + + b.Navigation("Room"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Doctor", b => + { + b.Navigation("Appointments"); + + b.Navigation("MedicalRecords"); + + b.Navigation("Treatments"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Patient", b => + { + b.Navigation("AffairItems"); + + b.Navigation("Appointments"); + + b.Navigation("MedicalRecords"); + + b.Navigation("Payments"); + + b.Navigation("Treatments"); + }); + + modelBuilder.Entity("MedX.Domain.Entities.Room", b => + { + b.Navigation("Patients"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/MedX.Data/Migrations/20231106075958_AddedDateTimeConfigure.cs b/src/MedX.Data/Migrations/20231106075958_AddedDateTimeConfigure.cs new file mode 100644 index 0000000..4156e3c --- /dev/null +++ b/src/MedX.Data/Migrations/20231106075958_AddedDateTimeConfigure.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MedX.Data.Migrations +{ + /// + public partial class AddedDateTimeConfigure : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "DateOfBirth", + table: "Patients", + type: "timestamp", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "DateOfBirth", + table: "Patients", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp"); + } + } +} diff --git a/src/MedX.Data/Migrations/AppDbContextModelSnapshot.cs b/src/MedX.Data/Migrations/AppDbContextModelSnapshot.cs index 557d139..66547bc 100644 --- a/src/MedX.Data/Migrations/AppDbContextModelSnapshot.cs +++ b/src/MedX.Data/Migrations/AppDbContextModelSnapshot.cs @@ -141,6 +141,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AccountNumber") .HasColumnType("text"); + b.Property("Amount") + .HasColumnType("numeric"); + b.Property("Balance") .HasColumnType("numeric"); @@ -156,11 +159,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("IsIncome") .HasColumnType("boolean"); + b.Property("PaymentId") + .HasColumnType("bigint"); + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); + b.HasIndex("PaymentId"); + b.ToTable("CashDesks"); }); @@ -277,7 +285,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone"); b.Property("DateOfBirth") - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp"); b.Property("FirstName") .HasColumnType("text"); @@ -525,6 +533,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Patient"); }); + modelBuilder.Entity("MedX.Domain.Entities.CashDesk", b => + { + b.HasOne("MedX.Domain.Entities.Payment", "Payment") + .WithMany() + .HasForeignKey("PaymentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Payment"); + }); + modelBuilder.Entity("MedX.Domain.Entities.Doctor", b => { b.HasOne("MedX.Domain.Entities.Assets.Asset", "Image") diff --git a/src/MedX.Domain/Entities/CashDesk.cs b/src/MedX.Domain/Entities/CashDesk.cs index 5325dfb..a4c953b 100644 --- a/src/MedX.Domain/Entities/CashDesk.cs +++ b/src/MedX.Domain/Entities/CashDesk.cs @@ -4,8 +4,12 @@ namespace MedX.Domain.Entities; public class CashDesk : Auditable { + public decimal? Amount { get; set; } + public string AccountNumber { get; set; } public string Description { get; set; } public decimal Balance { get; set; } - public string AccountNumber { get; set; } public bool IsIncome { get; set; } + + public long PaymentId { get; set; } + public Payment Payment { get; set; } } \ No newline at end of file diff --git a/src/MedX.Domain/Entities/Rooms/Room.cs b/src/MedX.Domain/Entities/Rooms/Room.cs index e69d255..4c4aa86 100644 --- a/src/MedX.Domain/Entities/Rooms/Room.cs +++ b/src/MedX.Domain/Entities/Rooms/Room.cs @@ -6,16 +6,9 @@ namespace MedX.Domain.Entities; public class Room : Auditable { - private int busy; public int Number { get; set; } public int Quantity { get; set; } - public int Busy { - get => busy; - set - { - busy += busy + value > Quantity ? 0 : value; - } - } + public int Busy { get; set; } public Gender Gender { get; set; } public TypeOfRoom Type { get; set; } public long? ImageId { get; set; } diff --git a/src/MedX.Service/DTOs/CashDesks/CashDeskBalanceDto.cs b/src/MedX.Service/DTOs/CashDesks/CashDeskBalanceDto.cs new file mode 100644 index 0000000..c9c8dd9 --- /dev/null +++ b/src/MedX.Service/DTOs/CashDesks/CashDeskBalanceDto.cs @@ -0,0 +1,6 @@ +namespace MedX.Service.DTOs.CashDesks; + +public class CashDeskBalanceDto +{ + public decimal Balance { get; set; } +} diff --git a/src/MedX.Service/DTOs/CashDesks/CashDeskCreationDto.cs b/src/MedX.Service/DTOs/CashDesks/CashDeskCreationDto.cs index 27f5e1d..130a7ce 100644 --- a/src/MedX.Service/DTOs/CashDesks/CashDeskCreationDto.cs +++ b/src/MedX.Service/DTOs/CashDesks/CashDeskCreationDto.cs @@ -3,7 +3,9 @@ public class CashDeskCreationDto { public string Description { get; set; } - public decimal Balance { get; set; } + public decimal? Amount { get; set; } public string AccountNumber { get; set; } + public decimal Balance { get; set; } + public long? PaymentId { get; set; } public bool IsIncome { get; set; } } \ No newline at end of file diff --git a/src/MedX.Service/DTOs/CashDesks/CashDeskResultDto.cs b/src/MedX.Service/DTOs/CashDesks/CashDeskResultDto.cs index 13bbf8f..77beb01 100644 --- a/src/MedX.Service/DTOs/CashDesks/CashDeskResultDto.cs +++ b/src/MedX.Service/DTOs/CashDesks/CashDeskResultDto.cs @@ -1,4 +1,6 @@ -namespace MedX.Service.DTOs.CashDesks; +using MedX.Domain.Entities; + +namespace MedX.Service.DTOs.CashDesks; public class CashDeskResultDto { @@ -6,5 +8,6 @@ public class CashDeskResultDto public string Description { get; set; } public decimal Balance { get; set; } public string AccountNumber { get; set; } + public Payment Payment { get; set; } public bool IsIncome { get; set; } } diff --git a/src/MedX.Service/DTOs/Patients/PatientResultDto.cs b/src/MedX.Service/DTOs/Patients/PatientResultDto.cs index c11383b..86073ab 100644 --- a/src/MedX.Service/DTOs/Patients/PatientResultDto.cs +++ b/src/MedX.Service/DTOs/Patients/PatientResultDto.cs @@ -1,4 +1,10 @@ -using MedX.Domain.Enums; +using MedX.Domain.Entities.Appointments; +using MedX.Domain.Entities.Services; +using MedX.Domain.Entities; +using MedX.Domain.Enums; +using MedX.Service.DTOs.Treatments; +using MedX.Service.DTOs.ServiceItems; +using MedX.Service.DTOs.Appointments; namespace MedX.Service.DTOs.Patients; @@ -14,4 +20,9 @@ public class PatientResultDto public Gender Gender { get; set; } public string Pinfl { get; set; } public decimal Balance { get; set; } + public string AccountNumber { get; set; } + + public ICollection Treatments { get; set; } + public ICollection AffairItems { get; set; } + public ICollection Appointments { get; set; } } diff --git a/src/MedX.Service/DTOs/Payments/PaymentCreationDto.cs b/src/MedX.Service/DTOs/Payments/PaymentCreationDto.cs index fc7850b..0ff8443 100644 --- a/src/MedX.Service/DTOs/Payments/PaymentCreationDto.cs +++ b/src/MedX.Service/DTOs/Payments/PaymentCreationDto.cs @@ -5,6 +5,7 @@ namespace MedX.Service.DTOs.Payments; public class PaymentCreationDto { public decimal Amount { get; set; } + public string Description { get; set; } public TypeOfPayment Type { get; set; } public long PatientId { get; set; } } \ No newline at end of file diff --git a/src/MedX.Service/DTOs/Payments/PaymentResultDto.cs b/src/MedX.Service/DTOs/Payments/PaymentResultDto.cs index a213b58..1fc4e88 100644 --- a/src/MedX.Service/DTOs/Payments/PaymentResultDto.cs +++ b/src/MedX.Service/DTOs/Payments/PaymentResultDto.cs @@ -9,6 +9,5 @@ public class PaymentResultDto public long Id { get; set; } public decimal Amount { get; set; } public TypeOfPayment Type { get; set; } - public PatientResultDto Patient { get; set; } } diff --git a/src/MedX.Service/DTOs/Payments/PaymentUpdateDto.cs b/src/MedX.Service/DTOs/Payments/PaymentUpdateDto.cs index 2908f71..cf6ab1b 100644 --- a/src/MedX.Service/DTOs/Payments/PaymentUpdateDto.cs +++ b/src/MedX.Service/DTOs/Payments/PaymentUpdateDto.cs @@ -7,5 +7,6 @@ public class PaymentUpdateDto public long Id { get; set; } public decimal Amount { get; set; } public TypeOfPayment Type { get; set; } + public string Description { get; set; } public long PatientId { get; set; } } diff --git a/src/MedX.Service/DTOs/Rooms/RoomCreationDto.cs b/src/MedX.Service/DTOs/Rooms/RoomCreationDto.cs index 92cf33c..2d7e8fc 100644 --- a/src/MedX.Service/DTOs/Rooms/RoomCreationDto.cs +++ b/src/MedX.Service/DTOs/Rooms/RoomCreationDto.cs @@ -7,7 +7,7 @@ public class RoomCreationDto { public int Number { get; set; } public int Quantity { get; set; } - public int Busy { get; set; } + public int Busy { get; set; } = 0; public TypeOfRoom Type { get; set; } public IFormFile Image { get; set; } } \ No newline at end of file diff --git a/src/MedX.Service/Interfaces/ICashDeskService.cs b/src/MedX.Service/Interfaces/ICashDeskService.cs index 53b4cfc..9efd5f7 100644 --- a/src/MedX.Service/Interfaces/ICashDeskService.cs +++ b/src/MedX.Service/Interfaces/ICashDeskService.cs @@ -1,4 +1,5 @@ using MedX.Domain.Configurations; +using MedX.Domain.Entities; using MedX.Service.DTOs.CashDesks; namespace MedX.Service.Interfaces; @@ -8,6 +9,8 @@ public interface ICashDeskService Task AddAsync(CashDeskCreationDto dto); Task UpdateAsync(CashDeskUpdateDto dto); Task DeleteAsync(long id); + Task DeleteByPaymentIdAsync(long paymentId); Task GetAsync(long id); + Task GetLastCashDeskAsync(); Task> GetAllAsync(PaginationParams @params, string search = null); } diff --git a/src/MedX.Service/Services/AssetService.cs b/src/MedX.Service/Services/AssetService.cs index 3c10e25..b34c020 100644 --- a/src/MedX.Service/Services/AssetService.cs +++ b/src/MedX.Service/Services/AssetService.cs @@ -4,15 +4,18 @@ using MedX.Service.Interfaces; using MedX.Service.DTOs.Assets; using MedX.Domain.Entities.Assets; +using Microsoft.AspNetCore.Http; namespace MedX.Service.Services; public class AssetService : IAssetService { private readonly IRepository repository; - public AssetService(IRepository repository) + private readonly IHttpContextAccessor httpContextAccessor; + public AssetService(IRepository repository, IHttpContextAccessor httpContextAccessor) { this.repository = repository; + this.httpContextAccessor = httpContextAccessor; } public async Task UploadAsync(AssetCreationDto dto) @@ -22,18 +25,23 @@ public async Task UploadAsync(AssetCreationDto dto) if (!Directory.Exists(webRootPath)) Directory.CreateDirectory(webRootPath); - var fileExtention = Path.GetExtension(dto.FormFile.FileName); - var fileName = $"{Guid.NewGuid().ToString("N")}{fileExtention}"; + var fileExtension = Path.GetExtension(dto.FormFile.FileName); + var fileName = $"{Guid.NewGuid().ToString("N")}{fileExtension}"; var filePath = Path.Combine(webRootPath, fileName); - var fileStream = new FileStream(filePath, FileMode.OpenOrCreate); - await fileStream.WriteAsync(dto.FormFile.ToByte()); + using (var fileStream = new FileStream(filePath, FileMode.Create)) + { + await dto.FormFile.CopyToAsync(fileStream); + } + + var imageUrl = $"{httpContextAccessor.HttpContext.Request.Scheme}://{httpContextAccessor.HttpContext.Request.Host}/Images/{fileName}"; var asset = new Asset() { FileName = fileName, - FilePath = filePath, + FilePath = imageUrl, }; + await this.repository.CreateAsync(asset); await this.repository.SaveChanges(); return asset; diff --git a/src/MedX.Service/Services/CashDeskService.cs b/src/MedX.Service/Services/CashDeskService.cs index 9271215..b292a4d 100644 --- a/src/MedX.Service/Services/CashDeskService.cs +++ b/src/MedX.Service/Services/CashDeskService.cs @@ -2,9 +2,7 @@ using MedX.Data.IRepositories; using MedX.Domain.Configurations; using MedX.Domain.Entities; -using MedX.Domain.Entities.Services; using MedX.Service.DTOs.CashDesks; -using MedX.Service.DTOs.Services; using MedX.Service.Exceptions; using MedX.Service.Extensions; using MedX.Service.Interfaces; @@ -14,48 +12,58 @@ namespace MedX.Service.Services; public class CashDeskService : ICashDeskService { - private readonly IRepository CashDeskRepository; + private readonly IRepository cashDeskRepository; + private readonly IRepository paymentRepository; private readonly IMapper mapper; - public CashDeskService(IMapper mapper, IRepository CashDeskRepository) + public CashDeskService(IMapper mapper, + IRepository cashDeskRepository, + IRepository paymentRepository) { this.mapper = mapper; - this.CashDeskRepository = CashDeskRepository; + this.paymentRepository = paymentRepository; + this.cashDeskRepository = cashDeskRepository; } public async Task AddAsync(CashDeskCreationDto dto) { - var mappedCashDesk = this.mapper.Map(dto); + var payment = await paymentRepository.GetAsync(p => p.Id.Equals(dto.PaymentId)) + ?? throw new NotFoundException("This payment is not found!"); - await this.CashDeskRepository.CreateAsync(mappedCashDesk); - await this.CashDeskRepository.SaveChanges(); + var cashDesk = this.mapper.Map(dto); + cashDesk.Payment = payment; - return this.mapper.Map(mappedCashDesk); + await this.cashDeskRepository.CreateAsync(cashDesk); + await this.cashDeskRepository.SaveChanges(); + + return this.mapper.Map(cashDesk); } public async Task DeleteAsync(long id) { - var existCashDesk = await this.CashDeskRepository.GetAsync(r => r.Id == id) + var existCashDesk = await this.cashDeskRepository.GetAsync(r => r.Id == id) ?? throw new NotFoundException($"This CashDesk not found with id: {id}"); - this.CashDeskRepository.Delete(existCashDesk); - await this.CashDeskRepository.SaveChanges(); + this.cashDeskRepository.Delete(existCashDesk); + await this.cashDeskRepository.SaveChanges(); return true; } + public async Task UpdateAsync(CashDeskUpdateDto dto) { - var existCashDesk = await this.CashDeskRepository.GetAsync(r => r.Id == dto.Id) + var existCashDesk = await this.cashDeskRepository.GetAsync(r => r.Id == dto.Id) ?? throw new NotFoundException($"This CashDesk not found with id: {dto.Id}"); this.mapper.Map(dto, existCashDesk); - this.CashDeskRepository.Update(existCashDesk); - await this.CashDeskRepository.SaveChanges(); + this.cashDeskRepository.Update(existCashDesk); + await this.cashDeskRepository.SaveChanges(); return this.mapper.Map(existCashDesk); } + public async Task GetAsync(long id) { - var existCashDesk = await this.CashDeskRepository.GetAsync(r => r.Id == id) + var existCashDesk = await this.cashDeskRepository.GetAsync(r => r.Id == id) ?? throw new NotFoundException($"This CashDesk not found with id: {id}"); return this.mapper.Map(existCashDesk); @@ -63,7 +71,7 @@ public async Task GetAsync(long id) public async Task> GetAllAsync(PaginationParams @params, string search = null) { - var allCashDesks = await this.CashDeskRepository.GetAll() + var allCashDesks = await this.cashDeskRepository.GetAll() .ToPaginate(@params) .ToListAsync(); @@ -75,4 +83,24 @@ public async Task> GetAllAsync(PaginationParams @ return this.mapper.Map>(allCashDesks); } + + public async Task GetLastCashDeskAsync() + { + var result = await cashDeskRepository.GetAll() + .OrderByDescending(c => c.Id) + .FirstOrDefaultAsync(); + + return result; + } + + public async Task DeleteByPaymentIdAsync(long paymentId) + { + var cashDesk = await cashDeskRepository.GetAsync(p => p.PaymentId.Equals(paymentId)) + ?? throw new NotFoundException("This payment is not found!"); + + this.cashDeskRepository.Delete(cashDesk); + await this.cashDeskRepository.SaveChanges(); + + return true; + } } \ No newline at end of file diff --git a/src/MedX.Service/Services/MedicalRecordService.cs b/src/MedX.Service/Services/MedicalRecordService.cs index 1242a54..a0afa8e 100644 --- a/src/MedX.Service/Services/MedicalRecordService.cs +++ b/src/MedX.Service/Services/MedicalRecordService.cs @@ -57,6 +57,7 @@ public async Task DeleteAsync(long id) return true; } + public async Task UpdateAsync(MedicalRecordUpdateDto dto) { var existMedicalRecord = await this.medicalRecordRepository.GetAsync(r => r.Id == dto.Id, includes: new[] { "Doctor", "Patient" }) diff --git a/src/MedX.Service/Services/PatientService.cs b/src/MedX.Service/Services/PatientService.cs index 9280a4b..251ede5 100644 --- a/src/MedX.Service/Services/PatientService.cs +++ b/src/MedX.Service/Services/PatientService.cs @@ -7,6 +7,7 @@ using MedX.Domain.Configurations; using MedX.Service.DTOs.Patients; using Microsoft.EntityFrameworkCore; +using MedX.Domain.Entities.Services; namespace MedX.Service.Services; @@ -50,16 +51,6 @@ public async Task DeleteAsync(long id) return true; } - - public async Task GetAsync(long id) - { - Patient existPatient = await this.repository.GetAsync(p => p.Id == id) - ?? throw new NotFoundException($"This patient is not found {id}"); - - PatientResultDto result = this.mapper.Map(existPatient); - return result; - } - public async Task UpdateAsync(PatientUpdateDto dto) { Patient existPatient = await this.repository.GetAsync(p => p.Id == dto.Id) @@ -73,6 +64,16 @@ public async Task UpdateAsync(PatientUpdateDto dto) return result; } + public async Task GetAsync(long id) + { + Patient existPatient = await this.repository.GetAsync(p => p.Id == id) + ?? throw new NotFoundException($"This patient is not found {id}"); + + PatientResultDto result = this.mapper.Map(existPatient); + return result; + } + + public async Task> GetAllAsync(PaginationParams @params, string search = null) { var patients = await this.repository.GetAll(includes: new[] { "Treatments", "Appointments" }) diff --git a/src/MedX.Service/Services/PaymentService.cs b/src/MedX.Service/Services/PaymentService.cs index 0847f03..7e1ac4f 100644 --- a/src/MedX.Service/Services/PaymentService.cs +++ b/src/MedX.Service/Services/PaymentService.cs @@ -7,6 +7,7 @@ using MedX.Domain.Configurations; using MedX.Service.DTOs.Payments; using Microsoft.EntityFrameworkCore; +using MedX.Service.DTOs.CashDesks; namespace MedX.Service.Services; @@ -15,12 +16,15 @@ public class PaymentService : IPaymentService private readonly IMapper mapper; private readonly IRepository repository; private readonly IRepository patientRepository; + private readonly ICashDeskService cashDeskService; public PaymentService(IMapper mapper, IRepository repository, - IRepository patientRepository) + IRepository patientRepository, + ICashDeskService cashDeskService) { this.mapper = mapper; this.repository = repository; + this.cashDeskService = cashDeskService; this.patientRepository = patientRepository; } @@ -35,6 +39,21 @@ public async Task AddAsync(PaymentCreationDto dto) await this.repository.CreateAsync(payment); await this.repository.SaveChanges(); + var cashDesk = await cashDeskService.GetLastCashDeskAsync(); + if (cashDesk == null) + cashDesk = new CashDesk(); + + cashDesk.Balance += dto.Amount; + cashDesk.PaymentId = payment.Id; + cashDesk.Description = dto.Description; + var createCash = mapper.Map(cashDesk); + + await cashDeskService.AddAsync(createCash); + + existPatient.Balance += dto.Amount; + this.patientRepository.Update(existPatient); + await this.patientRepository.SaveChanges(); + return this.mapper.Map(payment); } @@ -46,11 +65,29 @@ public async Task UpdateAsync(PaymentUpdateDto dto) var existPatient = await this.patientRepository.GetAsync(d => d.Id.Equals(dto.PatientId)) ?? throw new NotFoundException($"This Patient not found with id: {dto.PatientId}"); + decimal oldAmount = payment.Amount; Payment mappedPayment = this.mapper.Map(dto, payment); payment.Patient = existPatient; this.repository.Update(mappedPayment); await this.repository.SaveChanges(); + if (oldAmount != dto.Amount) + { + var cashDesk = await cashDeskService.GetLastCashDeskAsync(); + if (cashDesk == null) + cashDesk = new CashDesk(); + + decimal amountDifference = dto.Amount - oldAmount; + cashDesk.Balance += amountDifference; + var cashDeskDto = new CashDeskCreationDto + { + Amount = amountDifference, + Description = dto.Description, + IsIncome = amountDifference > 0 + }; + await cashDeskService.AddAsync(cashDeskDto); + } + return this.mapper.Map(mappedPayment); } @@ -61,6 +98,8 @@ public async Task DeleteAsync(long id) this.repository.Delete(payment); await this.repository.SaveChanges(); + await this.cashDeskService.DeleteByPaymentIdAsync(id); + return true; } diff --git a/src/MedX.Service/Services/RoomService.cs b/src/MedX.Service/Services/RoomService.cs index da56d77..72a3173 100644 --- a/src/MedX.Service/Services/RoomService.cs +++ b/src/MedX.Service/Services/RoomService.cs @@ -9,6 +9,7 @@ using MedX.Domain.Configurations; using MedX.Domain.Entities.Assets; using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; namespace MedX.Service.Services; diff --git a/src/MedX.WebApi/Controllers/AffairItemsController.cs b/src/MedX.WebApi/Controllers/AffairItemsController.cs index 8f80101..8afb76b 100644 --- a/src/MedX.WebApi/Controllers/AffairItemsController.cs +++ b/src/MedX.WebApi/Controllers/AffairItemsController.cs @@ -47,7 +47,7 @@ public async Task UpdateAsync(AffairItemUpdateDto dto) }); } - [HttpPut("get/{id:long}")] + [HttpGet("get/{id:long}")] public async Task GetAsync(long id) { return Ok(new Response @@ -58,7 +58,7 @@ public async Task GetAsync(long id) }); } - [HttpPut("get-all")] + [HttpGet("get-all")] public async Task GetAllAsync([FromQuery] PaginationParams @params, [FromQuery] string search = null) { return Ok(new Response @@ -69,7 +69,7 @@ public async Task GetAllAsync([FromQuery] PaginationParams @param }); } - [HttpPut("get-all-by-service/{serviceId:long}")] + [HttpGet("get-all-by-service/{serviceId:long}")] public async Task GetAllByAffairIdAsync([FromQuery] long affairId, PaginationParams @params, [FromQuery] string search = null) { return Ok(new Response @@ -80,7 +80,7 @@ public async Task GetAllByAffairIdAsync([FromQuery] long affairId }); } - [HttpPut("get-all-by-patient/{patientId:long}")] + [HttpGet("get-all-by-patient/{patientId:long}")] public async Task GetAllByPatientIdAsync([FromQuery] long patientId, PaginationParams @params, [FromQuery] string search = null) { return Ok(new Response diff --git a/src/MedX.WebApi/Controllers/AffairsController.cs b/src/MedX.WebApi/Controllers/AffairsController.cs index c951ca4..d7c09e9 100644 --- a/src/MedX.WebApi/Controllers/AffairsController.cs +++ b/src/MedX.WebApi/Controllers/AffairsController.cs @@ -47,7 +47,7 @@ public async Task UpdateAsync(AffairUpdateDto dto) }); } - [HttpPut("get/{id:long}")] + [HttpGet("get/{id:long}")] public async Task GetAsync(long id) { return Ok(new Response @@ -58,7 +58,7 @@ public async Task GetAsync(long id) }); } - [HttpPut("get-all")] + [HttpGet("get-all")] public async Task GetAllAsync([FromQuery] PaginationParams @params, [FromQuery] string search = null) { return Ok(new Response diff --git a/src/MedX.WebApi/Controllers/AppointmentsController.cs b/src/MedX.WebApi/Controllers/AppointmentsController.cs index b437ac0..195762e 100644 --- a/src/MedX.WebApi/Controllers/AppointmentsController.cs +++ b/src/MedX.WebApi/Controllers/AppointmentsController.cs @@ -47,7 +47,7 @@ public async Task UpdateAsync([FromForm] AppointmentUpdateDto dto }); } - [HttpPut("get/{id:long}")] + [HttpGet("get/{id:long}")] public async Task GetAsync(long id) { return Ok(new Response @@ -58,7 +58,7 @@ public async Task GetAsync(long id) }); } - [HttpPut("get-all")] + [HttpGet("get-all")] public async Task GetAllAsync([FromQuery] PaginationParams @params, string search) { return Ok(new Response @@ -69,7 +69,7 @@ public async Task GetAllAsync([FromQuery] PaginationParams @param }); } - [HttpPut("get-all-by-patient/{patientId:long}")] + [HttpGet("get-all-by-patient/{patientId:long}")] public async Task GetAllByPatientIdAsync(long patientId) { return Ok(new Response @@ -80,7 +80,7 @@ public async Task GetAllByPatientIdAsync(long patientId) }); } - [HttpPut("get-all-by-doctor/{doctorId:long}")] + [HttpGet("get-all-by-doctor/{doctorId:long}")] public async Task GetAllByDoctorIdAsync(long doctorId, PaginationParams @params, string search = null) { return Ok(new Response diff --git a/src/MedX.WebApi/Controllers/CashDeskController.cs b/src/MedX.WebApi/Controllers/CashDeskController.cs new file mode 100644 index 0000000..9120354 --- /dev/null +++ b/src/MedX.WebApi/Controllers/CashDeskController.cs @@ -0,0 +1,25 @@ +using MedX.Service.Interfaces; +using MedX.WebApi.Models; +using Microsoft.AspNetCore.Mvc; + +namespace MedX.WebApi.Controllers; + +public class CashDeskController : BaseController +{ + private readonly ICashDeskService service; + public CashDeskController(ICashDeskService service) + { + this.service = service; + } + + [HttpGet("get-balance")] + public async Task GetBalance() + { + return Ok(new Response + { + StatusCode = 200, + Message = "Success", + Data = await service.GetLastCashDeskAsync() + }); + } +} diff --git a/src/MedX.WebApi/Controllers/DoctorsController.cs b/src/MedX.WebApi/Controllers/DoctorsController.cs index a7af77e..10d4d3d 100644 --- a/src/MedX.WebApi/Controllers/DoctorsController.cs +++ b/src/MedX.WebApi/Controllers/DoctorsController.cs @@ -26,11 +26,11 @@ public async Task DeleteAsync(long id) public async Task UpdateAsync([FromForm] DoctorUpdateDto dto) => Ok(new Response { Data = await doctorService.UpdateAsync(dto) }); - [HttpPut("get/{id:long}")] + [HttpGet("get/{id:long}")] public async Task GetAsync(long id) => Ok(new Response { Data = await doctorService.GetAsync(id) }); - [HttpPut("get-all")] + [HttpGet("get-all")] public async Task GetAllAsync([FromQuery] PaginationParams @params, string search) => Ok(new Response { Data = await doctorService.GetAllAsync(@params, search) }); } diff --git a/src/MedX.WebApi/Controllers/MedicalRecordsControllers.cs b/src/MedX.WebApi/Controllers/MedicalRecordsControllers.cs index cf927e9..ce26f19 100644 --- a/src/MedX.WebApi/Controllers/MedicalRecordsControllers.cs +++ b/src/MedX.WebApi/Controllers/MedicalRecordsControllers.cs @@ -48,7 +48,7 @@ public async Task UpdateAsync([FromForm] MedicalRecordUpdateDto d }); } - [HttpPut("get/{id:long}")] + [HttpGet("get/{id:long}")] public async Task GetAsync(long id) { return Ok(new Response @@ -59,7 +59,7 @@ public async Task GetAsync(long id) }); } - [HttpPut("get-all")] + [HttpGet("get-all")] public async Task GetAllAsync([FromQuery] PaginationParams @params, string search) { return Ok(new Response @@ -70,7 +70,7 @@ public async Task GetAllAsync([FromQuery] PaginationParams @param }); } - [HttpPut("get-all-by-patient/{patientId:long}")] + [HttpGet("get-all-by-patient/{patientId:long}")] public async Task GetAllByPatientIdAsync(long patientId) { return Ok(new Response @@ -81,7 +81,7 @@ public async Task GetAllByPatientIdAsync(long patientId) }); } - [HttpPut("get-all-by-doctor/{doctorId:long}")] + [HttpGet("get-all-by-doctor/{doctorId:long}")] public async Task GetAllByDoctorIdAsync(long doctorId, PaginationParams @params, string search = null) { return Ok(new Response diff --git a/src/MedX.WebApi/Controllers/PatientsController.cs b/src/MedX.WebApi/Controllers/PatientsController.cs index 5a9f949..7addd4f 100644 --- a/src/MedX.WebApi/Controllers/PatientsController.cs +++ b/src/MedX.WebApi/Controllers/PatientsController.cs @@ -15,7 +15,7 @@ public PatientsController(IPatientService patientService) } [HttpPost("create")] - public async Task PostAsync(PatientCreationDto dto) + public async Task PostAsync([FromForm] PatientCreationDto dto) { return Ok(new Response { @@ -37,7 +37,7 @@ public async Task DeleteAsync(long id) } [HttpPut("update")] - public async Task UpdateAsync(PatientUpdateDto dto) + public async Task UpdateAsync([FromForm] PatientUpdateDto dto) { return Ok(new Response { diff --git a/src/MedX.WebApi/Controllers/PaymentsController.cs b/src/MedX.WebApi/Controllers/PaymentsController.cs index 9296c53..3b0f8ba 100644 --- a/src/MedX.WebApi/Controllers/PaymentsController.cs +++ b/src/MedX.WebApi/Controllers/PaymentsController.cs @@ -1,4 +1,5 @@ using MedX.Domain.Configurations; +using MedX.Service.DTOs.CashDesks; using MedX.Service.DTOs.Payments; using MedX.Service.Interfaces; using MedX.WebApi.Models; @@ -24,6 +25,7 @@ public async Task PostAsync([FromForm] PaymentCreationDto dto) Data = await paymentService.AddAsync(dto) }); } + [HttpDelete("delete/{id:long}")] public async Task DeleteAsync(long id) @@ -47,7 +49,7 @@ public async Task UpdateAsync([FromForm] PaymentUpdateDto dto) }); } - [HttpPut("get/{id:long}")] + [HttpGet("get/{id:long}")] public async Task GetAsync(long id) { return Ok(new Response @@ -58,7 +60,7 @@ public async Task GetAsync(long id) }); } - [HttpPut("get-all")] + [HttpGet("get-all")] public async Task GetAllAsync([FromQuery] PaginationParams @params, string search) { return Ok(new Response @@ -69,7 +71,7 @@ public async Task GetAllAsync([FromQuery] PaginationParams @param }); } - [HttpPut("get-all-by-patient/{patientId:long}")] + [HttpGet("get-all-by-patient/{patientId:long}")] public async Task GetAllByPatientIdAsync(long patientId) { return Ok(new Response diff --git a/src/MedX.WebApi/Controllers/RoomsController.cs b/src/MedX.WebApi/Controllers/RoomsController.cs index 1c66156..1a317f9 100644 --- a/src/MedX.WebApi/Controllers/RoomsController.cs +++ b/src/MedX.WebApi/Controllers/RoomsController.cs @@ -47,7 +47,7 @@ public async Task UpdateAsync(RoomUpdateDto dto) }); } - [HttpPut("get/{id:long}")] + [HttpGet("get/{id:long}")] public async Task GetAsync(long id) { return Ok(new Response @@ -58,7 +58,7 @@ public async Task GetAsync(long id) }); } - [HttpPut("get-all")] + [HttpGet("get-all")] public async Task GetAllAsync([FromQuery] PaginationParams @params, [FromQuery] int? search) { return Ok(new Response diff --git a/src/MedX.WebApi/Controllers/TreatmentsController.cs b/src/MedX.WebApi/Controllers/TreatmentsController.cs index 13c04d6..0bc2b95 100644 --- a/src/MedX.WebApi/Controllers/TreatmentsController.cs +++ b/src/MedX.WebApi/Controllers/TreatmentsController.cs @@ -47,7 +47,7 @@ public async Task UpdateAsync(TreatmentUpdateDto dto) }); } - [HttpPut("get/{id:long}")] + [HttpGet("get/{id:long}")] public async Task GetAsync(long id) { return Ok(new Response @@ -58,7 +58,7 @@ public async Task GetAsync(long id) }); } - [HttpPut("get-all")] + [HttpGet("get-all")] public async Task GetAllAsync([FromQuery] PaginationParams @params, string search) { return Ok(new Response diff --git a/src/MedX.WebApi/Dockerfile b/src/MedX.WebApi/Dockerfile new file mode 100644 index 0000000..4ae104c --- /dev/null +++ b/src/MedX.WebApi/Dockerfile @@ -0,0 +1,12 @@ +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +WORKDIR /App +COPY . ./ +RUN dotnet restore +RUN dotnet publish -c Release -o out + +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /App +COPY --from=build-env /App/out . +EXPOSE 82 +ENV ASPNETCORE_URLS=http://+:82 +ENTRYPOINT ["dotnet", "MedX.WebApi.dll"] \ No newline at end of file diff --git a/src/MedX.WebApi/Extensions/ServiceCollection.cs b/src/MedX.WebApi/Extensions/ServiceCollection.cs index 73c893b..ac9d839 100644 --- a/src/MedX.WebApi/Extensions/ServiceCollection.cs +++ b/src/MedX.WebApi/Extensions/ServiceCollection.cs @@ -29,6 +29,7 @@ public static void AddServices(this IServiceCollection services) services.AddScoped(); services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); services.AddScoped(); + services.AddHttpContextAccessor(); } public static void AddJwt(this IServiceCollection services, IConfiguration configuration) { diff --git a/src/MedX.WebApi/MedX.WebApi.csproj b/src/MedX.WebApi/MedX.WebApi.csproj index 0507967..176710a 100644 --- a/src/MedX.WebApi/MedX.WebApi.csproj +++ b/src/MedX.WebApi/MedX.WebApi.csproj @@ -12,7 +12,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -22,7 +21,6 @@ - diff --git a/src/MedX.WebApi/Program.cs b/src/MedX.WebApi/Program.cs index 2fbeb79..3581b60 100644 --- a/src/MedX.WebApi/Program.cs +++ b/src/MedX.WebApi/Program.cs @@ -3,7 +3,6 @@ using MedX.WebApi.Extensions; using MedX.WebApi.Middlewares; using Microsoft.EntityFrameworkCore; -using Serilog; var builder = WebApplication.CreateBuilder(args); @@ -25,12 +24,12 @@ builder.Services.ConfigureSwagger(); // Logger -var logger = new LoggerConfiguration() +/*var logger = new LoggerConfiguration() .ReadFrom.Configuration(builder.Configuration) .Enrich.FromLogContext() .CreateLogger(); builder.Logging.ClearProviders(); -builder.Logging.AddSerilog(logger); +builder.Logging.AddSerilog(logger);*/ // Add JWT builder.Services.AddJwt(builder.Configuration); @@ -40,7 +39,7 @@ var app = builder.Build(); // Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) +if (app.Environment.IsDevelopment() || app.Environment.IsProduction()) { app.UseSwagger(); app.UseSwaggerUI(); @@ -48,6 +47,8 @@ app.UseMiddleware(); +app.UseStaticFiles(); + app.UseAuthentication(); app.UseHttpsRedirection(); diff --git a/src/MedX.WebApi/appsettings.json b/src/MedX.WebApi/appsettings.json index e201d4d..e7c2b8a 100644 --- a/src/MedX.WebApi/appsettings.json +++ b/src/MedX.WebApi/appsettings.json @@ -5,23 +5,6 @@ "Microsoft.AspNetCore": "Warning" } }, - "Serilog": { - "MinimumLevel": "Warning", - "Using": [ "Serilog.Sinks.File" ], - "WriteTo": [ - { - "Name": "File", - "Args": { - "fileSizeLimitBytes": 1000000, - "flushToDiskInterval": 1, - "outputTemplate": "[{Timestamp:yyyy/MM/dd HH:mm:ss} {Level:u10}] {Message:lj} {NewLine}{Exception}{NewLine}", - "path": "Log\\log.log", - "rollOnFileSizeLimit": true, - "shared": true - } - } - ] - }, "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "Host=localhost; Port=5432; User Id=postgres; Password=root; Database=MedXDb;" diff --git a/src/MedX.WebApi/wwwroot/Images/3551c01e8f2d492796dc5b858e8ffbce.jpg b/src/MedX.WebApi/wwwroot/Images/3551c01e8f2d492796dc5b858e8ffbce.jpg new file mode 100644 index 0000000000000000000000000000000000000000..689598bca7824e3e8a567ddfbc708d7642c36235 GIT binary patch literal 88304 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