Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 436e5d7aca | |||
| 053c5a7c4f | |||
| 82944e11d0 | |||
| bb20ad2c4e | |||
| 99aab3beff | |||
| 19be5dc9dc | |||
| 5c1e3cf601 | |||
| d82b7c28a8 | |||
| 3df738e496 | |||
| 3a9c3438e6 | |||
| 12ebdc9e87 | |||
| 9f1bc2bcdb | |||
| 354ba1dd1b | |||
| 3b9958a700 | |||
| 9c562b12c7 | |||
| 3c9957463d | |||
| 4df71107a9 | |||
| a7fc959d3e | |||
| d96558aa4b | |||
| a2d13c72c3 | |||
| 4e8caa6e2c | |||
| 37a017998c | |||
| 16acb8f3b5 | |||
| 34d2354fd4 | |||
| be2b22c50a | |||
| b260fd56fa | |||
| 1e3ba03c5c | |||
| ee7d29015f | |||
| b053d86006 | |||
| 15044c66cb | |||
| 0983cdf88f | |||
| b8120dd1b9 | |||
| 7ce5539793 | |||
| 9a4f1e6401 | |||
| 091a04f3aa | |||
| 62a0f8594d | |||
| fb81aff095 | |||
| 491b8a0d0f | |||
| be80136ae8 | |||
| 5e9984f0e5 | |||
| 1d07ae4f71 | |||
| 775a9bd75d | |||
| 2620a24597 | |||
| def3b94d89 | |||
| 3ebe0f9f48 | |||
| f4e24f7449 | |||
| 540a2ce4fc | |||
| e57f01965a | |||
| f97c4b0812 | |||
| fd32e15192 | |||
| 4456e504d4 | |||
| cec1e74f6c | |||
| dd9ac48474 | |||
| eb1b249869 | |||
| e8e964f9a0 | |||
| 9050c00297 | |||
| 6048824b33 | |||
| 62a247f097 | |||
| af12f996f1 | |||
| 3b0d9eadd3 | |||
| db07838ecb | |||
| 167991890c | |||
| 1a425f0d93 | |||
| 6f8aefc2ce | |||
| 2bd5c7db78 | |||
| 0728ec2b76 | |||
| 1425fb187b | |||
| 81d2b39ee1 | |||
| fce194a195 |
@@ -1,46 +0,0 @@
|
||||
|
||||
name: Push nuget and docker image Actions Workflow
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- stage_new
|
||||
jobs:
|
||||
Deploy:
|
||||
runs-on: windows
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://git.afrino.co/actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://git.afrino.co/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
- name: Remove Package Source
|
||||
run: dotnet nuget remove source FourSat
|
||||
continue-on-error: true
|
||||
- name: Add Package Source
|
||||
run: dotnet nuget add source --name FourSat --username systemuser --password sZSA7PTiv3pUSQZ https://git.afrino.co/api/packages/FourSat/nuget/index.json --store-password-in-clear-text
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore ".\src\CMSMicroservice.WebApi\CMSMicroservice.WebApi.csproj"
|
||||
- name: Build
|
||||
run: dotnet build ".\src\CMSMicroservice.WebApi\CMSMicroservice.WebApi.csproj" --configuration Release --no-restore
|
||||
- name: Test
|
||||
run: dotnet test ".\src\CMSMicroservice.WebApi\CMSMicroservice.WebApi.csproj" --no-restore --verbosity normal
|
||||
- name: Recycle Apppool
|
||||
run: |
|
||||
& "C:\Windows\System32\inetsrv\appcmd.exe" recycle apppool /apppool.name:cms.kbs1.ir
|
||||
shell: powershell
|
||||
- name: Stop Website
|
||||
run: |
|
||||
& "C:\Windows\System32\inetsrv\appcmd.exe" stop site /site.name:cms.kbs1.ir
|
||||
shell: powershell
|
||||
- name: Publish
|
||||
run: dotnet publish ".\src\CMSMicroservice.WebApi\CMSMicroservice.WebApi.csproj" -c Release -o publish
|
||||
- name: Copy Publish To IIS Directory
|
||||
run: Get-ChildItem -Path "publish\*" | Copy-Item -Destination "E:\kbs1.ir\cms.kbs1.ir\" -Recurse -Force
|
||||
- name: Start Website
|
||||
run: |
|
||||
& "C:\Windows\System32\inetsrv\appcmd.exe" start site /site.name:cms.kbs1.ir
|
||||
shell: powershell
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
done
|
||||
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
echo "❌ Docker daemon failed to start after 3 minutes"
|
||||
echo "❌ Docker daemon failed to start"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -75,8 +75,10 @@ jobs:
|
||||
|
||||
- name: Build Docker Image
|
||||
run: |
|
||||
DOCKER_BUILDKIT=0 docker build --network host -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod .
|
||||
DOCKER_BUILDKIT=0 docker build --network host \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod \
|
||||
.
|
||||
|
||||
- name: Push to Registry
|
||||
run: |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# CMS Microservice
|
||||
|
||||
Documentation moved to [totalDoc/INDEX.md](../totalDoc/INDEX.md).
|
||||
|
||||
|
||||
+1
-6
@@ -1,5 +1,3 @@
|
||||
using CMSMicroservice.Application.Common;
|
||||
|
||||
namespace CMSMicroservice.Application.CommissionCQ.Commands.RequestWithdrawal;
|
||||
|
||||
public class RequestWithdrawalCommandHandler : IRequestHandler<RequestWithdrawalCommand, Unit>
|
||||
@@ -39,10 +37,7 @@ public class RequestWithdrawalCommandHandler : IRequestHandler<RequestWithdrawal
|
||||
|
||||
if (request.Method == WithdrawalMethod.Cash)
|
||||
{
|
||||
var normalizedIban = IbanNormalizer.TryNormalize(request.IbanNumber);
|
||||
if (normalizedIban is null)
|
||||
throw new InvalidOperationException("فرمت شماره شبا معتبر نیست. باید IR و ۲۴ رقم باشد.");
|
||||
payout.IbanNumber = normalizedIban;
|
||||
payout.IbanNumber = request.IbanNumber;
|
||||
}
|
||||
|
||||
_context.UserCommissionPayouts.Update(payout);
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace CMSMicroservice.Application.Common;
|
||||
|
||||
/// <summary>
|
||||
/// نرمالسازی و اعتبارسنجی شماره شبا ایران (IR + 24 رقم).
|
||||
/// </summary>
|
||||
public static class IbanNormalizer
|
||||
{
|
||||
private static readonly Regex IranianIbanRegex = new(@"^IR\d{24}$", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// فاصله و خط تیره را حذف میکند، به حروف بزرگ تبدیل میکند و یک پیشوند IR میگذارد.
|
||||
/// در صورت نامعتبر بودن، null برمیگرداند.
|
||||
/// </summary>
|
||||
public static string? TryNormalize(string? iban)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(iban))
|
||||
return null;
|
||||
|
||||
var normalized = iban.Trim().ToUpperInvariant()
|
||||
.Replace(" ", "", StringComparison.Ordinal)
|
||||
.Replace("-", "", StringComparison.Ordinal);
|
||||
|
||||
if (normalized.StartsWith("IR", StringComparison.Ordinal))
|
||||
normalized = normalized[2..];
|
||||
|
||||
normalized = "IR" + normalized;
|
||||
|
||||
return IranianIbanRegex.IsMatch(normalized) ? normalized : null;
|
||||
}
|
||||
|
||||
public static bool IsValid(string? iban) => TryNormalize(iban) is not null;
|
||||
}
|
||||
+1
-94
@@ -1,4 +1,5 @@
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkTree;
|
||||
@@ -41,11 +42,6 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
|
||||
|
||||
// تبدیل نتایج flat به ساختار درختی
|
||||
var tree = BuildTreeFromFlatNodes(flatNodes);
|
||||
if (tree != null)
|
||||
{
|
||||
await EnrichWithPackageLegScoresAsync(tree, flatNodes, cancellationToken);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -198,93 +194,4 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
|
||||
var rootNode = flatNodes.FirstOrDefault(n => n.NetworkLevel == 0);
|
||||
return rootNode != null ? nodeDict[rootNode.UserId] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// آخرین Left/RightLegTotal طلایی و نقرهای برای هر نود درخت (برنامهریزی پای چپ/راست).
|
||||
/// </summary>
|
||||
private async Task EnrichWithPackageLegScoresAsync(
|
||||
NetworkTreeDto root,
|
||||
List<NetworkTreeNodeDto> flatNodes,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var userIds = flatNodes.Select(n => n.UserId).Distinct().ToList();
|
||||
if (userIds.Count == 0) return;
|
||||
|
||||
var packages = await _context.Packages
|
||||
.AsNoTracking()
|
||||
.Where(p => !p.IsDeleted && p.IsActive)
|
||||
.Select(p => new { p.Id, p.Title, p.IsBasePackage, p.MaxBalancesPerLeg })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var goldPackageId = packages.FirstOrDefault(p => p.IsBasePackage)?.Id
|
||||
?? packages.FirstOrDefault(p => p.Title.Contains("طلایی") || p.Title.Contains("طلايی"))?.Id;
|
||||
|
||||
var silverPackageId = packages.FirstOrDefault(p =>
|
||||
!p.IsBasePackage && (p.Title.Contains("نقره") || p.MaxBalancesPerLeg <= 30))?.Id
|
||||
?? packages.FirstOrDefault(p => !p.IsBasePackage)?.Id;
|
||||
|
||||
if (!goldPackageId.HasValue && !silverPackageId.HasValue)
|
||||
{
|
||||
_logger.LogWarning("GetNetworkTree: no gold/silver packages found for leg score enrichment");
|
||||
return;
|
||||
}
|
||||
|
||||
var packageIds = new List<long>();
|
||||
if (goldPackageId.HasValue) packageIds.Add(goldPackageId.Value);
|
||||
if (silverPackageId.HasValue && silverPackageId != goldPackageId)
|
||||
packageIds.Add(silverPackageId.Value);
|
||||
|
||||
var rawBalances = await _context.NetworkWeeklyBalances
|
||||
.AsNoTracking()
|
||||
.Where(b => userIds.Contains(b.UserId) && packageIds.Contains(b.PackageId))
|
||||
.Select(b => new LegScoreRow(
|
||||
b.UserId,
|
||||
b.PackageId,
|
||||
b.WeekDefinitionId,
|
||||
b.LeftLegTotal,
|
||||
b.RightLegTotal))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var latestByUserPackage = rawBalances
|
||||
.GroupBy(b => (b.UserId, b.PackageId))
|
||||
.ToDictionary(
|
||||
g => g.Key,
|
||||
g => g.OrderByDescending(x => x.WeekDefinitionId).First());
|
||||
|
||||
ApplyLegScores(root, goldPackageId, silverPackageId, latestByUserPackage);
|
||||
}
|
||||
|
||||
private static void ApplyLegScores(
|
||||
NetworkTreeDto node,
|
||||
long? goldPackageId,
|
||||
long? silverPackageId,
|
||||
Dictionary<(long UserId, long PackageId), LegScoreRow> latestByUserPackage)
|
||||
{
|
||||
if (goldPackageId.HasValue &&
|
||||
latestByUserPackage.TryGetValue((node.UserId, goldPackageId.Value), out var gold))
|
||||
{
|
||||
node.GoldLeftLegTotal = gold.LeftLegTotal;
|
||||
node.GoldRightLegTotal = gold.RightLegTotal;
|
||||
}
|
||||
|
||||
if (silverPackageId.HasValue &&
|
||||
latestByUserPackage.TryGetValue((node.UserId, silverPackageId.Value), out var silver))
|
||||
{
|
||||
node.SilverLeftLegTotal = silver.LeftLegTotal;
|
||||
node.SilverRightLegTotal = silver.RightLegTotal;
|
||||
}
|
||||
|
||||
if (node.LeftChild != null)
|
||||
ApplyLegScores(node.LeftChild, goldPackageId, silverPackageId, latestByUserPackage);
|
||||
|
||||
if (node.RightChild != null)
|
||||
ApplyLegScores(node.RightChild, goldPackageId, silverPackageId, latestByUserPackage);
|
||||
}
|
||||
|
||||
private sealed record LegScoreRow(
|
||||
long UserId,
|
||||
long PackageId,
|
||||
long WeekDefinitionId,
|
||||
int LeftLegTotal,
|
||||
int RightLegTotal);
|
||||
}
|
||||
|
||||
-12
@@ -60,18 +60,6 @@ public class NetworkTreeDto
|
||||
|
||||
public long? PackageId { get; set; }
|
||||
|
||||
/// <summary>آخرین LeftLegTotal پکیج طلایی (پایه)</summary>
|
||||
public int GoldLeftLegTotal { get; set; }
|
||||
|
||||
/// <summary>آخرین RightLegTotal پکیج طلایی (پایه)</summary>
|
||||
public int GoldRightLegTotal { get; set; }
|
||||
|
||||
/// <summary>آخرین LeftLegTotal پکیج نقرهای</summary>
|
||||
public int SilverLeftLegTotal { get; set; }
|
||||
|
||||
/// <summary>آخرین RightLegTotal پکیج نقرهای</summary>
|
||||
public int SilverRightLegTotal { get; set; }
|
||||
|
||||
public NetworkTreeDto? LeftChild { get; set; }
|
||||
public NetworkTreeDto? RightChild { get; set; }
|
||||
}
|
||||
|
||||
+5
-30
@@ -17,7 +17,7 @@ public class GetAllUserByFilterQueryHandler : IRequestHandler<GetAllUserByFilter
|
||||
if (request.Filter is not null)
|
||||
{
|
||||
query = query
|
||||
.Where(x => request.Filter.SearchText == null ||
|
||||
.Where(x => request.Filter.SearchText == null ||
|
||||
x.FirstName.Contains(request.Filter.SearchText) ||
|
||||
x.LastName.Contains(request.Filter.SearchText) ||
|
||||
x.Mobile.Contains(request.Filter.SearchText) ||
|
||||
@@ -34,39 +34,14 @@ public class GetAllUserByFilterQueryHandler : IRequestHandler<GetAllUserByFilter
|
||||
.Where(x => request.Filter.SmsNotifications == null || x.SmsNotifications == request.Filter.SmsNotifications)
|
||||
.Where(x => request.Filter.EmailNotifications == null || x.EmailNotifications == request.Filter.EmailNotifications)
|
||||
.Where(x => request.Filter.PushNotifications == null || x.PushNotifications == request.Filter.PushNotifications)
|
||||
.Where(x => request.Filter.BirthDate == null || x.BirthDate == request.Filter.BirthDate);
|
||||
.Where(x => request.Filter.BirthDate == null || x.BirthDate == request.Filter.BirthDate)
|
||||
;
|
||||
}
|
||||
|
||||
return new GetAllUserByFilterResponseDto
|
||||
{
|
||||
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
||||
Models = await query.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
.Select(x => new GetAllUserByFilterResponseModel
|
||||
{
|
||||
Id = x.Id,
|
||||
FirstName = x.FirstName,
|
||||
LastName = x.LastName,
|
||||
Mobile = x.Mobile,
|
||||
NationalCode = x.NationalCode,
|
||||
AvatarPath = x.AvatarPath,
|
||||
NetworkParentId = x.NetworkParentId,
|
||||
ReferralCode = x.ReferralCode,
|
||||
IsMobileVerified = x.IsMobileVerified,
|
||||
MobileVerifiedAt = x.MobileVerifiedAt,
|
||||
EmailNotifications = x.EmailNotifications,
|
||||
SmsNotifications = x.SmsNotifications,
|
||||
PushNotifications = x.PushNotifications,
|
||||
BirthDate = x.BirthDate,
|
||||
DefaultAddress = x.UserAddresses
|
||||
.Where(a => a.IsDefault && !a.IsDeleted)
|
||||
.Select(a => a.Address)
|
||||
.FirstOrDefault() ?? string.Empty,
|
||||
DefaultPostalCode = x.UserAddresses
|
||||
.Where(a => a.IsDefault && !a.IsDeleted)
|
||||
.Select(a => a.PostalCode)
|
||||
.FirstOrDefault() ?? string.Empty
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
};
|
||||
.ProjectToType<GetAllUserByFilterResponseModel>().ToListAsync(cancellationToken)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
-4
@@ -36,8 +36,4 @@ public class GetAllUserByFilterResponseDto
|
||||
public bool PushNotifications { get; set; }
|
||||
//تاریخ تولد
|
||||
public DateTime? BirthDate { get; set; }
|
||||
//آدرس پیشفرض
|
||||
public string DefaultAddress { get; set; } = string.Empty;
|
||||
//کدپستی پیشفرض
|
||||
public string DefaultPostalCode { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260222160755_u21")]
|
||||
[Migration("20260222155925_u21")]
|
||||
partial class u21
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -0,0 +1,136 @@
|
||||
-- =============================================
|
||||
-- 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
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Collections.Generic;
|
||||
@@ -14,12 +13,10 @@ namespace CMSMicroservice.Infrastructure.Services;
|
||||
public class GenerateJwtTokenService : IGenerateJwtToken
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public GenerateJwtTokenService(IConfiguration configuration, IApplicationDbContext context)
|
||||
public GenerateJwtTokenService(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<string> GenerateJwtToken(User user, int? expiryDays = null)
|
||||
@@ -61,10 +58,6 @@ public class GenerateJwtTokenService : IGenerateJwtToken
|
||||
claims.Add(new Claim("HasPurchasedPackage",
|
||||
(user.PackagePurchaseMethod != PackagePurchaseMethod.None).ToString()));
|
||||
|
||||
var hasAddress = await _context.UserAddresses
|
||||
.AnyAsync(a => a.UserId == user.Id && !a.IsDeleted);
|
||||
claims.Add(new Claim("HasAddress", hasAddress.ToString()));
|
||||
|
||||
if (user.UserRoles != null && user.UserRoles.Any())
|
||||
{
|
||||
foreach (var userRole in user.UserRoles)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.0.210</Version>
|
||||
<Version>0.0.208</Version>
|
||||
<DebugType>None</DebugType>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
|
||||
@@ -209,11 +209,6 @@ message NetworkTreeNodeModel
|
||||
google.protobuf.BoolValue is_new_activation = 22; // true=اولین فعالسازی، false=خرید مجدد، null=بدون Cycle
|
||||
string package_name = 23; // نام پکیج موثر
|
||||
google.protobuf.Int64Value package_id = 24; // شناسه پکیج
|
||||
// آخرین NetworkWeeklyBalance: امتیاز پای چپ/راست برای پکیج طلایی و نقرهای (D3)
|
||||
int32 gold_left_leg_total = 25;
|
||||
int32 gold_right_leg_total = 26;
|
||||
int32 silver_left_leg_total = 27;
|
||||
int32 silver_right_leg_total = 28;
|
||||
}
|
||||
|
||||
// GetHistory Query
|
||||
|
||||
@@ -238,8 +238,6 @@ message GetAllUserByFilterResponseModel
|
||||
bool sms_notifications = 12;
|
||||
bool push_notifications = 13;
|
||||
google.protobuf.Timestamp birth_date = 14;
|
||||
string default_address = 15;
|
||||
string default_postal_code = 16;
|
||||
}
|
||||
message GetJwtTokenRequest
|
||||
{
|
||||
|
||||
@@ -95,11 +95,7 @@ public class NetworkMembershipProfile : IRegister
|
||||
IsActivatedInTargetWeek = node.IsActivatedInTargetWeek,
|
||||
PackageName = node.PackageName ?? string.Empty,
|
||||
PackageId = node.PackageId.HasValue ? node.PackageId.Value : null,
|
||||
IsNewActivation = node.IsNewActivation.HasValue ? node.IsNewActivation.Value : null,
|
||||
GoldLeftLegTotal = node.GoldLeftLegTotal,
|
||||
GoldRightLegTotal = node.GoldRightLegTotal,
|
||||
SilverLeftLegTotal = node.SilverLeftLegTotal,
|
||||
SilverRightLegTotal = node.SilverRightLegTotal
|
||||
IsNewActivation = node.IsNewActivation.HasValue ? node.IsNewActivation.Value : null
|
||||
};
|
||||
|
||||
if (parentId.HasValue)
|
||||
|
||||
@@ -4,10 +4,7 @@ public class UserProfile : IRegister
|
||||
{
|
||||
void IRegister.Register(TypeAdapterConfig config)
|
||||
{
|
||||
config.NewConfig<Application.UserCQ.Queries.GetAllUserByFilter.GetAllUserByFilterResponseModel,
|
||||
CMSMicroservice.Protobuf.Protos.User.GetAllUserByFilterResponseModel>()
|
||||
.Map(dest => dest.ParentId, src => src.NetworkParentId)
|
||||
.Map(dest => dest.DefaultAddress, src => src.DefaultAddress ?? string.Empty)
|
||||
.Map(dest => dest.DefaultPostalCode, src => src.DefaultPostalCode ?? string.Empty);
|
||||
//config.NewConfig<Source,Destination>()
|
||||
// .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,11 +185,6 @@ public class NetworkMembershipService : NetworkMembershipContract.NetworkMembers
|
||||
node.PackageId = dto.PackageId.Value;
|
||||
}
|
||||
|
||||
node.GoldLeftLegTotal = dto.GoldLeftLegTotal;
|
||||
node.GoldRightLegTotal = dto.GoldRightLegTotal;
|
||||
node.SilverLeftLegTotal = dto.SilverLeftLegTotal;
|
||||
node.SilverRightLegTotal = dto.SilverRightLegTotal;
|
||||
|
||||
if (dto.IsNewActivation.HasValue)
|
||||
{
|
||||
node.IsNewActivation = dto.IsNewActivation.Value;
|
||||
|
||||
@@ -18,7 +18,6 @@ using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletHistory;
|
||||
using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawals;
|
||||
using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawalSettings;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Application.Common;
|
||||
using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Domain.Common;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
@@ -176,18 +175,7 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
|
||||
|
||||
// Update payout with withdrawal info
|
||||
payout.WithdrawalMethod = (Domain.Enums.WithdrawalMethod)request.WithdrawalMethod;
|
||||
if (payout.WithdrawalMethod == Domain.Enums.WithdrawalMethod.Cash)
|
||||
{
|
||||
var normalizedIban = IbanNormalizer.TryNormalize(request.IbanNumber);
|
||||
if (normalizedIban is null)
|
||||
throw new RpcException(new Status(StatusCode.InvalidArgument,
|
||||
"فرمت شماره شبا معتبر نیست. باید IR و ۲۴ رقم باشد."));
|
||||
payout.IbanNumber = normalizedIban;
|
||||
}
|
||||
else
|
||||
{
|
||||
payout.IbanNumber = null;
|
||||
}
|
||||
payout.IbanNumber = request.IbanNumber;
|
||||
payout.Status = Domain.Enums.CommissionPayoutStatus.WithdrawRequested;
|
||||
|
||||
await _context.SaveChangesAsync(context.CancellationToken);
|
||||
|
||||
+14
-11
@@ -2,19 +2,16 @@
|
||||
"PaymentProvider": "zarinpal",
|
||||
"ZarinPal": {
|
||||
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
|
||||
"UseSandbox": true
|
||||
},
|
||||
"CmsBaseUrl": "https://cms.se.kbs1.ir",
|
||||
"FrontOfficeBaseUrl": "https://frontoffice.se.kbs1.ir",
|
||||
"FMS": {
|
||||
"Address": "https://dl.afrino.co"
|
||||
"UseSandbox": false
|
||||
},
|
||||
"CmsBaseUrl": "https://cms.kbs2.ir",
|
||||
"FrontOfficeBaseUrl": "https://kbs2.ir",
|
||||
"JwtSecurityKey": "TvlZVx5TJaHs8e9HgUdGzhGP2CIidoI444nAj+8+g7c=",
|
||||
"JwtIssuer": "https://localhost",
|
||||
"JwtAudience": "https://localhost",
|
||||
"JwtExpiryInDays": 5,
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Data Source=194.5.195.53,31433; Initial Catalog=Foursat;User ID=sa;Password=87zH26nbqT;Connection Timeout=300000;MultipleActiveResultSets=True;Encrypt=False",
|
||||
"DefaultConnection": "Server=mssql-svc;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;",
|
||||
"providerName": "System.Data.SqlClient"
|
||||
},
|
||||
"Otp": {
|
||||
@@ -23,12 +20,12 @@
|
||||
},
|
||||
"Monitoring": {
|
||||
"SentryEnabled": false,
|
||||
"SentryDsn": "",
|
||||
"SentryDsn": "",
|
||||
"SlackEnabled": false,
|
||||
"SlackWebhookUrl": "",
|
||||
"EmailAlertsEnabled": false,
|
||||
"AdminEmails": [
|
||||
"admin@example.com"
|
||||
"admin@example.com"
|
||||
],
|
||||
"SmsNotificationsEnabled": false,
|
||||
"SmsApiKey": "",
|
||||
@@ -67,7 +64,7 @@
|
||||
},
|
||||
"BackgroundJobs": {
|
||||
"WeeklyCommissionCalculation": {
|
||||
"Enabled": true,
|
||||
"Enabled": false,
|
||||
"CronExpression": "5 0 * * 0"
|
||||
}
|
||||
},
|
||||
@@ -87,7 +84,13 @@
|
||||
"Audience": "domain_api"
|
||||
},
|
||||
"Seq": {
|
||||
"ServerUrl": "https://seq.afrino.co",
|
||||
"ServerUrl": "http://seq-svc:5341",
|
||||
"ApiKey": "oxpvpUzU1pZxMS4s3Fqq"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,16 @@
|
||||
"PaymentProvider": "zarinpal",
|
||||
"ZarinPal": {
|
||||
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
|
||||
"UseSandbox": true
|
||||
},
|
||||
"CmsBaseUrl": "https://localhost:32846",
|
||||
"FrontOfficeBaseUrl": "http://localhost:5268",
|
||||
"FMS": {
|
||||
"Address": "https://dl.afrino.co"
|
||||
"UseSandbox": false
|
||||
},
|
||||
"CmsBaseUrl": "https://cms.kbs2.ir",
|
||||
"FrontOfficeBaseUrl": "https://kbs2.ir",
|
||||
"JwtSecurityKey": "TvlZVx5TJaHs8e9HgUdGzhGP2CIidoI444nAj+8+g7c=",
|
||||
"JwtIssuer": "https://localhost",
|
||||
"JwtAudience": "https://localhost",
|
||||
"JwtExpiryInDays": 5,
|
||||
"Kestrel": {
|
||||
"EndpointDefaults": {
|
||||
"Protocols": "Http1AndHttp2"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Data Source=194.5.195.53,31433; Initial Catalog=Foursat;User ID=sa;Password=87zH26nbqT;Connection Timeout=300000;MultipleActiveResultSets=True;Encrypt=False",
|
||||
"DefaultConnection": "Server=45.149.79.127,31433;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;",
|
||||
"providerName": "System.Data.SqlClient"
|
||||
},
|
||||
"Otp": {
|
||||
@@ -27,12 +19,12 @@
|
||||
},
|
||||
"Monitoring": {
|
||||
"SentryEnabled": false,
|
||||
"SentryDsn": "",
|
||||
"SentryDsn": "",
|
||||
"SlackEnabled": false,
|
||||
"SlackWebhookUrl": "",
|
||||
"EmailAlertsEnabled": false,
|
||||
"AdminEmails": [
|
||||
"admin@example.com"
|
||||
"admin@example.com"
|
||||
],
|
||||
"SmsNotificationsEnabled": false,
|
||||
"SmsApiKey": "",
|
||||
@@ -51,7 +43,7 @@
|
||||
"Sms": {
|
||||
"Enabled": true,
|
||||
"Provider": "Kavenegar",
|
||||
"KavenegarApiKey": "43676A4E786A6C50452F2F6252507939346A5145764B33566B456454374E657A614468706658656D6534593D",
|
||||
"KavenegarApiKey": "497263626F32626A48685A6137524C4F78575A766E4C74694A556B79317648424964655030682B554545413D",
|
||||
"Sender": "1000001110100"
|
||||
},
|
||||
"DayaPayment": {
|
||||
@@ -71,7 +63,7 @@
|
||||
},
|
||||
"BackgroundJobs": {
|
||||
"WeeklyCommissionCalculation": {
|
||||
"Enabled": true,
|
||||
"Enabled": false,
|
||||
"CronExpression": "5 0 * * 0"
|
||||
}
|
||||
},
|
||||
@@ -84,12 +76,23 @@
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Kestrel": {
|
||||
"EndpointDefaults": {
|
||||
"Protocols": "Http2"
|
||||
}
|
||||
},
|
||||
"Authentication": {
|
||||
"Authority": "https://ids.domain.com/",
|
||||
"Audience": "domain_api"
|
||||
},
|
||||
"Seq": {
|
||||
"ServerUrl": "https://seq.afrino.co",
|
||||
"ServerUrl": "http://seq-svc:5341",
|
||||
"ApiKey": "oxpvpUzU1pZxMS4s3Fqq"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
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.';
|
||||
@@ -1,465 +0,0 @@
|
||||
/*
|
||||
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 + charge)
|
||||
|
||||
If ExistingDayaTxId found (wallet RefrenceId → Tx Description LIKE '%اعتبار دایا%'
|
||||
OR Tx.RefId = ContractNumber): REUSE that Tx — do NOT create Transaction / do NOT charge wallet.
|
||||
|
||||
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;
|
||||
|
||||
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, @Action NVARCHAR(200);
|
||||
|
||||
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
|
||||
|
||||
------------------------------------------------------------
|
||||
-- اگر تراکنش/شارژ «اعتبار دایا» از قبل هست → همان Tx را بگیر؛ شارژ نکن
|
||||
------------------------------------------------------------
|
||||
IF @ExistingDayaTxId IS NOT NULL
|
||||
BEGIN
|
||||
SET @TxId = @ExistingDayaTxId;
|
||||
SET @Action = @Action + CONCAT(N'REUSE_EXISTING_DAYA_TX=', @TxId, N';');
|
||||
|
||||
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(no charge);';
|
||||
END
|
||||
ELSE IF @HasCredit = 0
|
||||
BEGIN
|
||||
-- Full wallet charge only if never credited and no existing Daya tx
|
||||
INSERT INTO CMS.Transactions
|
||||
(
|
||||
Amount, Description, PaymentStatus, PaymentDate, RefId, Type,
|
||||
Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@PackagePrice,
|
||||
CONCAT(N'دریافت اعتبار دایا - قرارداد ', @ContractNumber),
|
||||
0, -- Success
|
||||
@Now,
|
||||
@ContractNumber,
|
||||
2, -- DepositExternal1
|
||||
@Now, NULL, @Now, NULL, 0
|
||||
);
|
||||
SET @TxId = SCOPE_IDENTITY();
|
||||
|
||||
SELECT @WalletId = Id FROM CMS.UserWallets WHERE UserId = @UserId;
|
||||
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();
|
||||
END
|
||||
|
||||
UPDATE CMS.UserWallets
|
||||
SET
|
||||
Balance = Balance + @PackagePrice,
|
||||
DiscountBalance = DiscountBalance + @DiscountAmount,
|
||||
LastModified = @Now
|
||||
WHERE Id = @WalletId;
|
||||
|
||||
INSERT INTO CMS.UserWalletHistories
|
||||
(
|
||||
WalletId, CurrentBalance, ChangeValue, CurrentNetworkBalance, ChangeNerworkValue,
|
||||
CurrentDiscountBalance, ChangeDiscountValue, IsIncrease, RefrenceId, PackageId,
|
||||
Created, LastModified, IsDeleted
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@WalletId, 0, @PackagePrice, 0, 0,
|
||||
0, 0, 1, @TxId, @PackageId,
|
||||
@Now, @Now, 0
|
||||
),
|
||||
(
|
||||
@WalletId, 0, 0, 0, 0,
|
||||
0, @DiscountAmount, 1, @TxId, @PackageId,
|
||||
@Now, @Now, 0
|
||||
);
|
||||
|
||||
UPDATE CMS.Users
|
||||
SET
|
||||
HasReceivedDayaCredit = 1,
|
||||
DayaCreditReceivedAt = @Now,
|
||||
PackagePurchaseMethod = 1, -- DayaLoan
|
||||
LastModified = @Now
|
||||
WHERE Id = @UserId;
|
||||
|
||||
SET @HasCredit = 1;
|
||||
SET @Action = @Action + N'CHARGE_WALLET+USER_FLAGS;';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
-- HasReceivedDayaCredit=1 ولی ExistingDayaTx پیدا نشد
|
||||
UPDATE CMS.Users
|
||||
SET
|
||||
PackagePurchaseMethod = 1,
|
||||
HasReceivedDayaCredit = 1,
|
||||
DayaCreditReceivedAt = COALESCE(DayaCreditReceivedAt, @Now),
|
||||
LastModified = @Now
|
||||
WHERE Id = @UserId;
|
||||
SET @Action = @Action + N'SKIP_CHARGE(HasReceivedDayaCredit);';
|
||||
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 '=== 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
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
Correct users wrongly marked as DayaLoan — should be Manual.
|
||||
|
||||
Affects:
|
||||
- CMS.Users.PackagePurchaseMethod (1 → 3)
|
||||
- CMS.ClubMemberships.PurchaseMethod (1 or 0 → 3)
|
||||
- CMS.ClubMembershipCycles.PurchaseMethod (1 or 0 → 3)
|
||||
- CMS.UserPackagePurchases.PurchaseMethod (1 → 3)
|
||||
- CMS.Users.HasReceivedDayaCredit / DayaCreditReceivedAt clear
|
||||
- CMS.DayaLoanContracts soft-delete stub rows
|
||||
(IsProcessed=0, no TransactionId, no ProcessedDate)
|
||||
for these UserIds only
|
||||
|
||||
PackagePurchaseMethod: None=0, DayaLoan=1, DirectPurchase=2, Manual=3
|
||||
|
||||
NOTE: UserId=43 shares NationalCode 5289780335 with UserId=42 (real Excel Daya).
|
||||
This script only touches UserId=43's own rows — not UserId=42's contract.
|
||||
|
||||
Defaults to ROLLBACK. Uncomment COMMIT after verifying PRINT counts.
|
||||
*/
|
||||
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
BEGIN TRAN;
|
||||
|
||||
DECLARE @FixUsers TABLE (UserId BIGINT PRIMARY KEY);
|
||||
INSERT INTO @FixUsers (UserId) VALUES
|
||||
(2),(7),(8),(9),(10),(11),(12),(13),(40),(41),(43),(45),(47),
|
||||
(52),(58),(69),(71),(72),(74),(75),(87),(88),(89),(90),(91),(93),
|
||||
(96),(99),(103),(105),(106),(111),(113),(115),(116),(119),(121),
|
||||
(125),(130),(142),(162),(169),(170),(171),(172),(175),(176);
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 1) Users
|
||||
------------------------------------------------------------
|
||||
UPDATE u
|
||||
SET
|
||||
u.PackagePurchaseMethod = 3, -- Manual
|
||||
u.HasReceivedDayaCredit = 0,
|
||||
u.DayaCreditReceivedAt = NULL,
|
||||
u.LastModified = SYSUTCDATETIME()
|
||||
FROM CMS.Users u
|
||||
INNER JOIN @FixUsers f ON f.UserId = u.Id
|
||||
WHERE u.IsDeleted = 0
|
||||
AND (
|
||||
u.PackagePurchaseMethod = 1 -- DayaLoan
|
||||
OR u.HasReceivedDayaCredit = 1
|
||||
OR u.DayaCreditReceivedAt IS NOT NULL
|
||||
);
|
||||
|
||||
PRINT CONCAT('Users updated: ', @@ROWCOUNT);
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 2) ClubMemberships
|
||||
------------------------------------------------------------
|
||||
UPDATE m
|
||||
SET
|
||||
m.PurchaseMethod = 3, -- Manual
|
||||
m.LastModified = SYSUTCDATETIME()
|
||||
FROM CMS.ClubMemberships m
|
||||
INNER JOIN @FixUsers f ON f.UserId = m.UserId
|
||||
WHERE m.IsDeleted = 0
|
||||
AND m.PurchaseMethod IN (0, 1); -- None or DayaLoan
|
||||
|
||||
PRINT CONCAT('ClubMemberships updated: ', @@ROWCOUNT);
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 3) ClubMembershipCycles
|
||||
------------------------------------------------------------
|
||||
UPDATE c
|
||||
SET
|
||||
c.PurchaseMethod = 3, -- Manual
|
||||
c.LastModified = SYSUTCDATETIME()
|
||||
FROM CMS.ClubMembershipCycles c
|
||||
INNER JOIN @FixUsers f ON f.UserId = c.UserId
|
||||
WHERE c.IsDeleted = 0
|
||||
AND c.PurchaseMethod IN (0, 1);
|
||||
|
||||
PRINT CONCAT('ClubMembershipCycles updated: ', @@ROWCOUNT);
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 4) UserPackagePurchases (incl. backfill rows)
|
||||
------------------------------------------------------------
|
||||
UPDATE upp
|
||||
SET
|
||||
upp.PurchaseMethod = 3, -- Manual
|
||||
upp.LastModified = SYSUTCDATETIME()
|
||||
FROM CMS.UserPackagePurchases upp
|
||||
INNER JOIN @FixUsers f ON f.UserId = upp.UserId
|
||||
WHERE upp.IsDeleted = 0
|
||||
AND upp.PurchaseMethod = 1; -- only Daya → Manual (keep Direct=2 if any)
|
||||
|
||||
PRINT CONCAT('UserPackagePurchases updated: ', @@ROWCOUNT);
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 5) Soft-delete stub DayaLoanContracts (never processed)
|
||||
------------------------------------------------------------
|
||||
UPDATE d
|
||||
SET
|
||||
d.IsDeleted = 1,
|
||||
d.LastModified = SYSUTCDATETIME()
|
||||
FROM CMS.DayaLoanContracts d
|
||||
INNER JOIN @FixUsers f ON f.UserId = d.UserId
|
||||
WHERE d.IsDeleted = 0
|
||||
AND d.IsProcessed = 0
|
||||
AND d.TransactionId IS NULL
|
||||
AND d.ProcessedDate IS NULL;
|
||||
|
||||
PRINT CONCAT('DayaLoanContracts soft-deleted (stubs): ', @@ROWCOUNT);
|
||||
|
||||
------------------------------------------------------------
|
||||
-- Verify
|
||||
------------------------------------------------------------
|
||||
SELECT 'Users still Daya' AS CheckName, COUNT(*) AS Cnt
|
||||
FROM CMS.Users u
|
||||
INNER JOIN @FixUsers f ON f.UserId = u.Id
|
||||
WHERE u.IsDeleted = 0 AND u.PackagePurchaseMethod = 1
|
||||
UNION ALL
|
||||
SELECT 'Club still Daya/None', COUNT(*)
|
||||
FROM CMS.ClubMemberships m
|
||||
INNER JOIN @FixUsers f ON f.UserId = m.UserId
|
||||
WHERE m.IsDeleted = 0 AND m.PurchaseMethod IN (0, 1)
|
||||
UNION ALL
|
||||
SELECT 'Cycle still Daya/None', COUNT(*)
|
||||
FROM CMS.ClubMembershipCycles c
|
||||
INNER JOIN @FixUsers f ON f.UserId = c.UserId
|
||||
WHERE c.IsDeleted = 0 AND c.PurchaseMethod IN (0, 1)
|
||||
UNION ALL
|
||||
SELECT 'UPP still Daya', COUNT(*)
|
||||
FROM CMS.UserPackagePurchases upp
|
||||
INNER JOIN @FixUsers f ON f.UserId = upp.UserId
|
||||
WHERE upp.IsDeleted = 0 AND upp.PurchaseMethod = 1
|
||||
UNION ALL
|
||||
SELECT 'Active stub Daya contracts', COUNT(*)
|
||||
FROM CMS.DayaLoanContracts d
|
||||
INNER JOIN @FixUsers f ON f.UserId = d.UserId
|
||||
WHERE d.IsDeleted = 0
|
||||
AND d.IsProcessed = 0
|
||||
AND d.TransactionId IS NULL
|
||||
AND d.ProcessedDate IS NULL;
|
||||
|
||||
-- Sample after
|
||||
SELECT u.Id AS UserId,
|
||||
u.FirstName, u.LastName, u.Mobile,
|
||||
u.PackagePurchaseMethod,
|
||||
m.PurchaseMethod AS ClubPurchaseMethod
|
||||
FROM CMS.Users u
|
||||
INNER JOIN @FixUsers f ON f.UserId = u.Id
|
||||
LEFT JOIN CMS.ClubMemberships m ON m.UserId = u.Id AND m.IsDeleted = 0
|
||||
WHERE u.IsDeleted = 0
|
||||
ORDER BY u.Id;
|
||||
|
||||
-- Uncomment to apply:
|
||||
-- COMMIT;
|
||||
ROLLBACK;
|
||||
PRINT 'Rolled back (safety). Uncomment COMMIT and remove ROLLBACK to apply.';
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
Rollback for Backfill_UserPackagePurchases_FromClubMemberships.sql
|
||||
|
||||
What it undoes:
|
||||
A) 77 synthetic rows: Tx with RefId LIKE 'UPP-BACKFILL-%' + their UPP
|
||||
B) 24 ManualPayment-linked UPP rows inserted by backfill
|
||||
(same TransactionId as Approved ManualPayments — NOT deleting those Transactions)
|
||||
|
||||
BEFORE RUN:
|
||||
Set @BackfillStartedAt to a timestamp slightly BEFORE you ran the backfill
|
||||
(e.g. if you ran at 06:40, use '2026-08-14 06:00:00').
|
||||
Part B only deletes UPP with Created >= that time so later real CreateManualPayment rows stay.
|
||||
|
||||
Safe to preview: leave @Commit = 0 (default) to only PRINT counts inside a rolled-back tran.
|
||||
Set @Commit = 1 to actually apply.
|
||||
*/
|
||||
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
DECLARE @BackfillStartedAt DATETIME2 = '2026-08-14 00:00:00'; -- << adjust
|
||||
DECLARE @Commit BIT = 0; -- 0 = dry-run (rollback), 1 = commit deletes
|
||||
|
||||
BEGIN TRAN;
|
||||
|
||||
------------------------------------------------------------
|
||||
-- A) Synthetic Tx + UPP (RefId marker)
|
||||
------------------------------------------------------------
|
||||
DECLARE @SyntheticUpp TABLE (Id BIGINT PRIMARY KEY, TransactionId BIGINT);
|
||||
|
||||
INSERT INTO @SyntheticUpp (Id, TransactionId)
|
||||
SELECT upp.Id, upp.TransactionId
|
||||
FROM CMS.UserPackagePurchases upp
|
||||
INNER JOIN CMS.Transactions t ON t.Id = upp.TransactionId
|
||||
WHERE t.RefId LIKE N'UPP-BACKFILL-%';
|
||||
|
||||
DECLARE @SynthUppCount INT = (SELECT COUNT(*) FROM @SyntheticUpp);
|
||||
DECLARE @SynthTxCount INT =
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM CMS.Transactions t
|
||||
WHERE t.RefId LIKE N'UPP-BACKFILL-%'
|
||||
);
|
||||
|
||||
DELETE upp
|
||||
FROM CMS.UserPackagePurchases upp
|
||||
INNER JOIN @SyntheticUpp s ON s.Id = upp.Id;
|
||||
|
||||
DELETE t
|
||||
FROM CMS.Transactions t
|
||||
WHERE t.RefId LIKE N'UPP-BACKFILL-%';
|
||||
|
||||
PRINT CONCAT('Deleted synthetic UPP: ', @SynthUppCount);
|
||||
PRINT CONCAT('Deleted synthetic Transactions: ', @SynthTxCount);
|
||||
|
||||
------------------------------------------------------------
|
||||
-- B) UPP created from ManualPayments reuse (no new Tx)
|
||||
------------------------------------------------------------
|
||||
DECLARE @ManualUpp TABLE (Id BIGINT PRIMARY KEY);
|
||||
|
||||
INSERT INTO @ManualUpp (Id)
|
||||
SELECT upp.Id
|
||||
FROM CMS.UserPackagePurchases upp
|
||||
INNER JOIN CMS.ManualPayments mp
|
||||
ON mp.TransactionId = upp.TransactionId
|
||||
AND mp.IsDeleted = 0
|
||||
AND mp.Status = 1 -- Approved
|
||||
WHERE upp.IsDeleted = 0
|
||||
AND upp.PurchaseMethod = 3 -- Manual
|
||||
AND upp.Created >= @BackfillStartedAt
|
||||
-- exclude anything already covered as synthetic (shouldn't overlap)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM CMS.Transactions t
|
||||
WHERE t.Id = upp.TransactionId AND t.RefId LIKE N'UPP-BACKFILL-%'
|
||||
);
|
||||
|
||||
DECLARE @ManualUppCount INT = (SELECT COUNT(*) FROM @ManualUpp);
|
||||
|
||||
DELETE upp
|
||||
FROM CMS.UserPackagePurchases upp
|
||||
INNER JOIN @ManualUpp m ON m.Id = upp.Id;
|
||||
|
||||
PRINT CONCAT('Deleted ManualPayment-linked UPP (Created >= @BackfillStartedAt): ', @ManualUppCount);
|
||||
PRINT CONCAT('@BackfillStartedAt used: ', CONVERT(varchar(30), @BackfillStartedAt, 126));
|
||||
|
||||
------------------------------------------------------------
|
||||
-- Optional sanity after rollback
|
||||
------------------------------------------------------------
|
||||
DECLARE @Orphans 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)
|
||||
)
|
||||
);
|
||||
|
||||
PRINT CONCAT('Club members without successful UPP after rollback: ', @Orphans);
|
||||
PRINT '(Expect around 101 if dump-era state, if no new purchases since.)';
|
||||
|
||||
IF @Commit = 1
|
||||
BEGIN
|
||||
COMMIT;
|
||||
PRINT 'Rollback COMMITTED.';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
ROLLBACK;
|
||||
PRINT 'Dry-run only — ROLLBACK. Set @Commit = 1 after verifying counts.';
|
||||
END
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
Update DayaLoanContracts.ContractNumber from MerchantCreditList.
|
||||
|
||||
Match: NationalCode (unique cases).
|
||||
Special case — same NationalCode 2490371436 on TWO users (two mobiles):
|
||||
UserId=50 (09207316560, Created 2025-11-11) → C4_D8815963 (فعالسازی 1404/8/24 = 2025-11-15)
|
||||
UserId=51 (09366426797, Created 2025-11-12) → C4_G8945289 (فعالسازی 1404/8/30 = 2025-11-21)
|
||||
User 51 may have no DayaLoanContracts row yet → INSERT.
|
||||
|
||||
Script defaults to ROLLBACK; uncomment COMMIT after verifying.
|
||||
*/
|
||||
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
BEGIN TRAN;
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 1) Unique NationalCodes → UPDATE ContractNumber
|
||||
------------------------------------------------------------
|
||||
;WITH Src AS (
|
||||
SELECT *
|
||||
FROM (VALUES
|
||||
(N'C4_C11231802', N'0312980973'),
|
||||
(N'C4_H10706299', N'4060667961'),
|
||||
(N'C4_E10490523', N'4072306754'),
|
||||
(N'C4_C10337736', N'0054140161'),
|
||||
(N'C4_Q10727431', N'5279791253'),
|
||||
(N'C4_W10568862', N'1960144804'),
|
||||
(N'C4_V10338780', N'4072240567'),
|
||||
(N'C4_G10538762', N'6639906902'),
|
||||
(N'C4_N10921338', N'0943060761'),
|
||||
(N'C4_P9320383', N'2572729636'),
|
||||
(N'C4_Y9276146', N'0012011061'),
|
||||
(N'C4_Q9465813', N'2480017796'),
|
||||
(N'C4_A9107753', N'0450314103'),
|
||||
(N'C4_B9348378', N'0452314488'),
|
||||
(N'C4_U9595222', N'2062876815'),
|
||||
(N'C4_X9630520', N'0680164286'),
|
||||
(N'C4_J9199624', N'0681699401'),
|
||||
(N'C4_B9813553', N'0055680364'),
|
||||
(N'C4_M9994735', N'0793851572'),
|
||||
(N'C4_S9301452', N'0082242925'),
|
||||
(N'C4_V9257168', N'4459676451'),
|
||||
(N'C4_T9961063', N'0312342993'),
|
||||
(N'C4_L9524515', N'0024757489'),
|
||||
(N'C4_Q9947535', N'2480259668'),
|
||||
(N'C4_T9892089', N'2451477016'),
|
||||
(N'C4_N9945163', N'1289603669'),
|
||||
(N'C4_W8785358', N'0322922720'),
|
||||
(N'C4_J8388494', N'2002637903'),
|
||||
(N'C4_V8165679', N'0081093829'),
|
||||
(N'C4_T8579002', N'5289780335')
|
||||
-- 2490371436 handled separately by UserId below
|
||||
) AS v(ContractNumber, NationalCode)
|
||||
)
|
||||
UPDATE d
|
||||
SET
|
||||
d.ContractNumber = s.ContractNumber,
|
||||
d.LastModified = SYSUTCDATETIME()
|
||||
FROM CMS.DayaLoanContracts d
|
||||
INNER JOIN Src s ON s.NationalCode = d.NationalCode
|
||||
WHERE d.IsDeleted = 0;
|
||||
|
||||
PRINT CONCAT('Updated by NationalCode (unique): ', @@ROWCOUNT);
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 2) Shared NC 2490371436 — match by UserId + Excel activation date
|
||||
------------------------------------------------------------
|
||||
-- UserId 50 ← earlier activation 1404/8/24 → C4_D8815963
|
||||
UPDATE CMS.DayaLoanContracts
|
||||
SET
|
||||
ContractNumber = N'C4_D8815963',
|
||||
LastModified = SYSUTCDATETIME()
|
||||
WHERE IsDeleted = 0
|
||||
AND UserId = 50
|
||||
AND NationalCode = N'2490371436';
|
||||
|
||||
PRINT CONCAT('Updated UserId=50 (C4_D8815963): ', @@ROWCOUNT);
|
||||
|
||||
-- UserId 51 ← later activation 1404/8/30 → C4_G8945289
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM CMS.DayaLoanContracts
|
||||
WHERE IsDeleted = 0 AND UserId = 51 AND NationalCode = N'2490371436'
|
||||
)
|
||||
BEGIN
|
||||
UPDATE CMS.DayaLoanContracts
|
||||
SET
|
||||
ContractNumber = N'C4_G8945289',
|
||||
LastModified = SYSUTCDATETIME()
|
||||
WHERE IsDeleted = 0
|
||||
AND UserId = 51
|
||||
AND NationalCode = N'2490371436';
|
||||
|
||||
PRINT CONCAT('Updated UserId=51 (C4_G8945289): ', @@ROWCOUNT);
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
INSERT INTO CMS.DayaLoanContracts
|
||||
(
|
||||
UserId, NationalCode, ContractNumber, Status, IsProcessed,
|
||||
LastCheckDate, ProcessedDate, TransactionId,
|
||||
Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
51,
|
||||
N'2490371436',
|
||||
N'C4_G8945289',
|
||||
0, -- NotRequested (same as sibling row for UserId=50 in dump; adjust if needed)
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
SYSUTCDATETIME(),
|
||||
NULL,
|
||||
SYSUTCDATETIME(),
|
||||
NULL,
|
||||
0
|
||||
);
|
||||
|
||||
PRINT 'Inserted DayaLoanContracts for UserId=51 (C4_G8945289).';
|
||||
END
|
||||
|
||||
------------------------------------------------------------
|
||||
-- Verify
|
||||
------------------------------------------------------------
|
||||
SELECT
|
||||
d.Id,
|
||||
d.UserId,
|
||||
u.Mobile,
|
||||
d.NationalCode,
|
||||
d.ContractNumber,
|
||||
d.Status,
|
||||
d.IsProcessed
|
||||
FROM CMS.DayaLoanContracts d
|
||||
INNER JOIN CMS.Users u ON u.Id = d.UserId
|
||||
WHERE d.IsDeleted = 0
|
||||
AND d.NationalCode = N'2490371436'
|
||||
ORDER BY d.UserId;
|
||||
|
||||
-- Uncomment to apply:
|
||||
-- COMMIT;
|
||||
ROLLBACK;
|
||||
PRINT 'Rolled back (safety). Uncomment COMMIT and remove ROLLBACK to apply.';
|
||||
Reference in New Issue
Block a user