Workflow problemleri giderildi.

This commit is contained in:
Sedat ÖZTÜRK 2026-07-08 14:53:23 +03:00
parent 17e4716ee8
commit f4d2be65e5
22 changed files with 517 additions and 128 deletions

View file

@ -17,8 +17,8 @@ public class CustomTenantDto
public string Address1 { get; set; } public string Address1 { get; set; }
public string Address2 { get; set; } public string Address2 { get; set; }
public string PostalCode { get; set; } public string PostalCode { get; set; }
public string MobileNumber { get; set; }
public string PhoneNumber { get; set; } public string PhoneNumber { get; set; }
public string MobileNumber { get; set; }
public string FaxNumber { get; set; } public string FaxNumber { get; set; }
public string Email { get; set; } public string Email { get; set; }
public string Website { get; set; } public string Website { get; set; }

View file

@ -272,6 +272,7 @@ public class ListFormWizardAppService(
input.Workflow ??= new WorkflowDto(); input.Workflow ??= new WorkflowDto();
input.Workflow.Criteria = input.WorkflowCriteria; input.Workflow.Criteria = input.WorkflowCriteria;
EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria); EnsureUniqueWorkflowCriteriaTitles(input.WorkflowCriteria);
NormalizeWorkflowCriteriaIds(listFormCode, input.WorkflowCriteria);
await repoListForm.InsertAsync(new ListForm await repoListForm.InsertAsync(new ListForm
{ {
@ -469,6 +470,55 @@ public class ListFormWizardAppService(
return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim(); return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim();
} }
private static void NormalizeWorkflowCriteriaIds(string listFormCode, List<ListFormWorkflowCriteriaDto> criteria)
{
if (string.IsNullOrWhiteSpace(listFormCode) || criteria == null || criteria.Count == 0)
{
return;
}
foreach (var item in criteria)
{
item.Id = NormalizeWorkflowCriteriaId(listFormCode, item.Id);
item.ListFormCode = listFormCode;
item.NextOnStart = NormalizeWorkflowTargetId(listFormCode, item.NextOnStart);
item.NextOnTrue = NormalizeWorkflowTargetId(listFormCode, item.NextOnTrue);
item.NextOnFalse = NormalizeWorkflowTargetId(listFormCode, item.NextOnFalse);
item.NextOnApprove = NormalizeWorkflowTargetId(listFormCode, item.NextOnApprove);
item.NextOnReject = NormalizeWorkflowTargetId(listFormCode, item.NextOnReject);
foreach (var outcome in item.CompareOutcomes ?? [])
{
outcome.TargetId = NormalizeWorkflowTargetId(listFormCode, outcome.TargetId);
}
}
}
private static string NormalizeWorkflowTargetId(string listFormCode, string id)
{
return string.IsNullOrWhiteSpace(id)
? string.Empty
: NormalizeWorkflowCriteriaId(listFormCode, id);
}
private static string NormalizeWorkflowCriteriaId(string listFormCode, string id)
{
var trimmed = id?.Trim();
if (string.IsNullOrWhiteSpace(trimmed))
{
return string.Empty;
}
if (trimmed.StartsWith($"{listFormCode}-", StringComparison.OrdinalIgnoreCase))
{
return trimmed;
}
return trimmed.StartsWith("N", StringComparison.OrdinalIgnoreCase)
? $"{listFormCode}-{trimmed}"
: trimmed;
}
/// <summary> /// <summary>
/// Wizard konfigürasyonunu JSON dosyası olarak kaydeder. /// Wizard konfigürasyonunu JSON dosyası olarak kaydeder.
/// Önce ContentRootPath'ten yukarı çıkarak Sozsoft.Platform.DbMigrator/Seeds/WizardData dizinini arar. /// Önce ContentRootPath'ten yukarı çıkarak Sozsoft.Platform.DbMigrator/Seeds/WizardData dizinini arar.

View file

@ -186,7 +186,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
var code = input.ListFormCode; var code = input.ListFormCode;
var isNew = input.Id.IsNullOrWhiteSpace(); var isNew = input.Id.IsNullOrWhiteSpace();
var criteria = isNew var criteria = isNew
? new ListFormWorkflow(await GenerateNextCriteriaIdAsync()) ? new ListFormWorkflow(await GenerateNextCriteriaIdAsync(code))
: await criteriaRepository.GetAsync(input.Id); : await criteriaRepository.GetAsync(input.Id);
if (!isNew && criteria.ListFormCode != code) if (!isNew && criteria.ListFormCode != code)
@ -201,16 +201,16 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
criteria.CompareOperator = NormalizeRequired(input.CompareOperator, ">"); criteria.CompareOperator = NormalizeRequired(input.CompareOperator, ">");
criteria.CompareValue = input.CompareValue; criteria.CompareValue = input.CompareValue;
criteria.Approver = input.Approver ?? string.Empty; criteria.Approver = input.Approver ?? string.Empty;
criteria.NextOnStart = input.NextOnStart ?? string.Empty; criteria.NextOnStart = NormalizeCriteriaTargetId(code, input.NextOnStart);
criteria.NextOnTrue = input.NextOnTrue ?? string.Empty; criteria.NextOnTrue = NormalizeCriteriaTargetId(code, input.NextOnTrue);
criteria.NextOnFalse = input.NextOnFalse ?? string.Empty; criteria.NextOnFalse = NormalizeCriteriaTargetId(code, input.NextOnFalse);
criteria.NextOnApprove = input.NextOnApprove ?? string.Empty; criteria.NextOnApprove = NormalizeCriteriaTargetId(code, input.NextOnApprove);
criteria.NextOnReject = input.NextOnReject ?? string.Empty; criteria.NextOnReject = NormalizeCriteriaTargetId(code, input.NextOnReject);
criteria.PositionX = input.PositionX <= 0 ? 32 : input.PositionX; criteria.PositionX = input.PositionX <= 0 ? 32 : input.PositionX;
criteria.PositionY = input.PositionY <= 0 ? 150 : input.PositionY; criteria.PositionY = input.PositionY <= 0 ? 150 : input.PositionY;
criteria.CompareOutcomesJson = SerializeCompareOutcomes(input.CompareOutcomes); var outcomes = NormalizeCompareOutcomeTargets(code, input.CompareOutcomes);
criteria.CompareOutcomesJson = SerializeCompareOutcomes(outcomes);
var outcomes = input.CompareOutcomes ?? [];
if (criteria.Kind == "Compare" && outcomes.Count > 0) if (criteria.Kind == "Compare" && outcomes.Count > 0)
{ {
criteria.NextOnTrue = outcomes.ElementAtOrDefault(0)?.TargetId ?? criteria.NextOnTrue; criteria.NextOnTrue = outcomes.ElementAtOrDefault(0)?.TargetId ?? criteria.NextOnTrue;
@ -1107,7 +1107,7 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
int positionY, int positionY,
string approver = "") string approver = "")
{ {
var criteria = new ListFormWorkflow(await GenerateNextCriteriaIdAsync()) var criteria = new ListFormWorkflow(await GenerateNextCriteriaIdAsync(listFormCode))
{ {
ListFormCode = listFormCode, ListFormCode = listFormCode,
Kind = kind, Kind = kind,
@ -1130,41 +1130,95 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA
return criteria; return criteria;
} }
private async Task<string> GenerateNextCriteriaIdAsync() private async Task<string> GenerateNextCriteriaIdAsync(string listFormCode)
{ {
var criteria = await criteriaRepository.GetListAsync(); var criteria = await criteriaRepository.GetListAsync(x => x.ListFormCode == listFormCode);
var maxNumber = criteria var maxNumber = criteria
.Select(x => TryParseCriteriaIdNumber(x.Id)) .Select(x => TryParseCriteriaIdNumber(listFormCode, x.Id))
.DefaultIfEmpty(0) .DefaultIfEmpty(0)
.Max(); .Max();
var nextNumber = maxNumber + 1; var nextNumber = maxNumber + 1;
var nextId = FormatCriteriaId(nextNumber); var nextId = FormatCriteriaId(listFormCode, nextNumber);
var existingIds = criteria.Select(x => x.Id).ToHashSet(); var existingIds = criteria.Select(x => x.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
while (existingIds.Contains(nextId)) while (existingIds.Contains(nextId))
{ {
nextNumber++; nextNumber++;
nextId = FormatCriteriaId(nextNumber); nextId = FormatCriteriaId(listFormCode, nextNumber);
} }
return nextId; return nextId;
} }
private static int TryParseCriteriaIdNumber(string id) private static int TryParseCriteriaIdNumber(string listFormCode, string id)
{ {
if (id.IsNullOrWhiteSpace() || var localId = GetLocalCriteriaId(listFormCode, id);
!id.StartsWith(CriteriaIdPrefix, StringComparison.OrdinalIgnoreCase)) if (localId.IsNullOrWhiteSpace() ||
!localId.StartsWith(CriteriaIdPrefix, StringComparison.OrdinalIgnoreCase))
{ {
return 0; return 0;
} }
return int.TryParse(id[CriteriaIdPrefix.Length..], out var number) ? number : 0; return int.TryParse(localId[CriteriaIdPrefix.Length..], out var number) ? number : 0;
} }
private static string FormatCriteriaId(int number) private static string FormatCriteriaId(string listFormCode, int number)
{ {
return $"{CriteriaIdPrefix}{number.ToString().PadLeft(CriteriaIdPadding, '0')}"; return $"{listFormCode}-{CriteriaIdPrefix}{number.ToString().PadLeft(CriteriaIdPadding, '0')}";
}
private static string NormalizeCriteriaTargetId(string listFormCode, string id)
{
return id.IsNullOrWhiteSpace()
? string.Empty
: NormalizeCriteriaId(listFormCode, id);
}
private static List<CompareOutcomeDto> NormalizeCompareOutcomeTargets(
string listFormCode,
List<CompareOutcomeDto> outcomes)
{
return (outcomes ?? [])
.Select(outcome => new CompareOutcomeDto
{
Label = outcome.Label,
TargetId = NormalizeCriteriaTargetId(listFormCode, outcome.TargetId),
Conditions = outcome.Conditions ?? []
})
.ToList();
}
private static string NormalizeCriteriaId(string listFormCode, string id)
{
var trimmed = id?.Trim();
if (trimmed.IsNullOrWhiteSpace())
{
return string.Empty;
}
if (trimmed.StartsWith($"{listFormCode}-", StringComparison.OrdinalIgnoreCase))
{
return trimmed;
}
return trimmed.StartsWith(CriteriaIdPrefix, StringComparison.OrdinalIgnoreCase)
? $"{listFormCode}-{trimmed}"
: trimmed;
}
private static string GetLocalCriteriaId(string listFormCode, string id)
{
var normalized = id?.Trim();
if (normalized.IsNullOrWhiteSpace())
{
return string.Empty;
}
var prefix = $"{listFormCode}-";
return normalized.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
? normalized[prefix.Length..]
: normalized;
} }
private static bool ClearDeletedTarget(ListFormWorkflow criteria, string deletedId) private static bool ClearDeletedTarget(ListFormWorkflow criteria, string deletedId)

View file

@ -8112,6 +8112,12 @@
"tr": "Referans", "tr": "Referans",
"en": "Reference" "en": "Reference"
}, },
{
"resourceName": "Platform",
"key": "Public.payment.customer.website",
"tr": "Web Sitesi",
"en": "Website"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "Public.payment.method.title", "key": "Public.payment.method.title",
@ -17118,6 +17124,30 @@
"en": "Approval or Rejection Comment", "en": "Approval or Rejection Comment",
"tr": "Onay veya red açıklaması" "tr": "Onay veya red açıklaması"
}, },
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.ApprovalUserName",
"en": "Approval User Name",
"tr": "Onaylayan Kullanıcı Adı"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.ApprovalStatus",
"en": "Approval Status",
"tr": "Onay Durumu"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.ApprovalDate",
"en": "Approval Date",
"tr": "Onay Tarihi"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.ApprovalDescription",
"en": "Approval Description",
"tr": "Onay Açıklaması"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Listform.ListformField.WorkflowDecisionMessage", "key": "App.Listform.ListformField.WorkflowDecisionMessage",

View file

@ -209,11 +209,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
HeaderFilterJson = DefaultHeaderFilterJson(), HeaderFilterJson = DefaultHeaderFilterJson(),
SearchPanelJson = DefaultSearchPanelJson(), SearchPanelJson = DefaultSearchPanelJson(),
GroupPanelJson = DefaultGroupPanelJson(), GroupPanelJson = DefaultGroupPanelJson(),
SelectionJson = JsonSerializer.Serialize(new SelectionDto SelectionJson = DefaultSelectionMultipleJson,
{
Mode = GridOptions.SelectionAllModeAllPages,
AllowSelectAll = true
}),
ColumnOptionJson = DefaultColumnOptionJson(), ColumnOptionJson = DefaultColumnOptionJson(),
PermissionJson = DefaultPermissionJson(AbpIdentity.Permissions.Create, listFormName, AbpIdentity.Permissions.Update, AbpIdentity.Permissions.Delete, AbpIdentity.Permissions.Export, AbpIdentity.Permissions.Import, AbpIdentity.Permissions.Note), PermissionJson = DefaultPermissionJson(AbpIdentity.Permissions.Create, listFormName, AbpIdentity.Permissions.Update, AbpIdentity.Permissions.Delete, AbpIdentity.Permissions.Export, AbpIdentity.Permissions.Import, AbpIdentity.Permissions.Note),
PagerOptionJson = DefaultPagerOptionJson, PagerOptionJson = DefaultPagerOptionJson,

View file

@ -84,7 +84,7 @@ public static class ListFormSeeder_DefaultJsons
}); });
public static readonly string DefaultSelectionSingleJson = JsonSerializer.Serialize(new SelectionDto public static readonly string DefaultSelectionSingleJson = JsonSerializer.Serialize(new SelectionDto
{ {
Mode = GridOptions.SelectionModeNone, Mode = GridOptions.SelectionModeSingle,
AllowSelectAll = false AllowSelectAll = false
}); });
public static readonly string DefaultSelectionMultipleJson = JsonSerializer.Serialize(new SelectionDto public static readonly string DefaultSelectionMultipleJson = JsonSerializer.Serialize(new SelectionDto

View file

@ -26,6 +26,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
{ {
private readonly IRepository<ListForm, Guid> _listFormRepository; private readonly IRepository<ListForm, Guid> _listFormRepository;
private readonly IRepository<ListFormField, Guid> _listFormFieldRepository; private readonly IRepository<ListFormField, Guid> _listFormFieldRepository;
private readonly IRepository<ListFormWorkflow, string> _listFormWorkflowRepository;
private readonly IdentityUserManager _identityUserManager; private readonly IdentityUserManager _identityUserManager;
private readonly IdentityRoleManager _identityRoleManager; private readonly IdentityRoleManager _identityRoleManager;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
@ -34,6 +35,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
public ListFormSeeder_Saas( public ListFormSeeder_Saas(
IRepository<ListForm, Guid> listFormRepository, IRepository<ListForm, Guid> listFormRepository,
IRepository<ListFormField, Guid> listFormFieldRepository, IRepository<ListFormField, Guid> listFormFieldRepository,
IRepository<ListFormWorkflow, string> listFormWorkflowRepository,
IdentityUserManager userManager, IdentityUserManager userManager,
IdentityRoleManager roleManager, IdentityRoleManager roleManager,
IConfiguration configuration, IConfiguration configuration,
@ -42,6 +44,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
{ {
_listFormRepository = listFormRepository; _listFormRepository = listFormRepository;
_listFormFieldRepository = listFormFieldRepository; _listFormFieldRepository = listFormFieldRepository;
_listFormWorkflowRepository = listFormWorkflowRepository;
_identityUserManager = userManager; _identityUserManager = userManager;
_identityRoleManager = roleManager; _identityRoleManager = roleManager;
_configuration = configuration; _configuration = configuration;
@ -452,15 +455,15 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
ListFormCode = listForm.ListFormCode, ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.String, SourceDbType = DbType.String,
FieldName = "MobileNumber", FieldName = "PhoneNumber",
CaptionName = "App.Listform.ListformField.MobileNumber", CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
Width = 0, Width = 0,
ListOrderNo = 16, ListOrderNo = 16,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
AllowSearch = true, AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
EditorOptions = EditorOptionValues.PhoneEditorOptions, EditorOptions = EditorOptionValues.PhoneEditorOptions,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(TenantManagementPermissions.Tenants.Create, TenantManagementPermissions.Tenants.Default, TenantManagementPermissions.Tenants.Update, true, true, false), PermissionJson = DefaultFieldPermissionJson(TenantManagementPermissions.Tenants.Create, TenantManagementPermissions.Tenants.Default, TenantManagementPermissions.Tenants.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson PivotSettingsJson = DefaultPivotSettingsJson
@ -470,8 +473,8 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
ListFormCode = listForm.ListFormCode, ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.String, SourceDbType = DbType.String,
FieldName = "PhoneNumber", FieldName = "MobileNumber",
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber", CaptionName = "App.Listform.ListformField.MobileNumber",
Width = 0, Width = 0,
ListOrderNo = 17, ListOrderNo = 17,
Visible = true, Visible = true,
@ -938,16 +941,16 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
ListFormCode = listForm.ListFormCode, ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.String, SourceDbType = DbType.String,
FieldName = "MobileNumber", FieldName = "PhoneNumber",
CaptionName = "App.Listform.ListformField.MobileNumber", CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
Width = 0, Width = 0,
ListOrderNo = 15, ListOrderNo = 15,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
AllowSearch = true, AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
EditorOptions = EditorOptionValues.PhoneEditorOptions, EditorOptions = EditorOptionValues.PhoneEditorOptions,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name), PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson PivotSettingsJson = DefaultPivotSettingsJson
@ -957,8 +960,8 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
ListFormCode = listForm.ListFormCode, ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.String, SourceDbType = DbType.String,
FieldName = "PhoneNumber", FieldName = "MobileNumber",
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber", CaptionName = "App.Listform.ListformField.MobileNumber",
Width = 0, Width = 0,
ListOrderNo = 16, ListOrderNo = 16,
Visible = true, Visible = true,
@ -6987,7 +6990,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
orderId = "@Id" orderId = "@Id"
}), }),
IsVisible = true, IsVisible = true,
VisibleExpression = "(e) => !e.row?.data?.TenantId", VisibleExpression = "(e) => !e.row?.data?.TenantId && e.row?.data?.ApprovalStatus === 'Approved'",
}, },
}), }),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto> EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
@ -7051,8 +7054,25 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
} }
} }
}, },
}) }),
}); WorkflowJson = JsonSerializer.Serialize(new WorkflowDto
{
ApprovalUserFieldName = "ApprovalUserName",
ApprovalStatusFieldName = "ApprovalStatus",
ApprovalDateFieldName = "ApprovalDate",
ApprovalDescriptionFieldName = "ApprovalDescription",
Criteria = []
}),
}, autoSave: true);
#region Sales Order Workflow
await _listFormWorkflowRepository.InsertManyAsync([
new ListFormWorkflow($"{listFormName}-N001") { ListFormCode = listFormName, Kind = "Start", Title = "İş Akışı Başlat1", CompareColumn="Price", CompareOperator=">", CompareValue=5000, Approver= "", NextOnStart = $"{listFormName}-N002", NextOnTrue = "", NextOnFalse = "", NextOnApprove= "", NextOnReject = "", PositionX = 47, PositionY = 76 },
new ListFormWorkflow($"{listFormName}-N002") { ListFormCode = listFormName, Kind = "Approval", Title = "Onay1", CompareColumn="Price", CompareOperator=">", CompareValue=5000, Approver = "system@sozsoft.com", NextOnStart = "", NextOnTrue = "", NextOnFalse = "", NextOnApprove = $"{listFormName}-N003", NextOnReject = $"{listFormName}-N004", PositionX = 391, PositionY = 76 },
new ListFormWorkflow($"{listFormName}-N003") { ListFormCode = listFormName, Kind = "End", Title = "Approved", CompareColumn="Price", CompareOperator=">", CompareValue=5000, Approver= "", NextOnStart = "", NextOnTrue = "", NextOnFalse = "", NextOnApprove = "", NextOnReject = "", PositionX = 843, PositionY = 61 },
new ListFormWorkflow($"{listFormName}-N004") { ListFormCode = listFormName, Kind = "End", Title = "Rejected", CompareColumn="Price", CompareOperator=">", CompareValue=5000, Approver= "", NextOnStart = "", NextOnTrue = "", NextOnFalse = "", NextOnApprove = "", NextOnReject = "", PositionX = 845, PositionY = 263 },
]);
#endregion
#region Sales Order Fields #region Sales Order Fields
await _listFormFieldRepository.InsertManyAsync([ await _listFormFieldRepository.InsertManyAsync([
@ -7091,7 +7111,6 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
LookupQuery = LookupQueryValues.TenantValues(), LookupQuery = LookupQueryValues.TenantValues(),
}), }),
AllowSearch = true, AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name), PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson, PivotSettingsJson = DefaultPivotSettingsJson,
@ -7441,7 +7460,6 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
AllowSearch = true, AllowSearch = true,
EditorOptions = EditorOptionValues.PhoneEditorOptions, EditorOptions = EditorOptionValues.PhoneEditorOptions,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name), PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson, PivotSettingsJson = DefaultPivotSettingsJson,
@ -7616,6 +7634,8 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name), PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson, PivotSettingsJson = DefaultPivotSettingsJson,
AllowAdding = false,
AllowEditing = false,
}, },
new() new()
{ {
@ -7652,6 +7672,74 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name), PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson, PivotSettingsJson = DefaultPivotSettingsJson,
},
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "ApprovalUserName",
CaptionName = "App.Listform.ListformField.ApprovalUserName",
Width = 0,
ListOrderNo = 31,
Visible = true,
IsActive = true,
AllowSearch = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson,
},
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "ApprovalStatus",
CaptionName = "App.Listform.ListformField.ApprovalStatus",
Width = 0,
ListOrderNo = 32,
Visible = true,
IsActive = true,
AllowSearch = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson,
},
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.DateTime,
FieldName = "ApprovalDate",
CaptionName = "App.Listform.ListformField.ApprovalDate",
Width = 0,
ListOrderNo = 33,
Visible = true,
IsActive = true,
AllowSearch = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson,
},
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "ApprovalDescription",
CaptionName = "App.Listform.ListformField.ApprovalDescription",
Width = 0,
ListOrderNo = 34,
Visible = true,
IsActive = true,
AllowSearch = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson,
} }
]); ]);
#endregion #endregion
@ -7907,6 +7995,23 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
EditorOptions=EditorOptionValues.NumberStandartFormat(), EditorOptions=EditorOptionValues.NumberStandartFormat(),
TotalSummaryJson = DefaultColumnTotalSummaryJson("TotalPrice") TotalSummaryJson = DefaultColumnTotalSummaryJson("TotalPrice")
}, },
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid,
FieldName = "OrderId",
CaptionName = "App.Listform.ListformField.OrderId",
Width = 0,
ListOrderNo = 10,
Visible = false,
IsActive = true,
AllowSearch = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson,
}
], autoSave: true); ], autoSave: true);
#endregion #endregion
} }

