-- Tamamla: yapılan miktarlar stoğa işlenir (Inv_T_Stock, ürün biriminde, lot kırılımlı). İç konumdan çıkışta stok yetmezse -- işlem durur; dış konumlar stok tutmaz. Kaynak ve hedefi aynı konum olan belge stoğa dokunmaz (teslim kaydıdır). Envanter takibi kapalı ürün stoğa girmez. Lot/seri takipli üründe lot toplamı -- yapılan miktara eşit olmalıdır. Sipariş kalemlerinin alınan, talep kalemlerinin teslim edilen miktarı güncellenir (iadede düşülür); -- siparişli alım ürün–tedarikçi fiyatını yazar (Inv_P_TransferSupplierPrice). -- Talepten az yapılan satırların kalanı aynı türde yeni belgeye (backorder) geçer. CREATE OR ALTER PROCEDURE [dbo].[Inv_P_TransferValidate] @Id uniqueidentifier, @UserId uniqueidentifier = NULL AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; DECLARE @Tenant uniqueidentifier, @Status nvarchar(16), @Kind nvarchar(32), @ReturnOf uniqueidentifier, @Src uniqueidentifier, @Dst uniqueidentifier, @SrcInternal bit = 0, @DstInternal bit = 0, @DstBranch uniqueidentifier, @DstWarehouse uniqueidentifier, @Sign int, @New uniqueidentifier, @Now datetime = GETUTCDATE(), @Msg nvarchar(512); BEGIN TRANSACTION; SELECT @Tenant = TenantId, @Status = Status, @Kind = OperationKind, @ReturnOf = ReturnOfId, @Src = SourceLocationId, @Dst = DestinationLocationId FROM Inv_T_Transfer WITH (UPDLOCK, HOLDLOCK) WHERE Id = @Id; IF @Status <> 'Ready' THROW 50306, N'Yalnız hazır belge tamamlanabilir.', 1; SET @Sign = CASE WHEN @ReturnOf IS NULL THEN 1 ELSE -1 END; SELECT @SrcInternal = CASE WHEN LocationType = 'Internal' THEN 1 ELSE 0 END FROM Inv_T_Location WHERE Id = @Src; SELECT @DstInternal = CASE WHEN LocationType = 'Internal' THEN 1 ELSE 0 END, @DstBranch = BranchId, @DstWarehouse = WarehouseId FROM Inv_T_Location WHERE Id = @Dst; IF NOT EXISTS (SELECT 1 FROM Inv_T_TransferLine WHERE TransferId = @Id AND IsDeleted = 0 AND Quantity > 0) THROW 50309, N'Yapılan miktarı girilmiş satır yok.', 1; SELECT TOP 1 @Msg = CONCAT(p.Name, N': lot/seri toplamı ', FORMAT(z.Quantity, '0.####'), N', yapılan miktar ', FORMAT(l.Quantity, '0.####')) FROM Inv_T_TransferLine l INNER JOIN Inv_T_Product p ON p.Id = l.ProductId CROSS APPLY (SELECT ISNULL(SUM(lt.Quantity), 0) AS Quantity FROM Inv_T_TransferLineLot lt WHERE lt.TransferLineId = l.Id AND lt.IsDeleted = 0) z WHERE l.TransferId = @Id AND l.IsDeleted = 0 AND l.Quantity > 0 AND p.TrackInventory = 1 AND p.TrackingMethod IN ('Lot', 'Serial') AND z.Quantity <> l.Quantity; IF @Msg IS NOT NULL BEGIN SET @Msg = CONCAT(N'Lot/seri miktarı yapılan miktara eşit değil — ', @Msg); THROW 50310, @Msg, 1; END SELECT TOP 1 @Msg = CONCAT(p.Name, ' / ', z.LotNo) FROM Inv_T_TransferLineLot z INNER JOIN Inv_T_TransferLine l ON l.Id = z.TransferLineId INNER JOIN Inv_T_Product p ON p.Id = l.ProductId WHERE l.TransferId = @Id AND l.IsDeleted = 0 AND z.IsDeleted = 0 AND p.TrackingMethod = 'Serial' AND (z.Quantity <> 1 OR EXISTS (SELECT 1 FROM Inv_T_TransferLineLot z2 INNER JOIN Inv_T_TransferLine l2 ON l2.Id = z2.TransferLineId WHERE l2.TransferId = @Id AND l2.IsDeleted = 0 AND z2.IsDeleted = 0 AND l2.ProductId = l.ProductId AND z2.LotNo = z.LotNo AND z2.Id <> z.Id)); IF @Msg IS NOT NULL BEGIN SET @Msg = CONCAT(N'Seri numaralı üründe her seri 1 adet ve tekil olmalıdır: ', @Msg); THROW 50311, @Msg, 1; END -- Stoğa işlenecek hareket: ürün × lot, ürün biriminde. DECLARE @Move TABLE (ProductId uniqueidentifier NOT NULL, LotNo nvarchar(64) NOT NULL, Quantity decimal(18, 4) NOT NULL, UomId nvarchar(64) NOT NULL, PRIMARY KEY (ProductId, LotNo)); INSERT INTO @Move (ProductId, LotNo, Quantity, UomId) SELECT m.ProductId, m.LotNo, SUM(m.Quantity), m.UomId FROM (SELECT l.ProductId, p.UomId, CASE WHEN p.TrackingMethod IN ('Lot', 'Serial') THEN z.LotNo ELSE '' END AS LotNo, dbo.Inv_F_UomConvert(CASE WHEN p.TrackingMethod IN ('Lot', 'Serial') THEN z.Quantity ELSE l.Quantity END, l.UomId, p.UomId) AS Quantity FROM Inv_T_TransferLine l INNER JOIN Inv_T_Product p ON p.Id = l.ProductId LEFT JOIN Inv_T_TransferLineLot z ON z.TransferLineId = l.Id AND z.IsDeleted = 0 AND p.TrackingMethod IN ('Lot', 'Serial') WHERE l.TransferId = @Id AND l.IsDeleted = 0 AND l.Quantity > 0 AND p.TrackInventory = 1) m GROUP BY m.ProductId, m.LotNo, m.UomId; IF @SrcInternal = 1 BEGIN SELECT TOP 1 @Msg = CONCAT(p.Name, CASE WHEN m.LotNo = '' THEN '' ELSE CONCAT(' / ', m.LotNo) END) FROM @Move m INNER JOIN Inv_T_Product p ON p.Id = m.ProductId LEFT JOIN Inv_T_Stock s WITH (UPDLOCK, HOLDLOCK) ON s.LocationId = @Src AND s.ProductId = m.ProductId AND s.LotNo = m.LotNo AND (s.TenantId = @Tenant OR (s.TenantId IS NULL AND @Tenant IS NULL)) WHERE ISNULL(s.Quantity, 0) < m.Quantity; IF @Msg IS NOT NULL BEGIN SET @Msg = CONCAT(N'Kaynak konumda yeterli stok yok: ', @Msg); THROW 50312, @Msg, 1; END -- Kaynak ve hedef aynıysa stok yerinde kalır: belge teslimi kaydeder (yeterlilik yine denetlenir). IF @Src <> @Dst UPDATE s SET Quantity = s.Quantity - m.Quantity, LastMoveDate = @Now, LastModificationTime = @Now, LastModifierId = @UserId FROM Inv_T_Stock s INNER JOIN @Move m ON m.ProductId = s.ProductId AND m.LotNo = s.LotNo WHERE s.LocationId = @Src AND (s.TenantId = @Tenant OR (s.TenantId IS NULL AND @Tenant IS NULL)); END IF @DstInternal = 1 AND @Src <> @Dst BEGIN UPDATE s SET Quantity = s.Quantity + m.Quantity, LastMoveDate = @Now, LastModificationTime = @Now, LastModifierId = @UserId, InDate = CASE WHEN s.Quantity <= 0 THEN @Now ELSE s.InDate END FROM Inv_T_Stock s WITH (UPDLOCK, HOLDLOCK) INNER JOIN @Move m ON m.ProductId = s.ProductId AND m.LotNo = s.LotNo WHERE s.LocationId = @Dst AND (s.TenantId = @Tenant OR (s.TenantId IS NULL AND @Tenant IS NULL)); INSERT INTO Inv_T_Stock (Id, TenantId, BranchId, WarehouseId, LocationId, ProductId, LotNo, Quantity, UomId, InDate, LastMoveDate, CreationTime, CreatorId, IsDeleted) SELECT NEWID(), @Tenant, @DstBranch, @DstWarehouse, @Dst, m.ProductId, m.LotNo, m.Quantity, m.UomId, @Now, @Now, @Now, @UserId, 0 FROM @Move m WHERE NOT EXISTS (SELECT 1 FROM Inv_T_Stock s WHERE s.LocationId = @Dst AND s.ProductId = m.ProductId AND s.LotNo = m.LotNo AND (s.TenantId = @Tenant OR (s.TenantId IS NULL AND @Tenant IS NULL))); END UPDATE Inv_T_Transfer SET Status = 'Done', EffectiveDate = @Now, ReceivedByUserId = CASE WHEN @Kind = 'InternalTransfer' THEN @UserId ELSE ReceivedByUserId END, ReceivedDate = CASE WHEN @Kind = 'InternalTransfer' THEN @Now ELSE ReceivedDate END WHERE Id = @Id; UPDATE ol SET ReceivedQuantity = ol.ReceivedQuantity + @Sign * x.Quantity FROM Prc_T_PurchaseOrderLine ol INNER JOIN (SELECT l.PurchaseOrderLineId, SUM(dbo.Inv_F_UomConvert(l.Quantity, l.UomId, pl.UomId)) AS Quantity FROM Inv_T_TransferLine l INNER JOIN Prc_T_PurchaseOrderLine pl ON pl.Id = l.PurchaseOrderLineId WHERE l.TransferId = @Id AND l.IsDeleted = 0 AND l.Quantity > 0 GROUP BY l.PurchaseOrderLineId) x ON x.PurchaseOrderLineId = ol.Id; EXEC Inv_P_TransferSupplierPrice @Id, @UserId; UPDATE i SET DeliveredQuantity = i.DeliveredQuantity + @Sign * x.Quantity, LineStatus = CASE WHEN i.DeliveredQuantity + @Sign * x.Quantity >= i.Quantity THEN 'Done' WHEN i.LineStatus = 'Done' THEN 'Waiting' ELSE i.LineStatus END FROM Prc_T_MaterialRequestItem i INNER JOIN (SELECT l.MaterialRequestItemId, SUM(dbo.Inv_F_UomConvert(l.Quantity, l.UomId, mi.UomId)) AS Quantity FROM Inv_T_TransferLine l INNER JOIN Prc_T_MaterialRequestItem mi ON mi.Id = l.MaterialRequestItemId WHERE l.TransferId = @Id AND l.IsDeleted = 0 AND l.Quantity > 0 GROUP BY l.MaterialRequestItemId) x ON x.MaterialRequestItemId = i.Id; IF @ReturnOf IS NULL AND EXISTS (SELECT 1 FROM Inv_T_TransferLine WHERE TransferId = @Id AND IsDeleted = 0 AND Quantity < DemandQuantity) EXEC Inv_P_TransferCopy @Id, 'Backorder', @UserId, @New OUTPUT; EXEC Inv_P_TransferRecalc @Id; COMMIT TRANSACTION; END