Blog Archives - Page 14 of 169 - - Page 14

Category Archives: Blog

Understanding the “Find Next” Option in Trade Agreements in D365 Finance & Operations

Introduction When setting up trade agreements in Dynamics 365 Finance & Operations (D365FO), you may notice the “Find Next” option. What does this Find Next flag do? How does it affect discount calculations in the system? This blog will break it down with a simple example! What Does “Find Next” Do? Find Next = ON → The system accumulates multiple applicable trade agreement lines and applies all eligible discounts. Find Next = OFF → The system only applies the first best-fit discount and ignores other possible discounts. Key Impact: When enabled, multiple discounts can be combined. If disabled, only the most specific discount is applied. Step-by-Step Example: Trade Agreement with “Find Next” Let’s walk through a real-world scenario to see how this setting changes discount application. Step 1: Create a Trade Agreement Step 2: Add Discount Lines We add three discount lines for Item A: Line Condition Discount % 1 Basic discount for Item A 5% 2 Quantity-based discount (10 – 100 units) 10% 3 Quantity-based discount (101 – 500 units) 20% Scenario 1: “Find Next” is OFF (Disabled) Step 3: Create a Purchase Order Step 4: Increase the Order Quantity Scenario 2: “Find Next” is ON (Enabled) Step 5: Enable “Find Next” Step 6: Recreate the Purchase Order Step 7: How the Discount is Applied Key Takeaways Find Next OFF → The system applies only the first matching discount. Find Next ON → The system adds up multiple applicable discounts. Best Practice: Use Find Next when you want multiple discounts to be applied together. Understanding how Find Next works helps businesses configure trade agreements properly and avoid unexpected pricing issues. Need more D365 tips? Stay tuned for our next blog! 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 :

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 :

How to Trim and Remove Spaces from Multiple Columns in Power Query

Efficient data cleaning is a crucial step in any data preparation process, and Power Query makes it easy to handle common tasks like trimming and removing unnecessary spaces with functions that you can apply across multiple columns and queries at once. By creating and invoking a function, you can quickly trim and remove spaces from all the columns & tables you need, saving time and effort. In this blog, we’ll show you how to use Power Query functions to streamline your data-cleaning process. The power query we are going to use to trim text in columns is – (text as text, optional char_to_trim as text) =>letchar = if char_to_trim = null then ” ” else char_to_trim,split = Text.Split(text, char),removeblanks = List.Select(split, each _ <> “”),result=Text.Combine(removeblanks, char)inresult This Power Query function takes text as input and removes extra spaces or a specified character from a text string. It splits the text into parts, filters out empty strings, and recombines the cleaned parts using the specified character. If no character is provided, it defaults to removing spaces. The power query we are going to use to remove spaces from the text is – (InputTxt as text) => let Clendata = Text.Combine(List.Select(Text.Split(Text.Trim(InputTxt),” “),each _ <> “”),“”) in Clendata The Power Query function removes all spaces from a given text string. It trims the input, splits it by spaces, filters out blanks, and then combines the parts into a single string. The result is a clean, space-free text, ideal for standardized data preparation. Now, we have our power query function ready, we can use this function across multiple columns or dataset. To do so, go to Add Column > Invoke Custom Function > Your Power Query Function. To conclude, Cleaning and transforming data in Power Query become much easier and more efficient with the use of custom functions. Whether you need to remove spaces, clean multiple columns, or standardize text, these functions save time and ensure consistency across your dataset. By applying these techniques, you can handle large, messy datasets with ease, making your data ready for analysis or reporting. Start implementing these simple yet powerful methods today to streamline your data preparation process! 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 :

Streamlining Document Uploads for Entities by Sales Team Members Using the Mobile Application

