Merge pull request 'feat(expeditions): prevent reuse of stockitem_id in active expeditions' (#6) from feature/leandro/5-double-trace-lock into master
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 2m18s
All checks were successful
CI/CD Pipeline / Build and Deploy with Docker Compose (push) Successful in 2m18s
Reviewed-on: http://saludlab.com.ar:3000/leandro/phronCare/pulls/6
This commit is contained in:
commit
d99f1c34d2
@ -1,4 +1,5 @@
|
||||
using Core.Interfaces.Stock;
|
||||
using Domain.Constants;
|
||||
using Domain.Dtos.Stock;
|
||||
using Domain.Entities;
|
||||
using Domain.Generics;
|
||||
@ -12,32 +13,155 @@ namespace Core.Services.Stock
|
||||
{
|
||||
#region Declaraciones
|
||||
private readonly IExpeditionRepository _repo;
|
||||
public ExpeditionService(IExpeditionRepository repo) => _repo = repo;
|
||||
private readonly IPhLSMStockItemRepository _stockItemRepository;
|
||||
public ExpeditionService(
|
||||
IExpeditionRepository repo,
|
||||
IPhLSMStockItemRepository stockItemRepository)
|
||||
{
|
||||
_repo = repo;
|
||||
_stockItemRepository = stockItemRepository;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Guardado completo de expedicion (encabezado + detalles)
|
||||
public async Task<(int Id, string ExpeditionNumber)> CreateAndIssueAsync(
|
||||
ELSExpeditionHeader header,
|
||||
IEnumerable<ELSExpeditionDetail> details,
|
||||
int formSeriesId)
|
||||
{
|
||||
if (header is null) throw new ArgumentNullException(nameof(header));
|
||||
if (header is null)
|
||||
throw new ArgumentNullException(nameof(header));
|
||||
|
||||
if (details is null || !details.Any())
|
||||
throw new InvalidOperationException("Debe incluir al menos un ítem.");
|
||||
|
||||
if (formSeriesId <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(formSeriesId), "Serie inválida.");
|
||||
|
||||
// Reemplazo directo de la colección (más claro que Clear()+Add)
|
||||
header.PhLsmExpeditionDetails = details.ToList();
|
||||
var detailList = details.ToList();
|
||||
|
||||
ValidateNoDuplicateStockItems(detailList);
|
||||
await ValidateSerializedConflictsAsync(detailList);
|
||||
await ValidateStockAvailabilityAsync(detailList);
|
||||
|
||||
header.PhLsmExpeditionDetails = detailList;
|
||||
|
||||
return await _repo.CreateFullExpeditionAsync(header, formSeriesId);
|
||||
}
|
||||
private static void ValidateNoDuplicateStockItems(List<ELSExpeditionDetail> detailList)
|
||||
{
|
||||
var duplicateIds = detailList
|
||||
.Where(d => d.StockitemId > 0)
|
||||
.GroupBy(d => d.StockitemId)
|
||||
.Where(g => g.Count() > 1)
|
||||
.Select(g => g.Key)
|
||||
.OrderBy(x => x)
|
||||
.ToList();
|
||||
|
||||
if (duplicateIds.Count == 0)
|
||||
return;
|
||||
|
||||
var msg = "No se puede emitir la expedición. " +
|
||||
"El mismo StockItem fue seleccionado más de una vez: " +
|
||||
string.Join(", ", duplicateIds);
|
||||
|
||||
throw new InvalidOperationException(msg);
|
||||
}
|
||||
private async Task ValidateSerializedConflictsAsync(List<ELSExpeditionDetail> detailList)
|
||||
{
|
||||
var requestedStockItemIds = detailList
|
||||
.Where(d => d.StockitemId > 0)
|
||||
.Select(d => d.StockitemId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (requestedStockItemIds.Count == 0)
|
||||
return;
|
||||
|
||||
var conflicts = await _repo.CheckStockItemConflictsAsync(
|
||||
requestedStockItemIds,
|
||||
ignoreExpeditionId: null);
|
||||
|
||||
if (conflicts.Count == 0)
|
||||
return;
|
||||
|
||||
var lines = new List<string>
|
||||
{
|
||||
$"No se puede emitir la expedición: se detectaron {conflicts.Count} stock items serializados ya asignados a expediciones activas."
|
||||
};
|
||||
|
||||
foreach (var conflict in conflicts
|
||||
.OrderBy(x => x.StockitemId)
|
||||
.ThenBy(x => x.Expeditionnumber))
|
||||
{
|
||||
var statusLabel = ((ExpeditionStatus)conflict.Status).ToLabel();
|
||||
lines.Add($"• StockItem {conflict.StockitemId} → {conflict.Expeditionnumber} ({statusLabel})");
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(string.Join(Environment.NewLine, lines));
|
||||
}
|
||||
private async Task ValidateStockAvailabilityAsync(List<ELSExpeditionDetail> detailList)
|
||||
{
|
||||
var requestedByStockItem = detailList
|
||||
.Where(d => d.StockitemId > 0 && d.Quantity > 0)
|
||||
.GroupBy(d => d.StockitemId)
|
||||
.Select(g => new
|
||||
{
|
||||
StockitemId = g.Key,
|
||||
RequestedQuantity = g.Sum(x => x.Quantity)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (requestedByStockItem.Count == 0)
|
||||
return;
|
||||
|
||||
var availability = await _stockItemRepository.GetAvailabilityByStockItemIdsAsync(
|
||||
requestedByStockItem.Select(x => x.StockitemId));
|
||||
|
||||
var availabilityMap = availability.ToDictionary(x => x.StockitemId);
|
||||
|
||||
var errors = new List<string>();
|
||||
|
||||
foreach (var request in requestedByStockItem.OrderBy(x => x.StockitemId))
|
||||
{
|
||||
if (!availabilityMap.TryGetValue(request.StockitemId, out var stock))
|
||||
{
|
||||
errors.Add($"• StockItem {request.StockitemId} → no fue encontrado en stock.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var hasSerial = !string.IsNullOrWhiteSpace(stock.Serial);
|
||||
|
||||
// Los serializados ya se validan por exclusividad en ValidateSerializedConflictsAsync
|
||||
if (hasSerial)
|
||||
continue;
|
||||
|
||||
if (request.RequestedQuantity > stock.AvailableQuantity)
|
||||
{
|
||||
errors.Add(
|
||||
$"• StockItem {request.StockitemId} → solicitado: {request.RequestedQuantity}, disponible: {stock.AvailableQuantity}.");
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.Count == 0)
|
||||
return;
|
||||
|
||||
var lines = new List<string>
|
||||
{
|
||||
"No se puede emitir la expedición: algunos stock items no serializados no tienen cantidad disponible suficiente."
|
||||
};
|
||||
|
||||
lines.AddRange(errors);
|
||||
|
||||
throw new InvalidOperationException(string.Join(Environment.NewLine, lines));
|
||||
}
|
||||
#endregion
|
||||
|
||||
// Otros métodos de la clase...
|
||||
public Task<ExpeditionDto?> GetDtoByExpeditionNumberAsync(string expeditionNumber)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
public Task<PagedResult<ExpeditionDto>> SearchAsync(
|
||||
string? expeditionNumber,
|
||||
string? status,
|
||||
@ -49,7 +173,6 @@ namespace Core.Services.Stock
|
||||
=> _repo.SearchAsync(expeditionNumber, status, issueDateFrom, issueDateTo, locationId, page, pageSize);
|
||||
public Task<ExpeditionDto?> GetDtoByIdAsync(int id)
|
||||
=> _repo.GetDtoByIdAsync(id);
|
||||
|
||||
public async Task<byte[]> ExportFilteredToExcelAsync(ExpeditionSearchParams searchParams)
|
||||
{
|
||||
try
|
||||
|
||||
11
Domain/Dtos/Stock/StockItemAvailabilityDto.cs
Normal file
11
Domain/Dtos/Stock/StockItemAvailabilityDto.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Domain.Dtos.Stock
|
||||
{
|
||||
public sealed class StockItemAvailabilityDto
|
||||
{
|
||||
public int StockitemId { get; set; }
|
||||
public decimal Quantity { get; set; }
|
||||
public decimal ReservedQuantity { get; set; }
|
||||
public decimal AvailableQuantity { get; set; }
|
||||
public string? Serial { get; set; }
|
||||
}
|
||||
}
|
||||
10
Domain/Dtos/Stock/StockItemExpeditionConflictDto.cs
Normal file
10
Domain/Dtos/Stock/StockItemExpeditionConflictDto.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Domain.Dtos.Stock
|
||||
{
|
||||
public sealed class StockItemExpeditionConflictDto
|
||||
{
|
||||
public int StockitemId { get; set; }
|
||||
public int ExpeditionId { get; set; }
|
||||
public string Expeditionnumber { get; set; } = string.Empty;
|
||||
public int Status { get; set; }
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,16 @@ namespace Models.Interfaces
|
||||
// 1.1 Data (Repo)
|
||||
public interface IExpeditionRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifica si alguno de los stock items indicados ya está asociado
|
||||
/// a otra expedición activa. Utilizado para prevenir doble traza.
|
||||
/// </summary>
|
||||
/// <param name="stockItemIds">Lista de stockitem_id a validar.</param>
|
||||
/// <param name="ignoreExpeditionId">
|
||||
/// Expedición a ignorar (usado en edición para no detectar conflicto consigo misma).
|
||||
/// </param>
|
||||
/// <returns>Lista de conflictos encontrados.</returns>
|
||||
Task<List<StockItemExpeditionConflictDto>> CheckStockItemConflictsAsync(IEnumerable<int> stockItemIds, int? ignoreExpeditionId);
|
||||
/// <summary>
|
||||
/// Crea la expedición completa (encabezado + detalles) y la deja emitida con numeración de serie.
|
||||
/// </summary>
|
||||
|
||||
@ -24,5 +24,9 @@ namespace Models.Interfaces
|
||||
int? locationId,
|
||||
int page,
|
||||
int take);
|
||||
|
||||
// Obtener disponibilidad por IDs de StockItem
|
||||
Task<List<StockItemAvailabilityDto>> GetAvailabilityByStockItemIdsAsync(
|
||||
IEnumerable<int> stockItemIds);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,12 @@
|
||||
using Domain.Dtos.Stock;
|
||||
using Domain.Entities;
|
||||
using Domain.Generics;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Models.Helpers;
|
||||
using Models.Interfaces;
|
||||
using Models.Models;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
@ -56,7 +58,6 @@ namespace Models.Repositories.Stock
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Devuelve el DTO completo de Expedición (cabecera + ítems) listo para UI/impresión.
|
||||
/// </summary>
|
||||
@ -164,9 +165,7 @@ namespace Models.Repositories.Stock
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
// ----- helpers -----
|
||||
|
||||
/// <summary>
|
||||
/// Mapea el estado entero a etiqueta amigable (enum: Emitida=1, EnTransito=2, EnDestino=3, Retorno=4, Cerrada=5, Anulada=6).
|
||||
/// </summary>
|
||||
@ -310,6 +309,73 @@ namespace Models.Repositories.Stock
|
||||
PageSize = pageSize
|
||||
};
|
||||
}
|
||||
public async Task<List<StockItemExpeditionConflictDto>> CheckStockItemConflictsAsync(
|
||||
IEnumerable<int> stockItemIds,
|
||||
int? ignoreExpeditionId)
|
||||
{
|
||||
// Normalización defensiva
|
||||
var ids = (stockItemIds ?? Enumerable.Empty<int>())
|
||||
.Where(x => x > 0)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (ids.Count == 0)
|
||||
return new List<StockItemExpeditionConflictDto>();
|
||||
|
||||
// TVP: dbo.PhLSM_StockItemIdList(stockitem_id int not null)
|
||||
var tvp = new DataTable();
|
||||
tvp.Columns.Add("stockitem_id", typeof(int));
|
||||
|
||||
foreach (var id in ids)
|
||||
tvp.Rows.Add(id);
|
||||
|
||||
var results = new List<StockItemExpeditionConflictDto>();
|
||||
|
||||
// Usamos la conexión del DbContext (no creamos otra)
|
||||
var conn = _context.Database.GetDbConnection();
|
||||
if (conn.State != ConnectionState.Open)
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "dbo.PhLSM_Expedition_CheckStockItemConflicts";
|
||||
cmd.CommandType = CommandType.StoredProcedure;
|
||||
|
||||
// Param TVP
|
||||
var pIds = new SqlParameter("@StockItemIds", SqlDbType.Structured)
|
||||
{
|
||||
TypeName = "dbo.PhLSM_StockItemIdList",
|
||||
Value = tvp
|
||||
};
|
||||
cmd.Parameters.Add(pIds);
|
||||
|
||||
// Param opcional para edición
|
||||
var pIgnore = new SqlParameter("@IgnoreExpeditionId", SqlDbType.Int)
|
||||
{
|
||||
Value = ignoreExpeditionId.HasValue ? ignoreExpeditionId.Value : DBNull.Value
|
||||
};
|
||||
cmd.Parameters.Add(pIgnore);
|
||||
|
||||
await using var reader = await cmd.ExecuteReaderAsync();
|
||||
|
||||
// Ordinals por nombre (más robusto ante cambios de orden)
|
||||
var ordStockItemId = reader.GetOrdinal("StockitemId");
|
||||
var ordExpId = reader.GetOrdinal("ExpeditionId");
|
||||
var ordExpNum = reader.GetOrdinal("Expeditionnumber");
|
||||
var ordStatus = reader.GetOrdinal("Status");
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
results.Add(new StockItemExpeditionConflictDto
|
||||
{
|
||||
StockitemId = reader.GetInt32(ordStockItemId),
|
||||
ExpeditionId = reader.GetInt32(ordExpId),
|
||||
Expeditionnumber = reader.IsDBNull(ordExpNum) ? string.Empty : reader.GetString(ordExpNum),
|
||||
Status = reader.GetInt32(ordStatus)
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
private static int? MapStatusLabelToInt(string labelOrNumber)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(labelOrNumber)) return null;
|
||||
@ -329,7 +395,6 @@ namespace Models.Repositories.Stock
|
||||
_ => (int?)null
|
||||
};
|
||||
}
|
||||
|
||||
private static string NormalizeKey(string s)
|
||||
{
|
||||
var norm = s.Trim().ToLowerInvariant().Normalize(NormalizationForm.FormD);
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
using Domain.Dtos.Stock; // StockItemScanResultDto
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Domain.Dtos.Stock; // StockItemScanResultDto
|
||||
using Domain.Generics; // PagedResult<T>
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Models.Helpers; // ToPagedResultAsync
|
||||
using Models.Interfaces; // IPhLSMStockItemRepository
|
||||
using Models.Models; // PhronCareOperationsHubContext
|
||||
using Models.Models;
|
||||
using System.Data; // PhronCareOperationsHubContext
|
||||
|
||||
namespace Models.Repositories.Stock
|
||||
{
|
||||
@ -324,5 +326,74 @@ namespace Models.Repositories.Stock
|
||||
PageSize = paged.PageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<List<StockItemAvailabilityDto>> GetAvailabilityByStockItemIdsAsync(
|
||||
IEnumerable<int> stockItemIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ids = stockItemIds?
|
||||
.Where(x => x > 0)
|
||||
.Distinct()
|
||||
.ToList() ?? new List<int>();
|
||||
|
||||
if (ids.Count == 0)
|
||||
return new List<StockItemAvailabilityDto>();
|
||||
|
||||
// TVP = Table Valued Parameter
|
||||
var tvp = new DataTable();
|
||||
tvp.Columns.Add("stockitem_id", typeof(int));
|
||||
|
||||
foreach (var id in ids)
|
||||
tvp.Rows.Add(id);
|
||||
|
||||
var result = new List<StockItemAvailabilityDto>();
|
||||
var conn = _context.Database.GetDbConnection();
|
||||
var cs = _context.Database.GetConnectionString();
|
||||
if (conn.State != ConnectionState.Open)
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
|
||||
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "dbo.PhLSM_Stock_GetAvailabilityByStockItemIds";
|
||||
cmd.CommandType = CommandType.StoredProcedure;
|
||||
|
||||
var pIds = new SqlParameter("@StockItemIds", SqlDbType.Structured)
|
||||
{
|
||||
TypeName = "dbo.PhLSM_StockItemIdList",
|
||||
Value = tvp
|
||||
};
|
||||
|
||||
cmd.Parameters.Add(pIds);
|
||||
|
||||
await using var reader = await cmd.ExecuteReaderAsync();
|
||||
|
||||
var ordStockItemId = reader.GetOrdinal("StockitemId");
|
||||
var ordQuantity = reader.GetOrdinal("Quantity");
|
||||
var ordReserved = reader.GetOrdinal("ReservedQuantity");
|
||||
var ordAvailable = reader.GetOrdinal("AvailableQuantity");
|
||||
var ordSerial = reader.GetOrdinal("Serial");
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
result.Add(new StockItemAvailabilityDto
|
||||
{
|
||||
StockitemId = reader.GetInt32(ordStockItemId),
|
||||
Quantity = reader.GetDecimal(ordQuantity),
|
||||
ReservedQuantity = reader.GetDecimal(ordReserved),
|
||||
AvailableQuantity = reader.GetDecimal(ordAvailable),
|
||||
Serial = reader.IsDBNull(ordSerial)
|
||||
? null
|
||||
: reader.GetString(ordSerial)
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
PhLSM_Expedition_CheckStockItemConflicts.sql
Normal file
44
PhLSM_Expedition_CheckStockItemConflicts.sql
Normal file
@ -0,0 +1,44 @@
|
||||
USE [phronCare_OperationsHub]
|
||||
GO
|
||||
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
|
||||
-- =============================================
|
||||
-- Procedure: PhLSM_Expedition_CheckStockItemConflicts
|
||||
-- Module: Logistics / Stock (PhLSM)
|
||||
-- Purpose: Detect serialized stock items already assigned
|
||||
-- to active expeditions.
|
||||
-- Author: Leandro Rojas
|
||||
-- Created: 2026-03-05
|
||||
-- =============================================
|
||||
|
||||
ALTER PROCEDURE [dbo].[PhLSM_Expedition_CheckStockItemConflicts]
|
||||
(
|
||||
@StockItemIds dbo.PhLSM_StockItemIdList READONLY,
|
||||
@IgnoreExpeditionId INT = NULL
|
||||
)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT
|
||||
d.stockitem_id AS StockitemId,
|
||||
h.id AS ExpeditionId,
|
||||
h.expeditionnumber AS Expeditionnumber,
|
||||
h.status AS Status
|
||||
FROM dbo.PhLSM_ExpeditionDetails d
|
||||
INNER JOIN @StockItemIds ids
|
||||
ON ids.stockitem_id = d.stockitem_id
|
||||
INNER JOIN dbo.PhLSM_ExpeditionHeaders h
|
||||
ON h.id = d.expedition_id
|
||||
INNER JOIN dbo.PhLSM_StockItem si
|
||||
ON si.id = d.stockitem_id
|
||||
WHERE h.status NOT IN (5,6) -- 5=Cerrada, 6=Anulada
|
||||
AND (@IgnoreExpeditionId IS NULL OR h.id <> @IgnoreExpeditionId)
|
||||
AND si.serial IS NOT NULL
|
||||
AND LTRIM(RTRIM(si.serial)) <> '';
|
||||
END
|
||||
GO
|
||||
5
PhLSM_StockItemIdList.sql
Normal file
5
PhLSM_StockItemIdList.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TYPE dbo.PhLSM_StockItemIdList AS TABLE
|
||||
(
|
||||
stockitem_id INT NOT NULL
|
||||
);
|
||||
GO
|
||||
36
PhLSM_Stock_GetAvailabilityByStockItemIds.sql
Normal file
36
PhLSM_Stock_GetAvailabilityByStockItemIds.sql
Normal file
@ -0,0 +1,36 @@
|
||||
USE [phronCare_OperationsHub]
|
||||
GO
|
||||
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
|
||||
-- =============================================
|
||||
-- Procedure: PhLSM_Stock_GetAvailabilityByStockItemIds
|
||||
-- Module: Logistics / Stock (PhLSM)
|
||||
-- Purpose: Returns quantity and availability data
|
||||
-- for the requested stock items.
|
||||
-- Author: Leandro Rojas
|
||||
-- Created: 2026-03-09
|
||||
-- =============================================
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[PhLSM_Stock_GetAvailabilityByStockItemIds]
|
||||
(
|
||||
@StockItemIds dbo.PhLSM_StockItemIdList READONLY
|
||||
)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT
|
||||
si.id AS StockitemId,
|
||||
si.quantity AS Quantity,
|
||||
ISNULL(si.reserved_quantity, 0) AS ReservedQuantity,
|
||||
si.quantity - ISNULL(si.reserved_quantity, 0) AS AvailableQuantity,
|
||||
si.serial AS Serial
|
||||
FROM dbo.PhLSM_StockItem si
|
||||
INNER JOIN @StockItemIds ids
|
||||
ON ids.stockitem_id = si.id;
|
||||
END
|
||||
GO
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.6.33815.320
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.3.11520.95
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "phronCare.API", "phronCare.API\phronCare.API.csproj", "{13FF5898-C6D4-42AE-AC36-8D42C88FEDAF}"
|
||||
EndProject
|
||||
@ -43,6 +43,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1.5 Documents", "1.5 Docume
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Documents", "Documents\Documents.csproj", "{0EFF27D3-C585-49F3-BBB5-A5E99C52207B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2.Database", "2.Database", "{E93C8350-6A6C-40F1-99C0-14CF7459EA8B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Migrations", "Migrations", "{5826BD19-0018-4F9C-B405-9BAB997CC8C7}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Types", "Types", "{3C3276F6-7320-4AB8-9C1E-1893E4646FD4}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
PhLSM_StockItemIdList.sql = PhLSM_StockItemIdList.sql
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Procedures", "Procedures", "{E4F7BA13-B838-42D5-AADD-481DD76DDD5E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
PhLSM_Expedition_CheckStockItemConflicts.sql = PhLSM_Expedition_CheckStockItemConflicts.sql
|
||||
PhLSM_Stock_GetAvailabilityByStockItemIds.sql = PhLSM_Stock_GetAvailabilityByStockItemIds.sql
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -104,6 +119,9 @@ Global
|
||||
{34FC5538-4779-41F5-8355-7866B1395A4F} = {13328F60-28A6-446D-9935-23866F3F3D9D}
|
||||
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {74280C0B-F2CC-4A3E-86D6-05530F9766D5}
|
||||
{0EFF27D3-C585-49F3-BBB5-A5E99C52207B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||
{5826BD19-0018-4F9C-B405-9BAB997CC8C7} = {E93C8350-6A6C-40F1-99C0-14CF7459EA8B}
|
||||
{3C3276F6-7320-4AB8-9C1E-1893E4646FD4} = {E93C8350-6A6C-40F1-99C0-14CF7459EA8B}
|
||||
{E4F7BA13-B838-42D5-AADD-481DD76DDD5E} = {E93C8350-6A6C-40F1-99C0-14CF7459EA8B}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {92DE3FEB-7D7E-4C78-BE8C-34931CA1DAED}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user