/* Staging fix: complete post-Daya processing for Excel rows with status «فعال شده» that are NOT_PROCESSED or PARTIAL. Classification: DONE — credit + processed + Daya UPP PARTIAL — HasReceivedDayaCredit OR IsProcessed OR existing «اعتبار دایا» Tx in wallet NOT_PROCESSED — none of the above (will create Tx + ledger WH) If ExistingDayaTxId found (wallet RefrenceId → Tx Description LIKE '%اعتبار دایا%' OR Tx.RefId = ContractNumber): REUSE that Tx — do NOT create Transaction. NO wallet Balance/DiscountBalance charge — UserWalletHistory is ledger-only when needed. Special UserId mapping (shared NationalCode): C4_D8815963 / 2490371436 → UserId 50 C4_G8945289 / 2490371436 → UserId 51 @Commit = 0 → dry-run (ROLLBACK) @Commit = 1 → apply */ SET NOCOUNT ON; SET XACT_ABORT ON; DECLARE @Commit BIT = 0; -- << set 1 to apply DECLARE @Now DATETIME2 = SYSUTCDATETIME(); DECLARE @PackageId BIGINT; DECLARE @PackagePrice BIGINT; DECLARE @DiscountMultiplier DECIMAL(18,4); DECLARE @DiscountAmount BIGINT; SELECT TOP (1) @PackageId = Id, @PackagePrice = Price, @DiscountMultiplier = DiscountMultiplier FROM CMS.Packages WHERE IsDeleted = 0 AND IsBasePackage = 1 ORDER BY Id; IF @PackageId IS NULL THROW 50001, 'Base package not found.', 1; SET @DiscountAmount = CAST(@PackagePrice * @DiscountMultiplier AS BIGINT); PRINT CONCAT('PackageId=', @PackageId, ' Price=', @PackagePrice, ' Discount=', @DiscountAmount); ------------------------------------------------------------ -- Targets: Excel «فعال شده» ------------------------------------------------------------ DECLARE @Targets TABLE ( ContractNumber NVARCHAR(50) NOT NULL, NationalCode NVARCHAR(20) NOT NULL, UserIdHint BIGINT NULL -- explicit override when NC is shared ); INSERT INTO @Targets (ContractNumber, NationalCode, UserIdHint) VALUES (N'C4_H10706299', N'4060667961', NULL), (N'C4_P9320383', N'2572729636', NULL), (N'C4_Q9465813', N'2480017796', NULL), (N'C4_B9348378', N'0452314488', NULL), (N'C4_X9630520', N'0680164286', NULL), (N'C4_B9813553', N'0055680364', NULL), -- may match DayaLoanContracts.NationalCode if Users.NC differs (N'C4_M9994735', N'0793851572', NULL), (N'C4_S9301452', N'0082242925', NULL), (N'C4_T9961063', N'0312342993', NULL), (N'C4_L9524515', N'0024757489', NULL), (N'C4_Q9947535', N'2480259668', NULL), (N'C4_T9892089', N'2451477016', NULL), (N'C4_W8785358', N'0322922720', NULL), (N'C4_D8815963', N'2490371436', 50), -- earlier activation → user 50 (N'C4_G8945289', N'2490371436', 51), -- later activation → user 51 (N'C4_J8388494', N'2002637903', NULL); DECLARE @Work TABLE ( ContractNumber NVARCHAR(50), NationalCode NVARCHAR(20), UserId BIGINT, DayaContractId BIGINT NULL, HasCredit BIT, IsProcessed BIT, HasDayaUpp BIT, ExistingDayaTxId BIGINT NULL, -- Tx از قبل با شرح «اعتبار دایا» / RefId قرارداد (شارژ شده) Verdict NVARCHAR(20) ); INSERT INTO @Work (ContractNumber, NationalCode, UserId, DayaContractId, HasCredit, IsProcessed, HasDayaUpp, ExistingDayaTxId, Verdict) SELECT t.ContractNumber, t.NationalCode, u.Id, d.Id, CAST(ISNULL(u.HasReceivedDayaCredit, 0) AS BIT), CAST(ISNULL(d.IsProcessed, 0) AS BIT), CAST(CASE WHEN EXISTS ( SELECT 1 FROM CMS.UserPackagePurchases upp LEFT JOIN CMS.Transactions tx ON tx.Id = upp.TransactionId AND tx.IsDeleted = 0 WHERE upp.UserId = u.Id AND upp.IsDeleted = 0 AND upp.PurchaseMethod = 1 AND (upp.TransactionId IS NULL OR tx.PaymentStatus = 0) ) THEN 1 ELSE 0 END AS BIT), etx.TxId, CASE WHEN u.Id IS NULL THEN N'NO_USER' WHEN ISNULL(u.HasReceivedDayaCredit, 0) = 1 AND ISNULL(d.IsProcessed, 0) = 1 AND EXISTS ( SELECT 1 FROM CMS.UserPackagePurchases upp LEFT JOIN CMS.Transactions tx ON tx.Id = upp.TransactionId AND tx.IsDeleted = 0 WHERE upp.UserId = u.Id AND upp.IsDeleted = 0 AND upp.PurchaseMethod = 1 AND (upp.TransactionId IS NULL OR tx.PaymentStatus = 0) ) THEN N'DONE' -- قبلاً شارژ دایا در کیف/تراکنش هست ولی فلگ/UPP/قرارداد کامل نیست WHEN etx.TxId IS NOT NULL THEN N'PARTIAL' WHEN ISNULL(u.HasReceivedDayaCredit, 0) = 1 OR ISNULL(d.IsProcessed, 0) = 1 THEN N'PARTIAL' ELSE N'NOT_PROCESSED' END FROM @Targets t OUTER APPLY ( SELECT TOP (1) u0.* FROM CMS.Users u0 WHERE u0.IsDeleted = 0 AND ( (t.UserIdHint IS NOT NULL AND u0.Id = t.UserIdHint) OR (t.UserIdHint IS NULL AND u0.NationalCode = t.NationalCode) OR (t.UserIdHint IS NULL AND EXISTS ( SELECT 1 FROM CMS.DayaLoanContracts dx WHERE dx.UserId = u0.Id AND dx.NationalCode = t.NationalCode AND dx.IsDeleted = 0 )) ) ORDER BY CASE WHEN t.UserIdHint IS NOT NULL AND u0.Id = t.UserIdHint THEN 0 ELSE 1 END, CASE WHEN u0.NationalCode = t.NationalCode THEN 0 ELSE 1 END, u0.Id ) u OUTER APPLY ( SELECT TOP (1) d0.* FROM CMS.DayaLoanContracts d0 WHERE d0.UserId = u.Id AND ( d0.ContractNumber = t.ContractNumber OR d0.NationalCode = t.NationalCode OR d0.IsDeleted IN (0, 1) ) ORDER BY CASE WHEN d0.ContractNumber = t.ContractNumber THEN 0 ELSE 1 END, CASE WHEN d0.IsDeleted = 0 THEN 0 ELSE 1 END, d0.Id DESC ) d OUTER APPLY ( -- تراکنش دایا که از قبل شارژ شده (مرجع کیف پول یا RefId قرارداد) SELECT TOP (1) t0.Id AS TxId FROM CMS.Transactions t0 WHERE t0.IsDeleted = 0 AND t0.PaymentStatus = 0 AND ( t0.RefId = t.ContractNumber OR ( t0.Description LIKE N'%اعتبار دایا%' AND EXISTS ( SELECT 1 FROM CMS.UserWallets w INNER JOIN CMS.UserWalletHistories h ON h.WalletId = w.Id WHERE w.UserId = u.Id AND h.RefrenceId = t0.Id ) ) ) ORDER BY CASE WHEN t0.RefId = t.ContractNumber THEN 0 ELSE 1 END, t0.Id DESC ) etx; PRINT '=== Before ==='; SELECT Verdict, COUNT(*) AS Cnt FROM @Work GROUP BY Verdict; SELECT ContractNumber, NationalCode, UserId, DayaContractId, HasCredit, IsProcessed, HasDayaUpp, ExistingDayaTxId, Verdict FROM @Work ORDER BY Verdict, ContractNumber; ------------------------------------------------------------ -- PREVIEW: ledger actions (no wallet charge) ------------------------------------------------------------ PRINT '=== LEDGER PREVIEW (Tx + WH history-only — NO wallet charge) ==='; SELECT w.ContractNumber, w.NationalCode, w.UserId, w.Verdict, w.HasCredit, w.ExistingDayaTxId, CASE WHEN w.UserId IS NULL THEN N'SKIP_NO_USER' WHEN w.ExistingDayaTxId IS NOT NULL THEN N'REUSE_TX_ENSURE_WH_UPP' WHEN w.HasCredit = 0 THEN N'CREATE_TX_WH_UPP' ELSE N'SET_FLAGS_ONLY_UPP' END AS LedgerAction FROM @Work w WHERE w.Verdict IN (N'NOT_PROCESSED', N'PARTIAL') ORDER BY LedgerAction, w.ContractNumber; SELECT CASE WHEN w.UserId IS NULL THEN N'SKIP_NO_USER' WHEN w.ExistingDayaTxId IS NOT NULL THEN N'REUSE_TX_ENSURE_WH_UPP' WHEN w.HasCredit = 0 THEN N'CREATE_TX_WH_UPP' ELSE N'SET_FLAGS_ONLY_UPP' END AS LedgerAction, COUNT(*) AS Cnt FROM @Work w WHERE w.Verdict IN (N'NOT_PROCESSED', N'PARTIAL') GROUP BY CASE WHEN w.UserId IS NULL THEN N'SKIP_NO_USER' WHEN w.ExistingDayaTxId IS NOT NULL THEN N'REUSE_TX_ENSURE_WH_UPP' WHEN w.HasCredit = 0 THEN N'CREATE_TX_WH_UPP' ELSE N'SET_FLAGS_ONLY_UPP' END ORDER BY LedgerAction; DECLARE @PreviewCreateTxCount INT = ( SELECT COUNT(*) FROM @Work w WHERE w.Verdict IN (N'NOT_PROCESSED', N'PARTIAL') AND w.UserId IS NOT NULL AND w.ExistingDayaTxId IS NULL AND w.HasCredit = 0 ); PRINT CONCAT('>>> CREATE_TX (no wallet charge) count = ', @PreviewCreateTxCount); BEGIN TRAN; DECLARE @ContractNumber NVARCHAR(50), @NationalCode NVARCHAR(20), @UserId BIGINT; DECLARE @DayaContractId BIGINT, @HasCredit BIT, @IsProcessed BIT, @HasDayaUpp BIT, @Verdict NVARCHAR(20); DECLARE @ExistingDayaTxId BIGINT; DECLARE @TxId BIGINT, @WalletId BIGINT, @NetworkBalance BIGINT, @Action NVARCHAR(200); DECLARE @CreateTxCount INT = 0; DECLARE @ReuseCount INT = 0; DECLARE @SkipFlagsOnlyCount INT = 0; DECLARE @WhCreatedCount INT = 0; DECLARE @Log TABLE ( ContractNumber NVARCHAR(50), UserId BIGINT, VerdictBefore NVARCHAR(20), ActionTaken NVARCHAR(200), TxId BIGINT NULL ); DECLARE cur CURSOR LOCAL FAST_FORWARD FOR SELECT ContractNumber, NationalCode, UserId, DayaContractId, HasCredit, IsProcessed, HasDayaUpp, ExistingDayaTxId, Verdict FROM @Work WHERE Verdict IN (N'NOT_PROCESSED', N'PARTIAL'); OPEN cur; FETCH NEXT FROM cur INTO @ContractNumber, @NationalCode, @UserId, @DayaContractId, @HasCredit, @IsProcessed, @HasDayaUpp, @ExistingDayaTxId, @Verdict; WHILE @@FETCH_STATUS = 0 BEGIN SET @TxId = NULL; SET @Action = N''; IF @UserId IS NULL BEGIN INSERT INTO @Log VALUES (@ContractNumber, NULL, @Verdict, N'SKIP NO_USER', NULL); GOTO NextRow; END ------------------------------------------------------------ -- Ensure / revive DayaLoanContract ------------------------------------------------------------ IF @DayaContractId IS NULL BEGIN INSERT INTO CMS.DayaLoanContracts ( UserId, NationalCode, ContractNumber, Status, IsProcessed, LastCheckDate, ProcessedDate, TransactionId, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted ) VALUES ( @UserId, @NationalCode, @ContractNumber, 2, 0, @Now, NULL, NULL, @Now, NULL, @Now, NULL, 0 ); SET @DayaContractId = SCOPE_IDENTITY(); SET @Action = @Action + N'INSERT_CONTRACT;'; END ELSE BEGIN UPDATE CMS.DayaLoanContracts SET IsDeleted = 0, NationalCode = @NationalCode, ContractNumber = @ContractNumber, Status = 2, -- Received («فعال شده») LastCheckDate = @Now, LastModified = @Now WHERE Id = @DayaContractId; SET @Action = @Action + N'UPDATE_CONTRACT;'; END ------------------------------------------------------------ -- Resolve Tx (reuse existing or create ledger Tx — NO wallet charge) ------------------------------------------------------------ IF @ExistingDayaTxId IS NOT NULL BEGIN SET @TxId = @ExistingDayaTxId; SET @Action = @Action + CONCAT(N'REUSE_EXISTING_DAYA_TX=', @TxId, N';'); SET @ReuseCount = @ReuseCount + 1; UPDATE CMS.Users SET PackagePurchaseMethod = 1, HasReceivedDayaCredit = 1, DayaCreditReceivedAt = COALESCE(DayaCreditReceivedAt, @Now), LastModified = @Now WHERE Id = @UserId; SET @HasCredit = 1; SET @Action = @Action + N'SET_USER_FLAGS;'; END ELSE IF @HasCredit = 0 BEGIN INSERT INTO CMS.Transactions ( Amount, Description, PaymentStatus, PaymentDate, RefId, Type, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted ) VALUES ( @PackagePrice, CONCAT(N'دریافت اعتبار دایا - قرارداد ', @ContractNumber), 0, @Now, @ContractNumber, 2, @Now, NULL, @Now, NULL, 0 ); SET @TxId = SCOPE_IDENTITY(); SET @CreateTxCount = @CreateTxCount + 1; UPDATE CMS.Users SET HasReceivedDayaCredit = 1, DayaCreditReceivedAt = @Now, PackagePurchaseMethod = 1, LastModified = @Now WHERE Id = @UserId; SET @HasCredit = 1; SET @Action = @Action + N'CREATE_TX+USER_FLAGS;'; END ELSE BEGIN UPDATE CMS.Users SET PackagePurchaseMethod = 1, HasReceivedDayaCredit = 1, DayaCreditReceivedAt = COALESCE(DayaCreditReceivedAt, @Now), LastModified = @Now WHERE Id = @UserId; SET @SkipFlagsOnlyCount = @SkipFlagsOnlyCount + 1; SET @Action = @Action + N'SET_FLAGS_ONLY;'; END ------------------------------------------------------------ -- UserWalletHistory for Tx (ledger-only — NO Balance/Discount charge) ------------------------------------------------------------ IF @TxId IS NOT NULL BEGIN SELECT @WalletId = Id, @NetworkBalance = COALESCE(NetworkBalance, 0) FROM CMS.UserWallets WHERE UserId = @UserId AND IsDeleted = 0; IF @WalletId IS NULL BEGIN INSERT INTO CMS.UserWallets (UserId, Balance, NetworkBalance, DiscountBalance, WalletMode, Created, LastModified, IsDeleted) VALUES (@UserId, 0, 0, 0, 0, @Now, @Now, 0); SET @WalletId = SCOPE_IDENTITY(); SET @NetworkBalance = 0; END IF NOT EXISTS ( SELECT 1 FROM CMS.UserWalletHistories h WHERE h.WalletId = @WalletId AND h.IsDeleted = 0 AND h.RefrenceId = @TxId ) BEGIN INSERT INTO CMS.UserWalletHistories ( WalletId, CurrentBalance, ChangeValue, CurrentNetworkBalance, ChangeNerworkValue, CurrentDiscountBalance, ChangeDiscountValue, IsIncrease, RefrenceId, PackageId, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted ) VALUES ( @WalletId, 0, @PackagePrice, @NetworkBalance, 0, 0, @DiscountAmount, 1, @TxId, @PackageId, @Now, NULL, @Now, NULL, 0 ); SET @WhCreatedCount = @WhCreatedCount + 1; SET @Action = @Action + N'INSERT_WH_HISTORY;'; END END ------------------------------------------------------------ -- Ensure successful Daya UPP (no duplicate if already exists) ------------------------------------------------------------ IF @HasDayaUpp = 0 BEGIN IF @TxId IS NULL BEGIN -- فقط لجر؛ بدون شارژ کیف (نباید برای کسی که Tx دایا دارد به اینجا برسد) INSERT INTO CMS.Transactions ( Amount, Description, PaymentStatus, PaymentDate, RefId, Type, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted ) VALUES ( @PackagePrice, CONCAT(N'لجر UPP دایا (بدون شارژ مجدد) - قرارداد ', @ContractNumber), 0, @Now, CONCAT(N'UPP-DAYA-FIX-', @ContractNumber), 2, @Now, NULL, @Now, NULL, 0 ); SET @TxId = SCOPE_IDENTITY(); SET @Action = @Action + N'LEDGER_TX;'; END INSERT INTO CMS.UserPackagePurchases ( UserId, PackageId, PurchaseMethod, PurchasedAt, Amount, OrderId, TransactionId, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted ) VALUES ( @UserId, @PackageId, 1, @Now, @PackagePrice, NULL, @TxId, @Now, NULL, @Now, NULL, 0 ); SET @Action = @Action + N'INSERT_UPP;'; END ------------------------------------------------------------ -- Mark contract processed ------------------------------------------------------------ UPDATE CMS.DayaLoanContracts SET IsProcessed = 1, ProcessedDate = COALESCE(ProcessedDate, @Now), TransactionId = COALESCE(TransactionId, @TxId), Status = 2, ContractNumber = @ContractNumber, IsDeleted = 0, LastModified = @Now WHERE Id = @DayaContractId; SET @Action = @Action + N'MARK_PROCESSED;'; INSERT INTO @Log VALUES (@ContractNumber, @UserId, @Verdict, @Action, @TxId); NextRow: FETCH NEXT FROM cur INTO @ContractNumber, @NationalCode, @UserId, @DayaContractId, @HasCredit, @IsProcessed, @HasDayaUpp, @ExistingDayaTxId, @Verdict; END CLOSE cur; DEALLOCATE cur; PRINT '=== LEDGER SUMMARY (no wallet charge) ==='; PRINT CONCAT('CREATE_TX count = ', @CreateTxCount); PRINT CONCAT('REUSE_TX count = ', @ReuseCount); PRINT CONCAT('SET_FLAGS_ONLY count = ', @SkipFlagsOnlyCount); PRINT CONCAT('WH history created = ', @WhCreatedCount); PRINT '=== Actions ==='; SELECT * FROM @Log ORDER BY VerdictBefore, ContractNumber; -- Re-score after changes PRINT '=== After (same targets) ==='; SELECT t.ContractNumber, t.NationalCode, u.Id AS UserId, u.HasReceivedDayaCredit, d.IsProcessed, d.ContractNumber AS DbContractNumber, d.Status, CASE WHEN EXISTS ( SELECT 1 FROM CMS.UserPackagePurchases upp LEFT JOIN CMS.Transactions tx ON tx.Id = upp.TransactionId AND tx.IsDeleted = 0 WHERE upp.UserId = u.Id AND upp.IsDeleted = 0 AND upp.PurchaseMethod = 1 AND (upp.TransactionId IS NULL OR tx.PaymentStatus = 0) ) THEN 1 ELSE 0 END AS HasDayaUpp, CASE WHEN u.Id IS NULL THEN N'NO_USER' WHEN u.HasReceivedDayaCredit = 1 AND d.IsProcessed = 1 AND EXISTS ( SELECT 1 FROM CMS.UserPackagePurchases upp LEFT JOIN CMS.Transactions tx ON tx.Id = upp.TransactionId AND tx.IsDeleted = 0 WHERE upp.UserId = u.Id AND upp.IsDeleted = 0 AND upp.PurchaseMethod = 1 AND (upp.TransactionId IS NULL OR tx.PaymentStatus = 0) ) THEN N'DONE' WHEN u.HasReceivedDayaCredit = 1 OR d.IsProcessed = 1 THEN N'PARTIAL' ELSE N'NOT_PROCESSED' END AS Verdict FROM @Targets t LEFT JOIN CMS.Users u ON u.IsDeleted = 0 AND ( (t.UserIdHint IS NOT NULL AND u.Id = t.UserIdHint) OR (t.UserIdHint IS NULL AND u.NationalCode = t.NationalCode) ) LEFT JOIN CMS.DayaLoanContracts d ON d.IsDeleted = 0 AND d.UserId = u.Id AND (d.ContractNumber = t.ContractNumber OR d.NationalCode = t.NationalCode) ORDER BY Verdict, t.ContractNumber; IF @Commit = 1 BEGIN COMMIT; PRINT 'COMMITTED.'; END ELSE BEGIN ROLLBACK; PRINT 'Dry-run ROLLBACK. Set @Commit = 1 to apply.'; END