From 2c255f1fd23d691f3d16ab558a872e19bdd7f48c Mon Sep 17 00:00:00 2001 From: Tord Austad Date: Tue, 25 Oct 2022 13:43:27 +0200 Subject: [PATCH] Production profiles tab fixes, add cost and summary tab (#790) * Remove facilities availability from drainage strategy and topside * Remove facilities availability from tests * Add year range input to production profiles table * Set limits for table years * Add case cost tab * Remove setting created and modify date from frontend * Add case summary tab * Make tabs and case name position fixed * Hide bottom scrollbar Vertical scrollbar shows main view position * Set tab menu and case title fixed position * Use loading icon for create case when waiting for server * Add table minwidth * Fix production profiles order * Align cost tab grids * Add save functionality to cost tab * Remove number in name when copying case * Remove comented out code * Add migration files * Add cost profiles to start and end year calculation --- backend/api/Adapters/CaseAdapter.cs | 15 +- backend/api/Adapters/CaseDtoAdapter.cs | 4 + .../api/Adapters/DrainageStrategyAdapter.cs | 4 +- .../Adapters/DrainageStrategyDtoAdapter.cs | 2 +- backend/api/Adapters/TopsideAdapter.cs | 2 - backend/api/Adapters/TopsideDtoAdapter.cs | 1 - .../api/Controllers/ExplorationsController.cs | 6 + .../api/Controllers/WellProjectsController.cs | 6 + backend/api/Dtos/CaseDto.cs | 4 + backend/api/Dtos/DrainageStrategyDto.cs | 2 +- backend/api/Dtos/TopsideDto.cs | 1 - .../20221025103929_CaseChanges.Designer.cs | 1845 +++++++++++++++++ .../Migrations/20221025103929_CaseChanges.cs | 70 + .../Migrations/DcdDbContextModelSnapshot.cs | 17 +- backend/api/Models/Case.cs | 6 +- backend/api/Models/DrainageStrategy.cs | 7 +- backend/api/Models/Topside.cs | 1 - .../Generators/SampleAssetGenerator.cs | 1 - backend/api/Services/CaseService.cs | 16 +- backend/api/Services/ExplorationService.cs | 30 + backend/api/Services/WellProjectService.cs | 15 + backend/tests/Services/CaseServiceShould.cs | 1 - .../tests/Services/TopsideServiceShould.cs | 2 - backend/tests/TestHelper.cs | 2 - .../src/Components/Case/EditCaseModal.tsx | 28 +- frontend/src/Components/SideMenu/SideMenu.tsx | 3 +- frontend/src/Services/ExplorationService.ts | 6 + frontend/src/Services/WellProjectService.ts | 6 + frontend/src/Views/Case/CaseCostTab.tsx | 520 +++++ .../src/Views/Case/CaseDescriptionTab.tsx | 17 +- frontend/src/Views/Case/CaseFacilitiesTab.tsx | 8 +- .../Views/Case/CaseProductionProfilesTab.tsx | 224 +- frontend/src/Views/Case/CaseSummaryTab.tsx | 279 +++ ...nProfilesTabTable.tsx => CaseTabTable.tsx} | 67 +- frontend/src/Views/CaseView.tsx | 363 ++-- frontend/src/Views/DrainageStrategyView.tsx | 14 +- frontend/src/Views/TopsideView.tsx | 15 +- .../drainagestrategy/DrainageStrategy.ts | 4 +- frontend/src/models/assets/topside/Topside.ts | 2 - frontend/src/models/case/Case.ts | 16 +- frontend/src/types.d.ts | 8 +- 41 files changed, 3329 insertions(+), 311 deletions(-) create mode 100644 backend/api/Migrations/20221025103929_CaseChanges.Designer.cs create mode 100644 backend/api/Migrations/20221025103929_CaseChanges.cs create mode 100644 frontend/src/Views/Case/CaseCostTab.tsx create mode 100644 frontend/src/Views/Case/CaseSummaryTab.tsx rename frontend/src/Views/Case/{CaseProductionProfilesTabTable.tsx => CaseTabTable.tsx} (73%) diff --git a/backend/api/Adapters/CaseAdapter.cs b/backend/api/Adapters/CaseAdapter.cs index e4e88925b..658f442bc 100644 --- a/backend/api/Adapters/CaseAdapter.cs +++ b/backend/api/Adapters/CaseAdapter.cs @@ -24,8 +24,8 @@ public static Case Convert(CaseDto caseDto) DG2Date = caseDto.DG2Date, DG3Date = caseDto.DG3Date, DG4Date = caseDto.DG4Date, - CreateTime = caseDto.CreateTime, - ModifyTime = caseDto.ModifyTime, + CreateTime = DateTimeOffset.Now, + ModifyTime = DateTimeOffset.Now, DrainageStrategyLink = caseDto.DrainageStrategyLink, ExplorationLink = caseDto.ExplorationLink, WellProjectLink = caseDto.WellProjectLink, @@ -39,6 +39,10 @@ public static Case Convert(CaseDto caseDto) GasInjectorCount = caseDto.GasInjectorCount, WaterInjectorCount = caseDto.WaterInjectorCount, FacilitiesAvailability = caseDto.FacilitiesAvailability, + CapexFactorFeasibilityStudies = caseDto.CapexFactorFeasibilityStudies, + CapexFactorFEEDStudies = caseDto.CapexFactorFEEDStudies, + NPV = caseDto.NPV, + BreakEven = caseDto.BreakEven, SharepointFileId = caseDto.SharepointFileId, SharepointFileName = caseDto.SharepointFileName, SharepointFileUrl = caseDto.SharepointFileUrl, @@ -62,8 +66,7 @@ public static void ConvertExisting(Case existing, CaseDto caseDto) existing.DG2Date = caseDto.DG2Date; existing.DG3Date = caseDto.DG3Date; existing.DG4Date = caseDto.DG4Date; - existing.CreateTime = caseDto.CreateTime; - existing.ModifyTime = caseDto.ModifyTime; + existing.ModifyTime = DateTimeOffset.Now; existing.DrainageStrategyLink = caseDto.DrainageStrategyLink; existing.ExplorationLink = caseDto.ExplorationLink; existing.WellProjectLink = caseDto.WellProjectLink; @@ -77,6 +80,10 @@ public static void ConvertExisting(Case existing, CaseDto caseDto) existing.GasInjectorCount = caseDto.GasInjectorCount; existing.WaterInjectorCount = caseDto.WaterInjectorCount; existing.FacilitiesAvailability = caseDto.FacilitiesAvailability; + existing.CapexFactorFeasibilityStudies = caseDto.CapexFactorFeasibilityStudies; + existing.CapexFactorFEEDStudies = caseDto.CapexFactorFEEDStudies; + existing.NPV = caseDto.NPV; + existing.BreakEven = caseDto.BreakEven; existing.SharepointFileId = caseDto.SharepointFileId; existing.SharepointFileName = caseDto.SharepointFileName; } diff --git a/backend/api/Adapters/CaseDtoAdapter.cs b/backend/api/Adapters/CaseDtoAdapter.cs index 8eb988276..83dee507d 100644 --- a/backend/api/Adapters/CaseDtoAdapter.cs +++ b/backend/api/Adapters/CaseDtoAdapter.cs @@ -39,6 +39,10 @@ public static CaseDto Convert(Case case_) GasInjectorCount = case_.GasInjectorCount, WaterInjectorCount = case_.WaterInjectorCount, FacilitiesAvailability = case_.FacilitiesAvailability, + CapexFactorFeasibilityStudies = case_.CapexFactorFeasibilityStudies, + CapexFactorFEEDStudies = case_.CapexFactorFEEDStudies, + NPV = case_.NPV, + BreakEven = case_.BreakEven, SharepointFileId = case_.SharepointFileId, SharepointFileName = case_.SharepointFileName, SharepointFileUrl = case_.SharepointFileUrl, diff --git a/backend/api/Adapters/DrainageStrategyAdapter.cs b/backend/api/Adapters/DrainageStrategyAdapter.cs index 98e4488bd..d7a6e9b32 100644 --- a/backend/api/Adapters/DrainageStrategyAdapter.cs +++ b/backend/api/Adapters/DrainageStrategyAdapter.cs @@ -103,10 +103,10 @@ private static DrainageStrategy DrainagestrategyDtoToDrainagestrategy(DrainageSt ProjectId = drainageStrategyDto.ProjectId, NGLYield = drainageStrategyDto.NGLYield, ArtificialLift = drainageStrategyDto.ArtificialLift, + GasSolution = drainageStrategyDto.GasSolution, ProducerCount = drainageStrategyDto.ProducerCount, GasInjectorCount = drainageStrategyDto.GasInjectorCount, WaterInjectorCount = drainageStrategyDto.WaterInjectorCount, - FacilitiesAvailability = drainageStrategyDto.FacilitiesAvailability }; } existing.Id = drainageStrategyDto.Id; @@ -115,10 +115,10 @@ private static DrainageStrategy DrainagestrategyDtoToDrainagestrategy(DrainageSt existing.ProjectId = drainageStrategyDto.ProjectId; existing.NGLYield = drainageStrategyDto.NGLYield; existing.ArtificialLift = drainageStrategyDto.ArtificialLift; + existing.GasSolution = drainageStrategyDto.GasSolution; existing.ProducerCount = drainageStrategyDto.ProducerCount; existing.GasInjectorCount = drainageStrategyDto.GasInjectorCount; existing.WaterInjectorCount = drainageStrategyDto.WaterInjectorCount; - existing.FacilitiesAvailability = drainageStrategyDto.FacilitiesAvailability; return existing; } diff --git a/backend/api/Adapters/DrainageStrategyDtoAdapter.cs b/backend/api/Adapters/DrainageStrategyDtoAdapter.cs index 8b5de3a4a..9d994c91a 100644 --- a/backend/api/Adapters/DrainageStrategyDtoAdapter.cs +++ b/backend/api/Adapters/DrainageStrategyDtoAdapter.cs @@ -15,10 +15,10 @@ public static DrainageStrategyDto Convert(DrainageStrategy drainageStrategy, Phy ProjectId = drainageStrategy.ProjectId, NGLYield = drainageStrategy.NGLYield, ArtificialLift = drainageStrategy.ArtificialLift, + GasSolution = drainageStrategy.GasSolution, ProducerCount = drainageStrategy.ProducerCount, GasInjectorCount = drainageStrategy.GasInjectorCount, WaterInjectorCount = drainageStrategy.WaterInjectorCount, - FacilitiesAvailability = drainageStrategy.FacilitiesAvailability, ProductionProfileOil = Convert(drainageStrategy.ProductionProfileOil, unit), ProductionProfileGas = Convert(drainageStrategy.ProductionProfileGas, unit), ProductionProfileWater = Convert(drainageStrategy.ProductionProfileWater, unit), diff --git a/backend/api/Adapters/TopsideAdapter.cs b/backend/api/Adapters/TopsideAdapter.cs index 9f87b249b..8bf440fa8 100644 --- a/backend/api/Adapters/TopsideAdapter.cs +++ b/backend/api/Adapters/TopsideAdapter.cs @@ -15,7 +15,6 @@ public static Topside Convert(TopsideDto topsideDto) DryWeight = topsideDto.DryWeight, OilCapacity = topsideDto.OilCapacity, GasCapacity = topsideDto.GasCapacity, - FacilitiesAvailability = topsideDto.FacilitiesAvailability, ArtificialLift = topsideDto.ArtificialLift, Maturity = topsideDto.Maturity, Currency = topsideDto.Currency, @@ -61,7 +60,6 @@ public static void ConvertExisting(Topside existing, TopsideDto topsideDto) existing.DryWeight = topsideDto.DryWeight; existing.OilCapacity = topsideDto.OilCapacity; existing.GasCapacity = topsideDto.GasCapacity; - existing.FacilitiesAvailability = topsideDto.FacilitiesAvailability; existing.ArtificialLift = topsideDto.ArtificialLift; existing.Maturity = topsideDto.Maturity; existing.Currency = topsideDto.Currency; diff --git a/backend/api/Adapters/TopsideDtoAdapter.cs b/backend/api/Adapters/TopsideDtoAdapter.cs index df5150ef8..677c265cb 100644 --- a/backend/api/Adapters/TopsideDtoAdapter.cs +++ b/backend/api/Adapters/TopsideDtoAdapter.cs @@ -15,7 +15,6 @@ public static TopsideDto Convert(Topside topside) DryWeight = topside.DryWeight, OilCapacity = topside.OilCapacity, GasCapacity = topside.GasCapacity, - FacilitiesAvailability = topside.FacilitiesAvailability, ArtificialLift = topside.ArtificialLift, Maturity = topside.Maturity, Currency = topside.Currency, diff --git a/backend/api/Controllers/ExplorationsController.cs b/backend/api/Controllers/ExplorationsController.cs index 61af727f5..f7366d454 100644 --- a/backend/api/Controllers/ExplorationsController.cs +++ b/backend/api/Controllers/ExplorationsController.cs @@ -47,4 +47,10 @@ public ProjectDto UpdateExploration([FromBody] ExplorationDto eplorationDto) { return _explorationService.UpdateExploration(eplorationDto); } + + [HttpPut("new", Name = "NewUpdateExploration")] + public ExplorationDto NewUpdateExploration([FromBody] ExplorationDto eplorationDto) + { + return _explorationService.NewUpdateExploration(eplorationDto); + } } diff --git a/backend/api/Controllers/WellProjectsController.cs b/backend/api/Controllers/WellProjectsController.cs index 9e0b86c8b..7d18c1f49 100644 --- a/backend/api/Controllers/WellProjectsController.cs +++ b/backend/api/Controllers/WellProjectsController.cs @@ -48,4 +48,10 @@ public ProjectDto UpdateWellProject([FromBody] WellProjectDto wellProjectDto) { return _wellProjectService.UpdateWellProject(wellProjectDto); } + + [HttpPut("new", Name = "NewUpdateWellProject")] + public WellProjectDto NewUpdateWellProject([FromBody] WellProjectDto wellProjectDto) + { + return _wellProjectService.NewUpdateWellProject(wellProjectDto); + } } diff --git a/backend/api/Dtos/CaseDto.cs b/backend/api/Dtos/CaseDto.cs index 0ca9585db..da661b229 100644 --- a/backend/api/Dtos/CaseDto.cs +++ b/backend/api/Dtos/CaseDto.cs @@ -15,6 +15,10 @@ public class CaseDto public int GasInjectorCount { get; set; } public int WaterInjectorCount { get; set; } public double FacilitiesAvailability { get; set; } + public double CapexFactorFeasibilityStudies { get; set; } + public double CapexFactorFEEDStudies { get; set; } + public double NPV { get; set; } + public double BreakEven { get; set; } public DateTimeOffset DGADate { get; set; } public DateTimeOffset DGBDate { get; set; } public DateTimeOffset DGCDate { get; set; } diff --git a/backend/api/Dtos/DrainageStrategyDto.cs b/backend/api/Dtos/DrainageStrategyDto.cs index 497e2c448..facf6b8ed 100644 --- a/backend/api/Dtos/DrainageStrategyDto.cs +++ b/backend/api/Dtos/DrainageStrategyDto.cs @@ -13,6 +13,7 @@ public class DrainageStrategyDto public int GasInjectorCount { get; set; } public int WaterInjectorCount { get; set; } public ArtificialLift ArtificialLift { get; set; } + public GasSolution GasSolution { get; set; } public ProductionProfileOilDto? ProductionProfileOil { get; set; } public ProductionProfileGasDto? ProductionProfileGas { get; set; } public ProductionProfileWaterDto? ProductionProfileWater { get; set; } @@ -21,7 +22,6 @@ public class DrainageStrategyDto public NetSalesGasDto? NetSalesGas { get; set; } public Co2EmissionsDto? Co2Emissions { get; set; } public ProductionProfileNGLDto? ProductionProfileNGL { get; set; } - public double FacilitiesAvailability { get; set; } } public class ProductionProfileOilDto : TimeSeriesVolumeDto { diff --git a/backend/api/Dtos/TopsideDto.cs b/backend/api/Dtos/TopsideDto.cs index aa823806f..f98b9257e 100644 --- a/backend/api/Dtos/TopsideDto.cs +++ b/backend/api/Dtos/TopsideDto.cs @@ -13,7 +13,6 @@ public class TopsideDto public double DryWeight { get; set; } public double OilCapacity { get; set; } public double GasCapacity { get; set; } - public double FacilitiesAvailability { get; set; } public ArtificialLift ArtificialLift { get; set; } public Maturity Maturity { get; set; } public Currency Currency { get; set; } diff --git a/backend/api/Migrations/20221025103929_CaseChanges.Designer.cs b/backend/api/Migrations/20221025103929_CaseChanges.Designer.cs new file mode 100644 index 000000000..930137cca --- /dev/null +++ b/backend/api/Migrations/20221025103929_CaseChanges.Designer.cs @@ -0,0 +1,1845 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using api.Context; + +#nullable disable + +namespace api.Migrations +{ + [DbContext(typeof(DcdDbContext))] + [Migration("20221025103929_CaseChanges")] + partial class CaseChanges + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("api.Models.Case", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("APXDate") + .HasColumnType("datetimeoffset"); + + b.Property("APZDate") + .HasColumnType("datetimeoffset"); + + b.Property("ArtificialLift") + .HasColumnType("int"); + + b.Property("BreakEven") + .HasColumnType("float"); + + b.Property("CapexFactorFEEDStudies") + .HasColumnType("float"); + + b.Property("CapexFactorFeasibilityStudies") + .HasColumnType("float"); + + b.Property("CreateTime") + .HasColumnType("datetimeoffset"); + + b.Property("DG0Date") + .HasColumnType("datetimeoffset"); + + b.Property("DG1Date") + .HasColumnType("datetimeoffset"); + + b.Property("DG2Date") + .HasColumnType("datetimeoffset"); + + b.Property("DG3Date") + .HasColumnType("datetimeoffset"); + + b.Property("DG4Date") + .HasColumnType("datetimeoffset"); + + b.Property("DGADate") + .HasColumnType("datetimeoffset"); + + b.Property("DGBDate") + .HasColumnType("datetimeoffset"); + + b.Property("DGCDate") + .HasColumnType("datetimeoffset"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DrainageStrategyLink") + .HasColumnType("uniqueidentifier"); + + b.Property("ExplorationLink") + .HasColumnType("uniqueidentifier"); + + b.Property("FacilitiesAvailability") + .HasColumnType("float"); + + b.Property("GasInjectorCount") + .HasColumnType("int"); + + b.Property("ModifyTime") + .HasColumnType("datetimeoffset"); + + b.Property("NPV") + .HasColumnType("float"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProducerCount") + .HasColumnType("int"); + + b.Property("ProductionStrategyOverview") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReferenceCase") + .HasColumnType("bit"); + + b.Property("SharepointFileId") + .HasColumnType("nvarchar(max)"); + + b.Property("SharepointFileName") + .HasColumnType("nvarchar(max)"); + + b.Property("SharepointFileUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("SubstructureLink") + .HasColumnType("uniqueidentifier"); + + b.Property("SurfLink") + .HasColumnType("uniqueidentifier"); + + b.Property("TopsideLink") + .HasColumnType("uniqueidentifier"); + + b.Property("TransportLink") + .HasColumnType("uniqueidentifier"); + + b.Property("WaterInjectorCount") + .HasColumnType("int"); + + b.Property("WellProjectLink") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Cases"); + }); + + modelBuilder.Entity("api.Models.Co2Emissions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrainageStrategy.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DrainageStrategy.Id") + .IsUnique(); + + b.ToTable("Co2Emissions"); + }); + + modelBuilder.Entity("api.Models.CountryOfficeCost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Exploration.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Exploration.Id") + .IsUnique(); + + b.ToTable("CountryOfficeCost"); + }); + + modelBuilder.Entity("api.Models.DevelopmentOperationalWellCosts", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnnualWellInterventionCostPerWell") + .HasColumnType("float"); + + b.Property("PluggingAndAbandonment") + .HasColumnType("float"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("RigMobDemob") + .HasColumnType("float"); + + b.Property("RigUpgrading") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId") + .IsUnique(); + + b.ToTable("DevelopmentOperationalWellCosts"); + }); + + modelBuilder.Entity("api.Models.DrainageStrategy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ArtificialLift") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("GasInjectorCount") + .HasColumnType("int"); + + b.Property("GasSolution") + .HasColumnType("int"); + + b.Property("NGLYield") + .HasColumnType("float"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProducerCount") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("WaterInjectorCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("DrainageStrategies"); + }); + + modelBuilder.Entity("api.Models.DrillingSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("DrillingSchedule"); + }); + + modelBuilder.Entity("api.Models.Exploration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("RigMobDemob") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Explorations"); + }); + + modelBuilder.Entity("api.Models.ExplorationCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Exploration.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Override") + .HasColumnType("bit"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Exploration.Id") + .IsUnique(); + + b.ToTable("ExplorationCostProfile"); + }); + + modelBuilder.Entity("api.Models.ExplorationOperationalWellCosts", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppraisalProjectDrillingCosts") + .HasColumnType("float"); + + b.Property("AppraisalRigMobDemob") + .HasColumnType("float"); + + b.Property("ExplorationProjectDrillingCosts") + .HasColumnType("float"); + + b.Property("ExplorationRigMobDemob") + .HasColumnType("float"); + + b.Property("ExplorationRigUpgrading") + .HasColumnType("float"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId") + .IsUnique(); + + b.ToTable("ExplorationOperationalWellCosts"); + }); + + modelBuilder.Entity("api.Models.ExplorationWell", b => + { + b.Property("ExplorationId") + .HasColumnType("uniqueidentifier"); + + b.Property("WellId") + .HasColumnType("uniqueidentifier"); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("DrillingScheduleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("ExplorationId", "WellId"); + + b.HasIndex("DrillingScheduleId"); + + b.HasIndex("WellId"); + + b.ToTable("ExplorationWell"); + }); + + modelBuilder.Entity("api.Models.FuelFlaringAndLosses", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrainageStrategy.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DrainageStrategy.Id") + .IsUnique(); + + b.ToTable("FuelFlaringAndLosses"); + }); + + modelBuilder.Entity("api.Models.GAndGAdminCost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Exploration.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Exploration.Id") + .IsUnique(); + + b.ToTable("GAndGAdminCost"); + }); + + modelBuilder.Entity("api.Models.NetSalesGas", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrainageStrategy.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DrainageStrategy.Id") + .IsUnique(); + + b.ToTable("NetSalesGas"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileGas", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrainageStrategy.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DrainageStrategy.Id") + .IsUnique(); + + b.ToTable("ProductionProfileGas"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileNGL", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrainageStrategy.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DrainageStrategy.Id") + .IsUnique(); + + b.ToTable("ProductionProfileNGL"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileOil", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrainageStrategy.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DrainageStrategy.Id") + .IsUnique(); + + b.ToTable("ProductionProfileOil"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileWater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrainageStrategy.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DrainageStrategy.Id") + .IsUnique(); + + b.ToTable("ProductionProfileWater"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileWaterInjection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrainageStrategy.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DrainageStrategy.Id") + .IsUnique(); + + b.ToTable("ProductionProfileWaterInjection"); + }); + + modelBuilder.Entity("api.Models.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CommonLibraryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CommonLibraryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Country") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreateDate") + .HasColumnType("datetimeoffset"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FusionProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PhysicalUnit") + .HasColumnType("int"); + + b.Property("ProjectCategory") + .HasColumnType("int"); + + b.Property("ProjectPhase") + .HasColumnType("int"); + + b.Property("SharepointSiteUrl") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Projects"); + }); + + modelBuilder.Entity("api.Models.SeismicAcquisitionAndProcessing", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Exploration.Id") + .HasColumnType("uniqueidentifier"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Exploration.Id") + .IsUnique(); + + b.ToTable("SeismicAcquisitionAndProcessing"); + }); + + modelBuilder.Entity("api.Models.Substructure", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApprovedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Concept") + .HasColumnType("int"); + + b.Property("CostYear") + .HasColumnType("int"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("DG3Date") + .HasColumnType("datetimeoffset"); + + b.Property("DG4Date") + .HasColumnType("datetimeoffset"); + + b.Property("DryWeight") + .HasColumnType("float"); + + b.Property("LastChangedDate") + .HasColumnType("datetimeoffset"); + + b.Property("Maturity") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProspVersion") + .HasColumnType("datetimeoffset"); + + b.Property("Source") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Substructures"); + }); + + modelBuilder.Entity("api.Models.SubstructureCessationCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Substructure.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Substructure.Id") + .IsUnique(); + + b.ToTable("SubstructureCessationCostProfiles"); + }); + + modelBuilder.Entity("api.Models.SubstructureCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Substructure.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Substructure.Id") + .IsUnique(); + + b.ToTable("SubstructureCostProfiles"); + }); + + modelBuilder.Entity("api.Models.Surf", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApprovedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ArtificialLift") + .HasColumnType("int"); + + b.Property("CessationCost") + .HasColumnType("float"); + + b.Property("CostYear") + .HasColumnType("int"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("DG3Date") + .HasColumnType("datetimeoffset"); + + b.Property("DG4Date") + .HasColumnType("datetimeoffset"); + + b.Property("GasInjectorCount") + .HasColumnType("int"); + + b.Property("InfieldPipelineSystemLength") + .HasColumnType("float"); + + b.Property("LastChangedDate") + .HasColumnType("datetimeoffset"); + + b.Property("Maturity") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProducerCount") + .HasColumnType("int"); + + b.Property("ProductionFlowline") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProspVersion") + .HasColumnType("datetimeoffset"); + + b.Property("RiserCount") + .HasColumnType("int"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("TemplateCount") + .HasColumnType("int"); + + b.Property("UmbilicalSystemLength") + .HasColumnType("float"); + + b.Property("WaterInjectorCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Surfs"); + }); + + modelBuilder.Entity("api.Models.SurfCessationCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Surf.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Surf.Id") + .IsUnique(); + + b.ToTable("SurfCessationCostProfiles"); + }); + + modelBuilder.Entity("api.Models.SurfCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Surf.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Surf.Id") + .IsUnique(); + + b.ToTable("SurfCostProfile"); + }); + + modelBuilder.Entity("api.Models.Topside", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApprovedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ArtificialLift") + .HasColumnType("int"); + + b.Property("CO2OnMaxGasProfile") + .HasColumnType("float"); + + b.Property("CO2OnMaxOilProfile") + .HasColumnType("float"); + + b.Property("CO2OnMaxWaterInjectionProfile") + .HasColumnType("float"); + + b.Property("CO2ShareGasProfile") + .HasColumnType("float"); + + b.Property("CO2ShareOilProfile") + .HasColumnType("float"); + + b.Property("CO2ShareWaterInjectionProfile") + .HasColumnType("float"); + + b.Property("CostYear") + .HasColumnType("int"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("DG3Date") + .HasColumnType("datetimeoffset"); + + b.Property("DG4Date") + .HasColumnType("datetimeoffset"); + + b.Property("DryWeight") + .HasColumnType("float"); + + b.Property("FacilityOpex") + .HasColumnType("float"); + + b.Property("FlaredGas") + .HasColumnType("float"); + + b.Property("FuelConsumption") + .HasColumnType("float"); + + b.Property("GasCapacity") + .HasColumnType("float"); + + b.Property("GasInjectorCount") + .HasColumnType("int"); + + b.Property("LastChangedDate") + .HasColumnType("datetimeoffset"); + + b.Property("Maturity") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OilCapacity") + .HasColumnType("float"); + + b.Property("ProducerCount") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProspVersion") + .HasColumnType("datetimeoffset"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("WaterInjectorCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Topsides"); + }); + + modelBuilder.Entity("api.Models.TopsideCessationCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Topside.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Topside.Id") + .IsUnique(); + + b.ToTable("TopsideCessationCostProfiles"); + }); + + modelBuilder.Entity("api.Models.TopsideCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Topside.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Topside.Id") + .IsUnique(); + + b.ToTable("TopsideCostProfiles"); + }); + + modelBuilder.Entity("api.Models.Transport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CostYear") + .HasColumnType("int"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("DG3Date") + .HasColumnType("datetimeoffset"); + + b.Property("DG4Date") + .HasColumnType("datetimeoffset"); + + b.Property("GasExportPipelineLength") + .HasColumnType("float"); + + b.Property("LastChangedDate") + .HasColumnType("datetimeoffset"); + + b.Property("Maturity") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OilExportPipelineLength") + .HasColumnType("float"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProspVersion") + .HasColumnType("datetimeoffset"); + + b.Property("Source") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Transports"); + }); + + modelBuilder.Entity("api.Models.TransportCessationCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Transport.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Transport.Id") + .IsUnique(); + + b.ToTable("TransportCessationCostProfiles"); + }); + + modelBuilder.Entity("api.Models.TransportCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("Transport.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Transport.Id") + .IsUnique(); + + b.ToTable("TransportCostProfile"); + }); + + modelBuilder.Entity("api.Models.Well", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DrillingDays") + .HasColumnType("float"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PlugingAndAbandonmentCost") + .HasColumnType("float"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("WellCategory") + .HasColumnType("int"); + + b.Property("WellCost") + .HasColumnType("float"); + + b.Property("WellInterventionCost") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Wells"); + }); + + modelBuilder.Entity("api.Models.WellProject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AnnualWellInterventionCost") + .HasColumnType("float"); + + b.Property("ArtificialLift") + .HasColumnType("int"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PluggingAndAbandonment") + .HasColumnType("float"); + + b.Property("ProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("RigMobDemob") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("WellProjects"); + }); + + modelBuilder.Entity("api.Models.WellProjectCostProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Currency") + .HasColumnType("int"); + + b.Property("EPAVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InternalData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Override") + .HasColumnType("bit"); + + b.Property("StartYear") + .HasColumnType("int"); + + b.Property("WellProject.Id") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("WellProject.Id") + .IsUnique(); + + b.ToTable("WellProjectCostProfile"); + }); + + modelBuilder.Entity("api.Models.WellProjectWell", b => + { + b.Property("WellProjectId") + .HasColumnType("uniqueidentifier"); + + b.Property("WellId") + .HasColumnType("uniqueidentifier"); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("DrillingScheduleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("WellProjectId", "WellId"); + + b.HasIndex("DrillingScheduleId"); + + b.HasIndex("WellId"); + + b.ToTable("WellProjectWell"); + }); + + modelBuilder.Entity("api.Models.Case", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("Cases") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.Co2Emissions", b => + { + b.HasOne("api.Models.DrainageStrategy", "DrainageStrategy") + .WithOne("Co2Emissions") + .HasForeignKey("api.Models.Co2Emissions", "DrainageStrategy.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrainageStrategy"); + }); + + modelBuilder.Entity("api.Models.CountryOfficeCost", b => + { + b.HasOne("api.Models.Exploration", "Exploration") + .WithOne("CountryOfficeCost") + .HasForeignKey("api.Models.CountryOfficeCost", "Exploration.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Exploration"); + }); + + modelBuilder.Entity("api.Models.DevelopmentOperationalWellCosts", b => + { + b.HasOne("api.Models.Project", "Project") + .WithOne("DevelopmentOperationalWellCosts") + .HasForeignKey("api.Models.DevelopmentOperationalWellCosts", "ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.DrainageStrategy", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("DrainageStrategies") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.Exploration", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("Explorations") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.ExplorationCostProfile", b => + { + b.HasOne("api.Models.Exploration", "Exploration") + .WithOne("CostProfile") + .HasForeignKey("api.Models.ExplorationCostProfile", "Exploration.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Exploration"); + }); + + modelBuilder.Entity("api.Models.ExplorationOperationalWellCosts", b => + { + b.HasOne("api.Models.Project", "Project") + .WithOne("ExplorationOperationalWellCosts") + .HasForeignKey("api.Models.ExplorationOperationalWellCosts", "ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.ExplorationWell", b => + { + b.HasOne("api.Models.DrillingSchedule", "DrillingSchedule") + .WithMany() + .HasForeignKey("DrillingScheduleId"); + + b.HasOne("api.Models.Exploration", "Exploration") + .WithMany("ExplorationWells") + .HasForeignKey("ExplorationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api.Models.Well", "Well") + .WithMany("ExplorationWells") + .HasForeignKey("WellId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("DrillingSchedule"); + + b.Navigation("Exploration"); + + b.Navigation("Well"); + }); + + modelBuilder.Entity("api.Models.FuelFlaringAndLosses", b => + { + b.HasOne("api.Models.DrainageStrategy", "DrainageStrategy") + .WithOne("FuelFlaringAndLosses") + .HasForeignKey("api.Models.FuelFlaringAndLosses", "DrainageStrategy.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrainageStrategy"); + }); + + modelBuilder.Entity("api.Models.GAndGAdminCost", b => + { + b.HasOne("api.Models.Exploration", "Exploration") + .WithOne("GAndGAdminCost") + .HasForeignKey("api.Models.GAndGAdminCost", "Exploration.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Exploration"); + }); + + modelBuilder.Entity("api.Models.NetSalesGas", b => + { + b.HasOne("api.Models.DrainageStrategy", "DrainageStrategy") + .WithOne("NetSalesGas") + .HasForeignKey("api.Models.NetSalesGas", "DrainageStrategy.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrainageStrategy"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileGas", b => + { + b.HasOne("api.Models.DrainageStrategy", "DrainageStrategy") + .WithOne("ProductionProfileGas") + .HasForeignKey("api.Models.ProductionProfileGas", "DrainageStrategy.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrainageStrategy"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileNGL", b => + { + b.HasOne("api.Models.DrainageStrategy", "DrainageStrategy") + .WithOne("ProductionProfileNGL") + .HasForeignKey("api.Models.ProductionProfileNGL", "DrainageStrategy.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrainageStrategy"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileOil", b => + { + b.HasOne("api.Models.DrainageStrategy", "DrainageStrategy") + .WithOne("ProductionProfileOil") + .HasForeignKey("api.Models.ProductionProfileOil", "DrainageStrategy.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrainageStrategy"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileWater", b => + { + b.HasOne("api.Models.DrainageStrategy", "DrainageStrategy") + .WithOne("ProductionProfileWater") + .HasForeignKey("api.Models.ProductionProfileWater", "DrainageStrategy.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrainageStrategy"); + }); + + modelBuilder.Entity("api.Models.ProductionProfileWaterInjection", b => + { + b.HasOne("api.Models.DrainageStrategy", "DrainageStrategy") + .WithOne("ProductionProfileWaterInjection") + .HasForeignKey("api.Models.ProductionProfileWaterInjection", "DrainageStrategy.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrainageStrategy"); + }); + + modelBuilder.Entity("api.Models.SeismicAcquisitionAndProcessing", b => + { + b.HasOne("api.Models.Exploration", "Exploration") + .WithOne("SeismicAcquisitionAndProcessing") + .HasForeignKey("api.Models.SeismicAcquisitionAndProcessing", "Exploration.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Exploration"); + }); + + modelBuilder.Entity("api.Models.Substructure", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("Substructures") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.SubstructureCessationCostProfile", b => + { + b.HasOne("api.Models.Substructure", "Substructure") + .WithOne("CessationCostProfile") + .HasForeignKey("api.Models.SubstructureCessationCostProfile", "Substructure.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Substructure"); + }); + + modelBuilder.Entity("api.Models.SubstructureCostProfile", b => + { + b.HasOne("api.Models.Substructure", "Substructure") + .WithOne("CostProfile") + .HasForeignKey("api.Models.SubstructureCostProfile", "Substructure.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Substructure"); + }); + + modelBuilder.Entity("api.Models.Surf", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("Surfs") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.SurfCessationCostProfile", b => + { + b.HasOne("api.Models.Surf", "Surf") + .WithOne("CessationCostProfile") + .HasForeignKey("api.Models.SurfCessationCostProfile", "Surf.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Surf"); + }); + + modelBuilder.Entity("api.Models.SurfCostProfile", b => + { + b.HasOne("api.Models.Surf", "Surf") + .WithOne("CostProfile") + .HasForeignKey("api.Models.SurfCostProfile", "Surf.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Surf"); + }); + + modelBuilder.Entity("api.Models.Topside", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("Topsides") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.TopsideCessationCostProfile", b => + { + b.HasOne("api.Models.Topside", "Topside") + .WithOne("CessationCostProfile") + .HasForeignKey("api.Models.TopsideCessationCostProfile", "Topside.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Topside"); + }); + + modelBuilder.Entity("api.Models.TopsideCostProfile", b => + { + b.HasOne("api.Models.Topside", "Topside") + .WithOne("CostProfile") + .HasForeignKey("api.Models.TopsideCostProfile", "Topside.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Topside"); + }); + + modelBuilder.Entity("api.Models.Transport", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("Transports") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.TransportCessationCostProfile", b => + { + b.HasOne("api.Models.Transport", "Transport") + .WithOne("CessationCostProfile") + .HasForeignKey("api.Models.TransportCessationCostProfile", "Transport.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Transport"); + }); + + modelBuilder.Entity("api.Models.TransportCostProfile", b => + { + b.HasOne("api.Models.Transport", "Transport") + .WithOne("CostProfile") + .HasForeignKey("api.Models.TransportCostProfile", "Transport.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Transport"); + }); + + modelBuilder.Entity("api.Models.Well", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("Wells") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.WellProject", b => + { + b.HasOne("api.Models.Project", "Project") + .WithMany("WellProjects") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("api.Models.WellProjectCostProfile", b => + { + b.HasOne("api.Models.WellProject", "WellProject") + .WithOne("CostProfile") + .HasForeignKey("api.Models.WellProjectCostProfile", "WellProject.Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WellProject"); + }); + + modelBuilder.Entity("api.Models.WellProjectWell", b => + { + b.HasOne("api.Models.DrillingSchedule", "DrillingSchedule") + .WithMany() + .HasForeignKey("DrillingScheduleId"); + + b.HasOne("api.Models.Well", "Well") + .WithMany("WellProjectWells") + .HasForeignKey("WellId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("api.Models.WellProject", "WellProject") + .WithMany("WellProjectWells") + .HasForeignKey("WellProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DrillingSchedule"); + + b.Navigation("Well"); + + b.Navigation("WellProject"); + }); + + modelBuilder.Entity("api.Models.DrainageStrategy", b => + { + b.Navigation("Co2Emissions"); + + b.Navigation("FuelFlaringAndLosses"); + + b.Navigation("NetSalesGas"); + + b.Navigation("ProductionProfileGas"); + + b.Navigation("ProductionProfileNGL"); + + b.Navigation("ProductionProfileOil"); + + b.Navigation("ProductionProfileWater"); + + b.Navigation("ProductionProfileWaterInjection"); + }); + + modelBuilder.Entity("api.Models.Exploration", b => + { + b.Navigation("CostProfile"); + + b.Navigation("CountryOfficeCost"); + + b.Navigation("ExplorationWells"); + + b.Navigation("GAndGAdminCost"); + + b.Navigation("SeismicAcquisitionAndProcessing"); + }); + + modelBuilder.Entity("api.Models.Project", b => + { + b.Navigation("Cases"); + + b.Navigation("DevelopmentOperationalWellCosts"); + + b.Navigation("DrainageStrategies"); + + b.Navigation("ExplorationOperationalWellCosts"); + + b.Navigation("Explorations"); + + b.Navigation("Substructures"); + + b.Navigation("Surfs"); + + b.Navigation("Topsides"); + + b.Navigation("Transports"); + + b.Navigation("WellProjects"); + + b.Navigation("Wells"); + }); + + modelBuilder.Entity("api.Models.Substructure", b => + { + b.Navigation("CessationCostProfile"); + + b.Navigation("CostProfile"); + }); + + modelBuilder.Entity("api.Models.Surf", b => + { + b.Navigation("CessationCostProfile"); + + b.Navigation("CostProfile"); + }); + + modelBuilder.Entity("api.Models.Topside", b => + { + b.Navigation("CessationCostProfile"); + + b.Navigation("CostProfile"); + }); + + modelBuilder.Entity("api.Models.Transport", b => + { + b.Navigation("CessationCostProfile"); + + b.Navigation("CostProfile"); + }); + + modelBuilder.Entity("api.Models.Well", b => + { + b.Navigation("ExplorationWells"); + + b.Navigation("WellProjectWells"); + }); + + modelBuilder.Entity("api.Models.WellProject", b => + { + b.Navigation("CostProfile"); + + b.Navigation("WellProjectWells"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/api/Migrations/20221025103929_CaseChanges.cs b/backend/api/Migrations/20221025103929_CaseChanges.cs new file mode 100644 index 000000000..c80d3d575 --- /dev/null +++ b/backend/api/Migrations/20221025103929_CaseChanges.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api.Migrations +{ + public partial class CaseChanges : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FacilitiesAvailability", + table: "Topsides"); + + migrationBuilder.DropColumn( + name: "FacilitiesAvailability", + table: "DrainageStrategies"); + + migrationBuilder.AddColumn( + name: "GasSolution", + table: "DrainageStrategies", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "BreakEven", + table: "Cases", + type: "float", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.AddColumn( + name: "NPV", + table: "Cases", + type: "float", + nullable: false, + defaultValue: 0.0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "GasSolution", + table: "DrainageStrategies"); + + migrationBuilder.DropColumn( + name: "BreakEven", + table: "Cases"); + + migrationBuilder.DropColumn( + name: "NPV", + table: "Cases"); + + migrationBuilder.AddColumn( + name: "FacilitiesAvailability", + table: "Topsides", + type: "float", + nullable: false, + defaultValue: 0.0); + + migrationBuilder.AddColumn( + name: "FacilitiesAvailability", + table: "DrainageStrategies", + type: "float", + nullable: false, + defaultValue: 0.0); + } + } +} diff --git a/backend/api/Migrations/DcdDbContextModelSnapshot.cs b/backend/api/Migrations/DcdDbContextModelSnapshot.cs index 45783d43d..6ad3a91b3 100644 --- a/backend/api/Migrations/DcdDbContextModelSnapshot.cs +++ b/backend/api/Migrations/DcdDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -37,6 +37,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ArtificialLift") .HasColumnType("int"); + b.Property("BreakEven") + .HasColumnType("float"); + b.Property("CapexFactorFEEDStudies") .HasColumnType("float"); @@ -89,6 +92,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ModifyTime") .HasColumnType("datetimeoffset"); + b.Property("NPV") + .HasColumnType("float"); + b.Property("Name") .IsRequired() .HasColumnType("nvarchar(max)"); @@ -236,12 +242,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("nvarchar(max)"); - b.Property("FacilitiesAvailability") - .HasColumnType("float"); - b.Property("GasInjectorCount") .HasColumnType("int"); + b.Property("GasSolution") + .HasColumnType("int"); + b.Property("NGLYield") .HasColumnType("float"); @@ -981,9 +987,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DryWeight") .HasColumnType("float"); - b.Property("FacilitiesAvailability") - .HasColumnType("float"); - b.Property("FacilityOpex") .HasColumnType("float"); diff --git a/backend/api/Models/Case.cs b/backend/api/Models/Case.cs index 855f45f76..37b1cfb65 100644 --- a/backend/api/Models/Case.cs +++ b/backend/api/Models/Case.cs @@ -28,8 +28,10 @@ public class Case public int GasInjectorCount { get; set; } public int WaterInjectorCount { get; set; } public double FacilitiesAvailability { get; set; } - public double CapexFactorFeasibilityStudies { get; set; } = 0.015; - public double CapexFactorFEEDStudies { get; set; } = 0.015; + public double CapexFactorFeasibilityStudies { get; set; } + public double CapexFactorFEEDStudies { get; set; } + public double NPV { get; set; } + public double BreakEven { get; set; } public Guid DrainageStrategyLink { get; set; } = Guid.Empty; public Guid WellProjectLink { get; set; } = Guid.Empty; public Guid SurfLink { get; set; } = Guid.Empty; diff --git a/backend/api/Models/DrainageStrategy.cs b/backend/api/Models/DrainageStrategy.cs index 6652da566..e732c3153 100644 --- a/backend/api/Models/DrainageStrategy.cs +++ b/backend/api/Models/DrainageStrategy.cs @@ -15,6 +15,7 @@ public class DrainageStrategy public int GasInjectorCount { get; set; } public int WaterInjectorCount { get; set; } public ArtificialLift ArtificialLift { get; set; } + public GasSolution GasSolution { get; set; } public ProductionProfileOil? ProductionProfileOil { get; set; } public ProductionProfileGas? ProductionProfileGas { get; set; } public ProductionProfileWater? ProductionProfileWater { get; set; } @@ -23,7 +24,11 @@ public class DrainageStrategy public NetSalesGas? NetSalesGas { get; set; } public Co2Emissions? Co2Emissions { get; set; } public ProductionProfileNGL? ProductionProfileNGL { get; set; } - public double FacilitiesAvailability { get; set; } +} +public enum GasSolution +{ + Export, + Injection, } public class ProductionProfileOil : TimeSeriesVolume { diff --git a/backend/api/Models/Topside.cs b/backend/api/Models/Topside.cs index eb830d499..cbe91fa3b 100644 --- a/backend/api/Models/Topside.cs +++ b/backend/api/Models/Topside.cs @@ -13,7 +13,6 @@ public class Topside public double DryWeight { get; set; } public double OilCapacity { get; set; } public double GasCapacity { get; set; } - public double FacilitiesAvailability { get; set; } public ArtificialLift ArtificialLift { get; set; } public Maturity Maturity { get; set; } public Currency Currency { get; set; } diff --git a/backend/api/SampleData/Generators/SampleAssetGenerator.cs b/backend/api/SampleData/Generators/SampleAssetGenerator.cs index a36d0e547..6f00da753 100644 --- a/backend/api/SampleData/Generators/SampleAssetGenerator.cs +++ b/backend/api/SampleData/Generators/SampleAssetGenerator.cs @@ -228,7 +228,6 @@ public static ProjectsBuilder initializeAssets() OilCapacity = 50.0, GasCapacity = 75.0, DryWeight = 45.1, - FacilitiesAvailability = 0.8, ArtificialLift = ArtificialLift.GasLift } .WithCostProfile(new TopsideCostProfile diff --git a/backend/api/Services/CaseService.cs b/backend/api/Services/CaseService.cs index 9a67f5266..739a9176e 100644 --- a/backend/api/Services/CaseService.cs +++ b/backend/api/Services/CaseService.cs @@ -25,7 +25,6 @@ public CaseService(DcdDbContext context, ProjectService projectService, ILoggerF public ProjectDto CreateCase(CaseDto caseDto) { var case_ = CaseAdapter.Convert(caseDto); - case_.CreateTime = DateTime.UtcNow; if (case_.DG4Date == DateTimeOffset.MinValue) { case_.DG4Date = new DateTimeOffset(2030, 1, 1, 0, 0, 0, 0, new GregorianCalendar(), TimeSpan.Zero); @@ -39,7 +38,6 @@ public ProjectDto CreateCase(CaseDto caseDto) public ProjectDto NewCreateCase(CaseDto caseDto) { - var drainageStrategyService = _serviceProvider.GetRequiredService(); var topsideService = _serviceProvider.GetRequiredService(); var surfService = _serviceProvider.GetRequiredService(); @@ -49,9 +47,10 @@ public ProjectDto NewCreateCase(CaseDto caseDto) var wellProjectService = _serviceProvider.GetRequiredService(); var case_ = CaseAdapter.Convert(caseDto); - case_.CreateTime = DateTime.UtcNow; var project = _projectService.GetProject(case_.ProjectId); case_.Project = project; + case_.CapexFactorFeasibilityStudies = 0.015; + case_.CapexFactorFEEDStudies = 0.015; var createdCase = _context.Cases!.Add(case_); _context.SaveChanges(); @@ -127,16 +126,7 @@ public ProjectDto DuplicateCase(Guid caseId) var project = _projectService.GetProject(case_.ProjectId); case_.Project = project; - List duplicateCaseNames = new List(); - foreach (Case c in project.Cases!) - { - string copyNumber = c.Name.Substring(c.Name.Length - 1, 1); - if (c.Name.Equals(case_.Name) || c.Name.Equals(case_.Name + " - copy #" + copyNumber)) - { - duplicateCaseNames.Add(c); - } - } - case_.Name = case_.Name + " - copy #" + duplicateCaseNames.Count(); + case_.Name = case_.Name + " - copy"; _context.Cases!.Add(case_); _context.SaveChanges(); return _projectService.GetProjectDto(project.Id); diff --git a/backend/api/Services/ExplorationService.cs b/backend/api/Services/ExplorationService.cs index 6aa989172..23d273bdb 100644 --- a/backend/api/Services/ExplorationService.cs +++ b/backend/api/Services/ExplorationService.cs @@ -263,6 +263,36 @@ public ProjectDto UpdateExploration(ExplorationDto updatedExplorationDto) return _projectService.GetProjectDto(existing.ProjectId); } + public ExplorationDto NewUpdateExploration(ExplorationDto updatedExplorationDto) + { + var existing = GetExploration(updatedExplorationDto.Id); + ExplorationAdapter.ConvertExisting(existing, updatedExplorationDto); + + if (updatedExplorationDto.CostProfile == null && existing.CostProfile != null) + { + _context.ExplorationCostProfile!.Remove(existing.CostProfile); + } + + if (updatedExplorationDto.GAndGAdminCost == null && existing.GAndGAdminCost != null) + { + _context.GAndGAdminCost!.Remove(existing.GAndGAdminCost); + } + + if (updatedExplorationDto.SeismicAcquisitionAndProcessing == null && existing.SeismicAcquisitionAndProcessing != null) + { + _context.SeismicAcquisitionAndProcessing!.Remove(existing.SeismicAcquisitionAndProcessing); + } + + if (updatedExplorationDto.CountryOfficeCost == null && existing.CountryOfficeCost != null) + { + _context.CountryOfficeCost!.Remove(existing.CountryOfficeCost); + } + + var updatedExploration = _context.Explorations!.Update(existing); + _context.SaveChanges(); + return ExplorationDtoAdapter.Convert(updatedExploration.Entity); + } + public Exploration GetExploration(Guid explorationId) { diff --git a/backend/api/Services/WellProjectService.cs b/backend/api/Services/WellProjectService.cs index e0428788f..7c7bde430 100644 --- a/backend/api/Services/WellProjectService.cs +++ b/backend/api/Services/WellProjectService.cs @@ -246,6 +246,21 @@ public ProjectDto UpdateWellProject(WellProjectDto updatedWellProject) return _projectService.GetProjectDto(updatedWellProject.ProjectId); } + public WellProjectDto NewUpdateWellProject(WellProjectDto updatedWellProjectDto) + { + var existing = GetWellProject(updatedWellProjectDto.Id); + WellProjectAdapter.ConvertExisting(existing, updatedWellProjectDto); + + if (updatedWellProjectDto.CostProfile == null && existing.CostProfile != null) + { + _context.WellProjectCostProfile!.Remove(existing.CostProfile); + } + + var updatedWellProject = _context.WellProjects!.Update(existing); + _context.SaveChanges(); + return WellProjectDtoAdapter.Convert(updatedWellProject.Entity); + } + public WellProject GetWellProject(Guid wellProjectId) { var wellProject = _context.WellProjects! diff --git a/backend/tests/Services/CaseServiceShould.cs b/backend/tests/Services/CaseServiceShould.cs index 045eda77f..f754b3a6b 100644 --- a/backend/tests/Services/CaseServiceShould.cs +++ b/backend/tests/Services/CaseServiceShould.cs @@ -54,7 +54,6 @@ public void CreateNewCase() Assert.Equal(expected.Description, actual.Description); Assert.Equal(expected.ReferenceCase, actual.ReferenceCase); Assert.Equal(expected.DG4Date, actual.DG4Date); - Assert.Equal(expected.ModifyTime, actual.ModifyTime); Assert.Equal(expected.ReferenceCase, actual.ReferenceCase); } diff --git a/backend/tests/Services/TopsideServiceShould.cs b/backend/tests/Services/TopsideServiceShould.cs index f685647da..20c345da9 100644 --- a/backend/tests/Services/TopsideServiceShould.cs +++ b/backend/tests/Services/TopsideServiceShould.cs @@ -195,7 +195,6 @@ private static Topside CreateTestTopside(Project project) DryWeight = 0.3e6, OilCapacity = 50e6, GasCapacity = 0, - FacilitiesAvailability = 0.2, ArtificialLift = ArtificialLift.GasLift, Maturity = Maturity.B }.WithCostProfile(new TopsideCostProfile() @@ -223,7 +222,6 @@ private static Topside CreateUpdatedTopside(Project project, Topside oldTopside) DryWeight = 5.3e6, OilCapacity = 52e6, GasCapacity = 7, - FacilitiesAvailability = 1.2, ArtificialLift = ArtificialLift.NoArtificialLift, Maturity = Maturity.C }.WithCostProfile(new TopsideCostProfile() diff --git a/backend/tests/TestHelper.cs b/backend/tests/TestHelper.cs index 2bea8b78d..21dce7e49 100644 --- a/backend/tests/TestHelper.cs +++ b/backend/tests/TestHelper.cs @@ -536,7 +536,6 @@ public static void CompareTopsides(Topside expected, Topside actual) Assert.Equal(expected.DryWeight, actual.DryWeight); Assert.Equal(expected.GasCapacity, actual.GasCapacity); Assert.Equal(expected.OilCapacity, actual.OilCapacity); - Assert.Equal(expected.FacilitiesAvailability, actual.FacilitiesAvailability); Assert.Equal(expected.ArtificialLift, actual.ArtificialLift); TestHelper.CompareCosts(expected.CostProfile, actual.CostProfile); } @@ -556,7 +555,6 @@ public static void CompareTopsides(Topside expected, TopsideDto actual) Assert.Equal(expected.DryWeight, actual.DryWeight); Assert.Equal(expected.GasCapacity, actual.GasCapacity); Assert.Equal(expected.OilCapacity, actual.OilCapacity); - Assert.Equal(expected.FacilitiesAvailability, actual.FacilitiesAvailability); Assert.Equal(expected.ArtificialLift, actual.ArtificialLift); TestHelper.CompareCosts(expected.CostProfile, actual.CostProfile); } diff --git a/frontend/src/Components/Case/EditCaseModal.tsx b/frontend/src/Components/Case/EditCaseModal.tsx index 788658e1c..8a05c004a 100644 --- a/frontend/src/Components/Case/EditCaseModal.tsx +++ b/frontend/src/Components/Case/EditCaseModal.tsx @@ -4,6 +4,7 @@ import { Input, Label, NativeSelect, + Progress, } from "@equinor/eds-core-react" import { useState, @@ -95,6 +96,7 @@ const EditCaseModal = ({ const [producerCount, setProducerWells] = useState() const [gasInjectorCount, setGasInjectorWells] = useState() const [waterInjectorCount, setWaterInjectorWells] = useState() + const [isLoading, setIsLoading] = useState(false) const [caseItem, setCaseItem] = useState() @@ -137,6 +139,8 @@ const EditCaseModal = ({ e.preventDefault() try { + setIsLoading(true) + let projectResult: Project if (editMode && caseItem) { const newCase = Case.Copy(caseItem) @@ -150,6 +154,7 @@ const EditCaseModal = ({ projectResult = await (await GetCaseService()).updateCase( newCase, ) + setIsLoading(false) } else { projectResult = await (await GetCaseService()).create({ projectId: project.projectId, @@ -161,6 +166,7 @@ const EditCaseModal = ({ waterInjectorCount, productionStrategyOverview: productionStrategy, }) + setIsLoading(false) history.push(`/${fusionContextId}/case/${projectResult.cases.find((o) => ( o.name === caseName ))?.id}`) @@ -169,6 +175,7 @@ const EditCaseModal = ({ toggleModal() } catch (error) { console.error("[ProjectView] error while submitting form data", error) + setIsLoading(false) } } @@ -284,13 +291,20 @@ const EditCaseModal = ({ Cancel - - {editMode ? "Save changes" : "Create case"} - + {isLoading ? ( + + + + ) : ( + + {editMode ? "Save changes" : "Create case"} + + )} + diff --git a/frontend/src/Components/SideMenu/SideMenu.tsx b/frontend/src/Components/SideMenu/SideMenu.tsx index 4d2afb3f3..962b8554b 100644 --- a/frontend/src/Components/SideMenu/SideMenu.tsx +++ b/frontend/src/Components/SideMenu/SideMenu.tsx @@ -19,7 +19,7 @@ const SidebarDiv = styled.div` const Wrapper = styled.div` display: flex; flex-direction: column; - height: 100vh; + height: 100%; width: 100vw; ` @@ -34,6 +34,7 @@ const Body = styled.div` const MainView = styled.div` width: calc(100% - 15rem); overflow: scroll; + overflow-x: hidden; ` interface Props { diff --git a/frontend/src/Services/ExplorationService.ts b/frontend/src/Services/ExplorationService.ts index 7585276e1..b01d563e6 100644 --- a/frontend/src/Services/ExplorationService.ts +++ b/frontend/src/Services/ExplorationService.ts @@ -4,6 +4,7 @@ import { __BaseService } from "./__BaseService" import { LoginAccessTokenKey, GetToken } from "../Utils/common" import { Project } from "../models/Project" import { IAssetService } from "./IAssetService" +import { Exploration } from "../models/assets/exploration/Exploration" export class __ExplorationService extends __BaseService implements IAssetService { public async create(sourceCaseId: string, body: Components.Schemas.ExplorationDto) :Promise { @@ -15,6 +16,11 @@ export class __ExplorationService extends __BaseService implements IAssetService const res: Components.Schemas.ProjectDto = await this.put("", { body }) return Project.fromJSON(res) } + + public async newUpdate(body: Components.Schemas.ExplorationDto): Promise { + const res: Components.Schemas.ExplorationDto = await this.put("/new", { body }) + return new Exploration(res) + } } export async function GetExplorationService() { diff --git a/frontend/src/Services/WellProjectService.ts b/frontend/src/Services/WellProjectService.ts index 4114778da..c6272d9a5 100644 --- a/frontend/src/Services/WellProjectService.ts +++ b/frontend/src/Services/WellProjectService.ts @@ -4,6 +4,7 @@ import { __BaseService } from "./__BaseService" import { LoginAccessTokenKey, GetToken } from "../Utils/common" import { Project } from "../models/Project" import { IAssetService } from "./IAssetService" +import { WellProject } from "../models/assets/wellproject/WellProject" export class __WellProjectService extends __BaseService implements IAssetService { public async create(sourceCaseId: string, body: Components.Schemas.WellProjectDto) : Promise { @@ -15,6 +16,11 @@ export class __WellProjectService extends __BaseService implements IAssetService const res: Components.Schemas.ProjectDto = await this.put("", { body }) return Project.fromJSON(res) } + + public async newUpdate(body: Components.Schemas.WellProjectDto): Promise { + const res: Components.Schemas.WellProjectDto = await this.put("/new", { body }) + return new WellProject(res) + } } export async function GetWellProjectService() { diff --git a/frontend/src/Views/Case/CaseCostTab.tsx b/frontend/src/Views/Case/CaseCostTab.tsx new file mode 100644 index 000000000..c37c3d411 --- /dev/null +++ b/frontend/src/Views/Case/CaseCostTab.tsx @@ -0,0 +1,520 @@ +import { + Dispatch, + SetStateAction, + ChangeEventHandler, + useState, + useEffect, + useRef, +} from "react" +import styled from "styled-components" + +import { + Button, NativeSelect, Typography, +} from "@equinor/eds-core-react" +import { Project } from "../../models/Project" +import { Case } from "../../models/case/Case" +import CaseNumberInput from "../../Components/Case/CaseNumberInput" +import { DrainageStrategy } from "../../models/assets/drainagestrategy/DrainageStrategy" +import CaseTabTable from "./CaseTabTable" +import { GetCaseService } from "../../Services/CaseService" +import { ITimeSeries } from "../../models/ITimeSeries" +import { StudyCostProfile } from "../../models/case/StudyCostProfile" +import { OpexCostProfile } from "../../models/case/OpexCostProfile" +import { CaseCessationCostProfile } from "../../models/case/CaseCessationCostProfile" +import { SeismicAcquisitionAndProcessing } from "../../models/assets/exploration/SeismicAcquisitionAndProcessing" +import { CountryOfficeCost } from "../../models/assets/exploration/CountryOfficeCost" +import { GAndGAdminCost } from "../../models/assets/exploration/GAndGAdminCost" +import { Exploration } from "../../models/assets/exploration/Exploration" +import { GetExplorationService } from "../../Services/ExplorationService" +import { Surf } from "../../models/assets/surf/Surf" +import { GetSurfService } from "../../Services/SurfService" +import { ExplorationCostProfile } from "../../models/assets/exploration/ExplorationCostProfile" +import { WellProjectCostProfile } from "../../models/assets/wellproject/WellProjectCostProfile" +import { WellProject } from "../../models/assets/wellproject/WellProject" +import { Substructure } from "../../models/assets/substructure/Substructure" +import { Topside } from "../../models/assets/topside/Topside" +import { Transport } from "../../models/assets/transport/Transport" +import { TopsideCostProfile } from "../../models/assets/topside/TopsideCostProfile" +import { SurfCostProfile } from "../../models/assets/surf/SurfCostProfile" +import { SubstructureCostProfile } from "../../models/assets/substructure/SubstructureCostProfile" +import { TransportCostProfile } from "../../models/assets/transport/TransportCostProfile" +import { GetTopsideService } from "../../Services/TopsideService" +import { GetSubstructureService } from "../../Services/SubstructureService" +import { GetTransportService } from "../../Services/TransportService" +import { GetWellProjectService } from "../../Services/WellProjectService" + +const ColumnWrapper = styled.div` + display: flex; + flex-direction: column; +` +const RowWrapper = styled.div` + display: flex; + flex-direction: row; + margin-bottom: 78px; +` +const TopWrapper = styled.div` + display: flex; + flex-direction: row; + margin-top: 20px; + margin-bottom: 20px; +` +const PageTitle = styled(Typography)` + flex-grow: 1; +` +const NativeSelectField = styled(NativeSelect)` + width: 200px; + padding-right: 20px; +` +const NumberInputField = styled.div` + padding-right: 20px; +` + +const TableYearWrapper = styled.div` + align-items: flex-end; + display: flex; + flex-direction: row; + align-content: right; + margin-left: auto; + margin-bottom: 20px; +` +const YearInputWrapper = styled.div` + width: 80px; + padding-right: 10px; +` +const YearDashWrapper = styled.div` + padding-right: 5px; +` +const TableWrapper = styled.div` + margin-bottom: 50px; +` + +interface Props { + project: Project, + setProject: Dispatch>, + caseItem: Case, + setCase: Dispatch>, + topside: Topside, + setTopside: Dispatch>, + surf: Surf, + setSurf: Dispatch>, + substructure: Substructure, + setSubstructure: Dispatch>, + transport: Transport, + setTransport: Dispatch>, + exploration: Exploration, + setExploration: Dispatch>, + wellProject: WellProject, + setWellProject: Dispatch>, + drainageStrategy: DrainageStrategy +} + +function CaseCostTab({ + project, setProject, + caseItem, setCase, + exploration, setExploration, + wellProject, setWellProject, + topside, setTopside, + surf, setSurf, + substructure, setSubstructure, + transport, setTransport, + drainageStrategy, +}: Props) { + // OPEX + const [studyCost, setStudyCost] = useState() + const [opexCost, setOpexCost] = useState() + const [cessationCost, setCessationCost] = useState() + + // CAPEX + const [topsideCost, setTopsideCost] = useState() + const [surfCost, setSurfCost] = useState() + const [substructureCost, setSubstructureCost] = useState() + const [transportCost, setTransportCost] = useState() + + // Development + const [wellProjectCost, setWellProjectCost] = useState() + + // Exploration + const [explorationCost, setExplorationCost] = useState() + const [seismicAcqAndProcCost, setseismicAcqAndProcCost] = useState() + const [countryOfficeCost, setCountryOfficeCost] = useState() + const [gAndGAdminCost, setGAndGAdminCost] = useState() + + const [startYear, setStartYear] = useState(2020) + const [endYear, setEndYear] = useState(2100) + const [tableYears, setTableYears] = useState<[number, number]>([2020, 2030]) + + const opexGridRef = useRef(null) + const capexGridRef = useRef(null) + const developmentWellsGridRef = useRef(null) + const explorationWellsGridRef = useRef(null) + + const getTimeSeriesLastYear = (timeSeries: ITimeSeries | undefined): number | undefined => { + if (timeSeries && timeSeries.startYear && timeSeries.values) { + return timeSeries.startYear + timeSeries.values.length - 1 + } return undefined + } + + const setTableYearsFromProfiles = (profiles: (ITimeSeries | undefined)[]) => { + let firstYear = Number.MAX_SAFE_INTEGER + let lastYear = Number.MIN_SAFE_INTEGER + profiles.forEach((p) => { + if (p && p.startYear !== undefined && p.startYear < firstYear) { + firstYear = p.startYear + } + const profileLastYear = getTimeSeriesLastYear(p) + if (profileLastYear !== undefined && profileLastYear > lastYear) { + lastYear = profileLastYear + } + }) + if (firstYear < Number.MAX_SAFE_INTEGER && lastYear > Number.MIN_SAFE_INTEGER) { + setStartYear(firstYear + caseItem.DG4Date.getFullYear()) + setEndYear(lastYear + caseItem.DG4Date.getFullYear()) + setTableYears([firstYear + caseItem.DG4Date.getFullYear(), lastYear + caseItem.DG4Date.getFullYear()]) + } + } + + useEffect(() => { + (async () => { + try { + // OPEX + const study = await (await GetCaseService()).generateStudyCost(caseItem.id) + setStudyCost(study) + const opex = await (await GetCaseService()).generateOpexCost(caseItem.id) + setOpexCost(opex) + const cessation = await (await GetCaseService()).generateCessationCost(caseItem.id) + setCessationCost(cessation) + + // CAPEX + const topsideCostProfile = topside.costProfile + setTopsideCost(topsideCostProfile) + const surfCostProfile = surf.costProfile + setSurfCost(surfCostProfile) + const substructureCostProfile = substructure.costProfile + setSubstructureCost(substructureCostProfile) + const transportCostProfile = transport.costProfile + setTransportCost(transportCostProfile) + + // Development + const wellProjectCostProfile = wellProject.costProfile + setWellProjectCost(wellProjectCostProfile) + + // Exploration + const explorationCostProfile = exploration.costProfile + setExplorationCost(explorationCostProfile) + const seismicAcqAndProc = exploration.seismicAcquisitionAndProcessing + setseismicAcqAndProcCost(seismicAcqAndProc) + const countryOffice = exploration.countryOfficeCost + setCountryOfficeCost(countryOffice) + const gAndGAdmin = await (await GetCaseService()).generateGAndGAdminCost(caseItem.id) + setGAndGAdminCost(gAndGAdmin) + + setTableYearsFromProfiles([study, opex, cessation, + surfCostProfile, topsideCostProfile, substructureCostProfile, transportCostProfile, + wellProjectCostProfile, + explorationCostProfile, seismicAcqAndProc, countryOffice, gAndGAdmin, + ]) + } catch (error) { + console.error("[CaseView] Error while generating cost profile", error) + } + })() + }, []) + + const updatedAndSetSurf = (surfItem: Surf) => { + const newSurf: Surf = { ...surfItem } + newSurf.costProfile = surfCost + setSurf(newSurf) + } + + const handleCaseFeasibilityChange: ChangeEventHandler = async (e) => { + const newCase = Case.Copy(caseItem) + const newCapexFactorFeasibilityStudies = Number(e.currentTarget.value) + newCase.capexFactorFeasibilityStudies = newCapexFactorFeasibilityStudies / 100 + setCase(newCase) + } + + const handleCaseFEEDChange: ChangeEventHandler = async (e) => { + const newCase = Case.Copy(caseItem) + const newCapexFactorFEEDStudies = Number(e.currentTarget.value) + newCase.capexFactorFEEDStudies = newCapexFactorFEEDStudies / 100 + setCase(newCase) + } + + const handleSurfMaturityChange: ChangeEventHandler = async (e) => { + if ([0, 1, 2, 3].indexOf(Number(e.currentTarget.value)) !== -1) { + // eslint-disable-next-line max-len + const newMaturity: Components.Schemas.Maturity = Number(e.currentTarget.value) as Components.Schemas.Maturity + const newSurf: Surf = { ...surf } + newSurf.maturity = newMaturity + updatedAndSetSurf(newSurf) + } + } + + const handleStartYearChange: ChangeEventHandler = async (e) => { + const newStartYear = Number(e.currentTarget.value) + if (newStartYear < 2010) { + setStartYear(2010) + return + } + setStartYear(newStartYear) + } + + const handleEndYearChange: ChangeEventHandler = async (e) => { + const newEndYear = Number(e.currentTarget.value) + if (newEndYear > 2100) { + setEndYear(2100) + return + } + setEndYear(newEndYear) + } + + interface ITimeSeriesData { + profileName: string + unit: string, + set?: Dispatch>, + profile: ITimeSeries | undefined + } + + const opexTimeSeriesData: ITimeSeriesData[] = [ + { + profileName: "Study cost", unit: "MNOK", profile: studyCost, + }, + { + profileName: "OPEX cost", unit: "MNOK", profile: opexCost, + }, + { + profileName: "Cessation cost", unit: "MNOK", profile: cessationCost, + }, + ] + + const capexTimeSeriesData: ITimeSeriesData[] = [ + { + profileName: "Subsea production system", unit: "MNOK", profile: surfCost, set: setSurfCost, + }, + { + profileName: "Topside", unit: "MNOK", profile: topsideCost, set: setTopsideCost, + }, + { + profileName: "Substructure", unit: "MNOK", profile: substructureCost, set: setSubstructureCost, + }, + { + profileName: "Transport system", unit: "MNOK", profile: transportCost, set: setTransportCost, + }, + ] + + const developmentTimeSeriesData: ITimeSeriesData[] = [ + { + profileName: "Development cost", unit: "MNOK", profile: wellProjectCost, set: setWellProjectCost, + }, + ] + + const explorationTimeSeriesData: ITimeSeriesData[] = [ + { + profileName: "G&G and admin costs", unit: "MNOK", profile: gAndGAdminCost, + }, + { + profileName: "Seismic acquisition and processing", + unit: "MNOK", + profile: seismicAcqAndProcCost, + set: setseismicAcqAndProcCost, + }, + { + profileName: "Country office cost", unit: "MNOK", profile: countryOfficeCost, set: setCountryOfficeCost, + }, + { + profileName: "Exploration cost", unit: "MNOK", profile: explorationCost, set: setExplorationCost, + }, + ] + + const handleTableYearsClick = () => { + setTableYears([startYear, endYear]) + } + + useEffect(() => { + const newSurf: Surf = { ...surf } + newSurf.costProfile = surfCost + setSurf(newSurf) + }, [surfCost]) + + useEffect(() => { + const newTopside: Topside = { ...topside } + newTopside.costProfile = topsideCost + setTopside(newTopside) + }, [topsideCost]) + + useEffect(() => { + const newSubstructure: Substructure = { ...substructure } + newSubstructure.costProfile = substructureCost + setSubstructure(newSubstructure) + }, [substructureCost]) + + useEffect(() => { + const newTransport: Transport = { ...transport } + newTransport.costProfile = transportCost + setTransport(newTransport) + }, [transportCost]) + + useEffect(() => { + const newWellProject: WellProject = { ...wellProject } + newWellProject.costProfile = wellProjectCost + setWellProject(newWellProject) + }, [wellProjectCost]) + + useEffect(() => { + const newExploration: Exploration = { ...exploration } + newExploration.costProfile = explorationCost + newExploration.seismicAcquisitionAndProcessing = seismicAcqAndProcCost + newExploration.countryOfficeCost = countryOfficeCost + setExploration(newExploration) + }, [explorationCost, seismicAcqAndProcCost, countryOfficeCost]) + + const handleSave = async () => { + const updatedSurfResult = await (await GetSurfService()).newUpdate(surf) + setSurf(updatedSurfResult) + const updatedTopsideResult = await (await GetTopsideService()).newUpdate(topside) + setTopside(updatedTopsideResult) + const updatedSubstructureResult = await (await GetSubstructureService()).newUpdate(substructure) + setSubstructure(updatedSubstructureResult) + const updatedTransportResult = await (await GetTransportService()).newUpdate(transport) + setTransport(updatedTransportResult) + const updatedWellProjectResult = await (await GetWellProjectService()).newUpdate(wellProject) + setWellProject(updatedWellProjectResult) + const updatedExplorationResult = await (await GetExplorationService()).newUpdate(exploration) + setExploration(updatedExplorationResult) + const updateedCaseResult = await (await GetCaseService()).update(caseItem) + setCase(updateedCaseResult) + } + + return ( + <> + + Cost + + + + + + + + + + + + + + + + + + + + + { }} + value={project.currency} + disabled + > + + + + + + + + - + + + + + + + + + + + + + + + + + + + ) +} + +export default CaseCostTab diff --git a/frontend/src/Views/Case/CaseDescriptionTab.tsx b/frontend/src/Views/Case/CaseDescriptionTab.tsx index b95543367..d1132038d 100644 --- a/frontend/src/Views/Case/CaseDescriptionTab.tsx +++ b/frontend/src/Views/Case/CaseDescriptionTab.tsx @@ -56,31 +56,31 @@ function CaseDescriptionTab({ setCase, }: Props) { const handleDescriptionChange: ChangeEventHandler = async (e) => { - const newCase = { ...caseItem } + const newCase: Case = { ...caseItem } newCase.description = e.currentTarget.value setCase(newCase) } const handleFacilitiesAvailabilityChange: ChangeEventHandler = async (e) => { - const newCase = { ...caseItem } + const newCase: Case = { ...caseItem } newCase.facilitiesAvailability = Number(e.currentTarget.value) setCase(newCase) } const handleProducerCountChange: ChangeEventHandler = async (e) => { - const newCase = { ...caseItem } + const newCase: Case = { ...caseItem } newCase.producerCount = Number(e.currentTarget.value) setCase(newCase) } const handleGasInjectorCountChange: ChangeEventHandler = async (e) => { - const newCase = { ...caseItem } + const newCase: Case = { ...caseItem } newCase.gasInjectorCount = Number(e.currentTarget.value) setCase(newCase) } const handletWaterInjectorCountChange: ChangeEventHandler = async (e) => { - const newCase = { ...caseItem } + const newCase: Case = { ...caseItem } newCase.waterInjectorCount = Number(e.currentTarget.value) setCase(newCase) } @@ -89,7 +89,7 @@ function CaseDescriptionTab({ if ([0, 1, 2, 3, 4].indexOf(Number(e.currentTarget.value)) !== -1) { // eslint-disable-next-line max-len const newProductionStrategy: Components.Schemas.ProductionStrategyOverview = Number(e.currentTarget.value) as Components.Schemas.ProductionStrategyOverview - const newCase = { ...caseItem } + const newCase: Case = { ...caseItem } newCase.productionStrategyOverview = newProductionStrategy setCase(newCase) } @@ -99,14 +99,15 @@ function CaseDescriptionTab({ if ([0, 1, 2, 3].indexOf(Number(e.currentTarget.value)) !== -1) { // eslint-disable-next-line max-len const newArtificialLift: Components.Schemas.ArtificialLift = Number(e.currentTarget.value) as Components.Schemas.ArtificialLift - const newCase = { ...caseItem } + const newCase: Case = { ...caseItem } newCase.artificialLift = newArtificialLift setCase(newCase) } } const handleSave = async () => { - const result = await (await GetCaseService()).update(caseItem) + const updatedCase = Case.Copy(caseItem) + const result = await (await GetCaseService()).update(updatedCase) setCase(result) } diff --git a/frontend/src/Views/Case/CaseFacilitiesTab.tsx b/frontend/src/Views/Case/CaseFacilitiesTab.tsx index 4105c37bc..71966bf57 100644 --- a/frontend/src/Views/Case/CaseFacilitiesTab.tsx +++ b/frontend/src/Views/Case/CaseFacilitiesTab.tsx @@ -51,13 +51,13 @@ interface Props { setProject: Dispatch>, caseItem: Case, setCase: Dispatch>, - topside: Topside | undefined, + topside: Topside, setTopside: Dispatch>, - surf: Surf | undefined, + surf: Surf, setSurf: Dispatch>, - substructure: Substructure | undefined, + substructure: Substructure, setSubstrucutre: Dispatch>, - transport: Transport | undefined, + transport: Transport, setTransport: Dispatch>, } diff --git a/frontend/src/Views/Case/CaseProductionProfilesTab.tsx b/frontend/src/Views/Case/CaseProductionProfilesTab.tsx index 9f0905c91..2766d9802 100644 --- a/frontend/src/Views/Case/CaseProductionProfilesTab.tsx +++ b/frontend/src/Views/Case/CaseProductionProfilesTab.tsx @@ -15,7 +15,7 @@ import { Case } from "../../models/case/Case" import CaseNumberInput from "../../Components/Case/CaseNumberInput" import { DrainageStrategy } from "../../models/assets/drainagestrategy/DrainageStrategy" import { GetDrainageStrategyService } from "../../Services/DrainageStrategyService" -import CaseProductionProfilesTabTable from "./CaseProductionProfilesTabTable" +import CaseTabTable from "./CaseTabTable" import { NetSalesGas } from "../../models/assets/drainagestrategy/NetSalesGas" import { FuelFlaringAndLosses } from "../../models/assets/drainagestrategy/FuelFlaringAndLosses" import { ProductionProfileGas } from "../../models/assets/drainagestrategy/ProductionProfileGas" @@ -23,6 +23,8 @@ import { ProductionProfileOil } from "../../models/assets/drainagestrategy/Produ import { ProductionProfileWater } from "../../models/assets/drainagestrategy/ProductionProfileWater" import { ProductionProfileNGL } from "../../models/assets/drainagestrategy/ProductionProfileNGL" import { ProductionProfileWaterInjection } from "../../models/assets/drainagestrategy/ProductionProfileWaterInjection" +import { GetCaseService } from "../../Services/CaseService" +import { ITimeSeries } from "../../models/ITimeSeries" const ColumnWrapper = styled.div` display: flex; @@ -50,12 +52,28 @@ const NumberInputField = styled.div` padding-right: 20px; ` +const TableYearWrapper = styled.div` + align-items: flex-end; + display: flex; + flex-direction: row; + align-content: right; + margin-left: auto; + margin-bottom: 20px; +` +const YearInputWrapper = styled.div` + width: 80px; + padding-right: 10px; +` +const YearDashWrapper = styled.div` + padding-right: 5px; +` + interface Props { project: Project, setProject: Dispatch>, caseItem: Case, setCase: Dispatch>, - drainageStrategy: DrainageStrategy | undefined, + drainageStrategy: DrainageStrategy, setDrainageStrategy: Dispatch>, } @@ -72,8 +90,9 @@ function CaseProductionProfilesTab({ const [nGL, setNGL] = useState() const [waterInjection, setWaterInjection] = useState() - const [firstYear, setFirstYear] = useState(2020) - const [lastYear, setLastYear] = useState(2030) + const [startYear, setStartYear] = useState(2020) + const [endYear, setEndYear] = useState(2030) + const [tableYears, setTableYears] = useState<[number, number]>([2020, 2030]) const updateAndSetDraiangeStrategy = (drainage: DrainageStrategy) => { const newDrainageStrategy: DrainageStrategy = { ...drainage } @@ -93,18 +112,53 @@ function CaseProductionProfilesTab({ updateAndSetDraiangeStrategy(newDrainageStrategy) } - const timeSeriesData = [ - { - profileName: "Net sales gas", - unit: `${project?.physUnit === 0 ? "GSm³/yr" : "Bscf/yr"}`, - set: setNetSalesGas, - profile: netSalesGas, - }, + const handleCaseFacilitiesAvailabilityChange: ChangeEventHandler = async (e) => { + const newCase = { ...caseItem } + newCase.facilitiesAvailability = Number(e.currentTarget.value) + setCase(newCase) + } + + const handleDrainageStrategyGasSolutinChange: ChangeEventHandler = async (e) => { + if ([0, 1].indexOf(Number(e.currentTarget.value)) !== -1) { + // eslint-disable-next-line max-len + const newGasSolution: Components.Schemas.GasSolution = Number(e.currentTarget.value) as Components.Schemas.GasSolution + const newDrainageStrategy: DrainageStrategy = { ...drainageStrategy } + newDrainageStrategy.gasSolution = newGasSolution + updateAndSetDraiangeStrategy(newDrainageStrategy) + } + } + + const handleStartYearChange: ChangeEventHandler = async (e) => { + const newStartYear = Number(e.currentTarget.value) + if (newStartYear < 2010) { + setStartYear(2010) + return + } + setStartYear(newStartYear) + } + + const handleEndYearChange: ChangeEventHandler = async (e) => { + const newEndYear = Number(e.currentTarget.value) + if (newEndYear > 2100) { + setEndYear(2100) + return + } + setEndYear(newEndYear) + } + + interface ITimeSeriesData { + profileName: string + unit: string, + set: Dispatch>, + profile: ITimeSeries | undefined + } + + const timeSeriesData: ITimeSeriesData[] = [ { - profileName: "Fuel flaring and losses", - unit: `${project?.physUnit === 0 ? "GSm³/yr" : "Bscf/yr"}`, - set: setFuelFlaringAndLosses, - profile: fuelFlaringAndLosses, + profileName: "Oil production", + unit: `${project?.physUnit === 0 ? "MSm³/yr" : "mill bbls/yr"}`, + set: setOil, + profile: oil, }, { profileName: "Gas production", @@ -112,12 +166,6 @@ function CaseProductionProfilesTab({ set: setGas, profile: gas, }, - { - profileName: "Oil production", - unit: `${project?.physUnit === 0 ? "MSm³/yr" : "mill bbls/yr"}`, - set: setOil, - profile: oil, - }, { profileName: "Water production", unit: `${project?.physUnit === 0 ? "MSm³/yr" : "mill bbls/yr"}`, @@ -130,19 +178,63 @@ function CaseProductionProfilesTab({ set: setWaterInjection, profile: waterInjection, }, + { + profileName: "Fuel flaring and losses", + unit: `${project?.physUnit === 0 ? "GSm³/yr" : "Bscf/yr"}`, + set: setFuelFlaringAndLosses, + profile: fuelFlaringAndLosses, + }, + { + profileName: "Net sales gas", + unit: `${project?.physUnit === 0 ? "GSm³/yr" : "Bscf/yr"}`, + set: setNetSalesGas, + profile: netSalesGas, + }, ] - useEffect(() => { - if (drainageStrategy) { - setNetSalesGas(drainageStrategy.netSalesGas) - setFuelFlaringAndLosses(drainageStrategy.fuelFlaringAndLosses) - setGas(drainageStrategy.productionProfileGas) - setOil(drainageStrategy.productionProfileOil) - setWater(drainageStrategy.productionProfileWater) - setNGL(drainageStrategy.productionProfileNGL) - setWaterInjection(drainageStrategy.productionProfileWaterInjection) + const getTimeSeriesLastYear = (timeSeries: ITimeSeries | undefined): number | undefined => { + if (timeSeries && timeSeries.startYear && timeSeries.values) { + return timeSeries.startYear + timeSeries.values.length - 1 + } return undefined + } + + const setTableYearsFromProfiles = (profiles: (ITimeSeries | undefined)[]) => { + let firstYear = Number.MAX_SAFE_INTEGER + let lastYear = Number.MIN_SAFE_INTEGER + profiles.forEach((p) => { + if (p && p.startYear !== undefined && p.startYear < firstYear) { + firstYear = p.startYear + } + const profileLastYear = getTimeSeriesLastYear(p) + if (profileLastYear !== undefined && profileLastYear > lastYear) { + lastYear = profileLastYear + } + }) + if (firstYear < Number.MAX_SAFE_INTEGER && lastYear > Number.MIN_SAFE_INTEGER) { + setStartYear(firstYear + caseItem.DG4Date.getFullYear()) + setEndYear(lastYear + caseItem.DG4Date.getFullYear()) + setTableYears([firstYear + caseItem.DG4Date.getFullYear(), lastYear + caseItem.DG4Date.getFullYear()]) } - }, [drainageStrategy]) + } + + const handleTableYearsClick = () => { + setTableYears([startYear, endYear]) + } + + useEffect(() => { + setTableYearsFromProfiles([drainageStrategy.netSalesGas, drainageStrategy.fuelFlaringAndLosses, + drainageStrategy.productionProfileGas, drainageStrategy.productionProfileOil, + drainageStrategy.productionProfileWater, drainageStrategy.productionProfileNGL, + drainageStrategy.productionProfileWaterInjection, + ]) + setNetSalesGas(drainageStrategy.netSalesGas) + setFuelFlaringAndLosses(drainageStrategy.fuelFlaringAndLosses) + setGas(drainageStrategy.productionProfileGas) + setOil(drainageStrategy.productionProfileOil) + setWater(drainageStrategy.productionProfileWater) + setNGL(drainageStrategy.productionProfileNGL) + setWaterInjection(drainageStrategy.productionProfileWaterInjection) + }, []) const handleSave = async () => { if (drainageStrategy) { @@ -157,6 +249,8 @@ function CaseProductionProfilesTab({ const result = await (await GetDrainageStrategyService()).newUpdate(newDrainageStrategy) setDrainageStrategy(result) } + const updateCaseResult = await (await GetCaseService()).update(caseItem) + setCase(updateCaseResult) } return ( @@ -169,20 +263,18 @@ function CaseProductionProfilesTab({ console.log("Facilities availability")} + onChange={handleCaseFacilitiesAvailabilityChange} value={caseItem.facilitiesAvailability} integer={false} - disabled label="Facilities availability (%)" /> console.log("Gas solution")} - value={0} + onChange={handleDrainageStrategyGasSolutinChange} + value={drainageStrategy?.gasSolution} > - @@ -196,18 +288,54 @@ function CaseProductionProfilesTab({ - {drainageStrategy && ( - - )} + + + { }} + value={project.physUnit} + disabled + > + + + + + + + + - + + + + + + + + ) } diff --git a/frontend/src/Views/Case/CaseSummaryTab.tsx b/frontend/src/Views/Case/CaseSummaryTab.tsx new file mode 100644 index 000000000..0539bcc6c --- /dev/null +++ b/frontend/src/Views/Case/CaseSummaryTab.tsx @@ -0,0 +1,279 @@ +import { + Dispatch, + SetStateAction, + ChangeEventHandler, + useState, + useEffect, +} from "react" +import styled from "styled-components" + +import { + Button, NativeSelect, Typography, +} from "@equinor/eds-core-react" +import { Project } from "../../models/Project" +import { Case } from "../../models/case/Case" +import CaseNumberInput from "../../Components/Case/CaseNumberInput" +import { DrainageStrategy } from "../../models/assets/drainagestrategy/DrainageStrategy" +import CaseTabTable from "./CaseTabTable" +import { NetSalesGas } from "../../models/assets/drainagestrategy/NetSalesGas" +import { FuelFlaringAndLosses } from "../../models/assets/drainagestrategy/FuelFlaringAndLosses" +import { ProductionProfileGas } from "../../models/assets/drainagestrategy/ProductionProfileGas" +import { ProductionProfileOil } from "../../models/assets/drainagestrategy/ProductionProfileOil" +import { ProductionProfileWater } from "../../models/assets/drainagestrategy/ProductionProfileWater" +import { ProductionProfileNGL } from "../../models/assets/drainagestrategy/ProductionProfileNGL" +import { ProductionProfileWaterInjection } from "../../models/assets/drainagestrategy/ProductionProfileWaterInjection" +import { GetCaseService } from "../../Services/CaseService" +import { ITimeSeries } from "../../models/ITimeSeries" +import { StudyCostProfile } from "../../models/case/StudyCostProfile" +import { OpexCostProfile } from "../../models/case/OpexCostProfile" +import { CaseCessationCostProfile } from "../../models/case/CaseCessationCostProfile" +import { Exploration } from "../../models/assets/exploration/Exploration" +import { Surf } from "../../models/assets/surf/Surf" +import { GetSurfService } from "../../Services/SurfService" +import { WellProject } from "../../models/assets/wellproject/WellProject" +import { Substructure } from "../../models/assets/substructure/Substructure" +import { Topside } from "../../models/assets/topside/Topside" +import { Transport } from "../../models/assets/transport/Transport" +import { TopsideCostProfile } from "../../models/assets/topside/TopsideCostProfile" +import { SurfCostProfile } from "../../models/assets/surf/SurfCostProfile" +import { SubstructureCostProfile } from "../../models/assets/substructure/SubstructureCostProfile" +import { TransportCostProfile } from "../../models/assets/transport/TransportCostProfile" + +const ColumnWrapper = styled.div` + display: flex; + flex-direction: column; +` +const RowWrapper = styled.div` + display: flex; + flex-direction: row; + margin-bottom: 78px; +` +const TopWrapper = styled.div` + display: flex; + flex-direction: row; + margin-top: 20px; + margin-bottom: 20px; +` +const PageTitle = styled(Typography)` + flex-grow: 1; +` +const NumberInputField = styled.div` + padding-right: 20px; +` +const TableWrapper = styled.div` + margin-bottom: 50px; +` + +interface Props { + project: Project, + setProject: Dispatch>, + caseItem: Case, + setCase: Dispatch>, + topside: Topside, + setTopside: Dispatch>, + surf: Surf, + setSurf: Dispatch>, + substructure: Substructure, + setSubstrucutre: Dispatch>, + transport: Transport, + setTransport: Dispatch>, + exploration: Exploration, + setExploration: Dispatch>, + wellProject: WellProject, + setWellProject: Dispatch>, + drainageStrategy: DrainageStrategy +} + +function CaseSummaryTab({ + project, setProject, + caseItem, setCase, + exploration, setExploration, + wellProject, setWellProject, + topside, setTopside, + surf, setSurf, + substructure, setSubstrucutre, + transport, setTransport, + drainageStrategy, +}: Props) { + // OPEX + const [studyCost, setStudyCost] = useState() + const [opexCost, setOpexCost] = useState() + const [cessationCost, setCessationCost] = useState() + + // CAPEX + const [topsideCost, setTopsideCost] = useState() + const [surfCost, setSurfCost] = useState() + const [substructureCost, setSubstructureCost] = useState() + const [transportCost, setTransportCost] = useState() + + const [startYear, setStartYear] = useState(2020) + const [endYear, setEndYear] = useState(2030) + const [tableYears, setTableYears] = useState<[number, number]>([2020, 2030]) + + const getTimeSeriesLastYear = (timeSeries: ITimeSeries | undefined): number | undefined => { + if (timeSeries && timeSeries.startYear && timeSeries.values) { + return timeSeries.startYear + timeSeries.values.length - 1 + } return undefined + } + + const setTableYearsFromProfiles = (profiles: (ITimeSeries | undefined)[]) => { + let firstYear = Number.MAX_SAFE_INTEGER + let lastYear = Number.MIN_SAFE_INTEGER + profiles.forEach((p) => { + if (p && p.startYear !== undefined && p.startYear < firstYear) { + firstYear = p.startYear + } + const profileLastYear = getTimeSeriesLastYear(p) + if (profileLastYear !== undefined && profileLastYear > lastYear) { + lastYear = profileLastYear + } + }) + if (firstYear < Number.MAX_SAFE_INTEGER && lastYear > Number.MIN_SAFE_INTEGER) { + setStartYear(firstYear + caseItem.DG4Date.getFullYear()) + setEndYear(lastYear + caseItem.DG4Date.getFullYear()) + setTableYears([firstYear + caseItem.DG4Date.getFullYear(), lastYear + caseItem.DG4Date.getFullYear()]) + } + } + + useEffect(() => { + (async () => { + try { + // OPEX + const study = await (await GetCaseService()).generateStudyCost(caseItem.id) + setStudyCost(study) + const opex = await (await GetCaseService()).generateOpexCost(caseItem.id) + setOpexCost(opex) + const cessation = await (await GetCaseService()).generateCessationCost(caseItem.id) + setCessationCost(cessation) + + // CAPEX + const topsideCostProfile = topside.costProfile + setTopsideCost(topsideCostProfile) + const surfCostProfile = surf.costProfile + setSurfCost(surfCostProfile) + const substructureCostProfile = substructure.costProfile + setSubstructureCost(substructureCostProfile) + const transportCostProfile = transport.costProfile + setTransportCost(transportCostProfile) + + setTableYearsFromProfiles([study, opex, cessation, + topsideCostProfile, surfCostProfile, substructureCostProfile, transportCostProfile, + ]) + } catch (error) { + console.error("[CaseView] Error while generating cost profile", error) + } + })() + }, []) + + const handleCaseNPVChange: ChangeEventHandler = async (e) => { + const newCase = Case.Copy(caseItem) + newCase.npv = Number(e.currentTarget.value) + setCase(newCase) + } + + const handleCaseBreakEvenChange: ChangeEventHandler = async (e) => { + const newCase = Case.Copy(caseItem) + newCase.breakEven = Number(e.currentTarget.value) + setCase(newCase) + } + + interface ITimeSeriesData { + profileName: string + unit: string, + set?: Dispatch>, + profile: ITimeSeries | undefined + } + + const opexTimeSeriesData: ITimeSeriesData[] = [ + { + profileName: "Study cost", unit: "MNOK", profile: studyCost, + }, + { + profileName: "OPEX cost", unit: "MNOK", profile: opexCost, + }, + { + profileName: "Cessation cost", unit: "MNOK", profile: cessationCost, + }, + ] + + const capexTimeSeriesData: ITimeSeriesData[] = [ + { + profileName: "Topside cost", unit: "MNOK", profile: topsideCost, set: setTopsideCost, + }, + { + profileName: "SURF cost", unit: "MNOK", profile: surfCost, set: setSurfCost, + }, + { + profileName: "Substructure cost", unit: "MNOK", profile: substructureCost, set: setSubstructureCost, + }, + { + profileName: "Transport cost", unit: "MNOK", profile: transportCost, set: setTransportCost, + }, + ] + + const handleTableYearsClick = () => { + setTableYears([startYear, endYear]) + } + + const handleSave = async () => { + const updatedSurfResult = await (await GetSurfService()).newUpdate(surf) + setSurf(updatedSurfResult) + const updateedCaseResult = await (await GetCaseService()).update(caseItem) + setCase(updateedCaseResult) + } + + return ( + <> + + Summary + + + + + + + + + + + + + + + + + + + + ) +} + +export default CaseSummaryTab diff --git a/frontend/src/Views/Case/CaseProductionProfilesTabTable.tsx b/frontend/src/Views/Case/CaseTabTable.tsx similarity index 73% rename from frontend/src/Views/Case/CaseProductionProfilesTabTable.tsx rename to frontend/src/Views/Case/CaseTabTable.tsx index b681ee56d..1b4364a65 100644 --- a/frontend/src/Views/Case/CaseProductionProfilesTabTable.tsx +++ b/frontend/src/Views/Case/CaseTabTable.tsx @@ -21,23 +21,20 @@ interface Props { setCase: Dispatch>, timeSeriesData: any[] dg4Year: number - firstYear: number - lastYear: number + tableYears: [number, number] + tableName: string + alignedGridsRef?: any[] + gridRef?: any } -function CaseProductionProfilesTabTable({ +function CaseTabTable({ project, setProject, caseItem, setCase, timeSeriesData, dg4Year, - firstYear, lastYear, + tableYears, tableName, + alignedGridsRef, gridRef, }: Props) { useAgGridStyles() - const gridRef = useRef(null) - - const onGridReady = (params: any) => { - gridRef.current = params.api - } - const [rowData, setRowData] = useState([{ name: "as" }]) const profilesToRowData = () => { @@ -62,9 +59,31 @@ function CaseProductionProfilesTabTable({ return tableRows } + const generateTableYearColDefs = () => { + const profileNameDef = { + field: "profileName", headerName: tableName, width: 250, editable: false, + } + const unitDef = { field: "unit", width: 100, editable: false } + const yearDefs = [] + for (let index = tableYears[0]; index <= tableYears[1]; index += 1) { + yearDefs.push({ + field: index.toString(), + flex: 1, + editable: (params: any) => params.data.set !== undefined, + minWidth: 100, + }) + } + const totalDef = { field: "total", flex: 2, editable: false } + return [profileNameDef, unitDef, ...yearDefs, totalDef] + } + + const [columnDefs, setColumnDefs] = useState(generateTableYearColDefs()) + useEffect(() => { setRowData(profilesToRowData()) - }, [timeSeriesData]) + const newColDefs = generateTableYearColDefs() + setColumnDefs(newColDefs) + }, [timeSeriesData, tableYears]) const handleCellValueChange = (p: any) => { const properties = Object.keys(p.data) @@ -106,19 +125,21 @@ function CaseProductionProfilesTabTable({ onCellValueChanged: handleCellValueChange, }), []) - const generateTableYearColDefs = () => { - const profileNameDef = { field: "profileName", headerName: "Production profiles", width: 250 } - const unitDef = { field: "unit", width: 100 } - const yearDefs = [] - for (let index = firstYear; index <= lastYear; index += 1) { - yearDefs.push({ field: index.toString(), flex: 1 }) + const gridRefArrayToAlignedGrid = () => { + if (alignedGridsRef && alignedGridsRef.length > 0) { + const refArray: any[] = [] + alignedGridsRef.forEach((agr: any) => { + if (agr && agr.current) { + refArray.push(agr.current) + } + }) + if (refArray.length > 0) { + return refArray + } } - const totalDef = { field: "total", flex: 2 } - return [profileNameDef, unitDef, ...yearDefs, totalDef] + return undefined } - const [columnDefs] = useState(generateTableYearColDefs()) - return (
) } -export default CaseProductionProfilesTabTable +export default CaseTabTable diff --git a/frontend/src/Views/CaseView.tsx b/frontend/src/Views/CaseView.tsx index fad748141..dd5065cf2 100644 --- a/frontend/src/Views/CaseView.tsx +++ b/frontend/src/Views/CaseView.tsx @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ /* eslint-disable camelcase */ import { Button, @@ -22,7 +21,6 @@ import { Case } from "../models/case/Case" import { GetProjectService } from "../Services/ProjectService" import CaseAsset from "../Components/Case/CaseAsset" import { unwrapProjectId } from "../Utils/common" -import CaseDescriptionTab from "./Case/CaseDescriptionTab" import { DrainageStrategy } from "../models/assets/drainagestrategy/DrainageStrategy" import { WellProject } from "../models/assets/wellproject/WellProject" import { Surf } from "../models/assets/surf/Surf" @@ -30,16 +28,18 @@ import { Topside } from "../models/assets/topside/Topside" import { Substructure } from "../models/assets/substructure/Substructure" import { Exploration } from "../models/assets/exploration/Exploration" import { Transport } from "../models/assets/transport/Transport" -import CaseScheduleTab from "./Case/CaseScheduleTab" +import EditTechnicalInputModal from "../Components/EditTechnicalInput/EditTechnicalInputModal" +import CaseCostTab from "./Case/CaseCostTab" +import CaseDescriptionTab from "./Case/CaseDescriptionTab" import CaseFacilitiesTab from "./Case/CaseFacilitiesTab" import CaseProductionProfilesTab from "./Case/CaseProductionProfilesTab" -import EditTechnicalInputModal from "../Components/EditTechnicalInput/EditTechnicalInputModal" +import CaseScheduleTab from "./Case/CaseScheduleTab" +import CaseSummaryTab from "./Case/CaseSummaryTab" const { Panel } = Tabs const { List, Tab, Panels } = Tabs const CaseViewDiv = styled.div` - margin: 2rem; display: flex; flex-direction: column; ` @@ -47,34 +47,71 @@ const CaseViewDiv = styled.div` const TopWrapper = styled.div` display: flex; flex-direction: row; - padding: 1.5rem 2rem; + z-index: 1000; ` const PageTitle = styled(Typography)` flex-grow: 1; + padding-left: 30px; ` const InvisibleButton = styled(Button)` border: 1px solid #007079; + ` const TransparentButton = styled(Button)` color: #007079; background-color: white; border: 1px solid #007079; + ` const DividerLine = styled.div` - background: gray; - height: 0.05rem; - width: 50rem; - margin-bottom: 2rem; - margin-top: 2rem; ` const StyledTabPanel = styled(Panel)` + margin-left: 40px; + margin-right: 40px; padding-top: 0px; - border-top: 1px solid LightGray; +` +const HeaderWrapper = styled.div` + background-color: white; + width: calc(100% - 16rem); + position: fixed; + z-index: 100; + padding-top: 30px; +` +const TabMenuWrapper = styled.div` + position: fixed; + z-index: 1000; + width: calc(100% - 16rem); + border-bottom: 1px solid LightGray; + margin-top: 95px; +` + +const TabContentWrapper = styled.div` + margin-top: 145px; +` + +const CaseButtonsWrapper = styled.div` + align-items: flex-end; + display: flex; + flex-direction: row; + align-content: right; + margin-left: auto; + z-index: 110; +` + +const ColumnWrapper = styled.div` + display: flex; + flex-direction: column; +` + +const RowWrapper = styled.div` + display: flex; + flex-direction: row; + margin-bottom: 78px; ` const CaseView = () => { @@ -82,7 +119,7 @@ const CaseView = () => { const [project, setProject] = useState() const [caseItem, setCase] = useState() - const [activeTab, setActiveTab] = useState(0) + const [activeTab, setActiveTab] = useState(5) const { caseId } = useParams>() const currentProject = useCurrentContext() @@ -94,10 +131,6 @@ const CaseView = () => { const [substructure, setSubstructure] = useState() const [transport, setTransport] = useState() - const [editCaseModalIsOpen, setEditCaseModalIsOpen] = useState(false) - - const [firstTSYear, setFirstTSYear] = useState() - const [lastTSYear, setLastTSYear] = useState() const [isMenuOpen, setIsMenuOpen] = useState(false) const [menuAnchorEl, setMenuAnchorEl] = useState(null) @@ -111,7 +144,9 @@ const CaseView = () => { setProject(projectResult) const caseResult = projectResult.cases.find((o) => o.id === caseId) setCase(caseResult) - setDrainageStrategy(projectResult?.drainageStrategies.find((drain) => drain.id === caseResult?.drainageStrategyLink)) + setDrainageStrategy( + projectResult?.drainageStrategies.find((drain) => drain.id === caseResult?.drainageStrategyLink), + ) setExploration(projectResult?.explorations.find((exp) => exp.id === caseResult?.explorationLink)) setWellProject(projectResult?.wellProjects.find((wp) => wp.id === caseResult?.wellProjectLink)) setSurf(projectResult?.surfs.find((sur) => sur.id === caseResult?.surfLink)) @@ -124,134 +159,182 @@ const CaseView = () => { })() }, [currentProject?.externalId, caseId]) - if (!project) return null - if (!caseItem) return null + if (!project || !caseItem + || !drainageStrategy || !exploration + || !wellProject || !surf || !topside + || !substructure || !transport) { return null } return (
- - {caseItem.name} - toggleTechnicalInputModal()} - > - Edit technical input - - (isMenuOpen ? setIsMenuOpen(false) : setIsMenuOpen(true))} - > - - - - setIsMenuOpen(false)} - placement="bottom" - > - console.log("Add new case clicked")} - > - - - Add New Case - - - console.log("Duplicate clicked")} - > - - - Duplicate - - - console.log("Rename clicked")} - > - - - Rename - - - console.log("Delete clicked")} + + + {caseItem.name} + + + toggleTechnicalInputModal()} + > + Edit technical input + + (isMenuOpen ? setIsMenuOpen(false) : setIsMenuOpen(true))} + > + + + + + + setIsMenuOpen(false)} + placement="bottom" > - - - Delete - - - + console.log("Add new case clicked")} + > + + + Add New Case + + + console.log("Duplicate clicked")} + > + + + Duplicate + + + console.log("Rename clicked")} + > + + + Rename + + + console.log("Delete clicked")} + > + + + Delete + + + + - - Description - Production Profiles - Schedule - Drilling Schedule - Facilities - Cost - CO2 Emissions - Summary - - - - - - - - - - - - -

