diff --git a/src/dbbkup/FixBulkCreditPayoutsWrongBalance.sql b/src/dbbkup/FixBulkCreditPayoutsWrongBalance.sql new file mode 100644 index 0000000..ef4dc69 --- /dev/null +++ b/src/dbbkup/FixBulkCreditPayoutsWrongBalance.sql @@ -0,0 +1,341 @@ +/* +================================================================================ +FixBulkCreditPayoutsWrongBalance.sql +================================================================================ +شناسایی لاگ اشتباه BulkCredit در UserWalletHistories: + + RefrenceId IN (payoutهای BulkCredit شده) + AND CreatedBy <> 'SP' -- لاگ SP = NetworkBalance درست، حذف نشود + AND ChangeValue > 0 -- واریز اشتباه به Balance + AND ChangeDiscountValue <= 0 + +اصلاح: + - کسر ChangeValue از Balance همان WalletId + - حذف رکوردهای UserWalletHistories اشتباه + + @DryRun = 1 → فقط گزارش + @DryRun = 0 → اعمال fix +================================================================================ +*/ + +SET NOCOUNT ON; +SET XACT_ABORT ON; + +DECLARE @DryRun BIT = 1; +DECLARE @WeeksBack INT = 4; +DECLARE @SinceDate DATETIME = DATEADD(WEEK, -@WeeksBack, GETUTCDATE()); + +-- کاربرانی که fix برایشان اعمال می‌شود حتی اگر Balance منفی شود +DECLARE @AllowNegativeUserIds TABLE (UserId BIGINT PRIMARY KEY); +INSERT INTO @AllowNegativeUserIds (UserId) VALUES (106); -- مهرداد شجاعی + +PRINT CONCAT('DryRun = ', @DryRun, ' | WeeksBack = ', @WeeksBack); +PRINT ''; + +-- payoutهای BulkCredit شده (از CommissionPayoutHistories) +;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 + AND cph.Action = 1 + AND cph.Created >= @SinceDate + AND ( + cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' + OR cph.Reason LIKE N'%واریز دسته%جمعی%' + ) +), +WrongWalletLogs AS ( + SELECT + wh.Id AS WalletHistoryId, + wh.WalletId, + wh.RefrenceId AS PayoutId, + p.UserId, + wh.ChangeValue AS WrongBalanceCredit, + wh.Created AS WrongCreditAt, + wh.CreatedBy + FROM CMS.UserWalletHistories wh + INNER JOIN CMS.UserCommissionPayouts p + ON p.Id = wh.RefrenceId AND p.IsDeleted = 0 + WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0 +) + +-- 1) خلاصه +SELECT + (SELECT COUNT(*) FROM BulkCreditedPayoutIds) AS BulkCreditedPayoutCount, + (SELECT COUNT(DISTINCT p.UserId) + FROM CMS.UserCommissionPayouts p + WHERE p.Id IN (SELECT PayoutId FROM BulkCreditedPayoutIds)) AS BulkCreditedUserCount, + (SELECT COUNT(*) FROM WrongWalletLogs) AS WrongWalletHistoryCount, + (SELECT COUNT(DISTINCT UserId) FROM WrongWalletLogs) AS AffectedUserCount, + (SELECT ISNULL(SUM(WrongBalanceCredit), 0) FROM WrongWalletLogs) AS TotalWrongBalanceCredit; + +PRINT ''; +PRINT '--- PayoutIdهای BulkCredit (برای مقایسه با لیست دستی) ---'; +SELECT PayoutId FROM ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') +) x ORDER BY PayoutId; + +PRINT ''; +PRINT '--- UserWalletHistories اشتباه ---'; + +;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') +) +SELECT + wh.Id AS WalletHistoryId, + wh.RefrenceId AS PayoutId, + p.UserId, + u.Mobile, + CONCAT(u.FirstName, N' ', u.LastName) AS FullName, + p.WeekDefinitionId, + p.TotalAmount AS PayoutAmount, + wh.ChangeValue AS WrongBalanceCredit, + uw.Balance AS CurrentBalance, + uw.NetworkBalance, + wh.Created AS WrongCreditAt, + wh.CreatedBy +FROM CMS.UserWalletHistories wh +INNER JOIN CMS.UserCommissionPayouts p ON p.Id = wh.RefrenceId AND p.IsDeleted = 0 +INNER JOIN CMS.UserWallets uw ON uw.Id = wh.WalletId AND uw.IsDeleted = 0 +INNER JOIN CMS.Users u ON u.Id = p.UserId AND u.IsDeleted = 0 +WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0 +ORDER BY p.UserId, wh.RefrenceId; + +PRINT ''; +PRINT '--- خلاصه به تفکیک کاربر ---'; + +;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') +), +WrongWalletLogs AS ( + SELECT wh.Id AS WalletHistoryId, wh.WalletId, wh.RefrenceId AS PayoutId, + p.UserId, wh.ChangeValue AS WrongBalanceCredit + FROM CMS.UserWalletHistories wh + INNER JOIN CMS.UserCommissionPayouts p ON p.Id = wh.RefrenceId AND p.IsDeleted = 0 + WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0 +) +SELECT + w.UserId, + u.Mobile, + CONCAT(u.FirstName, N' ', u.LastName) AS FullName, + COUNT(w.WalletHistoryId) AS WrongHistoryCount, + COUNT(DISTINCT w.PayoutId) AS AffectedPayoutCount, + SUM(w.WrongBalanceCredit) AS TotalWrongCredit, + uw.Balance AS CurrentBalance, + uw.Balance - SUM(w.WrongBalanceCredit) AS BalanceAfterFix, + CASE + WHEN w.UserId IN (SELECT UserId FROM @AllowNegativeUserIds) + AND uw.Balance < SUM(w.WrongBalanceCredit) THEN N'OK — منفی مجاز' + WHEN uw.Balance < SUM(w.WrongBalanceCredit) THEN N'⚠ موجودی کافی نیست' + ELSE N'OK' + END AS FixStatus +FROM WrongWalletLogs w +INNER JOIN CMS.Users u ON u.Id = w.UserId AND u.IsDeleted = 0 +INNER JOIN CMS.UserWallets uw ON uw.Id = w.WalletId AND uw.IsDeleted = 0 +GROUP BY w.UserId, u.Mobile, u.FirstName, u.LastName, uw.Id, uw.Balance +ORDER BY TotalWrongCredit DESC; + +PRINT ''; +PRINT '--- blockers (به‌جز کاربران با منفی مجاز) ---'; + +;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') +), +WrongPerWallet AS ( + SELECT wh.WalletId, SUM(wh.ChangeValue) AS TotalWrongCredit + FROM CMS.UserWalletHistories wh + WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0 + GROUP BY wh.WalletId +) +SELECT uw.UserId, u.Mobile, uw.Balance AS CurrentBalance, + w.TotalWrongCredit, uw.Balance - w.TotalWrongCredit AS BalanceAfterFix +FROM WrongPerWallet w +INNER JOIN CMS.UserWallets uw ON uw.Id = w.WalletId AND uw.IsDeleted = 0 +INNER JOIN CMS.Users u ON u.Id = uw.UserId AND u.IsDeleted = 0 +WHERE uw.Balance < w.TotalWrongCredit + AND uw.UserId NOT IN (SELECT UserId FROM @AllowNegativeUserIds); + +PRINT ''; +PRINT '--- منفی مجاز (fix اعمال می‌شود) ---'; + +;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') +), +WrongPerWallet AS ( + SELECT wh.WalletId, SUM(wh.ChangeValue) AS TotalWrongCredit + FROM CMS.UserWalletHistories wh + WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0 + GROUP BY wh.WalletId +) +SELECT uw.UserId, u.Mobile, uw.Balance AS CurrentBalance, + w.TotalWrongCredit, uw.Balance - w.TotalWrongCredit AS BalanceAfterFix +FROM WrongPerWallet w +INNER JOIN CMS.UserWallets uw ON uw.Id = w.WalletId AND uw.IsDeleted = 0 +INNER JOIN CMS.Users u ON u.Id = uw.UserId AND u.IsDeleted = 0 +WHERE uw.Balance < w.TotalWrongCredit + AND uw.UserId IN (SELECT UserId FROM @AllowNegativeUserIds); + +-- ── اعمال fix ── +IF @DryRun = 1 +BEGIN + PRINT ''; + PRINT 'DryRun — تغییری اعمال نشد. برای fix: @DryRun = 0'; + RETURN; +END + +DECLARE @BlockerCount INT; +DECLARE @FixedWallets INT; +DECLARE @DeletedHistories INT; +DECLARE @TotalReverted BIGINT; +DECLARE @ErrMsg NVARCHAR(4000); + +;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') +), +WrongPerWallet AS ( + SELECT wh.WalletId, SUM(wh.ChangeValue) AS TotalWrongCredit + FROM CMS.UserWalletHistories wh + WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0 + GROUP BY wh.WalletId +) +SELECT @BlockerCount = COUNT(*) +FROM WrongPerWallet w +INNER JOIN CMS.UserWallets uw ON uw.Id = w.WalletId AND uw.IsDeleted = 0 +WHERE uw.Balance < w.TotalWrongCredit + AND uw.UserId NOT IN (SELECT UserId FROM @AllowNegativeUserIds); + +IF @BlockerCount > 0 +BEGIN + RAISERROR(N'%d کیف پول Balance منفی می‌شود — fix متوقف شد.', 16, 1, @BlockerCount); + RETURN; +END + +BEGIN TRY + BEGIN TRANSACTION; + + ;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') + ), + WrongPerWallet AS ( + SELECT wh.WalletId, SUM(wh.ChangeValue) AS TotalWrongCredit + FROM CMS.UserWalletHistories wh + WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0 + GROUP BY wh.WalletId + ) + UPDATE uw + SET uw.Balance = uw.Balance - w.TotalWrongCredit, + uw.LastModified = GETUTCDATE(), + uw.LastModifiedBy = N'FixBulkCreditPayoutsWrongBalance' + FROM CMS.UserWallets uw + INNER JOIN WrongPerWallet w ON w.WalletId = uw.Id + WHERE uw.IsDeleted = 0; + + SET @FixedWallets = @@ROWCOUNT; + + ;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') + ) + SELECT @TotalReverted = ISNULL(SUM(wh.ChangeValue), 0) + FROM CMS.UserWalletHistories wh + WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0; + + ;WITH BulkCreditedPayoutIds AS ( + SELECT DISTINCT cph.UserCommissionPayoutId AS PayoutId + FROM CMS.CommissionPayoutHistories cph + WHERE cph.IsDeleted = 0 AND cph.Action = 1 AND cph.Created >= @SinceDate + AND (cph.Reason LIKE N'%تایید پرداخت دسته%جمعی%' OR cph.Reason LIKE N'%واریز دسته%جمعی%') + ) + DELETE wh + FROM CMS.UserWalletHistories wh + WHERE wh.IsDeleted = 0 + AND wh.RefrenceId IN (SELECT PayoutId FROM BulkCreditedPayoutIds) + AND wh.CreatedBy <> N'SP' + AND wh.ChangeValue > 0 + AND wh.ChangeDiscountValue <= 0; + + SET @DeletedHistories = @@ROWCOUNT; + + COMMIT TRANSACTION; + + PRINT CONCAT('کیف پول اصلاح‌شده: ', @FixedWallets); + PRINT CONCAT('لاگ حذف‌شده: ', @DeletedHistories); + PRINT CONCAT('مجموع برگردانده‌شده: ', @TotalReverted); +END TRY +BEGIN CATCH + IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; + SET @ErrMsg = ERROR_MESSAGE(); + RAISERROR(@ErrMsg, 16, 1); +END CATCH; + +GO + +/* +-- کوئری ساده دستی (معادل همین منطق با لیست PayoutId ثابت): + +SELECT * +FROM CMS.UserWalletHistories +WHERE RefrenceId IN ( + 115,116,117,118,119,125,126,127,128,129,130,131,132,133,134, + 135,136,137,138,139,140,141,142,143,144,145,146,147,148,149, + 150,151,152,153 +) +AND CreatedBy <> N'SP' +AND ChangeValue > 0 +AND ChangeDiscountValue <= 0; +*/