View file

@ -148,6 +148,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode) var listFormCode = string.IsNullOrWhiteSpace(input.ListFormCode)
? code ? code
: input.ListFormCode.Trim(); : input.ListFormCode.Trim();
NormalizeWorkflowCriteriaIds(listFormCode, input.WorkflowCriteria);
var titleLangKey = $"{listFormCode}.Title"; var titleLangKey = $"{listFormCode}.Title";
var nameLangKey = code; var nameLangKey = code;
var descLangKey = $"{listFormCode}.Desc"; var descLangKey = $"{listFormCode}.Desc";
@ -559,6 +560,55 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim(); return string.IsNullOrWhiteSpace(kind) ? "Step" : kind.Trim();
} }
private static void NormalizeWorkflowCriteriaIds(string listFormCode, List<ListFormWorkflowCriteriaDto> criteria)
{
if (string.IsNullOrWhiteSpace(listFormCode) || criteria == null || criteria.Count == 0)
{
return;
}
foreach (var item in criteria)
{
item.Id = NormalizeWorkflowCriteriaId(listFormCode, item.Id);
item.ListFormCode = listFormCode;
item.NextOnStart = NormalizeWorkflowTargetId(listFormCode, item.NextOnStart);
item.NextOnTrue = NormalizeWorkflowTargetId(listFormCode, item.NextOnTrue);
item.NextOnFalse = NormalizeWorkflowTargetId(listFormCode, item.NextOnFalse);
item.NextOnApprove = NormalizeWorkflowTargetId(listFormCode, item.NextOnApprove);
item.NextOnReject = NormalizeWorkflowTargetId(listFormCode, item.NextOnReject);
foreach (var outcome in item.CompareOutcomes ?? [])
{
outcome.TargetId = NormalizeWorkflowTargetId(listFormCode, outcome.TargetId);
}
}
}
private static string NormalizeWorkflowTargetId(string listFormCode, string id)
{
return string.IsNullOrWhiteSpace(id)
? string.Empty
: NormalizeWorkflowCriteriaId(listFormCode, id);
}
private static string NormalizeWorkflowCriteriaId(string listFormCode, string id)
{
var trimmed = id?.Trim();
if (string.IsNullOrWhiteSpace(trimmed))
{
return string.Empty;
}
if (trimmed.StartsWith($"{listFormCode}-", StringComparison.OrdinalIgnoreCase))
{
return trimmed;
}
return trimmed.StartsWith("N", StringComparison.OrdinalIgnoreCase)
? $"{listFormCode}-{trimmed}"
: trimmed;
}
private async Task CreateLangKeyAsync(string key, string textEn, string textTr) private async Task CreateLangKeyAsync(string key, string textEn, string textTr)
{ {
if (string.IsNullOrWhiteSpace(key)) return; if (string.IsNullOrWhiteSpace(key)) return;

View file

@ -42,6 +42,11 @@ public class Order : FullAuditedEntity<Guid>
public int? Installment { get; set; } public int? Installment { get; set; }
public string PaymentDataJson { get; set; } // JSON olarak saklanacak public string PaymentDataJson { get; set; } // JSON olarak saklanacak
public string ApprovalUserName { get; set; }
public string ApprovalStatus { get; set; }
public DateTime? ApprovalDate { get; set; }
public string ApprovalDescription { get; set; }
public List<OrderItem> Items { get; set; } = new(); public List<OrderItem> Items { get; set; } = new();
public Order() public Order()

View file

@ -406,6 +406,7 @@ public class PlatformDbContext :
b.HasMany<ListFormField>().WithOne().HasForeignKey(x => x.ListFormCode).HasPrincipalKey(x => x.ListFormCode).IsRequired(); b.HasMany<ListFormField>().WithOne().HasForeignKey(x => x.ListFormCode).HasPrincipalKey(x => x.ListFormCode).IsRequired();
b.HasMany<ListFormCustomization>().WithOne().HasForeignKey(x => x.ListFormCode).HasPrincipalKey(x => x.ListFormCode).IsRequired(); b.HasMany<ListFormCustomization>().WithOne().HasForeignKey(x => x.ListFormCode).HasPrincipalKey(x => x.ListFormCode).IsRequired();
b.HasMany<ListFormImport>().WithOne().HasForeignKey(x => x.ListFormCode).HasPrincipalKey(x => x.ListFormCode).IsRequired(); b.HasMany<ListFormImport>().WithOne().HasForeignKey(x => x.ListFormCode).HasPrincipalKey(x => x.ListFormCode).IsRequired();
b.HasMany<ListFormWorkflow>().WithOne().HasForeignKey(x => x.ListFormCode).HasPrincipalKey(x => x.ListFormCode).IsRequired();
}); });
builder.Entity<ListFormField>(b => builder.Entity<ListFormField>(b =>
@ -498,7 +499,7 @@ public class PlatformDbContext :
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ListFormWorkflow)), Prefix.DbSchema); b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ListFormWorkflow)), Prefix.DbSchema);
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.Id).HasMaxLength(50); b.Property(x => x.Id).HasMaxLength(128);
b.Property(x => x.ListFormCode).IsRequired().HasMaxLength(64); b.Property(x => x.ListFormCode).IsRequired().HasMaxLength(64);
b.Property(x => x.Kind).IsRequired().HasMaxLength(50); b.Property(x => x.Kind).IsRequired().HasMaxLength(50);
b.Property(x => x.Title).IsRequired().HasMaxLength(250); b.Property(x => x.Title).IsRequired().HasMaxLength(250);
@ -506,15 +507,14 @@ public class PlatformDbContext :
b.Property(x => x.CompareOperator).IsRequired().HasMaxLength(20); b.Property(x => x.CompareOperator).IsRequired().HasMaxLength(20);
b.Property(x => x.CompareValue).HasPrecision(18, 2); b.Property(x => x.CompareValue).HasPrecision(18, 2);
b.Property(x => x.Approver).IsRequired().HasMaxLength(250); b.Property(x => x.Approver).IsRequired().HasMaxLength(250);
b.Property(x => x.NextOnStart).IsRequired().HasMaxLength(50); b.Property(x => x.NextOnStart).IsRequired().HasMaxLength(128);
b.Property(x => x.NextOnTrue).IsRequired().HasMaxLength(50); b.Property(x => x.NextOnTrue).IsRequired().HasMaxLength(128);
b.Property(x => x.NextOnFalse).IsRequired().HasMaxLength(50); b.Property(x => x.NextOnFalse).IsRequired().HasMaxLength(128);
b.Property(x => x.NextOnApprove).IsRequired().HasMaxLength(50); b.Property(x => x.NextOnApprove).IsRequired().HasMaxLength(128);
b.Property(x => x.NextOnReject).IsRequired().HasMaxLength(50); b.Property(x => x.NextOnReject).IsRequired().HasMaxLength(128);
b.Property(x => x.PositionX).IsRequired(); b.Property(x => x.PositionX).IsRequired();
b.Property(x => x.PositionY).IsRequired(); b.Property(x => x.PositionY).IsRequired();
b.Property(x => x.CompareOutcomesJson).HasColumnType("text"); b.Property(x => x.CompareOutcomesJson).HasColumnType("text");
b.HasIndex(x => new { x.ListFormCode, x.Title }).IsUnique();
}); });
builder.Entity<Note>(b => builder.Entity<Note>(b =>
@ -1047,6 +1047,10 @@ public class PlatformDbContext :
b.Property(o => o.PaymentDataJson).HasMaxLength(4096); b.Property(o => o.PaymentDataJson).HasMaxLength(4096);
b.Property(o => o.ApprovalUserName).HasMaxLength(256);
b.Property(o => o.ApprovalStatus).HasMaxLength(64);
b.Property(o => o.ApprovalDescription).HasMaxLength(256);
b.HasMany(o => o.Items) b.HasMany(o => o.Items)
.WithOne(i => i.Order) .WithOne(i => i.Order)
.HasForeignKey(i => i.OrderId) .HasForeignKey(i => i.OrderId)

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations namespace Sozsoft.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20260707205022_Initial")] [Migration("20260708113003_Initial")]
partial class Initial partial class Initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -3652,8 +3652,8 @@ namespace Sozsoft.Platform.Migrations
modelBuilder.Entity("Sozsoft.Platform.Entities.ListFormWorkflow", b => modelBuilder.Entity("Sozsoft.Platform.Entities.ListFormWorkflow", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id")
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("Approver") b.Property<string>("Approver")
.IsRequired() .IsRequired()
@ -3689,28 +3689,28 @@ namespace Sozsoft.Platform.Migrations
b.Property<string>("NextOnApprove") b.Property<string>("NextOnApprove")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("NextOnFalse") b.Property<string>("NextOnFalse")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("NextOnReject") b.Property<string>("NextOnReject")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("NextOnStart") b.Property<string>("NextOnStart")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("NextOnTrue") b.Property<string>("NextOnTrue")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<int>("PositionX") b.Property<int>("PositionX")
.HasColumnType("int"); .HasColumnType("int");
@ -3725,8 +3725,7 @@ namespace Sozsoft.Platform.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("ListFormCode", "Title") b.HasIndex("ListFormCode");
.IsUnique();
b.ToTable("Sas_H_ListFormWorkflow", (string)null); b.ToTable("Sas_H_ListFormWorkflow", (string)null);
}); });
@ -4088,6 +4087,21 @@ namespace Sozsoft.Platform.Migrations
.HasMaxLength(512) .HasMaxLength(512)
.HasColumnType("nvarchar(512)"); .HasColumnType("nvarchar(512)");
b.Property<DateTime?>("ApprovalDate")
.HasColumnType("datetime2");
b.Property<string>("ApprovalDescription")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ApprovalStatus")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ApprovalUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("BillingCycle") b.Property<string>("BillingCycle")
.HasMaxLength(32) .HasMaxLength(32)
.HasColumnType("nvarchar(32)"); .HasColumnType("nvarchar(32)");
@ -8728,6 +8742,16 @@ namespace Sozsoft.Platform.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("Sozsoft.Platform.Entities.ListFormWorkflow", b =>
{
b.HasOne("Sozsoft.Platform.Entities.ListForm", null)
.WithMany()
.HasForeignKey("ListFormCode")
.HasPrincipalKey("ListFormCode")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Sozsoft.Platform.Entities.MessengerConversationMessage", b => modelBuilder.Entity("Sozsoft.Platform.Entities.MessengerConversationMessage", b =>
{ {
b.HasOne("Sozsoft.Platform.Entities.MessengerConversation", "Conversation") b.HasOne("Sozsoft.Platform.Entities.MessengerConversation", "Conversation")

View file

@ -1465,32 +1465,6 @@ namespace Sozsoft.Platform.Migrations
table.UniqueConstraint("AK_Sas_H_ListForm_ListFormCode", x => x.ListFormCode); table.UniqueConstraint("AK_Sas_H_ListForm_ListFormCode", x => x.ListFormCode);
}); });
migrationBuilder.CreateTable(
name: "Sas_H_ListFormWorkflow",
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
ListFormCode = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Kind = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Title = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
CompareColumn = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
CompareOperator = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
CompareValue = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
Approver = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
NextOnStart = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
NextOnTrue = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
NextOnFalse = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
NextOnApprove = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
NextOnReject = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
PositionX = table.Column<int>(type: "int", nullable: false),
PositionY = table.Column<int>(type: "int", nullable: false),
CompareOutcomesJson = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Sas_H_ListFormWorkflow", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Sas_H_LogEntry", name: "Sas_H_LogEntry",
columns: table => new columns: table => new
@ -1601,6 +1575,10 @@ namespace Sozsoft.Platform.Migrations
PaymentMethodId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), PaymentMethodId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Installment = table.Column<int>(type: "int", nullable: true), Installment = table.Column<int>(type: "int", nullable: true),
PaymentDataJson = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true), PaymentDataJson = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true),
ApprovalUserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ApprovalStatus = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
ApprovalDate = table.Column<DateTime>(type: "datetime2", nullable: true),
ApprovalDescription = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false), CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true), LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
@ -2852,6 +2830,38 @@ namespace Sozsoft.Platform.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "Sas_H_ListFormWorkflow",
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ListFormCode = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Kind = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Title = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
CompareColumn = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
CompareOperator = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
CompareValue = table.Column<decimal>(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false),
Approver = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
NextOnStart = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
NextOnTrue = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
NextOnFalse = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
NextOnApprove = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
NextOnReject = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
PositionX = table.Column<int>(type: "int", nullable: false),
PositionY = table.Column<int>(type: "int", nullable: false),
CompareOutcomesJson = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Sas_H_ListFormWorkflow", x => x.Id);
table.ForeignKey(
name: "FK_Sas_H_ListFormWorkflow_Sas_H_ListForm_ListFormCode",
column: x => x.ListFormCode,
principalTable: "Sas_H_ListForm",
principalColumn: "ListFormCode",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Sas_H_NotificationRule", name: "Sas_H_NotificationRule",
columns: table => new columns: table => new
@ -4094,10 +4104,9 @@ namespace Sozsoft.Platform.Migrations
column: "ImportId"); column: "ImportId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Sas_H_ListFormWorkflow_ListFormCode_Title", name: "IX_Sas_H_ListFormWorkflow_ListFormCode",
table: "Sas_H_ListFormWorkflow", table: "Sas_H_ListFormWorkflow",
columns: new[] { "ListFormCode", "Title" }, column: "ListFormCode");
unique: true);
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Sas_H_Menu_Code", name: "IX_Sas_H_Menu_Code",