Drilling Schedule

-
- - - - -

Cost

-
- -

CO2 Emissions

-
- -

Summary

-
-
+ + + Description + Production Profiles + Schedule + Drilling Schedule + Facilities + Cost + CO2 Emissions + Summary + + + + + + + + + + + + + + +

Drilling Schedule

+
+ + + + + + + +

CO2 Emissions

+
+ + + +
+
{ const [producerCount, setProducerCount] = useState() const [gasInjectorCount, setGasInjectorCount] = useState() const [waterInjectorCount, setWaterInjectorCount] = useState() - const [facilitiesAvailability, setFacilitiesAvailability] = useState() const [hasChanges, setHasChanges] = useState(false) const { fusionContextId, caseId, drainageStrategyId } = useParams>() @@ -109,7 +108,6 @@ const DrainageStrategyView = () => { setGasInjectorCount(newDrainage?.gasInjectorCount) setWaterInjectorCount(newDrainage?.waterInjectorCount) setProducerCount(newDrainage?.producerCount) - setFacilitiesAvailability(newDrainage?.facilitiesAvailability) if (caseResult?.DG4Date) { initializeFirstAndLastYear( @@ -141,7 +139,6 @@ const DrainageStrategyView = () => { newDrainage.producerCount = producerCount newDrainage.gasInjectorCount = gasInjectorCount newDrainage.waterInjectorCount = waterInjectorCount - newDrainage.facilitiesAvailability = facilitiesAvailability setDrainageStrategy(newDrainage) if (caseItem?.DG4Date) { @@ -157,8 +154,7 @@ const DrainageStrategyView = () => { } }, [nGLYield, co2Emissions, netSalesGas, fuelFlaringAndLosses, productionProfileGas, productionProfileOil, productionProfileWater, productionProfileWaterInjection, - productionProfileNGL, artificialLift, producerCount, gasInjectorCount, waterInjectorCount, - facilitiesAvailability]) + productionProfileNGL, artificialLift, producerCount, gasInjectorCount, waterInjectorCount]) const setAllStates = (timeSeries: any) => { if (timeSeries) { @@ -265,14 +261,6 @@ const DrainageStrategyView = () => { label="Water injector count" caseValue={caseItem?.waterInjectorCount} /> - { const [costProfile, setCostProfile] = useState() const [cessationCostProfile, setCessationCostProfile] = useState() const [currency, setCurrency] = useState(1) - const [facilitiesAvailability, setFacilitiesAvailability] = useState() const [artificialLift, setArtificialLift] = useState() const [cO2ShareOilProfile, setCO2ShareOilProfile] = useState() const [cO2ShareGasProfile, setCO2ShareGasProfile] = useState() @@ -100,7 +99,6 @@ const TopsideView = () => { newTopside = new Topside() newTopside.artificialLift = caseResult?.artificialLift newTopside.currency = project.currency - newTopside.facilitiesAvailability = caseResult?.facilitiesAvailability newTopside.producerCount = caseResult?.producerCount newTopside.gasInjectorCount = caseResult?.gasInjectorCount newTopside.waterInjectorCount = caseResult?.waterInjectorCount @@ -114,7 +112,6 @@ const TopsideView = () => { setGasCapacity(newTopside?.gasCapacity) setMaturity(newTopside?.maturity ?? undefined) setCurrency(newTopside.currency ?? 1) - setFacilitiesAvailability(newTopside?.facilitiesAvailability) setArtificialLift(newTopside.artificialLift) setCostYear(newTopside?.costYear) setCO2ShareOilProfile(newTopside?.cO2ShareOilProfile) @@ -159,7 +156,6 @@ const TopsideView = () => { newTopside.costProfile = costProfile newTopside.cessationCostProfile = cessationCostProfile newTopside.currency = currency - newTopside.facilitiesAvailability = facilitiesAvailability newTopside.artificialLift = artificialLift newTopside.costYear = costYear newTopside.cO2ShareOilProfile = cO2ShareOilProfile @@ -192,7 +188,7 @@ const TopsideView = () => { } }, [dryweight, oilCapacity, gasCapacity, maturity, costProfile, cessationCostProfile, currency, costYear, cO2ShareOilProfile, cO2ShareGasProfile, cO2ShareWaterInjectionProfile, cO2OnMaxOilProfile, cO2OnMaxGasProfile, - cO2OnMaxWaterInjectionProfile, approvedBy, facilitiesAvailability, artificialLift, + cO2OnMaxWaterInjectionProfile, approvedBy, artificialLift, producerCount, gasInjectorCount, waterInjectorCount, fuelConsumption, flaredGas, dG3Date, dG4Date, facilityOpex]) @@ -311,15 +307,6 @@ const TopsideView = () => { label="Water injector count" caseValue={caseItem?.waterInjectorCount} /> -