diff --git a/Samples/Dynamics365FODemo/.gitignore b/Samples/Dynamics365FODemo/.gitignore new file mode 100644 index 0000000..c194bb3 --- /dev/null +++ b/Samples/Dynamics365FODemo/.gitignore @@ -0,0 +1,15 @@ +**/.vs +**/obj +**/bin/* +**/Resources +**/XppMetadata +**/Reports +**/references + +BuildModelResult.log +BuildModelResult.xml +BuildModelResult.err.xml +BuildProjectResult.log +BuildProjectResult.xml +BuildProjectResult.err.xml +CompileLabels.xml \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/DeployablePackage/AXDeployablePackage_20231124_17_06_15.zip b/Samples/Dynamics365FODemo/DeployablePackage/AXDeployablePackage_20231124_17_06_15.zip new file mode 100644 index 0000000..b3dddb7 Binary files /dev/null and b/Samples/Dynamics365FODemo/DeployablePackage/AXDeployablePackage_20231124_17_06_15.zip differ diff --git a/Samples/Dynamics365FODemo/Dynamics365FODemo.sln b/Samples/Dynamics365FODemo/Dynamics365FODemo.sln new file mode 100644 index 0000000..c359b69 --- /dev/null +++ b/Samples/Dynamics365FODemo/Dynamics365FODemo.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.34031.81 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerFxDemoLibrary", "PowerFxDemoLibrary\PowerFxDemoLibrary.csproj", "{7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7DEDF1AD-DD13-4127-8EEC-12B9E30A7B9D}" + ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore + EndProjectSection +EndProject +Project("{FC65038C-1B2F-41E1-A629-BED71D161FFF}") = "PowerFxDemo D365FO (VAR) [PowerFx D365FO Demo]", "PowerFxDemo D365FO\PowerFxDemo D365FO.rnrproj", "{3877D19E-04BE-4209-BFB9-979C95B574BC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}.Release|Any CPU.Build.0 = Release|Any CPU + {3877D19E-04BE-4209-BFB9-979C95B574BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3877D19E-04BE-4209-BFB9-979C95B574BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3877D19E-04BE-4209-BFB9-979C95B574BC}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {3877D19E-04BE-4209-BFB9-979C95B574BC}.Release|Any CPU.Build.0 = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {914DB8F6-2C93-420B-9F34-90AF1B21D442} + EndGlobalSection +EndGlobal diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/Descriptor/PowerFxDemo.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/Descriptor/PowerFxDemo.xml new file mode 100644 index 0000000..6a5e9b2 --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/Descriptor/PowerFxDemo.xml @@ -0,0 +1,34 @@ + + + + Allow + PowerFx D365FO Demo + PowerFx D365FO Demo + 896000516 + + 10 + false + PowerFxDemo + + + ApplicationFoundation + ApplicationPlatform + ApplicationSuite + ContactPerson + Directory + FiscalBooks + Ledger + Retail + SourceDocumentation + SourceDocumentationTypes + Tax + + PowerFxDemo + Dominik Downarowicz + 00000000-0000-0000-0000-000000000000 + 0 + 1 + 0 + 0 + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo.xref b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo.xref new file mode 100644 index 0000000..6bcec00 Binary files /dev/null and b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo.xref differ diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDeliveryAddressContract.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDeliveryAddressContract.xml new file mode 100644 index 0000000..8849e22 --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDeliveryAddressContract.xml @@ -0,0 +1,96 @@ + + + PowerFxDeliveryAddressContract + + + + + parmName + + + + parmStreet + + + + parmCity + + + + parmZipCode + + + + parmCountryCode + + + + getInstance + + + + + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDemoRunnable.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDemoRunnable.xml new file mode 100644 index 0000000..5b37e3e --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDemoRunnable.xml @@ -0,0 +1,107 @@ + + + PowerFxDemoRunnable + + + + + main + + + + run + + + + modifiedSalesOrderSelection + + + + modifiedFormula + + + + + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesHeaderContract.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesHeaderContract.xml new file mode 100644 index 0000000..4d31575 --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesHeaderContract.xml @@ -0,0 +1,288 @@ + + + PowerFxSalesHeaderContract + + + + + parmSalesNumber + + + + parmCustName + + + + parmCustAccount + + + + parmCustGroup + + + + parmVATNum + + + + parmMarkupGroup + + + + parmDlvMode + + + + parmPaymMode + + + + parmSalesGroup + + + + parmSalesType + + + + parmDeliveryDate + + + + parmAmountInvoiced + + + + parmAmountOrderedNotInvoiced + + + + parmAmountRemainSalesFinancial + + + + parmAmountRemainSalesPhyscial + + + + parmContributionMargin + + + + parmDeliveryAddress + + + + parmSalesOrderLines + + + + getInstance + + + + + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesLineContract.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesLineContract.xml new file mode 100644 index 0000000..a4d2342 --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesLineContract.xml @@ -0,0 +1,183 @@ + + + PowerFxSalesLineContract + + + + + parmItemName + + + + parmItemId + + + + parmLineNum + + + + parmLineAmount + + + + parmSalesPrice + + + + parmDiscPercent + + + + parmQtyOrdered + + + + parmSalesQty + + + + parmUnitId + + + + parmCurrencyCode + + + + parmDeliveryDate + + + + getInstance + + + + + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.resources.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.resources.xml new file mode 100644 index 0000000..9062e53 --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.resources.xml @@ -0,0 +1,8 @@ + + + Microsoft.PowerFx.Core.resources + Microsoft.PowerFx.Core.resources + Microsoft.PowerFx.Core.resources, Version=1.1.0.0, Culture=en-US, PublicKeyToken=31bf3856ad364e35 + 31bf3856ad364e35 + 1.1.0.0 + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.xml new file mode 100644 index 0000000..bb7e872 --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.xml @@ -0,0 +1,8 @@ + + + Microsoft.PowerFx.Core + Microsoft.PowerFx.Core + Microsoft.PowerFx.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 + 31bf3856ad364e35 + 1.1.0.0 + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Interpreter.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Interpreter.xml new file mode 100644 index 0000000..c35a40f --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Interpreter.xml @@ -0,0 +1,8 @@ + + + Microsoft.PowerFx.Interpreter + Microsoft.PowerFx.Interpreter + Microsoft.PowerFx.Interpreter, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 + 31bf3856ad364e35 + 1.1.0.0 + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Json.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Json.xml new file mode 100644 index 0000000..86c3a83 --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Json.xml @@ -0,0 +1,8 @@ + + + Microsoft.PowerFx.Json + Microsoft.PowerFx.Json + Microsoft.PowerFx.Json, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 + 31bf3856ad364e35 + 1.1.0.0 + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Repl.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Repl.xml new file mode 100644 index 0000000..13649fc --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Repl.xml @@ -0,0 +1,8 @@ + + + Microsoft.PowerFx.Repl + Microsoft.PowerFx.Repl + Microsoft.PowerFx.Repl, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 + 31bf3856ad364e35 + 1.1.0.0 + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Transport.Attributes.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Transport.Attributes.xml new file mode 100644 index 0000000..04026bf --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Transport.Attributes.xml @@ -0,0 +1,8 @@ + + + Microsoft.PowerFx.Transport.Attributes + Microsoft.PowerFx.Transport.Attributes + Microsoft.PowerFx.Transport.Attributes, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 + 31bf3856ad364e35 + 1.1.0.0 + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/PowerFxDemoLibrary.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/PowerFxDemoLibrary.xml new file mode 100644 index 0000000..da83ddd --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/PowerFxDemoLibrary.xml @@ -0,0 +1,7 @@ + + + PowerFxDemoLibrary + PowerFxDemoLibrary + PowerFxDemoLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + 1.0.0.0 + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/Metadata/Readme.txt.txt b/Samples/Dynamics365FODemo/Metadata/Readme.txt.txt new file mode 100644 index 0000000..cf22995 --- /dev/null +++ b/Samples/Dynamics365FODemo/Metadata/Readme.txt.txt @@ -0,0 +1,29 @@ +Add all AX modules under the Metadata folder. + +Example tree structure: +$/Project/Trunk/Main/Metadata ++---MyMainModule +| +---Descriptor +| | MyFirstModel.xml +| | MyOtherModel.xml +| | +| +---MyFirstModel +| | +---AxClass +| | | MyClass.xml +| | | +| | \---AxTable +| | MyMainTable.xml +| | +| \---MyOtherModel +| \---AxForm +| MyOtherForm.xml +| +\---MyOtherModule + +---Descriptor + | MyTestModel.xml + | + \---MyTestModel + \---AxClass + MyTestClass.xml + +Please see https://ax.help.dynamics.com for more details. \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/PowerFxDemo D365FO/PowerFxDemo D365FO.rnrproj b/Samples/Dynamics365FODemo/PowerFxDemo D365FO/PowerFxDemo D365FO.rnrproj new file mode 100644 index 0000000..5287629 --- /dev/null +++ b/Samples/Dynamics365FODemo/PowerFxDemo D365FO/PowerFxDemo D365FO.rnrproj @@ -0,0 +1,89 @@ + + + + Debug + AnyCPU + $(MSBuildProgramFiles32)\MSBuild\Microsoft\Dynamics\AX + PowerFxDemo + v4.7.2 + bin + 2.0 + True + False + False + False + {3877d19e-04be-4209-bfb9-979c95b574bc} + PowerFxDemo D365FO + PowerFxDemo D365FO + Class + + + Debug + False + False + + + initial + AnyCPU + False + False + + + true + false + + + + Content + PowerFxDeliveryAddressContract + PowerFxDeliveryAddressContract + + + Content + PowerFxDemoRunnable + PowerFxDemoRunnable + + + Content + PowerFxSalesHeaderContract + PowerFxSalesHeaderContract + + + Content + PowerFxSalesLineContract + PowerFxSalesLineContract + + + + + Microsoft.PowerFx.Core + C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Core.dll + + + Microsoft.PowerFx.Core.resources + C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Core.resources.dll + + + Microsoft.PowerFx.Interpreter + C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Interpreter.dll + + + Microsoft.PowerFx.Json + C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Json.dll + + + Microsoft.PowerFx.Repl + C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Repl.dll + + + Microsoft.PowerFx.Transport.Attributes + C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Transport.Attributes.dll + + + PowerFxDemoLibrary + C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\PowerFxDemoLibrary.dll + + + + + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Evaluation.cs b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Evaluation.cs new file mode 100644 index 0000000..8dd0330 --- /dev/null +++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Evaluation.cs @@ -0,0 +1,119 @@ +using Microsoft.PowerFx; +using Microsoft.PowerFx.Syntax; +using Microsoft.PowerFx.Types; +using System; +using System.Collections.Generic; + +namespace PowerFxDemoLibrary +{ + public class Evaluation + { + public class Response + { + public string output { get; set; } + public string error { get; set; } + public bool errorThrown { get; set; } + } + + private class ThrowErrorFunction : ReflectionFunction + { + private string errorMessage = ""; + + public ThrowErrorFunction(): base("ThrowError", FormulaType.Blank, new[] { FormulaType.String }) { } + + public BlankValue Execute(StringValue x) + { + addError(x.Value); + return FormulaValue.NewBlank(); + } + + public void addError(string error) + { + if (error != "") + { + errorMessage += errorMessage + "\n" + error; + } + else + { + errorMessage += errorMessage + "\n"; + } + } + + public string getErrorMessage() + { + return errorMessage.TrimStart('\n').TrimEnd('\n'); + } + + public bool isFailed() + { + return errorMessage != "" ? true : false; + } + + } + + private class ThrowErrorFunctionEmtpy : ReflectionFunction + { + public ThrowErrorFunction function; + public ThrowErrorFunctionEmtpy(ThrowErrorFunction _function) : base("ThrowError", FormulaType.Blank) + { + function = _function; + } + + public BlankValue Execute() + { + function.addError(""); + return FormulaValue.NewBlank(); + } + } + + public Response EvaluateInput(string context, string expression) + { + Response response = new Response(); + IReadOnlyList tokens = null; + CheckResult check = null; + + ThrowErrorFunction errorFunction = new ThrowErrorFunction(); + ThrowErrorFunctionEmtpy errorFunctionEmpty = new ThrowErrorFunctionEmtpy(errorFunction); + + response.output = ""; + response.error = ""; + response.errorThrown = false; + try + { + var config = new PowerFxConfig(); + config.AddFunction(errorFunction); + config.AddFunction(errorFunctionEmpty); + var engine = new RecalcEngine(config); + + var parameters = (RecordValue)FormulaValueJSON.FromJson(context); + + if (parameters == null) + { + parameters = RecordValue.Empty(); + } + + tokens = engine.Tokenize(expression); + check = engine.Check(expression, parameters.Type, options: null); + check.ThrowOnErrors(); + var eval = check.GetEvaluator(); + var result = eval.Eval(parameters); + + response.output = HelperClass.TestToString(result); + } + catch(Exception ex) + { + errorFunction.addError(ex.Message); + } + finally + { + response.errorThrown = errorFunction.isFailed(); + response.error = errorFunction.getErrorMessage(); + } + + return response; + + } + + } + +} diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/HelperClass.cs b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/HelperClass.cs new file mode 100644 index 0000000..5b8ff1d --- /dev/null +++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/HelperClass.cs @@ -0,0 +1,149 @@ +using Microsoft.PowerFx.Types; +using System; +using System.Linq; +using System.Text; + +namespace PowerFxDemoLibrary +{ + // Helper to print results. + // This should get shared with Repl or from framework. + public class HelperClass + { + internal static string TestToString(FormulaValue result) + { + StringBuilder sb = new StringBuilder(); + try + { + TestToString(result, sb); + } + catch (Exception e) + { + // This will cause a diff and test failure below. + sb.Append($""); + } + + string actualStr = sb.ToString(); + return actualStr; + } + + // $$$ Move onto FormulaValue. + // Result here should be a string value that could be parsed. + // Normalize so we can use this in test cases. + internal static void TestToString(FormulaValue result, StringBuilder sb) + { + if (result is DecimalValue dec) + { + sb.Append(dec.Value); + } + else if (result is NumberValue n) + { + sb.Append(n.Value); + } + else if (result is BooleanValue b) + { + sb.Append(b.Value ? "true" : "false"); + } + else if (result is StringValue s) + { + // $$$ proper escaping? + sb.Append('"' + s.Value + '"'); + } + else if (result is TableValue t) + { + var tableType = (TableType)t.Type; + var canUseSquareBracketSyntax = t.IsColumn && t.Rows.All(r => r.IsValue) && tableType.FieldNames.First() == "Value"; + if (canUseSquareBracketSyntax) + { + sb.Append('['); + } + else + { + sb.Append("Table("); + } + + var dil = string.Empty; + foreach (var row in t.Rows) + { + sb.Append(dil); + dil = ","; + + if (canUseSquareBracketSyntax) + { + var val = row.Value.Fields.First().Value; + TestToString(val, sb); + } + else + { + if (row.IsValue) + { + TestToString(row.Value, sb); + } + else + { + TestToString(row.ToFormulaValue(), sb); + } + } + + dil = ","; + } + + if (canUseSquareBracketSyntax) + { + sb.Append(']'); + } + else + { + sb.Append(')'); + } + } + else if (result is RecordValue r) + { + var fields = r.Fields.ToArray(); + Array.Sort(fields, (a, c) => string.CompareOrdinal(a.Name, c.Name)); + + sb.Append('{'); + var dil = string.Empty; + + foreach (var field in fields) + { + sb.Append(dil); + sb.Append(field.Name); + sb.Append(':'); + TestToString(field.Value, sb); + + dil = ","; + } + + sb.Append('}'); + } + else if (result is TimeValue tv) + { + sb.Append(tv.Value.ToString()); + } + else if (result is BlankValue) + { + sb.Append("Blank()"); + } + else if (result is DateValue d) + { + // Date(YYYY,MM,DD) + var date = d.GetConvertedValue(null); + sb.Append($"Date({date.Year},{date.Month},{date.Day})"); + } + else if (result is DateTimeValue dt) + { + // DateTime(yyyy,MM,dd,HH,mm,ss,fff) + var dateTime = dt.GetConvertedValue(null); + sb.Append($"DateTime({dateTime.Year},{dateTime.Month},{dateTime.Day},{dateTime.Hour},{dateTime.Minute},{dateTime.Second},{dateTime.Millisecond})"); + } + else if (result is ErrorValue) + { + sb.Append(result); + } + else + { + throw new InvalidOperationException($"unsupported value type: {result.GetType().Name}"); + } + } + } +} \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/PowerFxDemoLibrary.csproj b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/PowerFxDemoLibrary.csproj new file mode 100644 index 0000000..e4f9fd9 --- /dev/null +++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/PowerFxDemoLibrary.csproj @@ -0,0 +1,115 @@ + + + + + Debug + AnyCPU + {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601} + Library + Properties + PowerFxDemoLibrary + PowerFxDemoLibrary + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\Microsoft.PowerFx.Core.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Core.dll + + + ..\packages\Microsoft.PowerFx.Interpreter.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Interpreter.dll + + + ..\packages\Microsoft.PowerFx.Json.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Json.dll + + + ..\packages\Microsoft.PowerFx.Repl.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Repl.dll + + + ..\packages\Microsoft.PowerFx.Transport.Attributes.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Transport.Attributes.dll + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Collections.Immutable.6.0.0\lib\net461\System.Collections.Immutable.dll + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.6.0.0\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.6.0.8\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Properties/AssemblyInfo.cs b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5fbe1a1 --- /dev/null +++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PowerFx D365 Demo")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Dominik Downarowicz")] +[assembly: AssemblyProduct("PowerFxDemoLibrary")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7bace988-ecce-4a1a-af7f-2a400bbf9601")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/app.config b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/app.config new file mode 100644 index 0000000..0453880 --- /dev/null +++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/packages.config b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/packages.config new file mode 100644 index 0000000..c6fea9b --- /dev/null +++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/packages.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/README.md b/Samples/Dynamics365FODemo/README.md new file mode 100644 index 0000000..1ec2016 --- /dev/null +++ b/Samples/Dynamics365FODemo/README.md @@ -0,0 +1,15 @@ +# Introduction +Demonstrates using PowerFx in a Dynamics 365 Finance and Operations (D365F&O) environment (Platform Version PU61). PowerFx is used here to evaluate SalesOrder data. + +![PowerFx Demo in D365 F&O](media/d365demo.gif) + +Additionally the custom function **ThrowError("...")** has been added to PowerFx. There is no concret use for that in the demo, I use the function to pass errors back to D365F&O. Outside the context of that demo, I implemented the **ThrowError** function for a validation framework where the customer can define by himeself with PowerFx the validation rules and throw errors without the need of a D365F&O developer. + +# Deploy and demo usage +In the folder **DeployablePackage** is a ready to deploy package for D365F&O works. Can be deployed like any other package for D365F&O. To run the demo script you then only need to call the **SysClassRunner** in the URL. + +**Example: https:///?cmp=USMF&mi=SysClassRunner&cls=PowerFxDemoRunnable** + +# Build +If you want to build the project by yourself on a cloud-hosted environment or local VHD image, do not forget to use the registerSymbolicLink.ps1 to link the Metadata folder to the AosService/PackagesLocalDirectory of the machine. You can also build the project on a new [Unified Experience environment](https://learn.microsoft.com/en-us/power-platform/developer/unified-experience/finance-operations-dev-overview). (2023-11-24: Unified experience environments are in preview and some features might not work). + diff --git a/Samples/Dynamics365FODemo/copyReferences.ps1 b/Samples/Dynamics365FODemo/copyReferences.ps1 new file mode 100644 index 0000000..9c311e5 --- /dev/null +++ b/Samples/Dynamics365FODemo/copyReferences.ps1 @@ -0,0 +1,5 @@ +$buildPath = ".\PowerFxDemoLibrary\bin\Debug" +$dest = ".\references" + +copy-item -path $buildPath\* -destination $dest -include *.dll +copy-item -path $buildPath\en-US\* -destination $dest -include *.dll \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/media/d365demo.gif b/Samples/Dynamics365FODemo/media/d365demo.gif new file mode 100644 index 0000000..7d3f6b9 Binary files /dev/null and b/Samples/Dynamics365FODemo/media/d365demo.gif differ diff --git a/Samples/Dynamics365FODemo/registerSymbolicLink.ps1 b/Samples/Dynamics365FODemo/registerSymbolicLink.ps1 new file mode 100644 index 0000000..ee7c2ae --- /dev/null +++ b/Samples/Dynamics365FODemo/registerSymbolicLink.ps1 @@ -0,0 +1,15 @@ +$AOSMetadataPath = "K:\AOSService\PackagesLocalDirectory" + +If(!(Test-Path -Path $AOSMetadataPath)) +{ + $AOSMetadataPath = "C:\AOSService\PackagesLocalDirectory" +} + +$RepoPath = "." +$RepoMetadataPath = $RepoPath + "\Metadata" +$RepoModelFolders = Get-ChildItem $RepoMetadataPath -Directory +foreach ($ModelFolder in $RepoModelFolders) +{ + $Target = "$RepoMetadataPath\$ModelFolder" + New-Item -ItemType SymbolicLink -Path "$AOSMetadataPath" -Name "$ModelFolder" -Value "$Target" +} \ No newline at end of file diff --git a/Samples/Dynamics365FODemo/unregisterSymbolicLink.ps1 b/Samples/Dynamics365FODemo/unregisterSymbolicLink.ps1 new file mode 100644 index 0000000..7ee62f1 --- /dev/null +++ b/Samples/Dynamics365FODemo/unregisterSymbolicLink.ps1 @@ -0,0 +1,14 @@ +$AOSMetadataPath = "K:\AOSService\PackagesLocalDirectory" + +If(!(Test-Path -Path $AOSMetadataPath)) +{ + $AOSMetadataPath = "C:\AOSService\PackagesLocalDirectory" +} + +$RepoPath = "." +$RepoMetadataPath = $RepoPath + "\Metadata" +$RepoModelFolders = Get-ChildItem $RepoMetadataPath -Directory +foreach ($ModelFolder in $RepoModelFolders) +{ + cmd /c rmdir "$AOSMetadataPath\$ModelFolder" +} \ No newline at end of file