-- ============================================= -- Stored Procedure: GetNetworkTree -- Description: دریافت درخت شبکه باینری با CTE recursive -- Author: FourSat Team -- Updated: 2026-04-30 — اضافه کردن IsNewActivation، PackageName، PackageId -- منطق هفته‌بندی از ActivatedAt به PackagePurchasedAt تغییر کرد -- ============================================= CREATE OR ALTER PROCEDURE [CMS].[GetNetworkTree] @RootUserId BIGINT, @MaxDepth INT = 100, @IsClubActive BIT = NULL, @ActivationWeekDefinitionId BIGINT = NULL AS BEGIN SET NOCOUNT ON; -- تاریخ شروع و پایان هفته فیلتر DECLARE @WeekStartDate DATETIME = NULL; DECLARE @WeekEndDate DATETIME = NULL; IF @ActivationWeekDefinitionId IS NOT NULL BEGIN SELECT @WeekStartDate = StartDate, @WeekEndDate = EndDate FROM [CMS].[WeekDefinitions] WHERE Id = @ActivationWeekDefinitionId; END -- CTE برای پیمایش درخت باینری به صورت recursive ;WITH NetworkTreeCTE AS ( -- Base case: ریشه درخت SELECT u.Id AS UserId, u.Mobile, u.FirstName, u.LastName, u.ReferralCode, u.LegPosition, u.NetworkParentId AS ParentId, 0 AS NetworkLevel, u.Created AS UserCreated FROM [CMS].[Users] u WHERE u.Id = @RootUserId AND u.IsDeleted = 0 UNION ALL -- Recursive case: فرزندان SELECT u.Id AS UserId, u.Mobile, u.FirstName, u.LastName, u.ReferralCode, u.LegPosition, u.NetworkParentId AS ParentId, parent.NetworkLevel + 1 AS NetworkLevel, u.Created AS UserCreated FROM [CMS].[Users] u INNER JOIN NetworkTreeCTE parent ON u.NetworkParentId = parent.UserId WHERE u.IsDeleted = 0 AND parent.NetworkLevel < @MaxDepth ) SELECT t.UserId, t.Mobile, t.FirstName, t.LastName, t.ReferralCode, t.LegPosition, t.ParentId, t.NetworkLevel, cm.ActivatedAt AS ClubActivatedAt, ISNULL(cm.IsActive, 0) AS IsClubActive, -- هفته فعال‌سازی بر اساس Cycle جاری (PackagePurchasedAt) wd.Id AS ActivationWeekDefinitionId, wd.DisplayName AS ActivationWeekDisplayName, -- آیا کاربر در هفته هدف Cycle داشته؟ CASE WHEN cc_target.UserId IS NOT NULL THEN 1 ELSE 0 END AS IsActivatedInTargetWeek, -- نوع فعال‌سازی: 1=جدید، 0=تمدید/خرید مجدد، NULL=بدون Cycle CASE WHEN cc_eff.CycleNumber IS NULL THEN NULL WHEN cc_eff.CycleNumber = 1 THEN 1 ELSE 0 END AS IsNewActivation, -- نام پکیج موثر (هفته هدف اگر فیلتر باشد، وگرنه پکیج فعلی) ISNULL(p_eff.Title, '') AS PackageName, cc_eff.PackageId AS PackageId, t.UserCreated FROM NetworkTreeCTE t LEFT JOIN [CMS].[ClubMemberships] cm ON cm.UserId = t.UserId AND cm.IsDeleted = 0 -- Cycle جاری — برای نمایش هفته و پکیج وقتی فیلتر هفته نیست OUTER APPLY ( SELECT TOP 1 CycleNumber, PackageId, PackagePurchasedAt FROM [CMS].[ClubMembershipCycles] WHERE UserId = t.UserId AND IsCurrentCycle = 1 ) cc_current -- Cycle هفته هدف — برای IsActivatedInTargetWeek و نوع فعال‌سازی OUTER APPLY ( SELECT TOP 1 UserId, CycleNumber, PackageId FROM [CMS].[ClubMembershipCycles] WHERE UserId = t.UserId AND @WeekStartDate IS NOT NULL AND PackagePurchasedAt >= @WeekStartDate AND PackagePurchasedAt < @WeekEndDate ORDER BY CycleNumber ASC ) cc_target -- Cycle موثر: اگر هفته فیلتر داریم → cc_target، وگرنه → cc_current CROSS APPLY ( SELECT CASE WHEN @WeekStartDate IS NOT NULL AND cc_target.UserId IS NOT NULL THEN cc_target.CycleNumber ELSE cc_current.CycleNumber END AS CycleNumber, CASE WHEN @WeekStartDate IS NOT NULL AND cc_target.UserId IS NOT NULL THEN cc_target.PackageId ELSE cc_current.PackageId END AS PackageId ) cc_eff -- پکیج موثر LEFT JOIN [CMS].[Packages] p_eff ON p_eff.Id = cc_eff.PackageId AND p_eff.IsDeleted = 0 -- هفته بر اساس PackagePurchasedAt سایکل جاری LEFT JOIN [CMS].[WeekDefinitions] wd ON cc_current.PackagePurchasedAt >= wd.StartDate AND cc_current.PackagePurchasedAt < wd.EndDate WHERE (@IsClubActive IS NULL OR ISNULL(cm.IsActive, 0) = @IsClubActive OR t.NetworkLevel = 0) ORDER BY t.NetworkLevel, t.ParentId, t.LegPosition OPTION (MAXRECURSION 0); END