View file

@ -3649,8 +3649,8 @@ namespace Sozsoft.Platform.Migrations
modelBuilder.Entity("Sozsoft.Platform.Entities.ListFormWorkflow", b => modelBuilder.Entity("Sozsoft.Platform.Entities.ListFormWorkflow", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id")
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("Approver") b.Property<string>("Approver")
.IsRequired() .IsRequired()
@ -3686,28 +3686,28 @@ namespace Sozsoft.Platform.Migrations
b.Property<string>("NextOnApprove") b.Property<string>("NextOnApprove")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("NextOnFalse") b.Property<string>("NextOnFalse")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("NextOnReject") b.Property<string>("NextOnReject")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("NextOnStart") b.Property<string>("NextOnStart")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<string>("NextOnTrue") b.Property<string>("NextOnTrue")
.IsRequired() .IsRequired()
.HasMaxLength(50) .HasMaxLength(128)
.HasColumnType("nvarchar(50)"); .HasColumnType("nvarchar(128)");
b.Property<int>("PositionX") b.Property<int>("PositionX")
.HasColumnType("int"); .HasColumnType("int");
@ -3722,8 +3722,7 @@ namespace Sozsoft.Platform.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("ListFormCode", "Title") b.HasIndex("ListFormCode");
.IsUnique();
b.ToTable("Sas_H_ListFormWorkflow", (string)null); b.ToTable("Sas_H_ListFormWorkflow", (string)null);
}); });
@ -4085,6 +4084,21 @@ namespace Sozsoft.Platform.Migrations
.HasMaxLength(512) .HasMaxLength(512)
.HasColumnType("nvarchar(512)"); .HasColumnType("nvarchar(512)");
b.Property<DateTime?>("ApprovalDate")
.HasColumnType("datetime2");
b.Property<string>("ApprovalDescription")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ApprovalStatus")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<string>("ApprovalUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("BillingCycle") b.Property<string>("BillingCycle")
.HasMaxLength(32) .HasMaxLength(32)
.HasColumnType("nvarchar(32)"); .HasColumnType("nvarchar(32)");
@ -8725,6 +8739,16 @@ namespace Sozsoft.Platform.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("Sozsoft.Platform.Entities.ListFormWorkflow", b =>
{
b.HasOne("Sozsoft.Platform.Entities.ListForm", null)
.WithMany()
.HasForeignKey("ListFormCode")
.HasPrincipalKey("ListFormCode")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Sozsoft.Platform.Entities.MessengerConversationMessage", b => modelBuilder.Entity("Sozsoft.Platform.Entities.MessengerConversationMessage", b =>
{ {
b.HasOne("Sozsoft.Platform.Entities.MessengerConversation", "Conversation") b.HasOne("Sozsoft.Platform.Entities.MessengerConversation", "Conversation")

View file

@ -376,7 +376,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
{ {
var orderQuery = await orderRepository.WithDetailsAsync(order => order.Items); var orderQuery = await orderRepository.WithDetailsAsync(order => order.Items);
var activeOrders = await orderQuery var activeOrders = await orderQuery
.Where(order => order.TenantId == tenantId && order.IsActive) .Where(order => order.TenantId == tenantId && order.ApprovalStatus == "Approved" && order.IsActive)
.ToListAsync(); .ToListAsync();
return activeOrders return activeOrders

View file

@ -26,7 +26,6 @@ export const BillingControls: React.FC<BillingControlsProps> = ({
cartItemsCount, cartItemsCount,
cartState, cartState,
}) => { }) => {
// Check if cart has items to prevent billing cycle changes
const hasCartItems = cartState.items.length > 0 const hasCartItems = cartState.items.length > 0
const isScrolled = useScroll() const isScrolled = useScroll()
const { translate } = useLocalization() const { translate } = useLocalization()

View file

@ -50,9 +50,9 @@ export const OrderSuccess: React.FC<OrderSuccessProps> = ({ orderId, order }) =>
const billingCycleLabel = const billingCycleLabel =
order?.billingCycle === 'monthly' order?.billingCycle === 'monthly'
? 'Aylık' ? translate('::Public.products.billingcycle.monthly')
: order?.billingCycle === 'yearly' : order?.billingCycle === 'yearly'
? 'Yıllık' ? translate('::Public.products.billingcycle.yearly')
: '-' : '-'
const getItemPrice = (item: BasketItem) => const getItemPrice = (item: BasketItem) =>

View file

@ -140,7 +140,12 @@ export const PaymentForm: React.FC<PaymentFormProps> = ({ onBack, onComplete, ca
year: 'numeric', year: 'numeric',
}).format(date) }).format(date)
const billingCycleLabel = billingCycle === 'monthly' ? 'Aylık' : 'Yıllık' const billingCycleLabel =
billingCycle === 'monthly'
? translate('::Public.products.billingcycle.monthly')
: billingCycle === 'yearly'
? translate('::Public.products.billingcycle.yearly')
: '-'
return ( return (
<div className="mx-auto px-4 lg:px-8 mt-5"> <div className="mx-auto px-4 lg:px-8 mt-5">

View file

@ -87,7 +87,6 @@ export const ProductCard: React.FC<ProductCardProps> = ({
return globalPeriod > 1 ? ` (${globalPeriod} ${periodText})` : '' return globalPeriod > 1 ? ` (${globalPeriod} ${periodText})` : ''
} }
// Check if product has valid price for current billing cycle
const hasValidPrice = () => { const hasValidPrice = () => {
if (globalBillingCycle === 'monthly' && !product.monthlyPrice) return false if (globalBillingCycle === 'monthly' && !product.monthlyPrice) return false
if (globalBillingCycle === 'yearly' && !product.yearlyPrice) return false if (globalBillingCycle === 'yearly' && !product.yearlyPrice) return false

View file

@ -48,7 +48,6 @@ export const ProductCatalog: React.FC<ProductCatalogProps> = ({
setCartState((currentState) => addItemToCart(currentState, product, quantity, billingCycle)) setCartState((currentState) => addItemToCart(currentState, product, quantity, billingCycle))
} }
// Filter products based on billing cycle - only show products that have pricing for the selected cycle
const availableProducts = useMemo(() => { const availableProducts = useMemo(() => {
return products.filter((product) => { return products.filter((product) => {
// If monthly billing is selected, only show products with monthlyPrice // If monthly billing is selected, only show products with monthlyPrice

View file

@ -15,6 +15,7 @@ import {
FaUser, FaUser,
FaUserPlus, FaUserPlus,
FaCheckCircle, FaCheckCircle,
FaSitemap,
} from 'react-icons/fa' } from 'react-icons/fa'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
@ -1031,19 +1032,37 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
</div> </div>
</div> </div>
<div> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"> <div>
{translate('::Public.payment.customer.reference')} <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
</label> {translate('::Public.payment.customer.website')}
<div className="relative"> </label>
<FaUserPlus className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" /> <div className="relative">
<input <FaSitemap className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
type="text" <input
placeholder={translate('::Public.payment.customer.reference')} type="text"
value={formData.reference || ''} required
onChange={(e) => handleInputChange('reference', e.target.value)} placeholder={translate('::Public.payment.customer.website')}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent" value={formData.website || ''}
/> onChange={(e) => handleInputChange('website', e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{translate('::Public.payment.customer.reference')}
</label>
<div className="relative">
<FaUserPlus className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
<input
type="text"
placeholder={translate('::Public.payment.customer.reference')}
value={formData.reference || ''}
onChange={(e) => handleInputChange('reference', e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -302,6 +302,11 @@ export function FormTabWorkflow(
} }
const beginLink = (sourceId: string, outcome: string) => { const beginLink = (sourceId: string, outcome: string) => {
if (pendingLink && pendingLink.sourceId !== sourceId) {
connectNodes(pendingLink.sourceId, pendingLink.outcome, sourceId)
return
}
setPendingLink({ sourceId, outcome }) setPendingLink({ sourceId, outcome })
setSelectedId(sourceId) setSelectedId(sourceId)
} }

View file

@ -36,6 +36,7 @@ type FlowNodeProps = {
onOpenDetails: () => void onOpenDetails: () => void
onDelete: () => void onDelete: () => void
onBeginLink: (sourceId: string, outcome: string) => void onBeginLink: (sourceId: string, outcome: string) => void
onConnect: (sourceId: string, outcome: string, targetId: string) => void
} }
export function WorkflowCanvas({ export function WorkflowCanvas({
@ -241,6 +242,7 @@ export function WorkflowCanvas({
onOpenDetails={() => onOpenDetails(item.id)} onOpenDetails={() => onOpenDetails(item.id)}
onDelete={() => onDelete(item.id)} onDelete={() => onDelete(item.id)}
onBeginLink={onBeginLink} onBeginLink={onBeginLink}
onConnect={onConnect}
/> />
))} ))}
<svg <svg
@ -274,6 +276,7 @@ function FlowNode({
onOpenDetails, onOpenDetails,
onDelete, onDelete,
onBeginLink, onBeginLink,
onConnect,
}: FlowNodeProps) { }: FlowNodeProps) {
const { attributes, listeners, setNodeRef, transform } = useDraggable({ const { attributes, listeners, setNodeRef, transform } = useDraggable({
id: item.id, id: item.id,
@ -362,15 +365,24 @@ function FlowNode({
}, },
)} )}
onPointerDown={(event) => event.stopPropagation()} onPointerDown={(event) => event.stopPropagation()}
onPointerUp={(event) => event.stopPropagation()}
onClick={(event) => { onClick={(event) => {
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
if (pendingLink && pendingLink.sourceId !== item.id) {
onConnect(pendingLink.sourceId, pendingLink.outcome, item.id)
return
}
onBeginLink(item.id, outcome.field) onBeginLink(item.id, outcome.field)
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key !== 'Enter' && event.key !== ' ') return if (event.key !== 'Enter' && event.key !== ' ') return
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
if (pendingLink && pendingLink.sourceId !== item.id) {
onConnect(pendingLink.sourceId, pendingLink.outcome, item.id)
return
}
onBeginLink(item.id, outcome.field) onBeginLink(item.id, outcome.field)
}} }}
> >