D365 Business Central Archives - Page 4 of 32 - - Page 4

Category Archives: D365 Business Central

The Power of Real-Time Data: How Business Central Enhances Pharma Decision-Making

Consider the scenario of a pharmaceutical manufacturer facing an unexpected shortage of essential raw materials. This situation inevitably leads to production delays, potentially causing missed deadlines and expensive product recalls. In today’s dynamic pharmaceutical sector, where adherence to regulations and a responsive supply chain are crucial, outdated information poses a significant risk. What if these disruptions could be predicted and mitigated before they materialize? What if you had immediate, comprehensive visibility into your entire operational landscape? This is the advantage offered by real-time data, and solutions like Microsoft Business Central are spearheading the evolution of pharmaceutical decision-making. The Shortcomings of Traditional Pharmaceutical Data: Historically, the pharmaceutical industry has operated with data that is often delayed. Reports generated several days or weeks after events occur provide a historical perspective, lacking the current operational awareness needed for effective management. This results in: Real-Time Data: A Game-Changer for Pharma: Our Business Central Pharma module provides a unified platform that delivers real-time visibility across your entire pharmaceutical operation. This empowers you to: Practical Implementation and Tangible Benefits: Implementing Business Central can seem daunting, but the benefits are undeniable. By having the end-to-end process in one system can be very beneficiary. The Future of Data-Driven Pharma: The future of pharma lies in leveraging the power of data. Imagine being able to anticipate potential supply chain disruptions or quality issues before they occur. This is the promise of data-driven Pharma Module. To encapsulate, in the pharmaceutical industry, where precision and speed are critical, real-time data is no longer a luxury—it’s a necessity. Our Business Central pharma module helps companies to embrace the data revolution, enabling faster, more informed decisions that drive efficiency, compliance, and growth. Ready to unlock the power of real-time data for your pharmaceutical operations? Contact us today at transform@cloudfonts.com to learn how our Business Central pharma module can transform your business.

Share Story :

Enhancing the Recurring General Journal with Automated Approval Workflows in Dynamics 365 Business Central

