Files
CMS/src/dbbkup/Backfill_UserPackagePurchases_FromClubMemberships.sql
T
masoodafar-web 8ec0910cdf
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 6m36s
feat(jwt): enhance JWT token generation with user address check
- Updated GenerateJwtTokenService to include IApplicationDbContext for accessing user addresses.
- Added logic to check if a user has an address and included this information as a claim in the generated JWT token.
- Improved overall token generation process by incorporating additional user data for better context.
2026-08-23 22:32:36 +03:30

258 lines
8.4 KiB
Transact-SQL

/*
One-time cleanup: ClubMemberships without a successful UserPackagePurchases row.
Goal after this script:
every non-deleted ClubMembership user has >= 1 successful UPP
(TransactionId IS NULL OR Transaction.PaymentStatus = 0 / Success)
Priority for missing rows:
1) ManualPayments Approved + successful Transaction -> reuse same TransactionId
PurchaseMethod = Manual (3)
2) else ClubMembershipCycles / membership dates -> create synthetic Success Transaction + UPP
PurchaseMethod priority:
Cycle.PurchaseMethod (if not None)
-> ClubMembership.PurchaseMethod (if not None)
-> Users.PackagePurchaseMethod (if not None)
-> Manual (3) as last resort
Safe to re-run (skips users who already have a successful UPP).
Run against CMS database once, then verify Orphans left = 0.
*/
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRAN;
------------------------------------------------------------
-- 1) From ManualPayments (reuse existing Success transaction)
------------------------------------------------------------
;WITH OrphanClubUsers AS (
SELECT m.UserId,
COALESCE(NULLIF(m.LastPackageId, 0), NULLIF(m.FirstPackageId, 0)) AS PackageIdHint
FROM CMS.ClubMemberships m
WHERE m.IsDeleted = 0
AND NOT EXISTS (
SELECT 1
FROM CMS.UserPackagePurchases upp
LEFT JOIN CMS.Transactions t ON t.Id = upp.TransactionId AND t.IsDeleted = 0
WHERE upp.UserId = m.UserId
AND upp.IsDeleted = 0
AND (upp.TransactionId IS NULL OR t.PaymentStatus = 0)
)
),
BestManual AS (
SELECT mp.UserId,
mp.Id AS ManualPaymentId,
mp.Amount,
mp.TransactionId,
COALESCE(mp.ApprovedAt, mp.Created) AS PurchasedAt,
ROW_NUMBER() OVER (
PARTITION BY mp.UserId
ORDER BY COALESCE(mp.ApprovedAt, mp.Created) DESC, mp.Id DESC
) AS rn
FROM CMS.ManualPayments mp
INNER JOIN CMS.Transactions t ON t.Id = mp.TransactionId AND t.IsDeleted = 0 AND t.PaymentStatus = 0
WHERE mp.IsDeleted = 0
AND mp.Status = 1 -- Approved
AND mp.TransactionId IS NOT NULL
),
BasePkg AS (
SELECT TOP (1) Id AS PackageId, Price
FROM CMS.Packages
WHERE IsDeleted = 0 AND IsBasePackage = 1
ORDER BY Id
)
INSERT INTO CMS.UserPackagePurchases
(
UserId, PackageId, PurchaseMethod, PurchasedAt, Amount,
OrderId, TransactionId, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted
)
SELECT
o.UserId,
COALESCE(o.PackageIdHint, b.PackageId),
3, -- Manual
bm.PurchasedAt,
bm.Amount,
NULL,
bm.TransactionId,
SYSUTCDATETIME(),
NULL,
SYSUTCDATETIME(),
NULL,
0
FROM OrphanClubUsers o
INNER JOIN BestManual bm ON bm.UserId = o.UserId AND bm.rn = 1
CROSS JOIN BasePkg b
WHERE COALESCE(o.PackageIdHint, b.PackageId) IS NOT NULL;
DECLARE @FromManual INT = @@ROWCOUNT;
PRINT CONCAT('Inserted from ManualPayments: ', @FromManual);
------------------------------------------------------------
-- 2) Remaining orphans from Cycle / membership dates
------------------------------------------------------------
;WITH OrphanClubUsers AS (
SELECT
m.UserId,
m.PurchaseMethod,
u.PackagePurchaseMethod AS UserPurchaseMethod,
COALESCE(NULLIF(m.LastPackageId, 0), NULLIF(m.FirstPackageId, 0)) AS MembPackageId,
COALESCE(m.FirstActivationDate, m.LastActivationDate, m.ActivatedAt, m.Created) AS MembPurchasedAt
FROM CMS.ClubMemberships m
INNER JOIN CMS.Users u ON u.Id = m.UserId AND u.IsDeleted = 0
WHERE m.IsDeleted = 0
AND NOT EXISTS (
SELECT 1
FROM CMS.UserPackagePurchases upp
LEFT JOIN CMS.Transactions t ON t.Id = upp.TransactionId AND t.IsDeleted = 0
WHERE upp.UserId = m.UserId
AND upp.IsDeleted = 0
AND (upp.TransactionId IS NULL OR t.PaymentStatus = 0)
)
),
BestCycle AS (
SELECT
c.UserId,
c.PackageId,
c.PackageAmount,
c.PurchaseMethod,
c.PackagePurchasedAt,
ROW_NUMBER() OVER (PARTITION BY c.UserId ORDER BY c.CycleNumber ASC, c.Id ASC) AS rn
FROM CMS.ClubMembershipCycles c
WHERE c.IsDeleted = 0
),
BasePkg AS (
SELECT TOP (1) Id AS PackageId, Price
FROM CMS.Packages
WHERE IsDeleted = 0 AND IsBasePackage = 1
ORDER BY Id
),
ToInsert AS (
SELECT
o.UserId,
COALESCE(NULLIF(bc.PackageId, 0), o.MembPackageId, b.PackageId) AS PackageId,
CASE
WHEN bc.PurchaseMethod IS NOT NULL AND bc.PurchaseMethod <> 0 THEN bc.PurchaseMethod
WHEN o.PurchaseMethod IS NOT NULL AND o.PurchaseMethod <> 0 THEN o.PurchaseMethod
WHEN o.UserPurchaseMethod IS NOT NULL AND o.UserPurchaseMethod <> 0 THEN o.UserPurchaseMethod
ELSE 3 -- Manual (last resort)
END AS PurchaseMethod,
COALESCE(bc.PackagePurchasedAt, o.MembPurchasedAt, SYSUTCDATETIME()) AS PurchasedAt,
CASE
WHEN bc.PackageAmount IS NOT NULL AND bc.PackageAmount > 0 THEN bc.PackageAmount
ELSE COALESCE(p.Price, b.Price, 0)
END AS Amount
FROM OrphanClubUsers o
LEFT JOIN BestCycle bc ON bc.UserId = o.UserId AND bc.rn = 1
CROSS JOIN BasePkg b
LEFT JOIN CMS.Packages p ON p.Id = COALESCE(NULLIF(bc.PackageId, 0), o.MembPackageId, b.PackageId) AND p.IsDeleted = 0
)
SELECT *
INTO #BackfillCandidates
FROM ToInsert
WHERE PackageId IS NOT NULL AND Amount > 0;
DECLARE @Remaining INT = (SELECT COUNT(*) FROM #BackfillCandidates);
PRINT CONCAT('Remaining orphans to synthetic backfill: ', @Remaining);
DECLARE @UserId BIGINT, @PackageId BIGINT, @PurchaseMethod INT, @PurchasedAt DATETIME2, @Amount BIGINT;
DECLARE @TxId BIGINT;
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
SELECT UserId, PackageId, PurchaseMethod, PurchasedAt, Amount
FROM #BackfillCandidates;
OPEN cur;
FETCH NEXT FROM cur INTO @UserId, @PackageId, @PurchaseMethod, @PurchasedAt, @Amount;
DECLARE @Synthetic INT = 0;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO CMS.Transactions
(
Amount, Description, PaymentStatus, PaymentDate, RefId, Type,
Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted
)
VALUES
(
@Amount,
CONCAT(N'بک‌فیل عضویت باشگاه — UserId ', @UserId),
0, -- Success
@PurchasedAt,
CONCAT('UPP-BACKFILL-', @UserId, '-', @PackageId),
2, -- DepositExternal1
SYSUTCDATETIME(), NULL, SYSUTCDATETIME(), NULL, 0
);
SET @TxId = SCOPE_IDENTITY();
INSERT INTO CMS.UserPackagePurchases
(
UserId, PackageId, PurchaseMethod, PurchasedAt, Amount,
OrderId, TransactionId, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted
)
VALUES
(
@UserId, @PackageId, @PurchaseMethod, @PurchasedAt, @Amount,
NULL, @TxId, SYSUTCDATETIME(), NULL, SYSUTCDATETIME(), NULL, 0
);
SET @Synthetic += 1;
FETCH NEXT FROM cur INTO @UserId, @PackageId, @PurchaseMethod, @PurchasedAt, @Amount;
END
CLOSE cur;
DEALLOCATE cur;
PRINT CONCAT('Inserted synthetic UPP rows: ', @Synthetic);
DROP TABLE #BackfillCandidates;
------------------------------------------------------------
-- Verify invariant
------------------------------------------------------------
DECLARE @ClubCount INT =
(
SELECT COUNT(*) FROM CMS.ClubMemberships WHERE IsDeleted = 0
);
DECLARE @OrphansLeft INT =
(
SELECT COUNT(*)
FROM CMS.ClubMemberships m
WHERE m.IsDeleted = 0
AND NOT EXISTS (
SELECT 1
FROM CMS.UserPackagePurchases upp
LEFT JOIN CMS.Transactions t ON t.Id = upp.TransactionId AND t.IsDeleted = 0
WHERE upp.UserId = m.UserId
AND upp.IsDeleted = 0
AND (upp.TransactionId IS NULL OR t.PaymentStatus = 0)
)
);
DECLARE @SuccessfulBuyers INT =
(
SELECT COUNT(DISTINCT upp.UserId)
FROM CMS.UserPackagePurchases upp
LEFT JOIN CMS.Transactions t ON t.Id = upp.TransactionId AND t.IsDeleted = 0
WHERE upp.IsDeleted = 0
AND (upp.TransactionId IS NULL OR t.PaymentStatus = 0)
);
PRINT CONCAT('ClubMemberships: ', @ClubCount);
PRINT CONCAT('Distinct successful UPP users: ', @SuccessfulBuyers);
PRINT CONCAT('Orphans left (must be 0): ', @OrphansLeft);
IF @OrphansLeft > 0
BEGIN
ROLLBACK;
THROW 50001, 'Backfill incomplete — orphans remain; transaction rolled back.', 1;
END
COMMIT;
PRINT 'Backfill committed successfully.';