-- Tamamla: yapılan miktarlar stoğa işlenir (Stk_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 (Stk_P_TransferSupplierPrice). -- Talepten az yapılan satırların kalanı aynı türde yeni belgeye (backorder) geçer. CREATE OR REPLACE PROCEDURE public."Stk_P_TransferValidate"(p_id uuid, p_user_id uuid DEFAULT NULL) LANGUAGE plpgsql AS $$ DECLARE v_tenant uuid; v_status varchar(16); v_kind varchar(32); v_return_of uuid; v_src uuid; v_dst uuid; v_src_internal boolean := false; v_dst_internal boolean := false; v_dst_branch uuid; v_dst_warehouse uuid; v_sign int; v_new uuid; v_now timestamp := NOW() AT TIME ZONE 'UTC'; v_msg varchar(512); BEGIN SELECT "TenantId", "Status", "OperationKind", "ReturnOfId", "SourceLocationId", "DestinationLocationId" INTO v_tenant, v_status, v_kind, v_return_of, v_src, v_dst FROM "Stk_T_Transfer" WHERE "Id" = p_id FOR UPDATE; IF v_status <> 'Ready' THEN RAISE EXCEPTION 'Yalnız hazır belge tamamlanabilir.' USING ERRCODE = 'P0001', DETAIL = '50306'; END IF; v_sign := CASE WHEN v_return_of IS NULL THEN 1 ELSE -1 END; SELECT "LocationType" = 'Internal' INTO v_src_internal FROM "Stk_T_Location" WHERE "Id" = v_src; SELECT "LocationType" = 'Internal', "BranchId", "WarehouseId" INTO v_dst_internal, v_dst_branch, v_dst_warehouse FROM "Stk_T_Location" WHERE "Id" = v_dst; v_src_internal := COALESCE(v_src_internal, false); v_dst_internal := COALESCE(v_dst_internal, false); IF NOT EXISTS (SELECT 1 FROM "Stk_T_TransferLine" WHERE "TransferId" = p_id AND "IsDeleted" = false AND "Quantity" > 0) THEN RAISE EXCEPTION 'Yapılan miktarı girilmiş satır yok.' USING ERRCODE = 'P0001', DETAIL = '50309'; END IF; SELECT p."Name" || ': lot/seri toplamı ' || rtrim(rtrim(z.q::text, '0'), '.') || ', yapılan miktar ' || rtrim(rtrim(l."Quantity"::text, '0'), '.') INTO v_msg FROM "Stk_T_TransferLine" l INNER JOIN "Stk_T_Product" p ON p."Id" = l."ProductId" CROSS JOIN LATERAL (SELECT COALESCE(SUM(lt."Quantity"), 0) AS q FROM "Stk_T_TransferLineLot" lt WHERE lt."TransferLineId" = l."Id" AND lt."IsDeleted" = false) z WHERE l."TransferId" = p_id AND l."IsDeleted" = false AND l."Quantity" > 0 AND p."TrackInventory" = true AND p."TrackingMethod" IN ('Lot', 'Serial') AND z.q <> l."Quantity" LIMIT 1; IF v_msg IS NOT NULL THEN RAISE EXCEPTION 'Lot/seri miktarı yapılan miktara eşit değil — %', v_msg USING ERRCODE = 'P0001', DETAIL = '50310'; END IF; SELECT p."Name" || ' / ' || z."LotNo" INTO v_msg FROM "Stk_T_TransferLineLot" z INNER JOIN "Stk_T_TransferLine" l ON l."Id" = z."TransferLineId" INNER JOIN "Stk_T_Product" p ON p."Id" = l."ProductId" WHERE l."TransferId" = p_id AND l."IsDeleted" = false AND z."IsDeleted" = false AND p."TrackingMethod" = 'Serial' AND (z."Quantity" <> 1 OR EXISTS (SELECT 1 FROM "Stk_T_TransferLineLot" z2 INNER JOIN "Stk_T_TransferLine" l2 ON l2."Id" = z2."TransferLineId" WHERE l2."TransferId" = p_id AND l2."IsDeleted" = false AND z2."IsDeleted" = false AND l2."ProductId" = l."ProductId" AND z2."LotNo" = z."LotNo" AND z2."Id" <> z."Id")) LIMIT 1; IF v_msg IS NOT NULL THEN RAISE EXCEPTION 'Seri numaralı üründe her seri 1 adet ve tekil olmalıdır: %', v_msg USING ERRCODE = 'P0001', DETAIL = '50311'; END IF; -- Stoğa işlenecek hareket: ürün × lot, ürün biriminde. DROP TABLE IF EXISTS tmp_inv_move; CREATE TEMP TABLE tmp_inv_move AS SELECT m."ProductId", m."LotNo", SUM(m."Quantity") AS "Quantity", m."UomId" FROM (SELECT l."ProductId", p."UomId", CASE WHEN p."TrackingMethod" IN ('Lot', 'Serial') THEN z."LotNo" ELSE '' END AS "LotNo", public."Stk_F_UomConvert"(CASE WHEN p."TrackingMethod" IN ('Lot', 'Serial') THEN z."Quantity" ELSE l."Quantity" END, l."UomId", p."UomId") AS "Quantity" FROM "Stk_T_TransferLine" l INNER JOIN "Stk_T_Product" p ON p."Id" = l."ProductId" LEFT JOIN "Stk_T_TransferLineLot" z ON z."TransferLineId" = l."Id" AND z."IsDeleted" = false AND p."TrackingMethod" IN ('Lot', 'Serial') WHERE l."TransferId" = p_id AND l."IsDeleted" = false AND l."Quantity" > 0 AND p."TrackInventory" = true) m GROUP BY m."ProductId", m."LotNo", m."UomId"; IF v_src_internal THEN PERFORM 1 FROM "Stk_T_Stock" s WHERE s."LocationId" = v_src AND s."TenantId" IS NOT DISTINCT FROM v_tenant AND (s."ProductId", s."LotNo") IN (SELECT "ProductId", "LotNo" FROM tmp_inv_move) FOR UPDATE; SELECT p."Name" || CASE WHEN m."LotNo" = '' THEN '' ELSE ' / ' || m."LotNo" END INTO v_msg FROM tmp_inv_move m INNER JOIN "Stk_T_Product" p ON p."Id" = m."ProductId" LEFT JOIN "Stk_T_Stock" s ON s."LocationId" = v_src AND s."ProductId" = m."ProductId" AND s."LotNo" = m."LotNo" AND s."TenantId" IS NOT DISTINCT FROM v_tenant WHERE COALESCE(s."Quantity", 0) < m."Quantity" LIMIT 1; IF v_msg IS NOT NULL THEN RAISE EXCEPTION 'Kaynak konumda yeterli stok yok: %', v_msg USING ERRCODE = 'P0001', DETAIL = '50312'; END IF; -- Kaynak ve hedef aynıysa stok yerinde kalır: belge teslimi kaydeder (yeterlilik yine denetlenir). IF v_src <> v_dst THEN UPDATE "Stk_T_Stock" s SET "Quantity" = s."Quantity" - m."Quantity", "LastMoveDate" = v_now, "LastModificationTime" = v_now, "LastModifierId" = p_user_id FROM tmp_inv_move m WHERE m."ProductId" = s."ProductId" AND m."LotNo" = s."LotNo" AND s."LocationId" = v_src AND s."TenantId" IS NOT DISTINCT FROM v_tenant; END IF; END IF; IF v_dst_internal AND v_src <> v_dst THEN UPDATE "Stk_T_Stock" s SET "Quantity" = s."Quantity" + m."Quantity", "LastMoveDate" = v_now, "LastModificationTime" = v_now, "LastModifierId" = p_user_id, "InDate" = CASE WHEN s."Quantity" <= 0 THEN v_now ELSE s."InDate" END FROM tmp_inv_move m WHERE m."ProductId" = s."ProductId" AND m."LotNo" = s."LotNo" AND s."LocationId" = v_dst AND s."TenantId" IS NOT DISTINCT FROM v_tenant; INSERT INTO "Stk_T_Stock" ("Id", "TenantId", "BranchId", "WarehouseId", "LocationId", "ProductId", "LotNo", "Quantity", "UomId", "InDate", "LastMoveDate", "CreationTime", "CreatorId", "IsDeleted") SELECT gen_random_uuid(), v_tenant, v_dst_branch, v_dst_warehouse, v_dst, m."ProductId", m."LotNo", m."Quantity", m."UomId", v_now, v_now, v_now, p_user_id, false FROM tmp_inv_move m WHERE NOT EXISTS (SELECT 1 FROM "Stk_T_Stock" s WHERE s."LocationId" = v_dst AND s."ProductId" = m."ProductId" AND s."LotNo" = m."LotNo" AND s."TenantId" IS NOT DISTINCT FROM v_tenant); END IF; DROP TABLE tmp_inv_move; UPDATE "Stk_T_Transfer" SET "Status" = 'Done', "EffectiveDate" = v_now, "ReceivedByUserId" = CASE WHEN v_kind = 'InternalTransfer' THEN p_user_id ELSE "ReceivedByUserId" END, "ReceivedDate" = CASE WHEN v_kind = 'InternalTransfer' THEN v_now ELSE "ReceivedDate" END WHERE "Id" = p_id; UPDATE "Prc_T_PurchaseOrderLine" ol SET "ReceivedQuantity" = ol."ReceivedQuantity" + v_sign * x."Quantity" FROM (SELECT l."PurchaseOrderLineId", SUM(public."Stk_F_UomConvert"(l."Quantity", l."UomId", pl."UomId")) AS "Quantity" FROM "Stk_T_TransferLine" l INNER JOIN "Prc_T_PurchaseOrderLine" pl ON pl."Id" = l."PurchaseOrderLineId" WHERE l."TransferId" = p_id AND l."IsDeleted" = false AND l."Quantity" > 0 GROUP BY l."PurchaseOrderLineId") x WHERE x."PurchaseOrderLineId" = ol."Id"; CALL public."Stk_P_TransferSupplierPrice"(p_id, p_user_id); UPDATE "Prc_T_MaterialRequestItem" i SET "DeliveredQuantity" = i."DeliveredQuantity" + v_sign * x."Quantity", "LineStatus" = CASE WHEN i."DeliveredQuantity" + v_sign * x."Quantity" >= i."Quantity" THEN 'Done' WHEN i."LineStatus" = 'Done' THEN 'Waiting' ELSE i."LineStatus" END FROM (SELECT l."MaterialRequestItemId", SUM(public."Stk_F_UomConvert"(l."Quantity", l."UomId", mi."UomId")) AS "Quantity" FROM "Stk_T_TransferLine" l INNER JOIN "Prc_T_MaterialRequestItem" mi ON mi."Id" = l."MaterialRequestItemId" WHERE l."TransferId" = p_id AND l."IsDeleted" = false AND l."Quantity" > 0 GROUP BY l."MaterialRequestItemId") x WHERE x."MaterialRequestItemId" = i."Id"; IF v_return_of IS NULL AND EXISTS (SELECT 1 FROM "Stk_T_TransferLine" WHERE "TransferId" = p_id AND "IsDeleted" = false AND "Quantity" < "DemandQuantity") THEN CALL public."Stk_P_TransferCopy"(p_id, 'Backorder', p_user_id, v_new); END IF; CALL public."Stk_P_TransferRecalc"(p_id); END; $$;