In any accounting or financial system, ensuring that the proper approvals are in place is critical for maintaining audit trails and accountability. Microsoft Dynamics 365 Business Central (BC) offers powerful approval workflows that can automate this process, ensuring compliance and accuracy in your financial entries. This blog will guide you through creating an extension for the Recurring General Journal page, enabling automated approval requests for journal batches and journal lines. Understanding the Recurring General Journal The Recurring General Journal in Business Central allows users to post transactions that occur periodically, such as monthly expenses. However, posting journal entries often requires an approval process to guarantee compliance and accuracy. Our extension will empower users to request approvals, cancel requests, and manage the approval status directly from the Recurring General Journal. Solution Outline This extension will: Code Here’s the complete PageExtension code that extends the Recurring General Journal page: pageextension 50103 RecurringGenJnl extends “Recurring General Journal” {     layout     {         addafter(CurrentJnlBatchName)         {             field(StatusLine; Rec.StatusBatch)             {                 ApplicationArea = All;                 Caption = ‘Approval Status’;                 Editable = false;                 Visible = true;             }         }     }     actions     {         addafter(“F&unctions”)         {             group(“Request Approval”)             {                 Caption = ‘Request Approval’;                 group(SendApprovalRequest)                 {                     Caption = ‘Send Approval Request’;                     Image = SendApprovalRequest;                     action(SendApprovalRequestJournalLine)                     {                         ApplicationArea = Basic, Suite;                         Caption = ‘Journal Batch’;                         Image = SendApprovalRequest;                         Enabled = true;                         ToolTip = ‘Send all journal lines for approval, also those that you may not see because of filters.’;                         trigger OnAction()                         var                             GenJournalLine: Record “Gen. Journal Line”;                             GenJournalBatch: Record “Gen. Journal Batch”;                             ApprovalsMgmt: Codeunit “Approvals Mgmt.”;                             IsLineValid: Boolean;                         begin                             GenJournalBatch.Get(Rec.”Journal Template Name”, Rec.”Journal Batch Name”);                             if GenJournalLine.SetCurrentKey(“Journal Template Name”, “Journal Batch Name”) then begin                                 GenJournalLine.SetRange(“Journal Template Name”, GenJournalBatch.”Journal Template Name”);                                 GenJournalLine.SetRange(“Journal Batch Name”, GenJournalBatch.”Name”);                                 if GenJournalLine.FindSet() then                                     ApprovalsMgmt.TrySendJournalBatchApprovalRequest(GenJournalLine);                                 SetControlAppearanceFromBatch();                                 SetControlAppearance();                             end;                         end;                     }                 }                 group(CancelApprovalRequest)                 {                     Caption = ‘Cancel Approval Request’;                     Image = Cancel;                     Enabled = true;                     action(CancelApprovalRequestJournalBatch)                     {                         ApplicationArea = Basic, Suite;                         Caption = ‘Journal Batch’;                         Image = CancelApprovalRequest;                         ToolTip = ‘Cancel sending all journal lines for approval, also those that you may not see because of filters.’;                         trigger OnAction()                         var                             ApprovalsMgmt: Codeunit “Approvals Mgmt.”;                             GenJournalBatch: Record “Gen. Journal Batch”;                             GenJournalLine: Record “Gen. Journal Line”;                             IsLineValid: Boolean;                         begin                             GenJournalBatch.Get(Rec.”Journal Template Name”, Rec.”Journal Batch Name”);                             //ApprovalsMgmt.TryCancelJournalBatchApprovalRequest(GenJournalLine);                             if GenJournalLine.SetCurrentKey(“Journal Template Name”, “Journal Batch Name”) then begin                                 GenJournalLine.SetRange(“Journal Template Name”, GenJournalBatch.”Journal Template Name”);                                 GenJournalLine.SetRange(“Journal Batch Name”, GenJournalBatch.”Name”);                                 if GenJournalLine.FindSet() then                                     IsLineValid := true;                                 if (GenJournalLine.Status.ToUpper() = ‘OPEN’) or                                    (GenJournalLine.Status.ToUpper() = ‘APPROVED’) then begin                                     IsLineValid := false;                                 end;                                 if IsLineValid then                                     ApprovalsMgmt.TryCancelJournalBatchApprovalRequest(GenJournalLine);                                 //ApprovalsMgmt.TryCancelJournalLineApprovalRequests(GenJournalLine);                             end else begin                                 Message(‘Gen. Journal Batch not found for the provided template and batch names.’);                             end;                             SetControlAppearanceFromBatch();                             SetControlAppearance();                         end;                     }                 }                 group(Approval)                 {                     Caption = ‘Approval’;                     action(Approve)                     {                         ApplicationArea = All;                         Caption = ‘Approve’;                         Image = Approve;                         ToolTip = ‘Approve the requested changes.’;                         Visible = true;                         trigger OnAction()                         var                             ApprovalsMgmt: Codeunit “Approvals Mgmt.”;                         begin                             ApprovalsMgmt.ApproveGenJournalLineRequest(Rec);                         end;                     }                     action(Reject)                     {                         ApplicationArea = All;                         Caption = ‘Reject’;                         Image = Reject;                         ToolTip = ‘Reject the approval request.’;                         Visible = true;                         trigger OnAction()                         var                             ApprovalsMgmt: Codeunit “Approvals Mgmt.”;                         begin                             ApprovalsMgmt.RejectGenJournalLineRequest(Rec);                         end;                     }                     action(Delegate)                     {                         ApplicationArea = All;                         Caption = ‘Delegate’;                         Image = Delegate;                         ToolTip = ‘Delegate the approval to a substitute approver.’;                         Visible = OpenApprovalEntriesExistForCurrUser;                         trigger OnAction()                         var                             ApprovalsMgmt: Codeunit “Approvals Mgmt.”;                         begin                             ApprovalsMgmt.DelegateGenJournalLineRequest(Rec);                         end;                     }                 }             }         }     }     trigger OnOpenPage()     begin         SetControlAppearanceFromBatch();     end;     trigger OnAfterGetCurrRecord()     var         GenJournalBatch: Record “Gen. Journal Batch”;     begin         EnableApplyEntriesAction();         SetControlAppearance();         if GenJournalBatch.Get(GetJournalTemplateNameFromFilter(), CurrentJnlBatchName) then             SetApprovalStateForBatch(GenJournalBatch, Rec, OpenApprovalEntriesExistForCurrUser, OpenApprovalEntriesOnJnlBatchExist, OpenApprovalEntriesOnBatchOrAnyJnlLineExist, CanCancelApprovalForJnlBatch, CanRequestFlowApprovalForBatch, CanCancelFlowApprovalForBatch, CanRequestFlowApprovalForBatchAndAllLines, ApprovalEntriesExistSentByCurrentUser, EnabledGenJnlBatchWorkflowsExist, EnabledGenJnlLineWorkflowsExist);         ApprovalMgmt.GetGenJnlBatchApprovalStatus(Rec, Rec.StatusBatch, EnabledGenJnlBatchWorkflowsExist);     end;     trigger OnAfterGetRecord()     var         GenJournalLine: Record “Gen. Journal Line”;         GenJournalBatch: Record “Gen. Journal Batch”;         ApprovalsMgmt: Codeunit “Approvals Mgmt.”;     begin         GenJnlManagement.GetAccounts(Rec, AccName, BalAccName);         Rec.ShowShortcutDimCode(ShortcutDimCode);         SetUserInteractions();         GenJournalBatch.Get(Rec.”Journal Template Name”, Rec.”Journal Batch Name”);         if GenJournalLine.SetCurrentKey(“Journal Template Name”, “Journal Batch Name”) then begin             GenJournalLine.SetRange(“Journal Template Name”, GenJournalBatch.”Journal Template Name”);             GenJournalLine.SetRange(“Journal Batch Name”, GenJournalBatch.”Name”);             if GenJournalLine.FindSet() then                 repeat                     ApprovalMgmt.GetGenJnlLineApprovalStatus(Rec, Rec.StatusBatch, EnabledGenJnlLineWorkflowsExist);                 until GenJournalLine.Next() = 0;         end;     end;     trigger OnModifyRecord(): Boolean     var         GenJournalBatch: Record “Gen. Journal Batch”;         GenJournalLine: Record “Gen. Journal Line”;         ApprovalsMgmt: Codeunit “Approvals Mgmt.”;         IsLineValid: Boolean;     begin         SetUserInteractions();         ApprovalMgmt.CleanGenJournalApprovalStatus(Rec, Rec.StatusBatch, Rec.Status);         if Rec.StatusBatch = ‘Pending Approval’ then             Error(‘Modification not allowed. The journal batch “%1” has entries with a status of “Pending Approval”. Please approve, reject, or cancel these entries before making changes.’, GenJournalBatch.”Name”);     end;     var         GenJnlManagement: Codeunit GenJnlManagement;         JournalErrorsMgt: Codeunit “Journal Errors Mgt.”;         BackgroundErrorHandlingMgt: Codeunit “Background Error Handling Mgt.”;         ApprovalMgmt: Codeunit “Approvals Mgmt.”;         ChangeExchangeRate: Page “Change Exchange Rate”;         GLReconcile: Page Reconciliation;         GenJnlBatchApprovalStatus: Text[20];         GenJnlLineApprovalStatus: Text[20];         Balance: Decimal;         TotalBalance: Decimal;         NumberOfRecords: Integer;         ShowBalance: Boolean;         ShowTotalBalance: Boolean;         HasIncomingDocument: Boolean;         BalanceVisible: Boolean;         TotalBalanceVisible: Boolean;         StyleTxt: Text;         ApprovalEntriesExistSentByCurrentUser: Boolean;         OpenApprovalEntriesExistForCurrUser: Boolean;         OpenApprovalEntriesOnJnlBatchExist: Boolean;         OpenApprovalEntriesOnJnlLineExist: Boolean;         OpenApprovalEntriesOnBatchOrCurrJnlLineExist: Boolean;         OpenApprovalEntriesOnBatchOrAnyJnlLineExist: Boolean;         EnabledGenJnlLineWorkflowsExist: Boolean;         EnabledGenJnlBatchWorkflowsExist: Boolean;         ShowWorkflowStatusOnBatch: Boolean;         ShowWorkflowStatusOnLine: Boolean;         CanCancelApprovalForJnlBatch: Boolean;         CanCancelApprovalForJnlLine: Boolean;         ImportPayrollTransactionsAvailable: Boolean;         CanRequestFlowApprovalForBatch: Boolean;         CanRequestFlowApprovalForBatchAndAllLines: Boolean;         CanRequestFlowApprovalForBatchAndCurrentLine: Boolean;         CanCancelFlowApprovalForBatch: Boolean;         CanCancelFlowApprovalForLine: Boolean;         BackgroundErrorCheck: Boolean;         ShowAllLinesEnabled: Boolean;         CurrentPostingDate: Date;     protected var         ApplyEntriesActionEnabled: Boolean;         IsSimplePage: Boolean;     local procedure EnableApplyEntriesAction()     begin         ApplyEntriesActionEnabled :=           (Rec.”Account Type” in [Rec.”Account Type”::Customer, Rec.”Account Type”::Vendor, Rec.”Account Type”::Employee]) or           (Rec.”Bal. Account Type” in [Rec.”Bal. Account Type”::Customer, Rec.”Bal. Account Type”::Vendor, Rec.”Bal. Account Type”::Employee]);         OnAfterEnableApplyEntriesAction(Rec, ApplyEntriesActionEnabled);     end;     local procedure CurrentJnlBatchNameOnAfterVali()     begin         CurrPage.SaveRecord();         GenJnlManagement.SetName(CurrentJnlBatchName, Rec);         SetControlAppearanceFromBatch();         CurrPage.Update(false);     end;     procedure SetUserInteractions()     begin         StyleTxt := Rec.GetStyle();     end;     local procedure GetCurrentlySelectedLines(var GenJournalLine: Record “Gen. Journal Line”): Boolean     begin         CurrPage.SetSelectionFilter(GenJournalLine);         exit(GenJournalLine.FindSet());     end;     local procedure GetPostingDate(): Date     begin         if IsSimplePage then             exit(CurrentPostingDate);         exit(Workdate());     end;     internal procedure SetApprovalState(RecordId: RecordId; OpenApprovalEntriesOnJournalBatchExist: Boolean; LocalCanRequestFlowApprovalForBatch: Boolean; var LocalCanCancelFlowApprovalForLine: Boolean; var OpenApprovalEntriesExistForCurrentUser: Boolean; var OpenApprovalEntriesOnJournalLineExist: Boolean; var OpenApprovalEntriesOnBatchOrCurrentJournalLineExist: Boolean; var CanCancelApprovalForJournalLine: Boolean; var LocalCanRequestFlowApprovalForBatchAndCurrentLine: Boolean)     var         ApprovalsMgmt: Codeunit “Approvals Mgmt.”;         WorkflowWebhookManagement: Codeunit “Workflow Webhook Management”;         CanRequestFlowApprovalForLine: Boolean;     begin         OpenApprovalEntriesExistForCurrentUser := OpenApprovalEntriesExistForCurrentUser or ApprovalsMgmt.HasOpenApprovalEntriesForCurrentUser(RecordId);         OpenApprovalEntriesOnJournalLineExist := ApprovalsMgmt.HasOpenApprovalEntries(RecordId);         OpenApprovalEntriesOnBatchOrCurrentJournalLineExist := OpenApprovalEntriesOnJournalBatchExist or OpenApprovalEntriesOnJournalLineExist;         CanCancelApprovalForJournalLine := ApprovalsMgmt.CanCancelApprovalForRecord(RecordId);         WorkflowWebhookManagement.GetCanRequestAndCanCancel(RecordId, CanRequestFlowApprovalForLine, LocalCanCancelFlowApprovalForLine);         LocalCanRequestFlowApprovalForBatchAndCurrentLine := LocalCanRequestFlowApprovalForBatch and CanRequestFlowApprovalForLine;     end;     internal procedure SetApprovalStateForBatch(GenJournalBatch: Record “Gen. Journal Batch”; GenJournalLine: Record “Gen. Journal Line”; var OpenApprovalEntriesExistForCurrentUser: Boolean; var OpenApprovalEntriesOnJournalBatchExist: Boolean; var OpenApprovalEntriesOnBatchOrAnyJournalLineExist: Boolean; var CanCancelApprovalForJournalBatch: Boolean; var LocalCanRequestFlowApprovalForBatch: Boolean; var LocalCanCancelFlowApprovalForBatch: Boolean; var LocalCanRequestFlowApprovalForBatchAndAllLines: Boolean; var LocalApprovalEntriesExistSentByCurrentUser: Boolean; var EnabledGeneralJournalBatchWorkflowsExist: Boolean; var EnabledGeneralJournalLineWorkflowsExist: Boolean)     var         ApprovalsMgmt: Codeunit “Approvals Mgmt.”;         WorkflowWebhookManagement: Codeunit “Workflow Webhook Management”;         WorkflowEventHandling: Codeunit “Workflow Event Handling”;         WorkflowManagement: Codeunit “Workflow Management”;         CanRequestFlowApprovalForAllLines: Boolean;     begin         OpenApprovalEntriesExistForCurrentUser := OpenApprovalEntriesExistForCurrentUser or ApprovalsMgmt.HasOpenApprovalEntriesForCurrentUser(GenJournalBatch.RecordId);         OpenApprovalEntriesOnJournalBatchExist := ApprovalsMgmt.HasOpenApprovalEntries(GenJournalBatch.RecordId);         OpenApprovalEntriesOnBatchOrAnyJournalLineExist := OpenApprovalEntriesOnJournalBatchExist or ApprovalsMgmt.HasAnyOpenJournalLineApprovalEntries(GenJournalLine.”Journal Template Name”, GenJournalLine.”Journal Batch Name”);         CanCancelApprovalForJournalBatch := … Continue reading Enhancing the Recurring General Journal with Automated Approval Workflows in Dynamics 365 Business Central

Share Story :

Managing Profile Pictures on Custom Pages in Microsoft Dynamics 365 Business Central

When creating custom pages in Business Central, sometimes you need to allow users to handle profile pictures. Whether you’re working with a custom Employee Profile page or another entity, it’s crucial to provide intuitive ways for users to manage their pictures. In this blog, we’ll walk through the process of implementing four key actions for handling profile pictures in custom pages: These features can be achieved using AL (the programming language for Business Central). Setting Up the Custom UserProfile Page Let’s first define the User Profile page where the user can manage their profile picture. This page will provide the fields for Profile ID, Profile Name, and a FactBox to display the profile picture. Code: page 50213 “UserProfileCard” {     PageType = Card;     SourceTable = “UserProfile”;     ApplicationArea = All;     Caption = ‘User Profile’;     layout     {         area(content)         {             group(Group)             {                 field(“Profile ID”; Rec.”Profile ID”)                 {                     ApplicationArea = All;                 }                 field(“Profile Name”; Rec.”Profile Name”)                 {                     ApplicationArea = All;                 }             }         }         // FactBox area to display profile picture         area(factboxes)         {             part(“Profile Picture FactBox”; “ProfilePictureFactBoxPart”)             {                 ApplicationArea = All;             }         }     }     actions     {         area(Processing)         {             action(“Take Picture”)             {                 ApplicationArea = All;                 trigger OnAction()                 var                     Camera: Codeunit “Camera”;                     InS: InStream;                     PicName: Text;                 begin                     // Validate the selected profile                     if not IsProfileSelected(Rec.”Profile ID”) then                         exit;                     // Check if the camera is available                     if Camera.IsAvailable() then begin                         // Get and import the picture                         if Camera.GetPicture(InS, PicName) then begin                             // Import the picture into the profile record                             Rec.”Profile Picture”.ImportStream(InS, PicName);                             Rec.Modify(); // Save the modified record                             Message(‘Profile picture updated successfully.’);                         end                         else                             Message(‘Failed to capture the picture. Try again.’);                     end                     else                         Message(‘No camera detected. Please connect a camera.’);                 end;             }             fileuploadaction(“Import Picture”)             {                 ApplicationArea = All;                 Caption = ‘Import’;                 Image = Import;                 ToolTip = ‘Import a picture file.’;                 trigger OnAction(Files: List of [FileUpload])                 var                     FileName: Text;                     InStream: InStream;                     FileUpload: FileUpload;                 begin                     Rec.TestField(“Profile ID”);                     if Rec.”Profile Name” = ” then                         Error(MustSpecifyNameErr);                     if Rec.”Profile Picture”.HasValue() then                         if not Confirm(OverrideImageQst) then                             exit;                     // Ensure the file is valid                     if Files.Count = 0 then                         Error(‘No file selected.’);                     // Iterate through the Files list (typically just one file)                     foreach FileUpload in Files do begin                         // Create the InStream from the current file                         FileUpload.CreateInStream(InStream);                         Rec.”Profile Picture”.ImportStream(InStream, FileName);                     end;                     Rec.Modify(true);                     Message(‘Picture imported successfully: %1’, FileName);                 end;             }             action(“Export Picture”)             {                 ApplicationArea = All;                 Caption = ‘Export’;                 Image = Export;                 ToolTip = ‘Export the picture to a file.’;                 trigger OnAction()                 var                     FileName: Text;                     OutStream: OutStream;                     InStream: InStream;                     TempBlob: Codeunit “Temp Blob”; // Helps with the stream conversion                 begin                     Rec.TestField(“Profile ID”);                     Rec.TestField(“Profile Name”);                     // Ensure there’s a profile picture to export                     if not Rec.”Profile Picture”.HasValue() then begin                         Message(‘No profile picture found to export.’);                         exit;                     end;                     // Generate a file name for the exported picture                     FileName := Rec.”Profile Name” + ‘_ProfilePicture.jpg’;                     // Export the image to an OutStream via TempBlob                     TempBlob.CreateOutStream(OutStream); // Prepare the OutStream                     Rec.”Profile Picture”.ExportStream(OutStream); // Export the Media field content into the OutStream                     TempBlob.CreateInStream(InStream); // Create InStream from TempBlob to use for download                     // Trigger the file download                     DownloadFromStream(InStream, FileName, ”, ”, FileName);                     // Show success message                     Message(‘Profile picture exported successfully as %1’, FileName);                 end;             }             action(“Delete Picture”)             {                 ApplicationArea = All;                 Caption = ‘Delete’;                 Image = Delete;                 ToolTip = ‘Delete the picture.’;                 trigger OnAction()                 begin                     Rec.TestField(“Profile ID”);                     if not Confirm(DeleteImageQst) then                         exit;                     Clear(Rec.”Profile Picture”);                     Rec.Modify(true);                 end;             }         }     }     trigger OnAfterGetCurrRecord()     begin         SetEditableOnPictureActions();     end;     trigger OnOpenPage()     begin         CameraAvailable := Camera.IsAvailable();     end;     var         Camera: Codeunit Camera;         CameraAvailable: Boolean;         OverrideImageQst: Label ‘The existing picture will be replaced. Do you want to continue?’;         DeleteImageQst: Label ‘Are you sure you want to delete the picture?’;         SelectPictureTxt: Label ‘Select a picture to upload’;         FileManagement: Codeunit “File Management”;         MustSpecifyNameErr: Label ‘You must specify a profile name before you can import a picture.’;     local procedure SetEditableOnPictureActions()     begin         // Enabling or disabling the delete/export actions based on whether a picture is present.     end;     // Function to check if a profile is selected     procedure IsProfileSelected(ProfileID: Code[20]): Boolean     begin         if ProfileID = ” then begin             Message(‘Please select a profile first!’);             exit(False);         end;         exit(True);     end; } Understanding the Key Actions Let’s break down the actions implemented in this custom UserProfile page: 1. Take Picture This action utilizes the Camera Codeunit to capture a picture. If the camera is connected, it will fetch an image and store it in the “Profile Picture” field. 2. Import (Upload) Picture The Import Picture action allows users to upload a picture from their local system into the “Profile Picture” field. It uses the FileUpload control and confirms if the existing image should be replaced. 3. Export Picture The Export Picture action downloads the profile picture to the user’s system. The image is exported to an OutStream, then triggered for download using DownloadFromStream. 4. Delete Picture The Delete Picture action clears the profile picture field. It prompts for confirmation before removing the image. Benefits To encapsulate, in standard Business Central, the functionality for managing user profiles and pictures is built in. However, when working with custom pages, you often need to implement these features manually. By using the actions … Continue reading Managing Profile Pictures on Custom Pages in Microsoft Dynamics 365 Business Central

Share Story :

Integrating Sales, Project Management, and Billing with our Quick Start offer

Project Operations and Business Central together make a great ‘Better-Together’ pair for Services Automation which connects all the way from Sales to Billing.  However, from an implementation standpoint, doesn’t it look like a long-shot since there’s no out-of-the-box integration capability available?  Well, that’s where our PO-BC connector comes in and this Quick Start offer makes it a no-brainer for connecting getting up and running on Project Operations connected with Business Central end-to-end.  Let’s see how to do it and what gets configured.   What is the offer about?  First, you can familiarize yourself with the PO-BC Integration module by CloudFronts which connects Project Operations to Business Central for the business transactions between the two systems.  We have mentioned the pricing on the page, however, let me walk you through what’s get setup in this Quick Start –     PO Setup – No Customization  BC Setup – No Customization  PO-BC Integration Setup with Master Data Sync  Custom Reports – Project Profitability, Resource Allocation, AR Report & Invoice Schedule Report  To conclude, given that the above gets setup, you now have an end-to-end Project Operations to Business Central connected environment with our PO-BC connector setup. This helps you not miss another billing and ensures billing excellence.   We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfonts.com.

Share Story :

Understanding and Analyzing Customer Ledger Data with Business Charts in Dynamics 365

In today’s business world, understanding your financial data is crucial for making informed decisions. One of the key areas of focus for businesses is tracking customer payments and outstanding invoices. With Dynamics 365, you can leverage customer ledger entries to provide visual insights into customer behaviors, payment patterns, and outstanding amounts. These insights help businesses optimize collections, improve cash flow, and make data-driven decisions. In this blog, we’ll explore how to analyze Customer Ledger Entries through Business Charts in Dynamics 365, focusing on Outstanding Invoices, Payments Applied, and Aging of Outstanding Amounts. What Are Customer Ledger Entries? Customer Ledger Entries in Dynamics 365 track all transactions related to a customer, including invoices, payments, credit memos, and adjustments. Each entry contains details such as: By analyzing this data, businesses can gain valuable insights into a customer’s payment habits, outstanding debts, and the status of their invoices. Why Use Business Charts? Business Charts in Dynamics 365 provide a visual representation of your data, making it easier to spot trends and gain actionable insights. Instead of manually sorting through customer ledger entries, you can use charts to instantly assess: This allows teams to make timely decisions about follow-ups with customers and plan for collections. Creating Charts to Analyze Customer Ledger Data Let’s dive into some key charting logic you can apply to Customer Ledger Entries in Dynamics 365 to get more detailed insights into your data. 1. Outstanding Invoices (Remaining Amount per Invoice) The first and most essential data point to track is the Remaining Amount of each invoice. By grouping this data by invoice number, you can quickly identify which invoices are outstanding and need to be followed up. Logic: Buffer.AddMeasure(‘Remaining Amount’, 2, Buffer.”Data Type”::Decimal, ChartType.AsInteger()); Buffer.SetXAxis(‘Document No.’, Buffer.”Data Type”::String); // Group by invoice number The chart will help visualize which invoices are outstanding and need to be prioritized for payment. Code page 50215 “Business Charts” {     ApplicationArea = All;     Caption = ‘Business Charts’;     PageType = CardPart;     UsageCategory = Administration;     layout     {         area(Content)         {             usercontrol(chart; BusinessChart)             {                 ApplicationArea = All;                 trigger AddInReady()                 var                     Buffer: Record “Business Chart Buffer” temporary;                     CustLedgerEntry: Record “Cust. Ledger Entry”;                     Customer: Record Customer;                     ChartType: Enum “Business Chart Type”;                     AppliedAmount: Decimal;                     RemainingAmount: Decimal;                     s: Integer;                 begin                     // Initialize the chart buffer and variables                     Buffer.Initialize();                     ChartType := “Business Chart Type”::Pie; // Use a bar chart for better visual representation                     // Add measure for ‘Remaining Amount’                     Buffer.AddMeasure(‘Remaining Amount’, 2, Buffer.”Data Type”::Decimal, ChartType.AsInteger());                     // Set X-axis to ‘Invoice No.’ for grouping data by invoice                     Buffer.SetXAxis(‘Document No.’, Buffer.”Data Type”::String);                     // Loop through all customers                     if Customer.FindSet() then begin                         repeat                             // Loop through Customer Ledger Entries to accumulate remaining amounts                             if CustLedgerEntry.FindSet() then begin                                 repeat                                     CustLedgerEntry.CalcFields(“Remaining Amount”);                                     // Only accumulate amounts for the current customer based on Customer No.                                     if CustLedgerEntry.”Customer No.” = Customer.”No.” then begin                                         // If it is an Invoice, accumulate Remaining Amount                                         if CustLedgerEntry.”Document Type” = “Gen. Journal Document Type”::Invoice then begin                                             Buffer.AddColumn(CustLedgerEntry.”Document No.”);  // Label by Invoice No.                                             Buffer.SetValueByIndex(0, s, CustLedgerEntry.”Remaining Amount”);  // Set RemainingAmount for the invoice                                             s += 1;                                         end;                                     end;                                 until CustLedgerEntry.Next() = 0;                             end;                         until Customer.Next() = 0;                     end;                     // Update the chart with the accumulated data                     if s > 0 then                         Buffer.UpdateChart(CurrPage.Chart)                     else                         Message(‘No outstanding invoices to display in the chart.’);                 end;             }         }     } } 2. Payments Applied (Amount Applied to Invoices) Another important metric is the Amount Applied to customer invoices. Tracking payments allows you to understand customer payment behavior and outstanding balances. By focusing on Payments, you can track how much a customer has paid against their total balance. Logic: Buffer.AddMeasure(‘Amount Applied’, 2, Buffer.”Data Type”::Decimal, ChartType.AsInteger()); Buffer.SetXAxis(‘Customer No.’, Buffer.”Data Type”::String); // Group by customer This chart will help businesses track customer payments and identify any customers with overdue payments. 3. Aging of Outstanding Amounts (Bucketed by Days Overdue) Aging reports are an essential tool for understanding the timeliness of payments. By grouping outstanding amounts into aging buckets (e.g., 0-30 days, 31-60 days, etc.), businesses can better assess which invoices are overdue and prioritize collection efforts. Logic: // Calculate aging based on Due Date if (Today – CustLedgerEntry.”Due Date”) <= 30 then     AgingBucket := ‘0-30 Days’ elseif (Today – CustLedgerEntry.”Due Date”) <= 60 then     AgingBucket := ’31-60 Days’ Buffer.SetXAxis(‘Aging Bucket’, Buffer.”Data Type”::String); // Group by aging bucket This chart will provide a clear picture of which invoices are overdue and for how long, helping businesses prioritize collections. Benefits of Using Business Charts for Customer Ledger Analysis By leveraging Customer Ledger Entries and Business Charts in Dynamics 365, businesses can transform raw data into valuable insights. Visualizing outstanding invoices, payments applied, and aging amounts helps businesses prioritize collections, forecast cash flow, and ultimately improve their financial health. These charts make it easier for accounting and finance teams to manage customer payments and reduce the risk of overdue balances. The ability to track customer behavior and quickly identify payment issues gives businesses a competitive edge, helping them maintain a healthy cash flow and strong customer relationships. We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfonts.com.

Share Story :

Mastering Date Manipulation with CALCDATE in Microsoft Dynamics 365 Business Central

Microsoft Dynamics 365 Business Central provides a comprehensive suite of tools designed to streamline business processes, and one of the most powerful tools for managing dates and times is the CALCDATE function. This versatile function enables users to perform complex date calculations with ease, making it indispensable for developers, consultants, and power users. In this blog post, we’ll dive deep into the CALCDATE function, explain its syntax, and explore how you can leverage it in your Business Central environment. Understanding CALCDATE The CALCDATE function is used to calculate a new date based on a specific date expression and an optional reference date. It is particularly helpful in scenarios where you need to determine dates relative to a given point in time. This could include calculating due dates, forecasting future events, setting up recurring transactions, or determining any other date relative to the system’s current or a user-defined date. For example, if you need to find the first day of next month or calculate a due date based on the current date, CALCDATE can handle these tasks efficiently. Syntax of CALCDATE The syntax of the CALCDATE function is simple, but the power lies in how you use the date expressions to represent relative time periods. NewDate := System.CalcDate(DateExpression: Text [, Date: Date]) Parameters DateExpression (Type: Text): This is the key input to the function, where you specify the date you want to calculate. The date expression can represent a variety of time periods, ranging from days to weeks, months, quarters, and years. The expression is evaluated from left to right, and each subexpression is processed one at a time. The valid syntax for the date expression follows a set of rules: Subexpression: A date expression consists of one or more subexpressions, each of which may be prefixed with a + or – sign. The subexpression can specify a time unit (day, week, month, etc.) along with a number. Here’s the structure of a typical date expression: <Subexpression> = [<Sign>] <Term> <Sign> = + | – <Term> = <Number><Unit> | <Unit><Number> | <Prefix><Unit> Examples of valid date expressions: The calendar in Business Central starts on Monday and ends on Sunday, where Monday is considered weekday 1 and Sunday is weekday 7. An invalid date expression, such as specifying an incorrect syntax, will result in a runtime error. 2. [Optional] Date (Type: Date):This optional parameter is used to define the reference date. If you omit it, the system defaults to the current date. You can specify any date here, and CALCDATE will perform the calculation based on that reference date instead of the current system date. Return Value Example: pageextension 50103 CustomerPageExt1 extends “Customer Card” {     trigger OnOpenPage()     var         StartDate: Date;         EndDate: Date;         FirstDateofPreviousMonth: Date;         LastDateofPreviousMonth: Date;         FirstDateofNextMonth: Date;         LastDateofNextMonth: Date;         TodayDate: Date;         FirstDateofYear: Date;         LastDateofYear: Date;         FirstDayOfNextQuarter: Date;         LastDayOfCurrentQuarter: Date;         FirstDayOfNextWeek: Date;         FirstDayOfNextWeek10D: Date;     begin         // Current Month Start and End Dates         StartDate := System.CalcDate(‘<-CM>’, Today);         EndDate := System.CalcDate(‘<CM>’, Today);         // Previous Month Start and End Dates         TodayDate := TODAY;         FirstDateOfPreviousMonth := CALCDATE(‘<-1M>’, CALCDATE(‘<-CM>’, TodayDate));         LastDateOfPreviousMonth := CALCDATE(‘<-1M>’, CALCDATE(‘<CM>+1D’, TodayDate) – 1);         // Next Month Start and End Dates         FirstDateOfNextMonth := CALCDATE(‘<+1M>’, CALCDATE(‘<-CM>’, TodayDate));         LastDateOfNextMonth := CALCDATE(‘<+1M>’, CALCDATE(‘<CM>+1D’, TodayDate) – 1);         // First and Last Date of the Current Year         FirstDateofYear := CALCDATE(‘<-CY>’, TodayDate);         LastDateOfYear := CALCDATE(‘<CY>’, TODAY);         // First Day of the Next Quarter         FirstDayOfNextQuarter := CALCDATE(‘<+1Q>’, CALCDATE(‘<-CQ>’, TodayDate));         // Last Day of the Current Quarter         LastDayOfCurrentQuarter := CALCDATE(‘<CQ>’, TODAY);         // First Day of the Next Week         FirstDayOfNextWeek := CALCDATE(‘<+1W>’, CALCDATE(‘<-CW>’, TodayDate));         // First Day of the Next Week + 10D         FirstDayOfNextWeek10D := CALCDATE(‘<+1W>+10D’, CALCDATE(‘<-CW>’, TodayDate));         Message(             ‘Current Month: ‘ + ‘\’ +             ‘Start Date: %1, End Date: %2’ + ‘\’ +             ‘\’ +             ‘Previous Month: ‘ + ‘\’ +             ‘Start Date: %3, End Date: %4’ + ‘\’ +             ‘\’ +             ‘Next Month: ‘ + ‘\’ +             ‘Start Date: %5, End Date: %6’ + ‘\’ +             ‘\’ +             ‘Current Year: ‘ + ‘\’ +             ‘Start Date: %7, End Date: %8’ + ‘\’ +             ‘\’ +             ‘Next Quarter: ‘ + ‘\’ +             ‘Start Date: %9’ + ‘\’ +             ‘\’ +             ‘Current Quarter: ‘ + ‘\’ +             ‘End Date: %10’ + ‘\’ +             ‘\’ +             ‘Next Week: ‘ + ‘\’ +             ‘Start Date: %11’ + ‘\’ +             ‘\’ +             ‘Next Week + 10D: ‘ + ‘\’ +             ‘Start Date: %12’,             StartDate, EndDate, FirstDateOfPreviousMonth, LastDateOfPreviousMonth,             FirstDateOfNextMonth, LastDateOfNextMonth, FirstDateofYear, LastDateOfYear,             FirstDayOfNextQuarter, LastDayOfCurrentQuarter, FirstDayOfNextWeek, FirstDayOfNextWeek10D         );     end; } Why Use CALCDATE in Business Central? The CALCDATE function is incredibly useful for automating and simplifying date-based calculations in Microsoft Dynamics 365 Business Central. Whether you are calculating due dates, generating reports based on time periods, or working with recurring events, CALCDATE saves time and reduces the chances of errors by automating these calculations. Here are some scenarios where CALCDATE can be particularly useful: To conclude, the CALCDATE function is a vital tool for anyone working in Microsoft Dynamics 365 Business Central. It simplifies the process of calculating dates based on specific time intervals, allowing users to manage and manipulate time-based data with ease. By understanding its syntax and functionality, you can unlock the full potential of CALCDATE and streamline your business processes. If you’re a developer or power user, mastering the CALCDATE function will not only enhance your efficiency but also give you greater control over your business data and operations. We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfonts.com.

Share Story :

Building a Successful Framework with Microsoft D365 Business Central: Chart of Accounts Tips and Strategies

A well-structured Chart of Accounts (COA) is essential for achieving accuracy and efficiency in financial management. In Microsoft Dynamics 365 Business Central, the COA plays a key role in organizing financial data, supporting compliance, and enabling insightful reporting. By implementing effective strategies and tips, businesses can create a reliable and scalable financial environment. Below, we explore practical steps for setting up a successful environment with the Chart of Accounts in Business Central. Understand the Basics of the Chart of Accounts The Chart of Accounts in Business Central provides a framework for categorizing financial transactions. Key components include: Organize accounts into primary groups such as Assets, Liabilities, Equity, Income, and Expenses. Define Clear Financial Goals Establishing clear financial goals helps align the COA structure with business objectives: Streamline Account Structures To maintain efficiency, simplify the Chart of Accounts structure: Develop a Logical Numbering System A structured numbering system improves navigation and consistency: Configure Posting Groups Posting groups automate how transactions are allocated to accounts: Perform Testing and Validation Ensure the setup supports business requirements by testing thoroughly: Plan for Future Needs Business needs evolve over time, requiring flexibility in financial systems: To encapsulate, creating a successful financial environment with Microsoft Dynamics 365 Business Central’s Chart of Accounts involves careful planning, structured setup, and ongoing maintenance. By simplifying account structures, leveraging dimensions, and ensuring scalability, businesses can build a COA that supports both current operations and future growth. These strategies and tips will help your organization manage finances more effectively and ensure your systems are aligned with broader business goals. If you’d like guidance on setting up dimensions, configuring posting groups, or optimizing reporting, feel free to ask! We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfonts.com.

Share Story :

Billing on Time: Why PO and BC Integration is Essential 

For organizations, cashflow issues can be mitigated if billing happens on time. Having Project Operations and Business Central gets you covered from a Sales/Project standpoint and BC handles the accounting side, which is great. But when these systems are not connected end to end, it creates problems.  Let’s look at some of the issues and the need to have PO-BC integrated so that business transactions need not be left to manual dependency.  Disconnected Systems  Organizations like Professional Services or Project-based organizations in the SMB/SMC sector use Project Operations to handle Sales and Project Management and use Business Central as their backend Accounting.  Out-of-the-box, Project Operations and Business Central are not integrated from a transaction perspective. This leaves a gap for the organization to be filled manually and dependent on accounting/project management to ensure everything is accounted for in Business Central.  This adds human dependency to keep both the systems in sync and make sure everything gets billed on time.  But this is only the scenario, let’s talk about some problems with this scenario in the below section.  Why PO and BC Integration Matters  When we don’t have the systems connected all the way from Sales to Billing, the following issues start to add up resulting bigger cashflow issues –   These things add up causing loss in revenue and hence, causing cashflow issues. Profit and Loss statement might look great, but cash-in-hand makes organizations suffer and causes hindrance in growth.  Hence, the systems should be reliably integrated to eradicate the issues discussed above.  To give perspective of what all needs to be integrated to ensure that all transaction data between Project Operations and Business Central is taken care of, refer to the below architecture which we’ve incorporated in our PO-BC Integration Module –  To conclude that, when Organizations have their business systems connected to accounting and the financial transactions are taken care of, cashflow issues are eradicated so that organizations can bill on time and get paid on time.  Without this, cashflow chaos is difficult to overcome and fixing billing issues is of prime importance.  We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfonts.com.

Share Story :

Seamlessly Importing Images via URLs in Bulk into Business Central

Whether you’re adding product catalogs or updating images for an extensive inventory, having an efficient way to bulk import images can save time and effort. In this blog post, we will walk you through the steps to import images in bulk into Business Central, providing you with a seamless method to enhance your product data. In today’s fast-paced business environment, efficiency and accuracy in managing product data are crucial for maintaining a competitive edge. Microsoft Dynamics 365 Business Central (BC) is a comprehensive enterprise resource planning (ERP) system that integrates all business functions into one platform. One of the most time-consuming tasks for businesses, especially those with large inventories, is managing and uploading product images. 1. Create a Codeunit or Processing Report:    Since Business Central doesn’t have a built-in feature for bulk image import, you can create a custom codeunit or processing report to handle this task. In this example, we’ll use a codeunit. 2. Add a New Field for Image URL:     Create an Item Table Extension and add a new field called “Product Image URL” to the Item table. This field will hold the URL or path for each product image. 3. Set the Image URLs Using a Configuration Package:     Use a config package to set the image URLs in the “Product Image URL” field for each item. This is where you will provide the path or URL for the image associated with each product. 4. Run the Codeunit to Update Items:    After populating the image URLs via the configuration package, run the codeunit in the foreground. The codeunit will process each item and update the products that have a valid URL set, linking them to the corresponding images. Below is the logic which will use the url which is set in Item master table and update all the data in bulk codeunit 50112 SetImageUrl {     Permissions = tabledata Item = rimd;     Description = ‘Set Image URL’;     trigger OnRun()     var         RecItem: Record Item;         Rec_Item1: Record Item;         ItemPage: page “Item Card”;         PictureURLDialog: Page “Picture URL Dialog”;     begin         Clear(RecItem);         RecItem.Reset();         RecItem.SetFilter(“Product Image URL”, ‘<>%1’, ”);         if RecItem.FindSet() then             repeat                 Rec_Item1.SetRange(“No.”, RecItem.”No.”);                 if Rec_Item1.FindFirst() then begin                     PictureURLDialog.SetItemInfo(Rec_Item1.”No.”, Rec_Item1.Description, Rec_Item1.”Product Image URL”);                     PictureURLDialog.ImportItemPictureFromURL();                 end;             until RecItem.Next() = 0;     end; } This approach allows you to automate the bulk import of product images into Business Central efficiently. Conclusion Importing images in bulk into Business Central can significantly enhance your operational efficiency and ensure your product records are complete and accurate. By following the steps outlined in this blog, you can easily upload and manage product images, creating a more professional and visually appealing online presence, as well as improving internal processes. Whether you’re dealing with thousands of items or just a few, these steps will guide you through the bulk image import process, saving time, reducing errors, and providing a better user experience for both your team and customers. If you need further assistance or have specific questions about your Business Central setup, feel free to reach out for personalized guidance. Happy importing! We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfonts.com.

Share Story :

Phases of Quality Control in Business Central – 3

Welcome back to our series on navigating the GMP-compliant quality control module in Business Central! In our previous blog, we took you through the process up to the Goods Receipt Note (GRN), laying the foundation for efficient and compliant quality management. In this blog, we’ll dive deeper into an equally important aspect of the process: the quality control of raw materials and packing materials. Ensuring that your raw materials meet the necessary standards is crucial for maintaining product integrity and compliance with regulatory requirements. Let’s explore how Business Central helps streamline this critical step in the manufacturing process. Previously, we discussed the process of posting a purchase order in Business Central, which triggers several behind-the-scenes actions. When the purchase order is posted, the system generates a posted purchase receipt along with an inspection datasheet document. This seamless integration ensures that both the material tracking and quality control processes are aligned. In the background, the system also handles the transfer of items between locations. For instance, the raw material (RM) item gets posted to the location specified in the purchase order. If the item is Quality Control (QC) enabled, an inspection datasheet is automatically created upon posting. The system then transfers the item from the purchase order location to an “undertest” location, where the quality control checks are carried out before the materials are accepted into stock. Inspection Datasheet When a Goods Receipt Note (GRN) is created, an inspection datasheet is automatically generated. This datasheet pulls details from the posted purchase order, such as product information, quantities, and other relevant data.  The document type for this datasheet is classified as “Purchase” to indicate that it pertains to a purchased item from a vendor.    Users have the ability to edit the sample quantity on the inspection datasheet. This allows for flexibility in determining how much of the received goods will be inspected or tested When an inspection datasheet is generated from the Goods Receipt Note (GRN), the Specification ID specified on the Purchase Order (PO) for each item is automatically transferred to the datasheet. The Specification ID links to a detailed set of standards or criteria that are predefined for the item (e.g., testing methods, acceptable ranges for quality attributes). The user performs the required testing on the received goods, and after testing, the user records the test results (e.g., pass/fail, measured values) in the specification table on the inspection datasheet. After all the data is filled and verified, the user posts the inspection datasheet. Posting the datasheet signifies that the inspection process is complete, and the items are ready for further processing or acceptance.  Once posted, the system creates a final, official version of the inspection datasheet, capturing all test results and any other relevant data entered during the QC process. Along with the posted datasheet, the system generates an Inspection Receipt. This receipt serves as confirmation that the goods have passed or failed inspection, and it also indicate the status (e.g., approved or rejected) Inspection Receipt On the Inspection Receipt page, the user will review the test results and specifications from the inspection datasheet. a) Based on these results, the user decides whether to accept or reject the lot. Accept: If the results meet the required specifications. Reject: If the results fail to meet the specifications. b) After making the acceptance or rejection decision, the user will enter the location and bin information for the lot to be transferred. Undertest Location: Initially, the lot is in a holding or undertest location. Accepted Lot: If the lot is accepted, the user will move it to an appropriate approved location (e.g., RM-approve for raw materials). Rejected Lot: If the lot is rejected, the user will move it to a rejected location (e.g., RM-reject for raw materials). The bins will vary based on whether the lot is accepted or rejected and its type .Once posted, the system creates a final, posted inspection receipt. This document becomes part of the system’s records, confirming the final status of the lot. The lot is moved to its designated location (approved or rejected), and inventory records are updated accordingly. A transfer entry will be created in the Item Ledger to reflect that the material has been moved to an approved/Rejected location (e.g., RM-approve/reject). c) Posted inspection receipt On the posted inspection receipt page, the user can initiate the generation of the COA report. The Certificate of analysis(COA) report contains detailed test results, pass/fail statuses, specifications, and approval information, providing a formal certificate of compliance. Conclusion: In this blog, I’ve highlighted how a streamlined Quality Control (QC) process ensures that only materials meeting your standards are accepted into inventory. From automated inspection datasheets to real-time inventory updates and generating Certificates of Analysis (COA), you can be confident in the quality and compliance of every batch. Why It Matters for Your Business: a) Ensure Consistent Quality: Only accept materials that meet your standards. b) Save Time: Automation reduces manual work and errors. c) Stay Compliant: Easy access to COAs for audits and regulatory checks. d) Build Trust: Your customers will appreciate your commitment to quality. Ready to optimize your QC process and improve efficiency? We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfonts.com.

Share Story :

SEARCH BLOGS:

FOLLOW CLOUDFRONTS BLOG :


Secured By miniOrange