Uploading documents to Dynamics 365 through mobile devices can enhance the flexibility and efficiency of your team members. In this blog, I’ll Walk you through a step-by-step procedure for uploading documents using the **Sales Team Member** app on mobile devices. This guide focuses on document upload via the mobile version of Dynamics 365, showcasing how easy it is to manage files on the go. Whether you’re a Sales Team Member or an administrator, this simple process will help you streamline document management using the mobile application. The Use-Case: Uploading Documents in Sales Team Member Imagine a scenario where your team needs to upload documents related to orders, opportunities, or leads while out in the field. Using the Sales Team Member app, the document upload process is streamlined and works seamlessly. Here’s how you can do it: Key Components of the Solution PowerApps Mobile Application: This app provides an easy-to-use interface for users to interact with Dynamics 365 on their mobile devices, including document uploads. Document Upload in Dynamics 365: The process allows users to upload files to records in various entities such as Order Fulfillment, Leads, Opportunities, etc. SharePoint Integration: After uploading the document via mobile, it is stored in SharePoint, where it is accessible both on the mobile device and the web interface. Step-by-Step Process Step 1: Install the Power Apps Application Navigate to your App Store or Play Store, install the Power Apps app, and open it. Step 2: Sign In Sign in with your work account by adding it to the app. Enter your credentials when prompted. Step 3: Access the Sales Team Member App Once logged in, find and select the Sales Team Member app to access your required entities and records. Step 4: Navigate to the Order Fulfillment Entity To upload a document, go to the Order Fulfillment entity. The upload process is the same across all entities. Step 5: Upload the Document In the Order Fulfillment record, tap the Related tab, then select Documents. Tap the ellipsis (three dots) beside the Document Associated Grid and choose Upload. Select Choose File to upload the document from your mobile storage. Step 6: After selecting Upload, you’ll see a prompt. Tap Choose File to access your Gallery/File Storage. Select your desired file, then tap OK. Step 7: View Uploaded Document Once uploaded, the document will display on your mobile app, and you can also view it on the web version. The document will be stored in SharePoint and accessible from either platform. Mobile version: Web version: On opening the file, the file opens SharePoint and displays the document. To encapsulate, with the above procedure, Sales Team Members can easily upload documents via their mobile devices in Dynamics 365, making it easier to manage and share important files from anywhere. This streamlined process not only boosts efficiency but also ensures your team stays connected, even when working remotely or on the move. Hope this helps!!! 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 :

Get Started with Reservation Hierarchies in Dynamics 365 Finance & Operations

Managing inventory in a systematic way is essential for any business. Dynamics 365 Finance & Operations (D365F&O) provides reservation hierarchies to streamline how inventory is reserved and tracked across dimensions like site, warehouse, batch, or serial number. This guide explains the steps to enable reservation hierarchies and demonstrates their usage with practical examples. Reservation hierarchies are tools that determine the order in which inventory dimensions are used to allocate stock. For example, you might first reserve items by site and warehouse, followed by batch and serial numbers. This process helps ensure that inventory is allocated in a logical and efficient manner. Creating Reservation Hierarchies 2. Define the Hierarchy: 3. Select Dimensions: 4. Save and Finalize: Assigning Reservation Hierarchies to Products After creating the hierarchy, assign it to products to activate its functionality: Repeat these steps for all applicable products to standardize the process. Assigning Reservation Hierarchies to Products After creating the hierarchy, assign it to products to activate its functionality: Repeat these steps for all applicable products to standardize the process. Using Reservation Hierarchies in Transactions Sales Orders: When processing a sales order, the system automatically reserves inventory based on the hierarchy. It allocates stock step-by-step through the defined dimensions. Production Orders: For production, reservation hierarchies ensure materials are reserved systematically, avoiding stock conflicts. Transfer Orders: While transferring stock, the hierarchy helps select inventory from the correct dimensions, improving accuracy. Benefits of Reservation Hierarchies To conclude, reservation hierarchies are a simple yet powerful feature in D365F&O. They allow businesses to control how inventory is reserved, ensuring accuracy and efficiency in every transaction. By configuring them properly, you can streamline your operations and reduce errors. Take the time to test these features in a sandbox environment before using them in your live setup. This will help you understand how they work and ensure they fit your business needs. With reservation hierarchies in place, managing inventory becomes easier and more reliable, helping your business stay organized and efficient. That’s it for this blog! Hope this helps! Keep Sharing!! 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 :

How to Display the ‘+New’ Quote Button Only for System Administrators Using JavaScript and Ribbon Workbench in Dynamics 365 CRM

