### Create Module Setup Page Source: https://github.com/fbakkensen/bc-w1/blob/main/ContosoCoffeeDemoDataset/Source/Contoso Coffee Demo Dataset/Getting-Started.md Create a card page for the 'Module Setup' table to allow users to configure module settings. The page initializes the record upon opening. ```al page 5281 "Module Setup" { PageType = Card; Caption = 'Module Setup'; SourceTable = "Module Setup"; Extensible = false; DeleteAllowed = false; InsertAllowed = false; layout { area(Content) { group("Setup Data") { field(StartDate; Rec.StartDate) { } } } } trigger OnOpenPage() begin Rec.InitRecord(); end; } ``` -------------------------------- ### Work with SharePoint Drives and Files Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Provides examples for retrieving root folder items, downloading files, getting items by path, uploading files, and creating new folders within a SharePoint site. ```al var GraphDriveItem: Record "SharePoint Graph Drive Item" temporary; TempBlob: Codeunit "Temp Blob"; FileInStream: InStream; Response: Codeunit "SharePoint Graph Response"; begin // Get root folder items Response := SPGraphClient.GetRootItems(GraphDriveItem); if not Response.IsSuccessful() then Error(Response.GetError()); // Filter to files only and download first one GraphDriveItem.SetRange(IsFolder, false); if GraphDriveItem.FindFirst() then Response := SPGraphClient.DownloadFile(GraphDriveItem.Id, TempBlob); // Get items by path Response := SPGraphClient.GetItemsByPath('Documents/Folder1', GraphDriveItem); // Upload a file (empty path = root folder) Response := SPGraphClient.UploadFile('', 'file.pdf', FileInStream, GraphDriveItem); // Create a folder Response := SPGraphClient.CreateFolder('Documents', 'NewFolder', GraphDriveItem); ``` -------------------------------- ### Define Module Setup Table Source: https://github.com/fbakkensen/bc-w1/blob/main/ContosoCoffeeDemoDataset/Source/Contoso Coffee Demo Dataset/Getting-Started.md Define a table for module configuration, including a primary key and a start date field. This table is used to store user-configurable settings for the module. ```al table 5282 "Module Setup" { DataClassification = CustomerContent; InherentEntitlements = RMX; InherentPermissions = RMX; Extensible = false; DataPerCompany = true; ReplicateData = false; fields { field(1; "Primary Key"; Integer) { DataClassification = SystemMetadata; Caption = 'Primary Key'; } field(2; StartDate; Date) { Caption = 'Start Date'; ToolTip = 'Specifies the start date for the scenario.'; } } keys { key(Key1; "Primary Key") { Clustered = true; } } [InherentPermissions(PermissionObjectType::TableData, Database::"Module Setup", 'I')] internal procedure InitRecord() begin if Rec.Get() then exit; Rec.Insert(); end; } ``` -------------------------------- ### Get and Create SharePoint Lists Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Demonstrates how to retrieve all lists from a SharePoint site and how to create a new list with a specified name and description. ```al var GraphList: Record "SharePoint Graph List" temporary; Response: Codeunit "SharePoint Graph Response"; begin // Get all lists Response := SPGraphClient.GetLists(GraphList); // Create a new list Response := SPGraphClient.CreateList('My List', 'Description', GraphList); ``` -------------------------------- ### JSONL Dataset Example for AI Evals Source: https://github.com/fbakkensen/bc-w1/blob/main/TestFramework/AITestToolkit/AI Test Toolkit/README.md Example of a dataset file in JSONL format for AI evaluations. Each line represents a test case with a name, a question for the AI, and the expected data for validation. ```json {"name": "Eval01", "question": "A question", "expected_data": 5} {"name": "Eval02", "question": "A second question", "expected_data": 2} {"name": "Eval03", "question": "A third question", "expected_data": 2} ``` -------------------------------- ### Get and Create SharePoint List Items Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Shows how to fetch items from a specific list using its ID and how to create a new list item with a given title. ```al var GraphListItem: Record "SharePoint Graph List Item" temporary; begin // Get items from a list Response := SPGraphClient.GetListItems('', GraphListItem); // Create a new item Response := SPGraphClient.CreateListItem('', 'Item Title', GraphListItem); ``` -------------------------------- ### External Storage Folder Structure Example Source: https://github.com/fbakkensen/bc-w1/blob/main/External Storage - Document Attachments/Source/External Storage - Document Attachments/README.md Illustrates the hierarchical organization of files in external storage, including the environment hash, table name, and file name. ```plaintext RootFolder/ ├── [EnvironmentHash-1]/ │ ├── Sales_Header/ │ │ └── invoice-{guid}.pdf │ └── Purchase_Header/ │ └── order-{guid}.pdf └── [EnvironmentHash-2]/ └── Sales_Header/ └── quote-{guid}.pdf ``` -------------------------------- ### Implement Demo Data Module Interface Source: https://github.com/fbakkensen/bc-w1/blob/main/ContosoCoffeeDemoDataset/Source/Contoso Coffee Demo Dataset/Getting-Started.md Implement the 'Contoso Demo Data Module' interface in a codeunit. This involves defining procedures for running configuration pages, getting dependencies, and creating setup, master, and transactional data. ```al codeunit 50100 "Contoso Shoes Module" implements "Contoso Demo Data Module" { procedure RunConfigurationPage(); begin end; procedure GetDependencies() Dependencies: List of [Enum "Contoso Demo Data Module"] begin Dependencies.Add(Enum::"Contoso Demo Data Module"::"Common Module"); end; procedure CreateSetupData() begin Codeunit.Run(Codeunit::"Create Contoso Shoes Item Category"); Codeunit.Run(Codeunit::"Create Contoso Shoes Unit of Measure"); end; procedure CreateMasterData() begin Codeunit.Run(Codeunit::"Cearte Contoso Shoes Item"); Codeunit.Run(Codeunit::"Create Contoso Shoes Size"); end; procedure CreateTransactionalData(); begin end; procedure CreateHistoricalData(); begin end; } ``` -------------------------------- ### Add Demo Data to Localization Source: https://github.com/fbakkensen/bc-w1/blob/main/ContosoCoffeeDemoDataset/Source/Contoso Coffee Demo Dataset/Coding-Patterns.md Subscribe to the OnAfterGeneratingDemoData event to populate additional demo data for specific localizations. This is useful for module-specific setup data. ```AL [EventSubscriber(ObjectType::Codeunit, Codeunit::"Contoso Demo Tool", 'OnAfterGeneratingDemoData', '', false, false)] local procedure LocalizationContosoDemoData(Module: Enum "Contoso Demo Data Module"; ContosoDemoDataLevel: Enum "Contoso Demo Data Level") begin case Module of Enum::"Contoso Demo Data Module"::Foundation: FoundationModule(ContosoDemoDataLevel); ... end; end; local procedure FoundationModule(ContosoDemoDataLevel: Enum "Contoso Demo Data Level") begin case ContosoDemoDataLevel of Enum::"Contoso Demo Data Level"::"Setup Data": begin Codeunit.Run(Codeunit::"Create Job Queue Category US"); ... end; end; ``` -------------------------------- ### Implement IDocumentSender Send Method Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md Implement the 'Send' method of the 'IDocumentSender' interface to handle sending E-Documents. This example shows preparing an HTTP POST request, setting content from a TempBlob, adding authorization headers, sending the request, and handling the response. ```al procedure Send(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; SendContext: Codeunit SendContext) var MyServiceSetup: Record MyServiceSetup; TempBlob: Codeunit "Temp Blob"; HttpClient: HttpClient; HttpRequest: HttpRequestMessage; HttpResponse: HttpResponseMessage; begin // Retrieve the TempBlob from SendContext SendContext.GetTempBlob(TempBlob); // Prepare the HTTP request using the TempBlob content HttpRequest := SendContext.Http().GetHttpRequestMessage(); HttpRequest.Method := 'POST'; HttpRequest.SetRequestUri(MyServiceSetup."Service URL"); SetContent(HttpRequest, TempBlob); // Some method to do that // Add authorization headers HttpRequest.Headers.Add('Authorization', 'Bearer ' + MyServiceSetup."Access Token"); // Send the request and capture the response HttpClient.Send(HttpRequest, HttpResponse); // Handle the response if HttpResponse.IsSuccessStatusCode() then Message('E-Document sent successfully.') else Error('Failed to send E-Document: %1', HttpResponse.ReasonPhrase); end; ``` -------------------------------- ### Data Archive API Usage Example Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Data Archive/README.md Illustrates the basic code flow for using the Data Archive API within an application. It shows how to create a new archive, save a record, and finalize the save operation. ```AL procedure Foo() var DataArchive: Codeunit "Data Archive"; // System App Customer: Record Customer; RecRef: RecordRef; NewArchiveNo: Integer; begin ... NewArchiveNo := DataArchiveInterface.Create('New Archive'); ... RecRef.GetTable(Customer); DataArchiveInterface.SaveRecord(RecRef); ... DataArchiveInterface.Save(); ... end; ``` -------------------------------- ### IDocumentSender Interface Implementation Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md This section provides an example implementation of the `Send` method within the `IDocumentSender` interface. This method is responsible for sending an E-Document to an external service. ```APIDOC ## Send Method ### Description The `Send` method is responsible for sending an E-Document to an external service. It takes three parameters: `EDocument`, `EDocumentService`, and `SendContext`. ### Method Signature ```al procedure Send(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; SendContext: Codeunit SendContext) ``` ### Parameters - **EDocument** (Record "E-Document") - The record representing the E-Document to be sent. - **EDocumentService** (Record "E-Document Service") - The record containing service configuration details such as the URL and access tokens. - **SendContext** (Codeunit SendContext) - A codeunit that provides context and resources for the send operation. ### Example Implementation ```al procedure Send(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; SendContext: Codeunit SendContext) var MyServiceSetup: Record MyServiceSetup; TempBlob: Codeunit "Temp Blob"; HttpClient: HttpClient; HttpRequest: HttpRequestMessage; HttpResponse: HttpResponseMessage; begin // Retrieve the TempBlob from SendContext SendContext.GetTempBlob(TempBlob); // Prepare the HTTP request using the TempBlob content HttpRequest := SendContext.Http().GetHttpRequestMessage(); HttpRequest.Method := 'POST'; HttpRequest.SetRequestUri(MyServiceSetup."Service URL"); SetContent(HttpRequest, TempBlob); // Some method to do that // Add authorization headers HttpRequest.Headers.Add('Authorization', 'Bearer ' + MyServiceSetup."Access Token"); // Send the request and capture the response HttpClient.Send(HttpRequest, HttpResponse); // Handle the response if HttpResponse.IsSuccessStatusCode() then Message('E-Document sent successfully.'); else Error('Failed to send E-Document: %1', HttpResponse.ReasonPhrase); end; ``` ### Notes - To support asynchronous sending, implement the `IDocumentResponseHandler` interface. - For batch operations, the `EDocument` record is populated using filters. - Ensure proper error handling and logging of HTTP responses. - Utilize `SendContext` for logging HTTP request details. ``` -------------------------------- ### Use PeekNextNo instead of TryGetNextNo Source: https://github.com/fbakkensen/bc-w1/blob/main/BusinessFoundation/Source/Business Foundation/NoSeries/readme_refactoring.md Replace TryGetNextNo with PeekNextNo for getting the next number without modifying the No. Series. This change improves code readability. ```AL DocNo := NoSeriesMgt.TryGetNextNo(GenJnlBatch."No. Series", EndDateReq); ``` ```AL DocNo := NoSeries.PeekNextNo(GenJnlBatch."No. Series", EndDateReq); ``` -------------------------------- ### Implement Power BI Deployable Report Interface Source: https://github.com/fbakkensen/bc-w1/blob/main/BaseApp/Source/Base Application/Modules/System/PowerBI/README.md This codeunit implements the 'Power BI Deployable Report' interface to define the behavior for a new report. It specifies how to get the report name, PBIX stream, version, and dataset parameters. ```AL codeunit 50000 "My Sales Report Impl." implements "Power BI Deployable Report" { procedure GetReportName(): Text[200] begin exit('Sales'); end; procedure GetStream(var InStr: InStream) begin NavApp.GetResource('Sales.pbix', InStr); end; procedure GetVersion(): Integer begin exit(1); // Bump to trigger re-upload end; procedure GetDatasetParameters(): Dictionary of [Text, Text] var Params: Dictionary of [Text, Text]; begin Params.Add('COMPANY', CompanyName()); Params.Add('ENVIRONMENT', GetEnvironmentName()); exit(Params); end; } ``` -------------------------------- ### Implement IDocumentAction InvokeAction Method Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md Implement the InvokeAction method to define custom actions for E-Documents. This example shows how to send an HTTP POST request to an external API, process the response, and update the E-Document status. ```AL procedure InvokeAction(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; ActionContext: Codeunit ActionContext): Boolean var HttpClient: HttpClient; HttpRequestMessage: HttpRequestMessage; HttpResponseMessage: HttpResponseMessage; begin // Initialize the HTTP request HttpRequestMessage.Method := 'POST'; HttpRequestMessage.SetRequestUri('https://api.example.com/documents/reset'); HttpRequestMessage.Content.WriteFromText('{"documentId": "' + EDocument."Document ID" + '"}'); // Send the HTTP request and receive the response HttpClient.Send(HttpRequestMessage, HttpResponseMessage); // Process the response and set status if HttpResponseMessage.IsSuccessStatusCode() then begin ActionContext.SetStatus(Enum::"E-Document Service Status"::"MyStatus"); exit(true) end; exit(false) end; ``` -------------------------------- ### Initialize SharePoint Client Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Prepares the SharePoint client by providing the base URL of the SharePoint site or subsite and the authorization mechanism. ```APIDOC ## Initialize client Prepare client. > provide SharePoint site/subsite address i.e. '.sharepoint.com/sites/Test/'. > Authorization Codeunit. ## Example ```al var SPClient: Codeunit "SharePoint Client"; begin SPClient.Initialize('', SharePointAuthorization); ``` ``` -------------------------------- ### Initialize SharePoint Client Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Initializes the SharePoint Client with the base URL of the SharePoint site and the authorization object. Ensure the base URL is correctly formatted, e.g., '.sharepoint.com/sites/Test/'. ```AL var SPClient: Codeunit "SharePoint Client"; begin SPClient.Initialize('', SharePointAuthorization); ``` -------------------------------- ### Get number of pages Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Pdf/README.md Counts the total number of pages in a PDF document. ```APIDOC ## GetPdfPageCount ### Description Counts the total number of pages in a PDF document. ### Method Signature ```al procedure GetPdfPageCount(PdfStream: InStream): Integer ``` ### Parameters - **PdfStream** (InStream) - The input stream of the PDF document. ### Returns - **Integer** - The total number of pages in the PDF. ### Example ```al procedure Example(PdfStream: InStream) var PDFDocument: Codeunit "PDF Document"; PageCount: Integer; begin PageCount := PDFDocument.GetPdfPageCount(PdfStream); Message('PDF has %1 pages', PageCount); end; ``` ``` -------------------------------- ### Get Number of Pages in PDF Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Pdf/README.md Counts the total number of pages in a PDF document stream. ```AL procedure Example(PdfStream: InStream) var PDFDocument: Codeunit "PDF Document"; PageCount: Integer; begin PageCount := PDFDocument.GetPdfPageCount(PdfStream); Message('PDF has %1 pages', PageCount); end; ``` -------------------------------- ### Get names of embedded files Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Pdf/README.md Retrieves a list of names of all embedded attachments within a PDF document. ```APIDOC ## GetAttachmentNames ### Description Retrieves a list of names of all embedded attachments within a PDF document. ### Method Signature ```al procedure GetAttachmentNames(PdfStream: InStream): List of [Text] ``` ### Parameters - **PdfStream** (InStream) - The input stream of the PDF document. ### Returns - **List of [Text]** - A list containing the names of the embedded attachments. ### Example ```al procedure Example(PdfStream: InStream) var PDFDocument: Codeunit "PDF Document"; AttachmentNames: List of [Text]; AttachmentName: Text; Output: Text; begin Names := PDFDocument.GetAttachmentNames(PdfStream); foreach AttachmentName in AttachmentNames do begin if Output <> '' then Output += ', '; Output += AttachmentName; end; Message('Attachments: %1', Output); end; ``` ``` -------------------------------- ### SurveyTimerActivity Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Microsoft User Feedback/README.md Starts or stops a survey timer activity to track user usage time, which can trigger a survey prompt. ```APIDOC ## SurveyTimerActivity ### Description Starts or stops a survey timer activity. This is used to count user usage times, which can then trigger a survey prompt after a certain threshold is reached. ### Method ```al procedure SurveyTimerActivity(ActivityName: Text; Start: Boolean) ``` ### Parameters - **ActivityName** (Text) - Required - The name of the activity for which the timer is started or stopped. - **Start** (Boolean) - Required - If true, starts the timer; if false, stops the timer. ``` -------------------------------- ### Initialize Graph Authorization Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Use Graph Authorization from the Graph module to create an authorization object with client credentials. Replace placeholders with your actual Tenant ID, Client ID, and Client Secret. ```al var GraphAuth: Codeunit "Graph Authorization"; GraphAuthorization: Interface "Graph Authorization"; begin GraphAuthorization := GraphAuth.CreateAuthorizationWithClientCredentials( '', '', '', 'https://graph.microsoft.com/.default'); ``` -------------------------------- ### Initialize Client Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Initializes the SharePoint Graph Client with the site URL and authorization details. ```APIDOC ## Initialize Client ### Description Initializes the SharePoint Graph Client with the site URL and authorization details. ### Method Codeunit Initialization ### Parameters - **Site URL** (string) - The URL of the SharePoint site. - **Authorization** (Interface "Graph Authorization") - An instance of the Graph Authorization interface. ### Request Example ```al var SPGraphClient: Codeunit "SharePoint Graph Client"; GraphAuthorization: Interface "Graph Authorization"; begin SPGraphClient.Initialize('https://contoso.sharepoint.com/sites/MySite/', GraphAuthorization); ``` ``` -------------------------------- ### Get Files in a SharePoint Folder Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Retrieves data for all files within a specified SharePoint folder. Uses the 'Server Relative Url' of the folder. ```AL var SharePointFolder: Record "SharePoint Folder"; SharePointFile: Record "SharePoint File"; begin SPClient.GetFolderFilesByServerRelativeUrl(SharePointFolder."Server Relative Url", SharePointFile); ``` -------------------------------- ### Upload File via SFTP Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SFTP Client/README.md Demonstrates uploading a file to an SFTP server using username and password authentication. Ensure to add the server's SHA256 fingerprint before initializing the connection. Avoid hardcoding credentials in production environments. ```AL local procedure UploadMyFirstFile() var SFTPClient: Codeunit "SFTP Client"; InStream: InStream; ClearTextPassword: Text; Password: SecretText; SHA256FingerPrint: Text; begin SHA256FingerPrint := 'g3e54QTeJsKxMAo4EyZi+/WSGAnfxI2DkCD69g4bhsw='; SFTPClient.AddFingerPrintSHA256(SHA256FingerPrint); ClearTextPassword := 'password'; // Please dont hardcode credentials Password := ClearTextPassword; SFTPClient.Initialize('example.com', 22, 'username', Password); // Please dont hardcode credentials UploadIntoStream('', InStream); SFTPClient.PutFileStream('data.txt', InStream); SFTPClient.Disconnect(); end; ``` -------------------------------- ### Get Subfolders for a SharePoint Folder Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Retrieves all subfolders for a specified parent SharePoint folder. Uses the 'Server Relative Url' of the parent folder. ```AL var ParentSharePointFolder, SharePointFolder: Record "SharePoint Folder"; begin SPClient.GetSubFoldersByServerRelativeUrl(ParentSharePointFolder."Server Relative Url", SharePointFolder); ``` -------------------------------- ### Get Root Folder for a SharePoint List Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Retrieves the root folder data for a given SharePoint list. Requires the OdataId of the SharePoint List. ```AL var SharePointList: Record "SharePoint List" temporary; SharePointFolder: Record "SharePoint Folder" temporary; begin SPClient.GetDocumentLibraryRootFolder(SharePointList.OdataId, SharePointFolder); ``` -------------------------------- ### Populate Translated Field on Record Get Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Translation/README.md Populates the TranslatedName field with the translation for the 'Name' field when a record is retrieved. This should be used in the OnAfterGetRecord trigger. ```AL trigger OnAfterGetRecord() var Translation: Codeunit Translation; begin HelpAvailable := ''; VideoAvailable := ''; if "Help Url" <> '' then HelpAvailable := HelpLinkTxt; if "Video Url" <> '' then VideoAvailable := VideoLinkTxt; TranslatedName := Translation.Get(Rec, FieldNo(Name)); end; ``` -------------------------------- ### Get Names of Embedded Files Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Pdf/README.md Retrieves a list of names for all embedded files within a PDF document stream. Displays them as a comma-separated string. ```AL procedure Example(PdfStream: InStream) var PDFDocument: Codeunit "PDF Document"; AttachmentNames: List of [Text]; AttachmentName: Text; Output: Text; begin Names := PDFDocument.GetAttachmentNames(PdfStream); foreach AttachmentName in AttachmentNames do begin if Output <> '' then Output += ', '; Output += AttachmentName; end; Message('Attachments: %1', Output); end; ``` -------------------------------- ### Create First Blob in Azure Storage Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Azure Blob Services API/README.md Initializes clients for Azure Blob Storage, creates a container, and uploads a text blob. Requires storage account name, shared key, container name, and blob name. ```AL var procedure CreateMyFirstBlob() var ABSContainerClient: Codeunit "ABS Container Client"; ABSBlobClient: Codeunit "ABS Blob Client"; StorageServiceAuthorization: Codeunit "Storage Service Authorization"; Response: Codeunit "ABS Operation Response"; Authorization: Interface "Storage Service Authorization"; begin Authorization := StorageServiceAuthorization.CreateSharedKey(''); ABSContainerClient.Initialize('', Authorization); ABSContainerClient.CreateContainer(''); ABSBlobClient.Initialize('', '', Authorization); ABSBlobClient.PutBlobBlockBlobText('', 'Yay! This is my first BLOB in Azure Blob Storage services!') end; ``` -------------------------------- ### Lookup and Set Date-Time Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Date-Time Dialog/README.md Use this procedure to display the Date-Time dialog, set an initial date-time value, and retrieve the user's selection. ```AL procedure LookupDateTime(InitialValue: DateTime): DateTime var DateTimeDialog: Page "Date-Time Dialog"; NewValue: DateTime; begin DateTimeDialog.SetDateTime(InitialValue); if DateTimeDialog.RunModal() = Action::OK then NewValue := DateTimeDialog.GetDateTime(); exit(NewValue); end; ``` -------------------------------- ### Create a new list Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Creates a new list on the SharePoint site with the specified title and description. Returns information about the created list. ```APIDOC ## Create a new list Returns single temporary record with information on created list. ```al var SharePointList: Record "SharePoint List" temporary; begin SPClient.CreateList('List Title', 'List Description', SharePointList); ``` ``` -------------------------------- ### Combined Fluent API Usage for AI Feedback Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Microsoft User Feedback/README.md Demonstrates combining multiple fluent API methods to request AI-related feedback with custom questions and answer options. Ensure all necessary dictionaries are initialized before use. ```AL var MicrosoftUserFeedback: Codeunit "Microsoft User Feedback"; ContextProperties: Dictionary of [Text, Text]; AnswerOptions: Dictionary of [Text, Text]; begin // Add context properties ContextProperties.Add('UserRole', 'Power User'); ContextProperties.Add('FeatureVersion', '2.0'); // Setup answer options AnswerOptions.Add('yes', 'Yes'); AnswerOptions.Add('no', 'No'); AnswerOptions.Add('maybe', 'Maybe'); // Combine multiple fluent API methods MicrosoftUserFeedback .SetIsAIFeedback(true) .WithCustomQuestion('would_recommend', 'Would you recommend this feature?') .WithCustomQuestionType(FeedbackQuestionType::SingleChoice) .WithCustomQuestionAnswerOptions(AnswerOptions) .WithCustomQuestionRequiredBehavior(FeedbackRequiredBehavior::Required, true) .RequestLikeFeedback('AIAssistant', 'AI_ASSIST_001', 'AI Assistant Features', Dictionary of [Text, Text].Create(), ContextProperties); end; ``` -------------------------------- ### Prepare E-Document from Blob Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md A placeholder method to create an E-Document from an imported blob. It currently contains no implementation. ```AL procedure PrepareDocument(var EDocument: Record "E-Document"; var CreatedDocumentHeader: RecordRef; var CreatedDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob") begin end; ``` -------------------------------- ### SurveyTimerActivity Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Microsoft User Feedback/README.md Starts or stops a survey timer activity to track user usage time. This can be used to trigger a survey prompt after a certain usage threshold is met. ```APIDOC ## SurveyTimerActivity ### Description Starts or stops a survey timer activity. This is used to start a timer to count up user usage times, which can then trigger a survey prompt after a certain threshold is reached. For example: 5 minutes of usage time from timer start to timer end. ### Method ```al procedure SurveyTimerActivity(ActivityName: Text; Start: Boolean) ``` ### Parameters #### Path Parameters - `ActivityName` (Text) - Required - The name of the activity for which the timer is started or stopped. - `Start` (Boolean) - Required - If true, starts the timer; if false, stops the timer. ``` -------------------------------- ### Run Module Configuration Page Source: https://github.com/fbakkensen/bc-w1/blob/main/ContosoCoffeeDemoDataset/Source/Contoso Coffee Demo Dataset/Coding-Patterns.md This code snippet shows how to run a module's specific configuration page from a codeunit. ```AL ... procedure RunConfigurationPage() begin Page.Run(Page::"Manufacturing Module Setup"); end; procedure CreateSetupData() var ManufacturingDemoDataSetup: Record "Manufacturing Module Setup"; begin ManufacturingDemoDataSetup.InitRecord(); ... end; ... ``` -------------------------------- ### Start or Stop Survey Timer Activity Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Microsoft User Feedback/README.md Manages a timer for user activity to track usage duration. This can be used to trigger surveys after a specific time threshold. ```AL procedure SurveyTimerActivity(ActivityName: Text; Start: Boolean) ``` -------------------------------- ### Initialize SharePoint Graph Client Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Initialize the SharePoint Graph Client with the SharePoint site URL and the created Graph Authorization object. ```al var SPGraphClient: Codeunit "SharePoint Graph Client"; begin SPGraphClient.Initialize('https://contoso.sharepoint.com/sites/MySite/', GraphAuthorization); ``` -------------------------------- ### Event Subscriber for Export File Extension Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md An example event subscriber for the 'OnBeforeExportDataStorage' event on the 'E-Document Log' table. This can be used to specify a file extension when exporting E-Documents. ```AL [EventSubscriber(ObjectType::Table, Database::"E-Document Log", 'OnBeforeExportDataStorage', '', false, false)] local procedure MyProcedure() begin end; ``` -------------------------------- ### Create a New SharePoint List Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Creates a new list on the SharePoint site with a specified title and description. Returns a temporary record containing information about the created list. ```AL var SharePointList: Record "SharePoint List" temporary; begin SPClient.CreateList('List Title', 'List Description', SharePointList); ``` -------------------------------- ### Get Privacy Notice Approval State Without User Interaction Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Privacy Notice/README.md Retrieve the approval state of a privacy notice without prompting the user. The procedure exits if the state is not 'Agreed'. ```AL procedure CallTeamsService() var PrivacyNotice: Codeunit "Privacy Notice"; begin if PrivacyNotice.GetPrivacyNoticeApprovalState('Microsoft Teams') <> "Privacy Notice Approval State"::Agreed then exit; ExternalServiceCallToTeams(); end; ``` -------------------------------- ### Simulating New Numbers with SimulateGetNextNo Source: https://github.com/fbakkensen/bc-w1/blob/main/BusinessFoundation/Source/Business Foundation/NoSeries/readme_refactoring.md This snippet demonstrates using the 'SimulateGetNextNo' function from the 'No. Series - Batch' codeunit to preview the next document number without actually updating the No. Series. If the series does not exist, the number increments by one. ```AL "Document No." := NoSeriesBatch.SimulateGetNextNo(GenJnlBatch."No. Series", Rec."Posting Date", "Document No."); ``` -------------------------------- ### Get Basic E-Document Information from Blob Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md Parses an XML blob to extract basic E-Document information, such as bill-to/pay-to details and document date. Assumes the blob contains XML data with specific nodes. ```AL procedure GetBasicInfo(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob") var XmlDoc: XmlDocument; DocInstr: InStream; NamespaceManager: XmlNamespaceManager; begin // Create an XML document from the blob TempBlob.CreateInStream(DocInstr); XmlDocument.ReadFrom(DocInstr, XmlDoc); // Parse the document to fill EDocument information EDocument."Bill-to/Pay-to No." := CopyStr(GetPEPPOLNode('//cac:InvoiceLine/cbc:ID', XmlDoc, NamespaceManager), 1, 20); EDocument."Bill-to/Pay-to Name" := CopyStr(GetPEPPOLNode('//cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name', XmlDoc, NamespaceManager), 1, 20); Evaluate(EDocument."Document Date", GetPEPPOLNode('//cbc:IssueDate', XmlDoc, NamespaceManager)); EDocument."Document Type" := EDocument."Document Type"::"Purchase Invoice"; end; ``` -------------------------------- ### Track Survey Activity Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Microsoft User Feedback/README.md Monitor user engagement with surveys by timing specific activities. Use SurveyTimerActivity to start and stop timing, and SurveyTriggerActivity to mark specific user actions that might initiate a survey. ```AL var MicrosoftUserFeedback: Codeunit "Microsoft User Feedback"; begin // Start timing user activity MicrosoftUserFeedback.SurveyTimerActivity('DocumentProcessing', true); // ... user performs document processing tasks ... // Stop timing when task is complete MicrosoftUserFeedback.SurveyTimerActivity('DocumentProcessing', false); // Trigger survey based on specific user action MicrosoftUserFeedback.SurveyTriggerActivity('ReportGeneration'); end; ``` -------------------------------- ### Implement GetApprovalStatus for E-Documents Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md Implement the GetApprovalStatus method to check if a sent E-Document has been approved by an external service. This involves preparing and executing an HTTP request and processing the response to set the document's status. ```al procedure GetApprovalStatus(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; ActionContext: Codeunit ActionContext): Boolean var Request: Codeunit Requests; HttpExecutor: Codeunit "Http Executor"; ResponseContent: Text; begin // Prepare the HTTP request Request.Init(); Request.Authenticate().CreateApprovalRequest(EDocument."Document ID"); ActionContext.Http().SetHttpRequestMessage(Request.GetRequest()); // Execute the HTTP request ResponseContent := HttpExecutor.ExecuteHttpRequest(Request, ActionContext.Http().GetHttpResponseMessage()); // Process the response to determine the approval status if ResponseContent.Contains('approved') then begin ActionContext.SetStatus(ActionContext.GetStatus()."Approved"); exit(true); end else if ResponseContent.Contains('rejected') then begin ActionContext.SetStatus(ActionContext.GetStatus()."Rejected"); exit(true); end; exit(false); end; ``` -------------------------------- ### Upload File to SharePoint Folder (with UI) Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/SharePoint/README.md Uploads a file to a specified SharePoint folder, triggering a file selection dialog. Returns data of the created file. ```AL var SharePointFolder: Record "SharePoint Folder"; SharePointFile: Record "SharePoint File"; begin SPClient.AddFileToFolder(SharePointFolder."Server Relative Url", SharePointFile); ``` -------------------------------- ### Iterate Through Service Plans for Each SKU Source: https://github.com/fbakkensen/bc-w1/blob/main/System Application/Source/System Application/Azure AD Licensing/README.md Iterates through each subscribed SKU and then through all of its associated service plans, retrieving their details and inserting them into a local table. Ensure 'YOUR PLAN TABLE' is replaced with your actual table name. ```AL procedure GetPlansBySKUs() var Plan: Record "YOUR PLAN TABLE"; AzureADLic: codeunit "Azure AD Licensing"; begin while AzureADLic.NextSubscribedSKU() do begin AzureADLic.ResetServicePlans(); while AzureADLic.NextServicePlan() do begin Plan.ServicePlanId := AzureADLic.ServicePlanId(); Plan.ServicePlanName := AzureADLic.ServicePlanName(); Plan.SKUId := AzureADLic.SubscribedSKUId(); Plan.insert(); end; end; end; ``` -------------------------------- ### Implement Check Procedure for Sales Header (Basic) Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md Implement the Check procedure to validate essential fields for a Sales Header document before release or post actions. ```al procedure Check(var SourceDocumentHeader: RecordRef; EDocumentService: Record "E-Document Service"; EDocumentProcessingPhase: Enum "E-Document Processing Phase") var SalesHeader: Record "Sales Header"; begin Case SourceDocumentHeader.Number of Database::"Sales Header": begin SourceDocumentHeader.Field(SalesHeader.FieldNo("Customer Posting Group")).TestField(); SourceDocumentHeader.Field(SalesHeader.FieldNo("Posting Date")).TestField(); end; End; ``` -------------------------------- ### Implement IDocumentResponseHandler GetResponse Method Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md Implement the GetResponse procedure to retrieve the status of an asynchronously sent E-Document from an external service. This method prepares an HTTP GET request, sends it, and handles the response to update the E-Document status. ```AL procedure GetResponse( var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; SendContext: Codeunit SendContext ): Boolean var MyServiceSetup: Record MyServiceSetup; HttpClient: HttpClient; HttpRequest: HttpRequestMessage; HttpResponse: HttpResponseMessage; begin // Prepare the HTTP request to check the status of the E-Document HttpRequest := SendContext.Http().GetHttpRequestMessage(); HttpRequest.Method := 'GET'; HttpRequest.SetRequestUri(MyServiceSetup."Service URL" + '/status/' + EDocument."Document ID"); HttpRequest.Headers.Add('Authorization', 'Bearer ' + MyServiceSetup."Access Token"); // Send the HTTP request HttpClient.Send(HttpRequest, HttpResponse); // Set the response in SendContext for automatic logging SendContext.Http().SetHttpResponseMessage(HttpResponse); // Handle the response based on the HTTP status code if HttpResponse.IsSuccessStatusCode() then exit(true); // The document was successfully processed else if HttpResponse.HttpStatusCode() = 202 then exit(false); // The document is still being processed else begin // Log the error and set the status to "Sending Error" EDocumentErrorHelper.LogSimpleErrorMessage(EDocument, 'Error retrieving response: ' + Format(HttpResponse.HttpStatusCode())); exit(false) end; end; ``` -------------------------------- ### Extend Power BI Deployable Report Enum Source: https://github.com/fbakkensen/bc-w1/blob/main/BaseApp/Source/Base Application/Modules/System/PowerBI/README.md Partners can register new reports by extending the 'Power BI Deployable Report' enum. This example shows how to add a new report type named 'My Sales Report'. ```AL enumextension 50000 "My Reports" extends "Power BI Deployable Report" { value(50000; "My Sales Report") { Caption = 'My Sales Report'; Implementation = "Power BI Deployable Report" = "My Sales Report Impl."; } } ``` -------------------------------- ### PrepareDocument Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md Prepares an E-Document from an imported blob. This method is used to create a document based on data contained within a blob. ```APIDOC ## PrepareDocument ### Description Prepares an E-Document from an imported blob. This method is used to create a document based on data contained within a blob. ### Method Signature ```al procedure PrepareDocument(var EDocument: Record "E-Document"; var CreatedDocumentHeader: RecordRef; var CreatedDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob") ``` ### Parameters - **EDocument** (Record "E-Document") - Input/Output - The E-Document record to be prepared. - **CreatedDocumentHeader** (RecordRef) - Input/Output - A reference to the created document header. - **CreatedDocumentLines** (RecordRef) - Input/Output - A reference to the created document lines. - **TempBlob** (Codeunit "Temp Blob") - Input - A temporary blob containing the data to prepare the document. ``` -------------------------------- ### Create E-Document XML File Source: https://github.com/fbakkensen/bc-w1/blob/main/EDocument Core/Source/E-Document Core/README.md This procedure generates an XML file for e-documents, supporting Sales Invoices and Sales Credit Memos. It utilizes the 'E-Document Helper' codeunit for logging errors. ```AL procedure Create(EDocumentService: Record "E-Document Service"; var EDocument: Record "E-Document"; var SourceDocumentHeader: RecordRef; var SourceDocumentLines: RecordRef; var TempBlob: Codeunit "Temp Blob") var EDocumentHelper: Codeunit "E-Document Helper"; OutStr: OutStream; begin TempBlob.CreateOutStream(OutStr); case EDocument."Document Type" of EDocument."Document Type"::"Sales Invoice": if not GenerateInvoiceXMLFile(SourceDocumentHeader, OutStr) then EDocumeneHelper.LogSimpleErrorMessage(EDocument, 'Error <> happened while creating this document'); EDocument."Document Type"::"Sales Credit Memo": if not GenerateCrMemoXMLFile(SourceDocumentHeader, OutStr) then EDocumeneHelper.LogSimpleErrorMessage(EDocument, 'Error <> happened while creating this document'); end; end; ``` -------------------------------- ### Backwards compatible InitSeries with obsolete calls Source: https://github.com/fbakkensen/bc-w1/blob/main/BusinessFoundation/Source/Business Foundation/NoSeries/readme_refactoring.md To maintain backward compatibility with old events, include calls to NoSeriesManagement.RaiseObsoleteOnBeforeInitSeries and NoSeriesManagement.RaiseObsoleteOnAfterInitSeries. ```AL if "No." = '' then begin GLSetup.Get(); GLSetup.TestField("Bank Account Nos."); NoSeriesManagement.RaiseObsoleteOnBeforeInitSeries(GLSetup."Bank Account Nos.", xRec."No. Series", 0D, "No.", "No. Series", IsHandled); if not IsHandled then begin "No. Series" := GLSetup."Bank Account Nos."; if NoSeries.AreRelated(GLSetup."Bank Account Nos.", xRec."No. Series") then "No. Series" := xRec."No. Series"; "No." := NoSeries.GetNextNo("No. Series"); NoSeriesManagement.RaiseObsoleteOnAfterInitSeries("No. Series", GLSetup."Bank Account Nos.", 0D, "No."); end; end; ``` -------------------------------- ### AL Test Procedure for Copilot Feature Source: https://github.com/fbakkensen/bc-w1/blob/main/TestFramework/AITestToolkit/AI Test Toolkit/README.md Example of an AL test procedure designed to evaluate a copilot feature. It utilizes the 'AIT Test Context' codeunit to retrieve input data and expected outputs, and asserts the result of the copilot feature's call to an LLM. ```al [Test] procedure TestCopilotFeature() var AITestContext: Codeunit "AIT Test Context"; Question: Text; Output: Integer; ExpectedOutput: Integer; begin // [Scenario] AI Eval // Call the LLM to get an output Output := CopilotFeature.CallLLM(Question); // Get the expected output from the dataset ExpectedOutput := AITestContext.GetExpectedData().ValueAsInteger(); // Assert the result Assert.AreEqual(ExpectedOutput, Output, ''); end; ```