diff --git a/src/TALXIS.CLI.Core/Contracts/Dataverse/CreateAttributeOptions.cs b/src/TALXIS.CLI.Core/Contracts/Dataverse/CreateAttributeOptions.cs
index 6dfe2c02..ebbd1994 100644
--- a/src/TALXIS.CLI.Core/Contracts/Dataverse/CreateAttributeOptions.cs
+++ b/src/TALXIS.CLI.Core/Contracts/Dataverse/CreateAttributeOptions.cs
@@ -82,4 +82,9 @@ public sealed record CreateAttributeOptions
public bool IsAuditable { get; init; }
public bool IsSearchable { get; init; } = true;
public bool IsSecured { get; init; }
+
+ // === Formula ===
+
+ /// Power Fx formula expression. When set, SourceType is set to 3 (Formula). Supported base types: string, number, decimal, float, money, bool, datetime, choice, multichoice.
+ public string? FormulaDefinition { get; init; }
}
diff --git a/src/TALXIS.CLI.Features.Environment/Entity/AttributeTypeRegistry.cs b/src/TALXIS.CLI.Features.Environment/Entity/AttributeTypeRegistry.cs
index d22fa5e6..a42500a7 100644
--- a/src/TALXIS.CLI.Features.Environment/Entity/AttributeTypeRegistry.cs
+++ b/src/TALXIS.CLI.Features.Environment/Entity/AttributeTypeRegistry.cs
@@ -22,7 +22,7 @@ public static class AttributeTypeRegistry
public static IReadOnlyList SharedParameterNames { get; } = new[]
{
"entity", "name", "display-name", "description", "required", "solution",
- "is-auditable", "is-searchable", "is-secured"
+ "is-auditable", "is-searchable", "is-secured", "formula-definition"
};
private static ReadOnlyCollection BuildAllTypes()
diff --git a/src/TALXIS.CLI.Features.Environment/Entity/EntityAttributeCreateCliCommand.cs b/src/TALXIS.CLI.Features.Environment/Entity/EntityAttributeCreateCliCommand.cs
index f6fefc39..84f079bc 100644
--- a/src/TALXIS.CLI.Features.Environment/Entity/EntityAttributeCreateCliCommand.cs
+++ b/src/TALXIS.CLI.Features.Environment/Entity/EntityAttributeCreateCliCommand.cs
@@ -32,7 +32,7 @@ public class EntityAttributeCreateCliCommand : StagedCliCommand
[CliOption(Name = "--name", Description = "Schema name for the new column.", Required = true)]
public string Name { get; set; } = null!;
- [CliOption(Name = "--type", Description = "Column type: string, memo, number, decimal, float, money, bool, datetime, choice, multichoice, lookup, polymorphic-lookup, customer, image, file, bigint.", Required = true)]
+ [CliOption(Name = "--type", Description = "Column type: string, memo, number, decimal, float, money, bool, datetime, choice, multichoice, lookup, polymorphic-lookup, customer, image, file, bigint. Add --formula-definition to make any supported type a formula column.", Required = true)]
public AttributeTypeArg Type { get; set; }
// === Optional for all types ===
@@ -134,6 +134,11 @@ public class EntityAttributeCreateCliCommand : StagedCliCommand
[DefaultValue(true)]
public bool CanStoreFullImage { get; set; } = true;
+ // === Formula ===
+
+ [CliOption(Name = "--formula-definition", Description = "Power Fx formula expression. When provided, creates the column as a formula field (SourceType=3). Supported types: string, number, decimal, float, money, bool, datetime, choice, multichoice.", Required = false)]
+ public string? FormulaDefinition { get; set; }
+
protected override async Task ExecuteAsync()
{
ValidateExecutionMode();
@@ -177,6 +182,7 @@ protected override async Task ExecuteAsync()
["cascadeDelete"] = options.CascadeDelete,
["maxSizeKb"] = options.MaxSizeKb,
["canStoreFullImage"] = options.CanStoreFullImage,
+ ["formulaDefinition"] = options.FormulaDefinition,
["isAuditable"] = options.IsAuditable,
["isSearchable"] = options.IsSearchable,
["isSecured"] = options.IsSecured
@@ -273,6 +279,9 @@ private CreateAttributeOptions BuildCreateOptions()
MaxSizeKb = MaxSizeKb,
CanStoreFullImage = CanStoreFullImage,
+ // Formula
+ FormulaDefinition = FormulaDefinition,
+
// Shared metadata properties
IsAuditable = IsAuditable,
IsSearchable = IsSearchable,
diff --git a/src/TALXIS.CLI.Platform.Dataverse.Application/Services/DataverseEntityMetadataService.cs b/src/TALXIS.CLI.Platform.Dataverse.Application/Services/DataverseEntityMetadataService.cs
index 21f5f26b..0066d1a0 100644
--- a/src/TALXIS.CLI.Platform.Dataverse.Application/Services/DataverseEntityMetadataService.cs
+++ b/src/TALXIS.CLI.Platform.Dataverse.Application/Services/DataverseEntityMetadataService.cs
@@ -145,7 +145,7 @@ public async Task CreateAttributeAsync(
switch (options.Type)
{
case "string":
- await ExecuteCreateAttribute(conn, options, new StringAttributeMetadata
+ var strMeta = new StringAttributeMetadata
{
SchemaName = options.SchemaName,
DisplayName = displayLabel,
@@ -153,7 +153,9 @@ public async Task CreateAttributeAsync(
RequiredLevel = requiredLevel,
MaxLength = options.MaxLength ?? 200,
FormatName = options.StringFormat is not null ? MapStringFormat(options.StringFormat) : StringFormatName.Text
- }, ct).ConfigureAwait(false);
+ };
+ if (options.FormulaDefinition is not null) { strMeta.SourceType = 3; strMeta.FormulaDefinition = options.FormulaDefinition; }
+ await ExecuteCreateAttribute(conn, options, strMeta, ct).ConfigureAwait(false);
break;
case "memo":
@@ -178,6 +180,7 @@ public async Task CreateAttributeAsync(
if (options.MinValue.HasValue) intMeta.MinValue = (int)options.MinValue.Value;
if (options.MaxValue.HasValue) intMeta.MaxValue = (int)options.MaxValue.Value;
if (options.NumberFormat.HasValue()) intMeta.Format = MapIntegerFormat(options.NumberFormat!);
+ if (options.FormulaDefinition is not null) { intMeta.SourceType = 3; intMeta.FormulaDefinition = options.FormulaDefinition; }
await ExecuteCreateAttribute(conn, options, intMeta, ct).ConfigureAwait(false);
break;
@@ -192,6 +195,7 @@ public async Task CreateAttributeAsync(
if (options.MinValue.HasValue) decMeta.MinValue = (decimal)options.MinValue.Value;
if (options.MaxValue.HasValue) decMeta.MaxValue = (decimal)options.MaxValue.Value;
if (options.Precision.HasValue) decMeta.Precision = options.Precision.Value;
+ if (options.FormulaDefinition is not null) { decMeta.SourceType = 3; decMeta.FormulaDefinition = options.FormulaDefinition; }
await ExecuteCreateAttribute(conn, options, decMeta, ct).ConfigureAwait(false);
break;
@@ -206,6 +210,7 @@ public async Task CreateAttributeAsync(
if (options.MinValue.HasValue) dblMeta.MinValue = options.MinValue.Value;
if (options.MaxValue.HasValue) dblMeta.MaxValue = options.MaxValue.Value;
if (options.Precision.HasValue) dblMeta.Precision = options.Precision.Value;
+ if (options.FormulaDefinition is not null) { dblMeta.SourceType = 3; dblMeta.FormulaDefinition = options.FormulaDefinition; }
await ExecuteCreateAttribute(conn, options, dblMeta, ct).ConfigureAwait(false);
break;
@@ -221,11 +226,12 @@ public async Task CreateAttributeAsync(
if (options.MaxValue.HasValue) moneyMeta.MaxValue = options.MaxValue.Value;
if (options.Precision.HasValue) moneyMeta.Precision = options.Precision.Value;
if (options.PrecisionSource.HasValue) moneyMeta.PrecisionSource = options.PrecisionSource.Value;
+ if (options.FormulaDefinition is not null) { moneyMeta.SourceType = 3; moneyMeta.FormulaDefinition = options.FormulaDefinition; }
await ExecuteCreateAttribute(conn, options, moneyMeta, ct).ConfigureAwait(false);
break;
case "bool":
- await ExecuteCreateAttribute(conn, options, new BooleanAttributeMetadata
+ var boolMeta = new BooleanAttributeMetadata
{
SchemaName = options.SchemaName,
DisplayName = displayLabel,
@@ -234,7 +240,9 @@ public async Task CreateAttributeAsync(
OptionSet = new BooleanOptionSetMetadata(
new OptionMetadata(new Label(options.TrueLabel, 1033), 1),
new OptionMetadata(new Label(options.FalseLabel, 1033), 0))
- }, ct).ConfigureAwait(false);
+ };
+ if (options.FormulaDefinition is not null) { boolMeta.SourceType = 3; boolMeta.FormulaDefinition = options.FormulaDefinition; }
+ await ExecuteCreateAttribute(conn, options, boolMeta, ct).ConfigureAwait(false);
break;
case "datetime":
@@ -248,6 +256,7 @@ public async Task CreateAttributeAsync(
};
if (options.DateTimeBehavior is not null)
dtMeta.DateTimeBehavior = MapDateTimeBehavior(options.DateTimeBehavior);
+ if (options.FormulaDefinition is not null) { dtMeta.SourceType = 3; dtMeta.FormulaDefinition = options.FormulaDefinition; }
await ExecuteCreateAttribute(conn, options, dtMeta, ct).ConfigureAwait(false);
break;
@@ -919,6 +928,7 @@ private static async Task CreatePicklistAttribute(
picklistMeta.OptionSet = BuildLocalOptionSet(options);
}
+ if (options.FormulaDefinition is not null) { picklistMeta.SourceType = 3; picklistMeta.FormulaDefinition = options.FormulaDefinition; }
await ExecuteCreateAttribute(conn, options, picklistMeta, ct).ConfigureAwait(false);
}
@@ -944,6 +954,7 @@ private static async Task CreateMultiSelectPicklistAttribute(
multiMeta.OptionSet = BuildLocalOptionSet(options);
}
+ if (options.FormulaDefinition is not null) { multiMeta.SourceType = 3; multiMeta.FormulaDefinition = options.FormulaDefinition; }
await ExecuteCreateAttribute(conn, options, multiMeta, ct).ConfigureAwait(false);
}