Uploading and managing quotes efficiently is crucial for Dynamics 365 CRM users. However, sometimes you may want to restrict certain buttons, such as the ‘+New’ Quote button, to only users with specific roles, like the “System Administrator.” In this guide, I’ll walk you through how to achieve this by leveraging JavaScript and the Ribbon Workbench tool in Dynamics 365. This method allows administrators to control button visibility based on user roles, ensuring that only users with the correct permissions can access sensitive functionality. The Use-Case: Restricting Access to the ‘+New’ Quote Button for Non-Administrators. Imagine a scenario where your organization needs to ensure that only users with a “System Administrator” role can create new quotes in Dynamics 365. This is crucial for maintaining control over who can initiate important processes within your CRM system. Using JavaScript and Ribbon Workbench, you can easily customize the UI to hide the ‘+New’ Quote button for non-administrators. Here’s how this use case can be implemented: In this scenario, your team wants to ensure that only system administrators have access to the “+New” button for creating quotes in the system. For non-administrators, the button will be hidden from both the homepage subgrid and the main quote tab to prevent unauthorized users from creating quotes. By using the Ribbon Workbench tool, a custom JavaScript function is created to check if a user has the “System Administrator” role. If they do, the “+New” button remains visible, and they can create a new quote. For all other users, the button is hidden. Key Components of the Solution 1. Ribbon Workbench: The Ribbon Workbench tool allows you to customize the Dynamics 365 ribbon, enabling you to create custom buttons and define their visibility and actions. It is used to create the new custom “+New” Quote button, which replaces the default button while maintaining system integrity. 2. JavaScript Customization:  Custom JavaScript is used to manage role-based access for the “+New” Quote button. The script checks the user’s role within Dynamics 365 to ensure that only users with the “System Administrator” role can view and use the button. This helps enforce security and restricts unauthorized users from creating new quotes. 3. Enable Rule for Button Visibility:   An Enable Rule is set to control the visibility of the custom “+New” Quote button based on the user’s role. It ensures that only users with the “System Administrator” role can see and use the button, while hiding it for other users. 4. Custom Button Action (Command): The command linked to the custom “+New” button triggers a custom action (JavaScript function) to open the quote form. This ensures that the action associated with the button aligns with the business needs and provides a seamless user experience for administrators. Step-by-Step Process Sign in to Dynamics 365 using your URL, such as abc.dynamics.com, and enter your credentials or login to make.powerapps.com Create a solution and add the web resource. Once it’s done login to ribbon workbench from XRM toolbox and connect to your organization. After logging in, it is recommended to create a new solution for Ribbon Workbench in Dynamics 365. Ensure that no forms, views, charts, or other entities are included, as Ribbon Workbench may fail to upload the solution with excessive data. Only include the Quote entity with no additional dependencies. Ensure the existing +New Quote button is hidden, as modifying Microsoft-standard buttons is not recommended. Instead, create a new custom button and implement the functionality for creating a new quote Form using custom JavaScript. I have provided the code for this functionality as well. Ensure that the existing +New button for quote would be hidden from the homepage Subgrid and the quotes main tab. Next step would be to create a enable rule. Enable rule is used to control the visibility and availability of a button or command of the button. Name the id of your choice but make sure to add the suffix Enable Rule. Here, un-customised is set to False. By setting isCore (or Un customized) to false, you’re indicating that the button or element is a custom component, not part of the out-of-the-box (core) solution provided by Microsoft. This helps differentiate custom actions from the default ones in the system. Below is the code for the new quote form create and user role-based code. Make sure to select the Function name properly. After setting the enable rule, go to the Commands section in Ribbon Workbench and rename the command. A command defines the action triggered by a button click. Since this is a new button, you’ll need to add the custom form opening code. Below is the function for creating the form. Final Steps: Once the command is added, don’t forget to add the Enable rule that you have created above. Once the command is added, make sure to add all the rules we wrote into the custom button. The image also needs to be added so that the icon can be visible. My custom +New icon looks like this. Testing: Once everything is done, make sure Publish the changes. You can now try to log in from the user that has no System administrator role. Once logged in, you can see that button is not visible. Button will be only visible to user that have system Administrator role. User having no System Administrator role. You can see below that there’s no +New button displayed. To conclude, by following this guide, you can efficiently control the visibility of the ‘+New’ Quote button in Dynamics 365 CRM, making it accessible only to users with the “System Administrator” role. This ensures better control over who can create quotes in the system while maintaining the flexibility of user roles. 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 :

Azure Integration Services (AIS): The Key to Scalable Enterprise Integrations

In today’s dynamic business environment, organizations rely on multiple applications, systems, and cloud services to drive operations, making scalable enterprise integrations essential. As businesses grow, their data flow and process complexity increase, demanding integrations that can handle expanding workloads without performance bottlenecks. Scalable integrations ensure seamless data exchange, real-time process automation, and interoperability between diverse platforms like CRM, ERP, and third-party services. They also provide the flexibility to adapt to evolving business needs, supporting digital transformation and innovation. Without scalable integration frameworks, enterprises risk inefficiencies, data silos, and high maintenance costs, limiting their ability to scale operations effectively.  Are you finding it challenging to scale your business operations efficiently?  In this blog, we’ll look into key Azure Integration Services that can help overcome common integration hurdles.  Before we get into AIS, let’s start with some business numbers—after all, money is what matters most to any business.  Several organizations have reported significant cost savings and operational efficiencies after implementing Azure Integration Services (AIS). Here are some notable examples:  Measurable Business Benefits with AIS  A financial study evaluating the impact of deploying AIS found that organizations experienced benefits totalling $868,700 over three years. These included:  Here are some articles to support this data:   Modernizing Legacy Integration: BizTalk to AIS  A financial institution struggling with outdated integration adapters transitioned to Azure Integration Services. By leveraging Service Bus for reliable message delivery and API Management for secure external API access, they reduced operational costs by 25% and improved system scalability.  These examples demonstrate the substantial cost reductions and efficiency improvements that businesses can achieve by leveraging Azure Integration Services.  To put this into perspective, we’ll explore real-world industry challenges and how Azure’s integration solutions can effectively resolve them.  Example 1: Secure & Scalable API Management for a Manufacturing Company  Scenario: A global auto parts manufacturer supplies components to multiple automobile brands. They expose APIs for:  Challenges: However, they are facing serious challenges  These are some simple top-level issues there can be many more complexities.  Solution: Azure API Management (APIM)  The manufacturer deploys Azure API Management (APIM) to secure, manage, and monitor their APIs.   Step 1: Secure APIs – APIM enforces OAuth-based authentication so only authorized suppliers can access APIs. Rate limiting prevents overuse.  Step 2: API Versioning – Different suppliers use v1 and v2 of APIs. APIM ensures smooth version transitions without breaking old integrations.  Step 3: Analytics & Monitoring – The company gets real-time insights on API usage, detecting slow queries and bottlenecks.  Result:  Example 2: Reliable Order Processing with Azure Service Bus for an E-commerce Company  Scenario: A fast-growing e-commerce company processes over 50,000 orders daily across multiple sales channels (website, mobile app, and third-party marketplaces). Orders are routed to:  Challenges:  Solution: Azure Service Bus (Message Queueing)  Instead of direct connections, the company decouples services using Azure Service Bus.  Step 1: Queue-Based Processing – Orders are sent to an Azure Service Bus queue, ensuring no data loss even if systems go down.  Step 2: Asynchronous Processing – Inventory, payment, and fulfilment consume messages independently, avoiding system overload.  Step 3: Dead Letter Queue (DLQ) Handling – Failed orders are sent to a DLQ for retry instead of getting lost.  Result:  Example 3: Automating Invoice Processing with Logic Apps for a Logistics Company  Scenario: A global shipping company receives thousands of invoices from suppliers every month. These invoices must be:  Challenges:  Solution: Azure Logic Apps for End-to-End Automation  The company automates the entire invoice workflow using Azure Logic Apps.  Step 1: Extract Invoice Data – Logic Apps connects to Office 365 & Outlook, extracts PDFs, and uses AI-powered OCR to read invoice details.  Step 2: Validate Data – The system cross-checks invoice amounts and supplier details against purchase orders in the ERP.  Step 3: Approval Workflow – If all details match, the invoice is auto-approved. If there’s a discrepancy, it’s sent to finance via Teams for review.  Step 4: Update SAP & Notify Suppliers – Once approved, the invoice is automatically logged in SAP, and the supplier gets a payment confirmation email.  Result:  With Azure API Management, Service Bus, and Logic Apps, businesses can:  Many organizations are also shifting towards no-code solutions like Logic Apps for faster integrations. Whether you’re looking for API security, event-driven automation, or workflow orchestration, Azure Integration Services has a solution for you.  Azure Integration Services (AIS) is not just a collection of tools—it’s a game-changer for businesses looking to modernize their integrations, reduce operational costs, and improve scalability. From secure API management to reliable messaging and automation, AIS provides the flexibility and efficiency needed to handle complex business workflows seamlessly.  The numbers speak for themselves—organizations have saved hundreds of thousands of dollars while improving their integration capabilities. Whether you’re looking to streamline supplier connections, optimize order processing, or migrate from legacy systems, AIS has a solution for you.  What’s Next?  In our next article, we’ll take a deep dive into a real-world scenario, showcasing how we helped our customer Buchi transform their integration landscape with Azure Integration Services.  Next Up: Why AIS? How Easily Azure Integration Services Can Adapt to Your EDI Needs.  Would love to hear your thoughts! How are you handling enterprise integrations today? Comment down below ???? or contact us at transform@cloudfronts.com 

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 :

SEARCH BLOGS:

FOLLOW CLOUDFRONTS BLOG :


Secured By miniOrange