<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://sugarclub.sugarai.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Dev Tutorials</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials</link><description /><dc:language>en-US</dc:language><generator>Telligent Community 12</generator><item><title>Helpful Links</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/867/helpful-links</link><pubDate>Fri, 10 Apr 2026 18:15:03 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:5d5f8598-50d5-4ee3-9889-41e64df8c2b6</guid><dc:creator>Alex Nassi</dc:creator><description>Current Revision posted to Dev Tutorials by Alex Nassi on 4/10/2026 6:15:03 PM&lt;br /&gt;
&lt;p id="mcetoc_1hbeup99q0" style="line-height:1.5em;margin-bottom:5px;text-align:center;"&gt;&lt;span style="font-size:150%;"&gt;&lt;strong&gt;Helpful Links&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin-top:5px;text-align:center;"&gt;&lt;span style="font-size:75%;"&gt;&lt;em&gt;&lt;strong&gt;Everything a Sugar Developer needs!&lt;/strong&gt;&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;width:100%;" border="1px" cellpadding="0px" cellspacing="0px"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th style="background-color:#000000;height:120%;text-align:center;"&gt;&lt;span style="color:#ffffff;"&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/span&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a title="Developer Policy" href="https://support.sugarcrm.com/Resources/Developer_Policy/" rel="noopener noreferrer" target="_blank"&gt;Developer Policy&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a href="https://support.sugarcrm.com/Documentation/Sugar_Developer/" rel="noopener noreferrer" target="_blank"&gt;Developer Guides&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a href="/learn/b/sugar-for-developers" rel="noopener noreferrer" target="_blank"&gt;Developer &amp;amp; Solution Architect Training&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a class="hvr-pop" href="/dev-club/w/dev-tutorials"&gt;DevClub Tutorials&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a href="https://apidocs.sugarcrm.com/" rel="noopener noreferrer" target="_blank"&gt;API References&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th style="background-color:#000000;height:120%;text-align:center;"&gt;&lt;span style="color:#ffffff;"&gt;&lt;strong&gt;Downloads&lt;/strong&gt;&lt;/span&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a class="hvr-pop" href="/dev-club/mobile-developers/" rel="noopener noreferrer" target="_blank"&gt;Mobile Developers&lt;/a&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a class="hvr-pop" title="SugarCRM Marketplace" href="https://marketplace.sugarcrm.com/"&gt;SugarCRM Marketplace&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a class="hvr-pop" href="https://store.sugarcrm.com/download" rel="noopener noreferrer" target="_blank"&gt;Download Manager*&lt;br /&gt;&lt;/a&gt;&lt;a class="hvr-pop" href="/dev-club/developer-builds/"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a class="hvr-pop" href="/dev-club/developer-builds/"&gt;Developer Builds*&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;padding-left:30px;"&gt;&lt;span style="font-size:75%;"&gt;&lt;em&gt;*Must be a partner or customer with download permissions to access&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th style="background-color:#000000;height:120%;text-align:center;"&gt;&lt;span style="color:#ffffff;"&gt;&lt;strong&gt;Git Repositories (Code)&lt;/strong&gt;&lt;/span&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a href="https://github.com/sugarcrm" rel="noopener noreferrer" target="_blank"&gt;Open Source contributions&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a href="https://github.com/sugarcrm-developers"&gt;Developer Tools &amp;amp; Code Examples&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;th style="background-color:#000000;height:120%;text-align:center;"&gt;&lt;span style="color:#ffffff;"&gt;&lt;strong&gt;Social&lt;/strong&gt;&lt;/span&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a href="/dev-club/b/dev-blog"&gt;Developer Blog&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin-bottom:3px;margin-top:3px;"&gt;&lt;a href="https://twitter.com/sugarcrmdev" rel="noopener noreferrer" target="_blank"&gt;@sugarcrmdev Twitter&lt;/a&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Creating Complex Quotes Documents with Doc Merge</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/1020/creating-complex-quotes-documents-with-doc-merge</link><pubDate>Tue, 27 Jan 2026 00:10:05 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:0a2397e9-eb30-4c91-8d0e-1aad875f5073</guid><dc:creator>Keith Neuendorff</dc:creator><description>Current Revision posted to Dev Tutorials by Keith Neuendorff on 1/27/2026 12:10:05 AM&lt;br /&gt;
&lt;div class="table-of-contents"&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#mcetoc_1jfuamvps0"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#mcetoc_1jfuamvps1"&gt;A need for a complex quote document&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#mcetoc_1jfuar2ks4"&gt;A new approach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#mcetoc_1jfuamvps2"&gt;The technical details plus the code and template&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1jfuamvps0" class="p1"&gt;&lt;span style="font-size:inherit;"&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;Doc Merge is a very useful but underused feature in Sugar.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;We have leveraged it in a number of projects and recently had a chance to use it for a Quote generation project for a client that required us to think creatively to produce a complex quote document that&amp;nbsp;also required a&amp;nbsp;reasonably&amp;nbsp; short document generation time.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jfuamvps1" class="p1"&gt;&lt;span style="font-size:inherit;"&gt;A need for a complex quote document&lt;/span&gt;&lt;/h2&gt;
&lt;p class="p2"&gt;The client needed to be able to provide quotes that had multiple scenarios that&amp;nbsp;could show multiple items and the costs for each item across multiple years.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;They had previously developed a custom Line Item module that could represent an item and the pricing for&amp;nbsp;a single year and associate it to a scenario.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; An i&lt;/span&gt;tem could be in one or more scenarios.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; Even&lt;/span&gt;&amp;nbsp;though this was done with a custom module, the approach we used can also be applied to the stock Quoted Line Items module.&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;Here is an example of a set of Line Items that represents two scenarios with multiple items.&lt;/span&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;img style="max-height:375px;max-width:500px;" alt=" " src="/resized-image/__size/1000x750/__key/communityserver-wikis-components-files/00-00-00-00-14/ExampleData.png" /&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;They had a way to define their complex quote with the data for their custom module but they needed a way to translate it to a document they could present to their customers.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;They had an initial idea for the document which&amp;nbsp;we&amp;nbsp;then developed into a full version.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;Each scenario would exist as a separate section in the generated document.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;In each section, every item in that scenario would have its own table.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;In each of those tables there would be a line for each year showing the pricing being offered for that item in a single year.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;The following is an example of what we were trying to generated for a document&amp;nbsp;using&amp;nbsp;the previous example data.&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="max-height:375px;max-width:500px;" alt=" " src="/resized-image/__size/1000x750/__key/communityserver-wikis-components-files/00-00-00-00-14/ExampleResults.png" /&gt;&lt;/p&gt;
&lt;p class="p2"&gt;With some effort, a person&amp;nbsp;could sort the data into those sections and tables to produce a document, but it is error prone and time consuming.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;There was a definite value to being able to have Doc Merge inside Sugar produce&amp;nbsp;the document.&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;Our initial solution had a way of getting all of the item numbers for a scenario, looping through all of the items to create a table for each one and then for each item table again looping through the Line Items to produce a list of the line items ordered by year.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;This approach used the standard looping for a related module that Doc Merge provides and it worked and produced the desired quote.&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;However, we found that beyond very simple cases, the time to generated the document was growing very long.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;And the more scenarios, items, and years we added Line Item records for, the longer the generation time would get.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;For a few scenarios with a few items with less than five years, we were getting generation times of over 30 minutes and it was getting worse as we added more data.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;The two layers of looping through the related records was simply not going to work.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jfuar2ks4" class="p1"&gt;&lt;span style="font-size:inherit;"&gt;A new Doc Merge approach&lt;/span&gt;&lt;/h2&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;After brainstorming the technical side of this problem, we came up with a new approach for creating the data that would be picked up by Doc Merge.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;We decided to &amp;ldquo;pre-render&amp;rdquo; the data tables for each line item in a scenario so that Doc Merge would only have a single loop for each scenario where it would just&amp;nbsp;need to pick up the block of data to be shown in each table.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;This does required a code customization, but after considering a few ways to do it, we settled on&amp;nbsp;using just a before_save logic hook on the Line Item module.&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;The logic hook we made triggers when a Line Item record is created or updated.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;When this hook is triggered, the table for that line item is generated and saved and then all of the other tables are also updated.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;This full update of all tables was done to cover the case where a Line Item record was initially in one scenario/item but was moved to another scenario/item, which guarantees that all pre-rendered data is correct.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;This approach in the logic hook did not add any noticeable time to the save of a Line Item, but it did improve the generation time of a quote document from 30+ minutes to an average of 15 seconds.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;We also found that adding more scenarios, items, and years added very little time under the new approach and it satisfied the client needs and provided a good user experience.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jfuamvps2" class="p1"&gt;&lt;span style="font-size:inherit;"&gt;The technical details plus the code and template&lt;/span&gt;&lt;/h2&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;To make this approach work, we needed to specify on the quote what the first year covered by the Line Items would be.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;This was passed down to the individual Line Items on a calculated field.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;The pre-rendered table data is saved on the earliest year Line Item record for each table.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;For the text in the tables, we needed to have all the pre-rendered text across multiple lines be&amp;nbsp;presented as one text block in the resulting table.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;We generated column divisions in that text data with the &amp;ldquo;|&amp;rdquo; character substituting for vertical lines in the data and used a monospaced font to make sure everything lined up.&lt;span class="Apple-converted-space"&gt;&amp;nbsp; &lt;/span&gt;We did look at using tab characters in the template to line up columns, but the complexity was judged to not be worth it.&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span style="font-size:inherit;"&gt;Here is the code for the logic hook and the template file that was used.&amp;nbsp; We hope that this information provides some inspiration for solving interesting client customization challenges.&lt;/span&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;pre class="ui-code" data-mode="php"&gt;&amp;lt;?php
if (!defined(&amp;#39;sugarEntry&amp;#39;) || !sugarEntry) define(&amp;#39;sugarEntry&amp;#39;, true);

class CollectMatchingLineItemsHook
{
    /**
     * before_save hook: collect other line items under the same parent Quote
     * with quantity == 100 and attach them to $bean-&amp;gt;matching_line_items (non-persistent)
     */
    public function beforeSaveLineItemProcessing(&amp;amp;$bean, $event, $arguments)
    {
        $parentQuoteId = $this-&amp;gt;getParentQuoteId($bean);
        if (empty($parentQuoteId)) {
            $bean-&amp;gt;line_item_group_summary_c = &amp;#39;&amp;#39;;
            $bean-&amp;gt;save_flag_c = &amp;#39;&amp;#39;;
            return;
        }

        $subChar = &amp;#39;^&amp;#39;;
        $cpad = 6;
        $vpad = 7;
        $newRecordLine = &amp;#39;&amp;#39;;
        if (empty($bean-&amp;gt;fetched_row[&amp;#39;id&amp;#39;])) {
            if($bean-&amp;gt;scenario_key_value_c == &amp;#39;&amp;#39;) {
                if(strval($bean-&amp;gt;hide_line_item_c) !== &amp;#39;1&amp;#39;) {
                    $newRecordLine = str_pad(strval($bean-&amp;gt;year_text_c),$cpad).&amp;quot; |      &amp;quot;.sprintf(&amp;quot;%{$vpad}s&amp;quot;, number_format($bean-&amp;gt;volume)).&amp;quot; |  &amp;quot;.$this-&amp;gt;formatDecimal($bean-&amp;gt;base_unit_price).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($bean-&amp;gt;unit_surcharge).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($bean-&amp;gt;total_unit_price).$subChar;
                }
            }
        }

        if($bean-&amp;gt;save_flag_c == 1) {
            if($bean-&amp;gt;scenario_key_value_c == &amp;#39;&amp;#39;) {
                $bean-&amp;gt;line_item_group_summary_c = &amp;#39;&amp;#39;;
            }
            else {
                $this-&amp;gt;updateLineItemSummary($bean);
            }
            $bean-&amp;gt;save_flag_c = &amp;#39;&amp;#39;;
        }
        else {
            if($bean-&amp;gt;scenario_key_value_c == &amp;#39;&amp;#39;) {
                $bean-&amp;gt;line_item_group_summary_c = &amp;#39;&amp;#39;;
                $this-&amp;gt;getAndSaveQuoteBeanKeyItems( $parentQuoteId, null, $newRecordLine, $bean-&amp;gt;scenario_item_group_c );
            }
            else {
                $this-&amp;gt;updateLineItemSummary($bean);
                $this-&amp;gt;getAndSaveQuoteBeanKeyItems( $parentQuoteId, $bean-&amp;gt;id );
            }
        }
        $bean-&amp;gt;new_record_line_text_c = &amp;#39;&amp;#39;;
    }


    /* Returns array of beans for the related items
    * The primary key $bean will always be the one in the first [0] position
    */
    protected function updateLineItemSummary( &amp;amp;$lineItemBean ) {
        $parentQuoteId = $this-&amp;gt;getParentQuoteId($lineItemBean);
        if (empty($parentQuoteId)) {
            $lineItemBean-&amp;gt;line_item_group_summary_c = &amp;#39;&amp;#39;;
            return;
        }

        $newTextYear = &amp;#39;&amp;#39;;
        if($lineItemBean-&amp;gt;new_record_line_text_c !== &amp;#39;&amp;#39;) {
            $newTextYear = substr($lineItemBean-&amp;gt;new_record_line_text_c, 0, 4);
        }
        $lineArray = [];

        $itemBeansOnQuote = $this-&amp;gt;getItemBeansForQBean($parentQuoteId, $lineItemBean-&amp;gt;scenario_number, $lineItemBean-&amp;gt;item_number);

        $subChar = &amp;#39;^&amp;#39;;
        $cpad = 6;
        $vpad = 7;
        $lineItemSummaryNew = &amp;#39;&amp;#39;;
        if(strval($lineItemBean-&amp;gt;hide_line_item_c) !== &amp;#39;1&amp;#39;) {
            $lineItemSummaryNew .= str_pad(strval($lineItemBean-&amp;gt;year_text_c),$cpad).&amp;quot; |      &amp;quot;.sprintf(&amp;quot;%{$vpad}s&amp;quot;, number_format($lineItemBean-&amp;gt;volume)).&amp;quot; |  &amp;quot;.$this-&amp;gt;formatDecimal($lineItemBean-&amp;gt;base_unit_price).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($lineItemBean-&amp;gt;unit_surcharge).&amp;quot;| &amp;quot;.$this-&amp;gt;formatDecimal($lineItemBean-&amp;gt;total_unit_price).$subChar;
            $lineArray[$lineItemBean-&amp;gt;year_text_c] = str_pad(strval($lineItemBean-&amp;gt;year_text_c),$cpad).&amp;quot; |      &amp;quot;.sprintf(&amp;quot;%{$vpad}s&amp;quot;, number_format($lineItemBean-&amp;gt;volume)).&amp;quot; |  &amp;quot;.$this-&amp;gt;formatDecimal($lineItemBean-&amp;gt;base_unit_price).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($lineItemBean-&amp;gt;unit_surcharge).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($lineItemBean-&amp;gt;total_unit_price).$subChar;
        }
        $summaryRecordId = $lineItemBean-&amp;gt;id;
        foreach ($itemBeansOnQuote as $r) {
            if(strval($r-&amp;gt;hide_line_item_c) !== &amp;#39;1&amp;#39;) {
                if($r-&amp;gt;id == $lineItemBean-&amp;gt;id) {
                    //nothing
                }
                else {
                    $lineItemSummaryNew .= str_pad(strval($r-&amp;gt;year_text_c),$cpad).&amp;quot; |      &amp;quot;.sprintf(&amp;quot;%{$vpad}s&amp;quot;, number_format($r-&amp;gt;volume)).&amp;quot; |  &amp;quot;.$this-&amp;gt;formatDecimal($r-&amp;gt;base_unit_price).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($r-&amp;gt;unit_surcharge).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($r-&amp;gt;total_unit_price).$subChar;
                    $lineArray[$r-&amp;gt;year_text_c] = str_pad(strval($r-&amp;gt;year_text_c),$cpad).&amp;quot; |      &amp;quot;.sprintf(&amp;quot;%{$vpad}s&amp;quot;, number_format($r-&amp;gt;volume)).&amp;quot; |  &amp;quot;.$this-&amp;gt;formatDecimal($r-&amp;gt;base_unit_price).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($r-&amp;gt;unit_surcharge).&amp;quot;|  &amp;quot;.$this-&amp;gt;formatDecimal($r-&amp;gt;total_unit_price).$subChar;
                }                    
            }
        }
        $pos = strrpos($lineItemSummaryNew, $subChar);
        if($pos !== false)
        {
            $lineItemSummaryNew = substr_replace($lineItemSummaryNew, &amp;#39;&amp;#39;, $pos, 1);
        }
        $lineItemSummaryNew .= &amp;quot;\n&amp;quot;.$lineItemBean-&amp;gt;new_record_line_text_c;

        $reassemblItemsSummaryNew = &amp;#39;&amp;#39;;
        $extraLineInserted = false;
        foreach($lineArray as $k =&amp;gt; $v) {
            if ($k &amp;gt; $newTextYear &amp;amp;&amp;amp; !$extraLineInserted) {
                $reassemblItemsSummaryNew .= $lineItemBean-&amp;gt;new_record_line_text_c;
                $extraLineInserted = true;
            }
            $reassemblItemsSummaryNew .= $v;
        }
        if($extraLineInserted == false) {
            if($lineItemBean-&amp;gt;hide_line_item_c !== &amp;#39;1&amp;#39;) {
                $reassemblItemsSummaryNew .= $lineItemBean-&amp;gt;new_record_line_text_c;
            }
        }   
        $reassemblItemsSummaryNew = rtrim($reassemblItemsSummaryNew, &amp;quot;^&amp;quot;); 
        $reassemblItemsSummaryNew = str_replace($subChar, &amp;quot;\n&amp;quot;, $reassemblItemsSummaryNew);
        $lineItemBean-&amp;gt;line_item_group_summary_c = $reassemblItemsSummaryNew;
    }


    /* Returns array of beans for the related items
    * The primary key $bean will always be thte on ein the first [0] position
    */
    protected function getAndSaveQuoteBeanKeyItems( $quoteId, $omitBeanId = null, $newRecordTextLine = &amp;#39;&amp;#39;, $newRecordKey = &amp;#39;&amp;#39; ) {
        $quoteBean = BeanFactory::retrieveBean(&amp;#39;Quotes&amp;#39;, $quoteId);
        if(!$quoteBean) {
            return false;
        }

        $quoteBean-&amp;gt;load_relationship(&amp;#39;id_li_quotes&amp;#39;);
        $relatedBeans = $quoteBean-&amp;gt;id_li_quotes-&amp;gt;getBeans();    
        $filteredBeans = array();

        foreach($relatedBeans as $relatedBean) {
            if($relatedBean-&amp;gt;scenario_key_value_c != &amp;#39;&amp;#39;) {
                if($relatedBean-&amp;gt;id !== $omitBeanId) {
                    $relatedBean-&amp;gt;save_flag_c = 1;
                    $filteredBeans[] = $relatedBean;
                }
                else {
                    // continue;
                }
            }
        }

        foreach($filteredBeans as $b) {
            $b-&amp;gt;save_flag_c = 1;

            if($newRecordKey == $b-&amp;gt;scenario_key_value_c) {
                $b-&amp;gt;new_record_line_text_c = $newRecordTextLine;
            }
            else {
                $b-&amp;gt;new_record_line_text_c = &amp;#39;&amp;#39;;
            }
            $b-&amp;gt;save();
        }
    }


    /* Returns array of beans for the related items
    * The primary key $bean will always be thte on ein the first [0] position
    */
    protected function getItemBeansForQBean( $quoteId, $scenarioNumber, $itemNumber ) {
        $quoteBean = BeanFactory::retrieveBean(&amp;#39;Quotes&amp;#39;, $quoteId);
        if(!$quoteBean) {
            return false;
        }

        $quoteBean-&amp;gt;load_relationship(&amp;#39;id_li_quotes&amp;#39;);
        $relatedBeans = $quoteBean-&amp;gt;id_li_quotes-&amp;gt;getBeans();    
        $filteredBeans = array();

        foreach($relatedBeans as $relatedBean) {
            if($relatedBean-&amp;gt;scenario_number == $scenarioNumber &amp;amp;&amp;amp; $relatedBean-&amp;gt;item_number == $itemNumber ) {
                $filteredBeans[] = $relatedBean;
            }
        }

        //Bubble sort of relatedBeans by year_text_c
        $n = count($filteredBeans);
        for ($i = 0; $i &amp;lt; $n - 1; $i++) {
            for ($j = 0; $j &amp;lt; $n - $i - 1; $j++) {
                if (strcmp($filteredBeans[$j]-&amp;gt;year_text_c, $filteredBeans[$j + 1]-&amp;gt;year_text_c) &amp;gt; 0) {
                    $temp = $filteredBeans[$j];
                    $filteredBeans[$j] = $filteredBeans[$j + 1];
                    $filteredBeans[$j + 1] = $temp;
                }
            }
        }
        return $filteredBeans;
    }


    /**
     * Attempt to determine the parent Quote ID for a line-item bean.
     */
    protected function getParentQuoteId($bean)
    {
        try {
            if ($bean-&amp;gt;load_relationship(&amp;#39;id_li_quotes&amp;#39;)) {
                $related = $bean-&amp;gt;id_li_quotes-&amp;gt;getBeans();
                if (!empty($related)) {
                    $ids = array_keys($related);
                    return reset($ids);
                }
            }
        } catch (Exception $e) {
            // ignore
        }
        return null;
    }

    protected function formatDecimal($number)
    {
        $formattedNumber = sprintf(&amp;quot;%.5f&amp;quot;, $number);
        $paddedString = str_pad($formattedNumber, 12, &amp;quot; &amp;quot;, STR_PAD_LEFT).&amp;#39; &amp;#39;;
        return $paddedString;
    }
}

?&amp;gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;a href="https://sugarclub.sugarai.com/cfs-file/__key/communityserver-wikis-components-files/00-00-00-00-14/SugarClubDocMergeTemplateEx.docx"&gt;sugarclub.sugarai.com/.../SugarClubDocMergeTemplateEx.docx&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Sugar 25.2 Customization Guide</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/1013/sugar-25-2-customization-guide</link><pubDate>Wed, 01 Oct 2025 18:31:31 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:3307f36c-90a1-4f3c-b6a7-d6d6641bf5cb</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 10/1/2025 6:31:31 PM&lt;br /&gt;
&lt;p&gt;The purpose of this document is to provide insight to Sugar Developers for upgrading custom Sugar code, extensions, and integrations to the Sugar 25.2 release. This guide focuses on changes in&amp;nbsp;&lt;span&gt;Sugar 25.2&lt;/span&gt;&amp;nbsp;that could cause an immediate impact on Sugar customizations and integrations built for earlier Sugar versions.&lt;/p&gt;
&lt;p&gt;Please check out the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="/dev-club/m/event-recaps/3045"&gt;Sugar 25.2 Developer Webinar recording&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for more developer highlights.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;For Admin and End User release notes, please visit the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Release_Notes/25.2.0_Release_Notes/"&gt;Sugar 25.2.0 Release Notes&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1ff80k9i32"&gt;User Experience Updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;&lt;span style="font-size:inherit;"&gt;&lt;span class="TextRun SCXW218900661 BCX0" lang="EN" data-contrast="auto"&gt;&lt;span class="NormalTextRun SCXW218900661 BCX0" data-ccp-parastyle="heading 2"&gt;Modernized Dropdown Editor&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-start="232" data-end="471"&gt;In this release, we&amp;rsquo;ve modernized the Dropdown Editor UI, transitioning from BWC to Sidecar for a cleaner, more streamlined admin experience. Admins can now manage entire dropdown lists from a single view&amp;mdash;no more editing one row at a time.&lt;/p&gt;
&lt;p data-start="115" data-end="326"&gt;&lt;strong data-start="328" data-end="343"&gt;What&amp;rsquo;s new:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="115" data-end="326"&gt;
&lt;p&gt;&lt;strong data-start="492" data-end="516"&gt;Redesigned Interface&lt;/strong&gt;: New Sidecar layout with updated header, inline editing, and consistent list view styling. Supports dark mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="115" data-end="326"&gt;
&lt;p&gt;&lt;strong&gt;Formatting Flow Fix &amp;amp; Toggle Control:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Chart colors no longer drive dropdown styling; instead, formatting is now defined at the dropdown level and pushed to charts for consistency..&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="296" data-end="364"&gt;
&lt;p data-start="298" data-end="364"&gt;New&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="302" data-end="337"&gt;&amp;quot;Enable formatting in dropdown&amp;quot;&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;toggle added per dropdown.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="367" data-end="486"&gt;
&lt;p data-start="369" data-end="486"&gt;Toggle is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="379" data-end="401"&gt;enabled by default&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="407" data-end="424"&gt;sales_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="426" data-end="443"&gt;quote_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="445" data-end="462"&gt;case_status_dom&lt;/code&gt;, and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="468" data-end="485"&gt;lead_status_dom&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="489" data-end="580"&gt;
&lt;p data-start="491" data-end="580"&gt;When&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="496" data-end="508"&gt;disabled&lt;/strong&gt;, formatting is hidden in the UI but preserved and still used in charts.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="583" data-end="683"&gt;
&lt;p data-start="585" data-end="683"&gt;When&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="590" data-end="601"&gt;enabled&lt;/strong&gt;, users can customize styles (colors, fonts) via the conditional formatting panel.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="686" data-end="803"&gt;
&lt;p data-start="688" data-end="803"&gt;Chart colors fallback to the chart palette for items without formatting; white and black are excluded and replaced.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Conditional Formatting&lt;/strong&gt;: New Conditional Format Options control panel that allows&lt;span&gt;&amp;nbsp;users to customize the appearance of dropdown values using styles, icons, and colors&lt;/span&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The following stock/out-of-the-box dropdowns were predefined with conditional formatting for awareness and usability:&amp;nbsp;&lt;code&gt;sales_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;quote_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;case_status_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;lead_status_dom&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Formatting will be reflected as soon as saved on List View, Record View, Focus Drawer, Reports, Other components using the dropdown values, All views in sugar&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Even if user selects RGB, we only reflect Hex number in the input&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong data-start="630" data-end="646"&gt;Add New Rows&lt;/strong&gt;: Easily add multiple items with editable fields for Display Label and Classification (for&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="737" data-end="754"&gt;sales_stage_dom&lt;/code&gt;). Only completed rows are saved.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="790" data-end="897"&gt;
&lt;p&gt;&lt;strong data-start="790" data-end="809"&gt;Improved Search&lt;/strong&gt;: Real-time search across Item Name and Display Label fields with partial match support.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="900" data-end="1048"&gt;
&lt;p&gt;&lt;strong data-start="900" data-end="922"&gt;Restore to Default&lt;/strong&gt;: Revert dropdowns to their original out-of-the-box (OOTB) state, including default sort order. Available only for OOTB lists.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1051" data-end="1148"&gt;
&lt;p&gt;&lt;strong data-start="1051" data-end="1084"&gt;Bulk Editing &amp;amp; Reduced Clicks&lt;/strong&gt;: Edit multiple rows at once and save all changes in one action.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1151" data-end="1272"&gt;
&lt;p&gt;&lt;strong data-start="1151" data-end="1176"&gt;Sort by Display Label&lt;/strong&gt;: New sort button allows ascending/descending sort; order persists across sessions and is saved.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1275" data-end="1392"&gt;
&lt;p&gt;&lt;strong data-start="1275" data-end="1304"&gt;Inline Editing &amp;amp; Deletion&lt;/strong&gt;: Edit Display Label and Classification fields directly; delete items with confirmation.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1395" data-end="1487"&gt;
&lt;p&gt;&lt;strong data-start="1395" data-end="1423"&gt;Drag-and-Drop Reordering&lt;/strong&gt;: Rearrange items via drag-and-drop. Changes are saved on click.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1490" data-end="1579"&gt;
&lt;p&gt;&lt;strong data-start="1490" data-end="1516"&gt;Unsaved Change Warning&lt;/strong&gt;: Users are prompted before navigating away from unsaved edits.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Devs can still define&amp;nbsp;custom styles (e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;backgroundColor, textColor, icon, iconColor&lt;/code&gt;) for dropdown fields using extension framework (&lt;code&gt;custom/application/Ext/DropdownsStyle/dropdowns_style.ext.php&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;Adding support to Shared Drive files on Google Drive&lt;/h3&gt;
&lt;p data-start="232" data-end="471"&gt;The Google Drive connector for Sugar has been enhanced to support access to shared files. Users can now interact with files that are shared with them or located in Shared Drives. This includes the ability to set a Shared Drive as the root path, define variable file paths on record views, and seamlessly navigate through folders within Shared Drives. Additionally, users can switch between viewing files in &amp;quot;My Files,&amp;quot; &amp;quot;Shared with me,&amp;quot; and Shared Drives. If a user attempts to access a Shared Drive they do not have permission to view, a message will appear within the dashlet indicating the lack of access, with the option to navigate to an accessible area instead.&lt;/p&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;h3 id="mcetoc_1j5hknrrm0"&gt;Enable Row-Level Actions in List View Dashlets&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="e60a4390-8308-4251-a10b-5a6239bb908d" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="19" data-end="705" data-is-last-node="" data-is-only-node=""&gt;In this release, we&amp;#39;ve&amp;nbsp;added row-level actions on List view dashlets, enabling users to take immediate action on records directly within dashboards and Focus Drawers. This improvement aligns with Sugar&amp;rsquo;s goal of&amp;nbsp;UX efficiency, reducing clicks and streamlining workflows. The available actions now mirror those found in the standard module list views, using the familiar action menu design for consistency. The preview option has been intentionally excluded to suit the context of dashlets.&lt;/p&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="269059f9-331f-43d5-a7e5-913f02e20322" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="671" data-end="888" data-is-last-node="" data-is-only-node=""&gt;&lt;strong data-start="671" data-end="680"&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This functionality has been implemented as a new plugin and integrated into relevant views. Custom or extended views that include inline editing may require a review to ensure compatibility with this update.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm1"&gt;Opportunity&amp;nbsp;Delta&amp;nbsp;in List and Record Views&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="e60a4390-8308-4251-a10b-5a6239bb908d" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p&gt;To improve visibility into how opportunities evolve over time, we&amp;#39;ve introduced a new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;Change Over Time&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;dropdown labeling &amp;quot;Show changes&amp;quot; across Opportunity list views (module and subpanels), record views, and dashlets (LV &amp;amp; RV). This enables sellers and managers to quickly see cumulative changes without piecing together discrete audit log entries.&lt;/p&gt;
&lt;p data-start="518" data-end="535"&gt;&lt;strong data-start="518" data-end="535"&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="536" data-end="941"&gt;
&lt;li data-start="536" data-end="650"&gt;
&lt;p data-start="538" data-end="650"&gt;&lt;strong data-start="538" data-end="576"&gt;New Time-Based Comparison Dropdown&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;with four options:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="596" data-end="602"&gt;None&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="604" data-end="617"&gt;Last 7 days&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="619" data-end="633"&gt;Last 14 days&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="635" data-end="649"&gt;Last 30 days&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="651" data-end="736"&gt;
&lt;p data-start="653" data-end="736"&gt;Compares current opportunity data to its value at the end of the selected past day.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="737" data-end="835"&gt;
&lt;p data-start="739" data-end="765"&gt;Highlights value changes in::&lt;/p&gt;
&lt;ul data-start="768" data-end="835"&gt;
&lt;li data-start="768" data-end="787"&gt;
&lt;p data-start="770" data-end="787"&gt;&lt;strong data-start="770" data-end="787"&gt;Likely Amount&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="790" data-end="815"&gt;
&lt;p data-start="792" data-end="815"&gt;&lt;strong data-start="792" data-end="815"&gt;Expected Close Date&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="818" data-end="835"&gt;
&lt;p data-start="820" data-end="835"&gt;&lt;strong data-start="820" data-end="835"&gt;Sales Stage&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul data-start="965" data-end="1259"&gt;
&lt;li data-start="965" data-end="1068"&gt;
&lt;p data-start="967" data-end="1068"&gt;Selection is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="980" data-end="999"&gt;sticky per view&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(e.g., list view and record view can maintain independent settings).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1069" data-end="1189"&gt;
&lt;p data-start="1071" data-end="1189"&gt;When navigating using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="1097" data-end="1111"&gt;focus icon&lt;/strong&gt;, the source view&amp;rsquo;s selection is respected and carried to the target dashlets.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1190" data-end="1259"&gt;
&lt;p data-start="1192" data-end="1259"&gt;If no prior context exists, the default selection is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="1245" data-end="1258"&gt;Last 7 days&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This feature leverages historical data available through Sugar&amp;#39;s Enhanced Forecasting and is only available to customers with the Opportunities dataset. For customers without this dataset, the feature will not be functional.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv0j7tfp2" data-start="83" data-end="136"&gt;Add Transcription, Recording, and Summary fields on the Meetings module&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;We&amp;#39;re improving Meetings module in this release by adding transcription, recording and summary fields to the record view in view/edit modes.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm2"&gt;&lt;span&gt;Dynamic Record View Dashlet Rendering for Flex Relate Fields in Focus Drawer&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Enhanced the Focus Drawer experience by enabling dynamic rendering (new tab) of related records defined via flex relate (&amp;quot;Relate To&amp;quot;) fields within a Record View Dashlet. Users can now configure a dashlet to automatically display the appropriate related record based on the selected module and record in the flex relate field without needing to manually toggle between module tabs. This&amp;nbsp;improves context access and ensures only relevant record views are shown, improving usability and efficiency when working across related modules.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1i7u16d9e5"&gt;&lt;span&gt;&lt;span class="TextRun SCXW118627354 BCX0" lang="EN-US" data-contrast="auto"&gt;&lt;span class="NormalTextRun SCXW118627354 BCX0" data-ccp-parastyle="heading 2"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="NormalTextRun SCXW249156672 BCX0"&gt;&lt;/span&gt;&lt;/span&gt;Sugar REST API updates&lt;/h2&gt;
&lt;p&gt;This Sugar release introduces REST&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;&lt;code&gt;v11_27&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Dropdown Editor&lt;/h3&gt;
&lt;p&gt;Set details of dropdown.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;PUT &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/:dropdownName&lt;/pre&gt;
&lt;p&gt;Get details of dropdown that can be changed.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;GET &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/:dropdownName&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Create Dropdown DOM issue as a php-file in customer storage directory.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/create&lt;/pre&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Enhancements to&amp;nbsp;Package Builder APIs&amp;rsquo; error handling&lt;/h3&gt;
&lt;p&gt;We have improved our Package Builder API for clear and consistent error codes that are returned for known failure scenarios.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong data-renderer-mark="true"&gt;For Example:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="402"&gt;Error messages include actionable context (e.g., missing fields, invalid formats, unsupported package types)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="516"&gt;All existing error-prone areas are covered (e.g., input validation, external service failures, internal exceptions)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="637"&gt;Fallbacks or default behaviors are implemented where appropriate&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="707"&gt;Errors follow our API error response standard (e.g., HTTP status codes, message structure)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following endpoints were improved:&lt;/p&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/customizations&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package&lt;br /&gt;&lt;br /&gt;GET &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/id&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/remote&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/data&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/getRemotePackages&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Historical Deltas API&lt;/h3&gt;
&lt;p&gt;Retrieves historical delta changes for a module based on a timestamp and list of records.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/historically/delta&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1ff80k9i33"&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id="mcetoc_1ff80k9i33"&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h2 id="mcetoc_1ftl03hrg6"&gt;&lt;span style="font-size:inherit;"&gt;PHP Library Upgrades&lt;/span&gt;&lt;/h2&gt;
&lt;h3 id="mcetoc_1iuf1asts0"&gt;Upgrade PHP libs to the latest Minor versions without breaking changes&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;ve updated all minor and patch versions of our PHP libraries as declared in the composer. Since these are minor and patch updates, no breaking changes are expected. The updates primarily include newly generated models and improved PHP version compatibility.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;aws/aws-sdk-php             3.342.19 AWS SDK for PHP - Use Amazon Web Services in your PHP project
doctrine/dbal               3.9.4    Powerful PHP database abstraction layer (DBAL) with many features for da...
egulias/email-validator     4.0.4    A library for validating emails against several RFCs
guzzlehttp/guzzle           7.9.3    Guzzle is a PHP HTTP client library
laminas/laminas-escaper     2.16.0   Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs
league/oauth2-client        2.8.1    OAuth 2.0 Client Library
microsoft/microsoft-graph   2.29.0   The Microsoft Graph SDK for PHP
rector/rector               2.0.11   Instant Upgrade and Automated Refactoring of any PHP code
rlanvin/php-rrule           2.5.2    Lightweight and fast recurrence rules for PHP (RFC 5545)&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1gj2kiufk0"&gt;PHP-Parser&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;Sugar utilizes a&amp;nbsp;PHP library called&amp;nbsp;&lt;a href="https://github.com/nikic/PHP-Parser/tree/master"&gt;PHP-Parser&lt;/a&gt;&amp;nbsp;Its purpose is to simplify static code analysis and manipulation.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There are major internal changes in this library, if you use them, please refer to this library&amp;rsquo;s upgrade procedure&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;nikic/php-parser &amp;rarr; ^v5.4.0&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gj2kiufk0"&gt;&lt;/h3&gt;
&lt;div&gt;&lt;/div&gt;
&lt;h2 id="mcetoc_1ff80k9i33"&gt;&lt;span style="font-size:inherit;"&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;JS Library Upgrades&lt;/h2&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Low to Minimum Impact&amp;nbsp;Upgrade on various JS&amp;nbsp;libraries&lt;/h3&gt;
&lt;p&gt;We have performed a patch upgrade on several JavaScript libraries used within our codebase. These upgrades are considered low to minimum impact and should not affect Sugar developers in any meaningful way. Most of the components upgraded are used for internal build processes or tooling.&lt;/p&gt;
&lt;p&gt;There are no known breaking changes associated with these updates.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;@babel/core -&amp;gt; 7.27.4
@sugarcrm/ventana -&amp;gt; 1.1.19
handlebars -&amp;gt; 4.7.8-sugarcrm
babel-loader -&amp;gt; 9.2.1
bootstrap  -&amp;gt; 5.3.7
gulp -&amp;gt; 5.0.1
jquery-migrate -&amp;gt; 3.5.2
sinon -&amp;gt; 21.0.0
tailwindcss -&amp;gt; 3.4.17
underscore -&amp;gt; 1.13.7
webpack -&amp;gt; 5.99.9
eslint-plugin-import -&amp;gt; 2.31.0
cure53/DOMPurify -&amp;gt; 3.2.6
stylelint-config-standard -&amp;gt; 38.0.0&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm3"&gt;&lt;span&gt;DOMPurify JS Library&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="8778637e-7a32-4e7c-a539-f7b8b9aba45c" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="19" data-end="397" data-is-last-node="" data-is-only-node=""&gt;The DOMPurify library is now integrated as a standard dependency, replacing the previously used forked version. This update improves the maintainability and transparency of the codebase by aligning with standard package management practices. Functionality that relies on DOMPurify remains unchanged, and the library is properly bundled and referenced throughout the application.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1iipslfhq0"&gt;Supported Platforms Update&lt;/h2&gt;
&lt;h3 id="mcetoc_1j5hknrrm4"&gt;&lt;span style="font-size:inherit;"&gt;Support for OAuth 2.0 token-based authorization with Exchange Online and Sugar email functions&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="97" data-end="525"&gt;Sugar now supports sending emails via Microsoft Exchange Online using OAuth 2.0, in alignment with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://learn.microsoft.com/en-us/answers/questions/2202498/basic-authentication-for-smtp-deprecation"&gt;Microsoft&amp;rsquo;s announcement to permanently disable Basic Authentication for SMTP AUTH in Exchange Online starting September 2025.&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This enhancement ensures continued compatibility and improved security by allowing outbound email functionality to operate without relying on SMTP when using Microsoft OAuth 2.0.&lt;/p&gt;
&lt;p data-start="527" data-end="849"&gt;With this update, when an outbound email account is configured using Microsoft OAuth 2.0, Sugar will send emails through the Microsoft Graph API instead of SMTP. This change is specific to Exchange Online accounts using Microsoft OAuth and does not impact other connection types, such as username/password or Google OAuth.&lt;/p&gt;
&lt;p data-start="851" data-end="1247"&gt;To enable this functionality, administrators must register an application in Azure with the necessary API permissions (Mail.Send, Mail.Read, IMAP.AccessAsUser.All, offline_access) and configure the Microsoft connector in Sugar with the application&amp;#39;s client ID and secret. Once authorized, the email status will indicate successful authentication, allowing emails to be sent using the new method.&lt;/p&gt;
&lt;p data-start="1249" data-end="1596"&gt;This transition will occur automatically, requiring no action from customers. However, the shift to the Microsoft Graph API will require adjusting OAuth scopes,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;necessitating users to re-authorize their email accounts in Sugar&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This improvement has been backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1ff80k9i33"&gt;Configurability&amp;nbsp;updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1iv0j7tfp1"&gt;&lt;a id="Improved_Control_Over_Field_Updates_by_Proces_Definitions"&gt;&lt;/a&gt;Improved Control Over Field Updates by Process Definitions&lt;/h3&gt;
&lt;p&gt;We are enhancing the control over the fields that can be modified by Process Definitions. By default, only fields that are visible in the user interface will be eligible for updates. Fields not exposed on the frontend will no longer be modified automatically by these processes, in other words, if the field is available to be added to any UI it means it is exposed, doesn&amp;#39;t necessarily mean it has be in a Record View for example.&lt;/p&gt;
&lt;p data-start="74" data-end="312"&gt;For teams needing more specific behavior, it&amp;rsquo;s now possible to configure a denylist of fields that should not be updated by Process Definitions. This provides greater flexibility and precision in how automation interacts with your data. You can define this using a new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="346" data-end="361"&gt;$sugar_config&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;setting. For example, the configuration below prevents&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="417" data-end="435"&gt;field_to_block_1&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="440" data-end="458"&gt;field_to_block_2&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;ModuleName1&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;from being updated by Process Definitions:&lt;/p&gt;
&lt;p data-start="504" data-end="747"&gt;&lt;pre class="ui-code" data-mode="php"&gt;$sugar_config[&amp;#39;bpm_denylisted_fields&amp;#39;] = [
  &amp;#39;ModuleName1&amp;#39; =&amp;gt; [&amp;#39;field_to_block_1&amp;#39;, &amp;#39;field_to_block_2&amp;#39;],
  &amp;#39;ModuleName2&amp;#39; =&amp;gt; [&amp;#39;another_field_to_block&amp;#39;],
];
&lt;/pre&gt;&lt;/p&gt;
&lt;p data-start="641" data-end="780"&gt;&lt;strong data-start="641" data-end="650"&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;When this&amp;nbsp;sugar_config is set, only the specified fields will be blocked &amp;mdash; regardless of their visibility in the user interface.&lt;/p&gt;
&lt;h3 id="mcetoc_1f77psegv7"&gt;Sugar Config Settings&lt;/h3&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Setting Name&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Default&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Override Example&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;bpm_denylisted_fields&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;[]&lt;/td&gt;
&lt;td&gt;&lt;span&gt;$sugar_config['bpm_denylisted_fields'] = [ &amp;#39;ModuleName1&amp;#39; =&amp;gt; ['field_to_block_1', 'field_to_block_2'], &amp;#39;ModuleName2&amp;#39; =&amp;gt; ['another_field_to_block']];&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;This new config will be used to include specific fields in the &amp;#39;allowed fields to be modified by BPM&amp;#39;.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;features.&lt;span&gt;enableStrictContentSecurityPolicy&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;&lt;span&gt;$sugar_config['features']['enableStrictContentSecurityPolicy'] = true;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;If enabled, the application will enforce a strict Content Security Policy (CSP)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="mcetoc_1f77psegvb"&gt;Removed Functions / Libraries / Features&lt;/h2&gt;
&lt;h3 id="mcetoc_1j5hknrrm5"&gt;Removal of Legacy Modules Metadata Extension Path&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to modernize Sugar Core, this release removes support for the legacy&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;include/modules_override.php&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;file path used for module metadata customizations. This path was used in versions prior to Sugar 6.3.x but is no longer recommended or necessary. You&amp;nbsp;should be using the supported&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/smartlinks/developer_guide/architecture/extensions/include/"&gt;Extensions Framework&lt;/a&gt;&amp;nbsp;with the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="534" data-end="543"&gt;Include&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;extension for all metadata customizations.&lt;/p&gt;
&lt;p&gt;A new healthcheck&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;bucket &amp;#39;F&amp;#39;&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(manual customization) has been added to ensure compatibility to this version.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv0j7tfp2" data-start="83" data-end="136"&gt;Removal of&amp;nbsp;Unused Platform&lt;/h3&gt;
&lt;p&gt;As part of ongoing SugarCore cleanup efforts, the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;kiosk&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;platform has been removed to streamline with our products and services.&lt;/p&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Removing gulp-filter JS lib&lt;/h3&gt;
&lt;p&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;gulp-filter&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;library has been removed from the project after verifying it was only used in a single instance within&amp;nbsp;a Gulp task. Its functionality has been replaced with a native filter function applied directly within the stream. This change retains the original filtering logic and preserves the existing behavior and output of the task.&lt;/p&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Removing uglify-js and socket.io JS lib&lt;/h3&gt;
&lt;p&gt;Removed the legacy socket.io dependency from package.json as it was no longer used anywhere in the codebase. This also removed its transitive dependency uglify-js.&lt;/p&gt;
&lt;h2 id="mcetoc_1g4djj0su0"&gt;Data Changes&lt;/h2&gt;
&lt;h3 id="mcetoc_1gj9pc6si2"&gt;New Indexes for Calls and Meetings&lt;/h3&gt;
&lt;p&gt;To enhance the performance of queries involving Calls and Meetings using our FilterApi, we&amp;rsquo;ve added new indexes to the corresponding module definitions (vardefs). These indexes are designed to optimize data retrieval on such modules by our FilterApi:&lt;/p&gt;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&lt;code&gt;idx_calls_series_event_deleted&lt;/code&gt;&amp;nbsp;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&amp;#39;series_deleted&amp;#39;, &amp;#39;event_type&amp;#39;, &amp;#39;deleted&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-renderer-start-pos="1"&gt;&lt;code&gt;idx_calls_date_mod_rep_parent_id&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&amp;nbsp;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&amp;#39;date_modified&amp;#39;, &amp;#39;repeat_parent_id&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcetoc_1f77psegvl"&gt;Platform Updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Scheduler Jobs Now Run on a Consistent Timezone&lt;/h3&gt;
&lt;p data-start="419" data-end="565"&gt;We&amp;rsquo;ve updated the Scheduler UI to make it easier to see exactly when jobs will run&amp;mdash;based on your local timezone. No more mental math or guesswork.&lt;/p&gt;
&lt;p data-start="567" data-end="832"&gt;While this improves clarity in the interface, the underlying execution behavior hasn&amp;rsquo;t changed yet: jobs still run based on the timezone of the system user (ID = 1). We&amp;rsquo;re actively working to align the backend behavior with UTC-based scheduling in a future release.&lt;/p&gt;
&lt;p data-start="834" data-end="899"&gt;For full details or clarification, read the &lt;a href="/dev-club/b/dev-blog/posts/retraction-update-on-scheduler-job-timezone-behavior"&gt;retraction here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm6"&gt;Enhanced SystemProcessLock Handling to Prevent Redundant Execution During Admin QRR&lt;/h3&gt;
&lt;p data-start="134" data-end="681"&gt;Resolved an issue where regular user requests could unintentionally trigger cascading expensive operations, such as metadata and language cache rebuilds, during or immediately after an admin-initiated Quick Repair &amp;amp; Rebuild (QRR). Previously, when cache files were deleted by an admin QRR, regular users encountering missing cache would queue behind a system-level lock and subsequently attempt to perform the same rebuild operations once the lock was released. This behavior caused unnecessary server load, timeouts, and a degraded user experience.&lt;/p&gt;
&lt;p data-start="683" data-end="1067"&gt;With this enhancement, the lock handling logic has been updated to prevent non-admin users from executing expensive operations when the lock cannot be acquired. Instead, such requests will now safely return without action, ensuring that only admin users can initiate these processes. This significantly reduces server load and improves system responsiveness during cache rebuilding scenarios.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This improvement has been backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1j5hknrrm7"&gt;Improved Deep Linking Support for SugarCRM on Mobile Devices&lt;/h3&gt;
&lt;p data-start="302" data-end="605"&gt;In this release, we have implemented improvements to the experience of opening Sugar links from mobile devices. This update addresses inconsistent behaviors when accessing CRM links from email or messages, ensuring a more predictable and seamless user journey on both Android and iOS platforms.&lt;/p&gt;
&lt;p data-start="612" data-end="633"&gt;&lt;strong data-start="612" data-end="633"&gt;What&amp;#39;s new:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="635" data-end="1391"&gt;
&lt;li data-start="635" data-end="869"&gt;
&lt;p data-start="637" data-end="869"&gt;&lt;strong data-start="637" data-end="664"&gt;Universal Link Support:&amp;nbsp;&lt;/strong&gt;SugarCRM mobile app now uses&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content/"&gt;Universal Links&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(iOS) and App Links (Android) to seamlessly redirect users from supported emails/messages directly into the app&amp;mdash;bypassing the mobile browser when possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="871" data-end="1043"&gt;
&lt;p data-start="873" data-end="1043"&gt;&lt;strong data-start="873" data-end="895"&gt;Smart App Prompts:&amp;nbsp;&lt;/strong&gt;Users without the SugarCRM mobile app installed are now prompted to download it from the App Store or Google Play when tapping a SugarCRM link.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1045" data-end="1212"&gt;
&lt;p data-start="1047" data-end="1212"&gt;&lt;strong data-start="1047" data-end="1083"&gt;Improved Workflow Link Behavior:&amp;nbsp;&lt;/strong&gt;Links generated from workflows (e.g., SugarBPM) now attempt to open in the app rather than defaulting to the mobile browser.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1214" data-end="1391"&gt;
&lt;p data-start="1216" data-end="1391"&gt;&lt;strong data-start="1216" data-end="1246"&gt;Browser Consistency Fixes:&amp;nbsp;&lt;/strong&gt;Addressed inconsistent behavior across third-party browsers (e.g., Edge, Chrome) especially on iPhone, ensuring reliable app-switch prompts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="2453" data-end="2679"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Support for this feature on custom or on-premise domains requires configuration changes and updates to the Mobile App to include those domains. Customers should contact support for implementation guidance.&lt;/p&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Enhanced Content Security Policy (CSP) for Improved Application Security&lt;/h3&gt;
&lt;p data-start="207" data-end="462"&gt;As part of our ongoing commitment to application security, we are introducing&amp;nbsp;&lt;strong data-start="287" data-end="327"&gt;Strict Content Security Policy (CSP)&lt;/strong&gt;. This policy significantly reduces the risk of malicious script execution by restricting the sources from which scripts can be loaded.&lt;/p&gt;
&lt;p data-start="464" data-end="531"&gt;To support this enhancement, we&amp;rsquo;ve refactored the frontend code to:&lt;/p&gt;
&lt;ul data-start="533" data-end="640"&gt;
&lt;li data-start="533" data-end="588"&gt;
&lt;p data-start="535" data-end="588"&gt;Remove inline event handlers and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="568" data-end="581"&gt;javascript:&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;URIs&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="589" data-end="640"&gt;
&lt;p data-start="591" data-end="640"&gt;Add&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="595" data-end="602"&gt;nonce&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;attributes to all&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="621" data-end="631"&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;elements&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="642" data-end="717"&gt;These changes ensure that only trusted scripts are executed in the browser.&lt;/p&gt;
&lt;p data-start="719" data-end="906"&gt;This feature is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="735" data-end="755"&gt;configurable via&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="756" data-end="768"&gt;App.config&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;code data-start="770" data-end="834"&gt;$sugar_config['features']['enableStrictContentSecurityPolicy']&lt;/code&gt;) and requires a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="851" data-end="885"&gt;QRR&lt;/strong&gt;&amp;nbsp;after being toggled.&lt;/p&gt;
&lt;p data-start="908" data-end="1101"&gt;The rollout will follow a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="934" data-end="953"&gt;phased approach&lt;/strong&gt;, beginning in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="968" data-end="988"&gt;report-only mode&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to monitor violations, with full enforcement to follow in both core and customization layers in future releases.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv32kek40"&gt;Declarative Metadata&amp;nbsp;Refactoring&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to make Sugar&amp;#39;s metadata fully declarative and serializable, this release includes the first phase of refactoring non-declarative components within Sugar Core. Previously, some metadata included conditional logic that needed refactoring. We&amp;rsquo;ve begun restructuring&amp;nbsp;some&amp;nbsp;areas to align with a declarative format, laying the groundwork for&amp;nbsp;the new Metadata generation we&amp;#39;ve discussed&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="/dev-club/m/event-recaps/3010"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm8"&gt;Replace the TCPDF with SugarPdf in MapsGenerator&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;ve updated&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="217" data-end="258"&gt;MapsGenerator.php::createPDF()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to use SugarPDF instead of directly using TCPDF.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm9"&gt;Added Backend Framework for Dropdown Classifications&lt;/h3&gt;
&lt;p data-start="130" data-end="389"&gt;In this release, we&amp;#39;ve introduced backend support for a generic dropdown classification framework as part of the Dropdown Editor enhancements. This replaces hardcoded logic (e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;sales_stage_dom&lt;/code&gt;) with a scalable, metadata-driven approach, enabling configurable mappings between dropdown values and classification categories..&lt;/p&gt;
&lt;p data-start="391" data-end="403"&gt;&lt;strong&gt;Key details:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="404" data-end="871"&gt;
&lt;li data-start="404" data-end="488"&gt;
&lt;p data-start="406" data-end="488"&gt;Classifications are defined using metadata and stored via the extension framework.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="489" data-end="669"&gt;
&lt;p data-start="491" data-end="517"&gt;A classification includes:&lt;/p&gt;
&lt;ul data-start="520" data-end="669"&gt;
&lt;li data-start="520" data-end="541"&gt;
&lt;p data-start="522" data-end="541"&gt;A unique identifier&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="544" data-end="602"&gt;
&lt;p data-start="546" data-end="602"&gt;Translatable classification options (&lt;code data-start="583" data-end="601"&gt;app_list_strings&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="605" data-end="669"&gt;
&lt;p data-start="607" data-end="669"&gt;Mappings between dropdown values and classification categories&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-start="670" data-end="794"&gt;
&lt;p data-start="672" data-end="794"&gt;No API or UI changes in this implementation &amp;mdash; a helper class is provided to handle loading and saving classification data.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="795" data-end="871"&gt;
&lt;p data-start="797" data-end="871"&gt;Designed to support future use with multiple classifications per dropdown.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="873" data-end="956"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This implementation does&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="904" data-end="911"&gt;not&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;expand classifications to all dropdowns yet,&amp;nbsp;&lt;code&gt;sales_stage_dom&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;only&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrma"&gt;Prevent Data Loss on Market MLP Uninstallation and upgrade&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;re addressing a critical issue where upgrading to Market MLP version 25.1 resulted in unintended data loss, specifically affecting connector setting configurations that were expected to persist through the uninstallation process. Additionally, the upgrade mechanism has been improved to ensure proper cleanup of residual files and directories from previous installations, which previously caused version conflicts, broken dependencies, and unexpected behavior in the new version. These enhancements ensure a smoother and more reliable upgrade experience..&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&amp;nbsp;&lt;span&gt;If on upgrade customer does not have active market license then the features would not show up after upgrade even if they have MLP installed.&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrmb"&gt;SugarLive&amp;nbsp;compatibility with the V2 console for Amazon Connect&lt;/h3&gt;
&lt;p&gt;For SugarLive, Sugar provides SugarServeSampleBot.zip which was only compatible with the Amazon Lex V1 console. However, in Amazon Connect, there is a warning about the Lex V1 console being discontinued after September 15, 2025. We have now implemented and updated the SugarServeSampleBot.zip file to make it compatible with the V2 console.&lt;/p&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;Bug Fixes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fixed an issue with the FilterApi that was returning extra fields in the response payload even&amp;nbsp;when filtering by using args&amp;nbsp;&lt;code&gt;fields.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fixed QRR Occasionally Fails to Rebuild Module/Class Cache, Causing Outages and UI Errors caused by race conditions during concurrent or overlapping QRR executions or user requests during cache rebuild.&lt;/li&gt;
&lt;li&gt;Fixed an issue for a specific scenario where user licenses were failing to update&lt;/li&gt;
&lt;li&gt;Fixed an issue that a&amp;nbsp;calculated field using the countConditional function in combination with a relate field (e.g. countConditional($notes,&amp;quot;name&amp;quot;,createList(&amp;quot;test&amp;quot;))) caused a &amp;ldquo;Too many tables&amp;rdquo; error when a large number of one-to-one relationships were created between modules&lt;/li&gt;
&lt;li&gt;Fixed an issue on&amp;nbsp;Uncaught TypeError: Sugarcrm\\Sugarcrm\\Security\\Escaper\\Escape::html() caught on upgrade from 14.0.1 to 25.1&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Fixed an issue where Sugar Automate licenses and UI elements disappear after manual license validation and only reappear when an online validation is performed.&lt;/li&gt;
&lt;li&gt;Fixed an issue with our silentUpgrader that, in some cases,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;the healthcheck was still executed during the upgrade&amp;nbsp;even if the instance was configured with&lt;/span&gt;&amp;nbsp;&amp;ldquo;Bypass Healthcheck&amp;rdquo; option enabled.&lt;/li&gt;
&lt;li&gt;Fixed an issue where&amp;nbsp;&lt;span&gt;People names (Contacts, Leads, Users) currently do not have a display option for how names are traditionally written in Chinese.&amp;nbsp;The LastFirst name format has been added to the default list to support proper name display across the product (e.g., Contacts list view, record view, Lead, and Assigned To fields)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue on the Case Summary Chart and Opportunity Metrics Chart (migrated from Sucrose to Chart.JS) where, if only one label was present, it&amp;nbsp;displayed at the bottom of the chart in a space too small to display it properly&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where&amp;nbsp;Users&amp;nbsp;were receiving a permission error when trying to use Doc Merge for Quote records and have {currency_name.iso4217} in the template.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where regular users triggered redundant rebuilds after admin QRR due to SystemProcessLock, causing server overload due to multiple expensive operations running simultaneously.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where Generating a Quote from the Revenue Line Item detail view doesn&amp;#39;t allow the generated Quote to be saved.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where Update Momentum of In-Progress Smart Guides&amp;#39; scheduler fails on NULL score or point values.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Fixed an issue where a single email failure due to a Laminas error could halt the entire import scheduler, leaving the email unread and causing repeated failures.&lt;/li&gt;
&lt;li&gt;Fixed an issue where&amp;nbsp;Developer user encounters error when editing Timeline. Developer only users will not see this option but users with Admin and Developer access will.&lt;/li&gt;
&lt;li&gt;Fixed an issue when a user has a profile date format that is not ISO compliant, the date outputted into a PDF is not valid in ISO format, Sugar was using current date but should interpret that date&amp;nbsp;correctly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Sugar Core Security Updates&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to keep Sugar clean, fast, reliable, and most importantly secure, we have updated&amp;nbsp;Sugar Core code in different areas of the application such as Package Scanner, Studio, Module Installer, UI rendering, API endpoints, Legacy SOAP API, File Uploads, Portal, PII Fields, ACL rules, Legacy Workflows, Smart Guides, HTTP Headers and BWC modules. Not only those changes but we&amp;#39;ve cleaned up and improve our logging messages around deprecations.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;Some of those security improvements&lt;/span&gt;&amp;nbsp;were backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gqa6vaoc8"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;h2 id="mcetoc_1hgt5tqlsa"&gt;Healthcheck&amp;nbsp;Updates&lt;/h2&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;ul&gt;
&lt;li&gt;Built a health scanner tool to detect cloud instances using&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;SugarMarket MLP&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;versions&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;&amp;lt; 2.2&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and send it to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;bucket F&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Built a healthcheck to detect and support&amp;nbsp;the removal of the legacy&amp;nbsp;&lt;code&gt;include/modules_override.php&lt;/code&gt;&amp;nbsp;file path and send it to&amp;nbsp;&lt;code&gt;bucket F&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Sugar 25.2&lt;/div&gt;
</description></item><item><title>Sugar 25.2 Customization Guide</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/1013/sugar-25-2-customization-guide/revision/3</link><pubDate>Mon, 29 Sep 2025 13:40:54 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:3307f36c-90a1-4f3c-b6a7-d6d6641bf5cb</guid><dc:creator>Rafael Fernandes</dc:creator><description>Revision 3 posted to Dev Tutorials by Rafael Fernandes on 9/29/2025 1:40:54 PM&lt;br /&gt;
&lt;p&gt;The purpose of this document is to provide insight to Sugar Developers for upgrading custom Sugar code, extensions, and integrations to the Sugar 25.2 release. This guide focuses on changes in&amp;nbsp;&lt;span&gt;Sugar 25.2&lt;/span&gt;&amp;nbsp;that could cause an immediate impact on Sugar customizations and integrations built for earlier Sugar versions.&lt;/p&gt;
&lt;p&gt;Please check out the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="/dev-club/m/event-recaps/3045"&gt;Sugar 25.2 Developer Webinar recording&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for more developer highlights.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;For Admin and End User release notes, please visit the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Release_Notes/25.2.0_Release_Notes/"&gt;Sugar 25.2.0 Release Notes&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1ff80k9i32"&gt;User Experience Updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;&lt;span style="font-size:inherit;"&gt;&lt;span class="TextRun SCXW218900661 BCX0" lang="EN" data-contrast="auto"&gt;&lt;span class="NormalTextRun SCXW218900661 BCX0" data-ccp-parastyle="heading 2"&gt;Modernized Dropdown Editor&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-start="232" data-end="471"&gt;In this release, we&amp;rsquo;ve modernized the Dropdown Editor UI, transitioning from BWC to Sidecar for a cleaner, more streamlined admin experience. Admins can now manage entire dropdown lists from a single view&amp;mdash;no more editing one row at a time.&lt;/p&gt;
&lt;p data-start="115" data-end="326"&gt;&lt;strong data-start="328" data-end="343"&gt;What&amp;rsquo;s new:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="115" data-end="326"&gt;
&lt;p&gt;&lt;strong data-start="492" data-end="516"&gt;Redesigned Interface&lt;/strong&gt;: New Sidecar layout with updated header, inline editing, and consistent list view styling. Supports dark mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="115" data-end="326"&gt;
&lt;p&gt;&lt;strong&gt;Formatting Flow Fix &amp;amp; Toggle Control:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Chart colors no longer drive dropdown styling; instead, formatting is now defined at the dropdown level and pushed to charts for consistency..&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="296" data-end="364"&gt;
&lt;p data-start="298" data-end="364"&gt;New&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="302" data-end="337"&gt;&amp;quot;Enable formatting in dropdown&amp;quot;&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;toggle added per dropdown.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="367" data-end="486"&gt;
&lt;p data-start="369" data-end="486"&gt;Toggle is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="379" data-end="401"&gt;enabled by default&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="407" data-end="424"&gt;sales_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="426" data-end="443"&gt;quote_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="445" data-end="462"&gt;case_status_dom&lt;/code&gt;, and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="468" data-end="485"&gt;lead_status_dom&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="489" data-end="580"&gt;
&lt;p data-start="491" data-end="580"&gt;When&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="496" data-end="508"&gt;disabled&lt;/strong&gt;, formatting is hidden in the UI but preserved and still used in charts.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="583" data-end="683"&gt;
&lt;p data-start="585" data-end="683"&gt;When&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="590" data-end="601"&gt;enabled&lt;/strong&gt;, users can customize styles (colors, fonts) via the conditional formatting panel.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="686" data-end="803"&gt;
&lt;p data-start="688" data-end="803"&gt;Chart colors fallback to the chart palette for items without formatting; white and black are excluded and replaced.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Conditional Formatting&lt;/strong&gt;: New Conditional Format Options control panel that allows&lt;span&gt;&amp;nbsp;users to customize the appearance of dropdown values using styles, icons, and colors&lt;/span&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The following stock/out-of-the-box dropdowns were predefined with conditional formatting for awareness and usability:&amp;nbsp;&lt;code&gt;sales_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;quote_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;case_status_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;lead_status_dom&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Formatting will be reflected as soon as saved on List View, Record View, Focus Drawer, Reports, Other components using the dropdown values, All views in sugar&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Even if user selects RGB, we only reflect Hex number in the input&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong data-start="630" data-end="646"&gt;Add New Rows&lt;/strong&gt;: Easily add multiple items with editable fields for Display Label and Classification (for&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="737" data-end="754"&gt;sales_stage_dom&lt;/code&gt;). Only completed rows are saved.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="790" data-end="897"&gt;
&lt;p&gt;&lt;strong data-start="790" data-end="809"&gt;Improved Search&lt;/strong&gt;: Real-time search across Item Name and Display Label fields with partial match support.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="900" data-end="1048"&gt;
&lt;p&gt;&lt;strong data-start="900" data-end="922"&gt;Restore to Default&lt;/strong&gt;: Revert dropdowns to their original out-of-the-box (OOTB) state, including default sort order. Available only for OOTB lists.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1051" data-end="1148"&gt;
&lt;p&gt;&lt;strong data-start="1051" data-end="1084"&gt;Bulk Editing &amp;amp; Reduced Clicks&lt;/strong&gt;: Edit multiple rows at once and save all changes in one action.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1151" data-end="1272"&gt;
&lt;p&gt;&lt;strong data-start="1151" data-end="1176"&gt;Sort by Display Label&lt;/strong&gt;: New sort button allows ascending/descending sort; order persists across sessions and is saved.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1275" data-end="1392"&gt;
&lt;p&gt;&lt;strong data-start="1275" data-end="1304"&gt;Inline Editing &amp;amp; Deletion&lt;/strong&gt;: Edit Display Label and Classification fields directly; delete items with confirmation.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1395" data-end="1487"&gt;
&lt;p&gt;&lt;strong data-start="1395" data-end="1423"&gt;Drag-and-Drop Reordering&lt;/strong&gt;: Rearrange items via drag-and-drop. Changes are saved on click.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1490" data-end="1579"&gt;
&lt;p&gt;&lt;strong data-start="1490" data-end="1516"&gt;Unsaved Change Warning&lt;/strong&gt;: Users are prompted before navigating away from unsaved edits.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Devs can still define&amp;nbsp;custom styles (e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;backgroundColor, textColor, icon, iconColor&lt;/code&gt;) for dropdown fields using extension framework (&lt;code&gt;custom/application/Ext/DropdownsStyle/dropdowns_style.ext.php&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;Adding support to Shared Drive files on Google Drive&lt;/h3&gt;
&lt;p data-start="232" data-end="471"&gt;The Google Drive connector for Sugar has been enhanced to support access to shared files. Users can now interact with files that are shared with them or located in Shared Drives. This includes the ability to set a Shared Drive as the root path, define variable file paths on record views, and seamlessly navigate through folders within Shared Drives. Additionally, users can switch between viewing files in &amp;quot;My Files,&amp;quot; &amp;quot;Shared with me,&amp;quot; and Shared Drives. If a user attempts to access a Shared Drive they do not have permission to view, a message will appear within the dashlet indicating the lack of access, with the option to navigate to an accessible area instead.&lt;/p&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;h3 id="mcetoc_1j5hknrrm0"&gt;Enable Row-Level Actions in List View Dashlets&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="e60a4390-8308-4251-a10b-5a6239bb908d" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="19" data-end="705" data-is-last-node="" data-is-only-node=""&gt;In this release, we&amp;#39;ve&amp;nbsp;added row-level actions on List view dashlets, enabling users to take immediate action on records directly within dashboards and Focus Drawers. This improvement aligns with Sugar&amp;rsquo;s goal of&amp;nbsp;UX efficiency, reducing clicks and streamlining workflows. The available actions now mirror those found in the standard module list views, using the familiar action menu design for consistency. The preview option has been intentionally excluded to suit the context of dashlets.&lt;/p&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="269059f9-331f-43d5-a7e5-913f02e20322" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="671" data-end="888" data-is-last-node="" data-is-only-node=""&gt;&lt;strong data-start="671" data-end="680"&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This functionality has been implemented as a new plugin and integrated into relevant views. Custom or extended views that include inline editing may require a review to ensure compatibility with this update.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm1"&gt;Opportunity&amp;nbsp;Delta&amp;nbsp;in List and Record Views&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="e60a4390-8308-4251-a10b-5a6239bb908d" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p&gt;To improve visibility into how opportunities evolve over time, we&amp;#39;ve introduced a new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;Change Over Time&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;dropdown labeling &amp;quot;Show changes&amp;quot; across Opportunity list views (module and subpanels), record views, and dashlets (LV &amp;amp; RV). This enables sellers and managers to quickly see cumulative changes without piecing together discrete audit log entries.&lt;/p&gt;
&lt;p data-start="518" data-end="535"&gt;&lt;strong data-start="518" data-end="535"&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="536" data-end="941"&gt;
&lt;li data-start="536" data-end="650"&gt;
&lt;p data-start="538" data-end="650"&gt;&lt;strong data-start="538" data-end="576"&gt;New Time-Based Comparison Dropdown&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;with four options:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="596" data-end="602"&gt;None&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="604" data-end="617"&gt;Last 7 days&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="619" data-end="633"&gt;Last 14 days&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="635" data-end="649"&gt;Last 30 days&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="651" data-end="736"&gt;
&lt;p data-start="653" data-end="736"&gt;Compares current opportunity data to its value at the end of the selected past day.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="737" data-end="835"&gt;
&lt;p data-start="739" data-end="765"&gt;Highlights value changes in::&lt;/p&gt;
&lt;ul data-start="768" data-end="835"&gt;
&lt;li data-start="768" data-end="787"&gt;
&lt;p data-start="770" data-end="787"&gt;&lt;strong data-start="770" data-end="787"&gt;Likely Amount&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="790" data-end="815"&gt;
&lt;p data-start="792" data-end="815"&gt;&lt;strong data-start="792" data-end="815"&gt;Expected Close Date&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="818" data-end="835"&gt;
&lt;p data-start="820" data-end="835"&gt;&lt;strong data-start="820" data-end="835"&gt;Sales Stage&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul data-start="965" data-end="1259"&gt;
&lt;li data-start="965" data-end="1068"&gt;
&lt;p data-start="967" data-end="1068"&gt;Selection is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="980" data-end="999"&gt;sticky per view&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(e.g., list view and record view can maintain independent settings).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1069" data-end="1189"&gt;
&lt;p data-start="1071" data-end="1189"&gt;When navigating using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="1097" data-end="1111"&gt;focus icon&lt;/strong&gt;, the source view&amp;rsquo;s selection is respected and carried to the target dashlets.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1190" data-end="1259"&gt;
&lt;p data-start="1192" data-end="1259"&gt;If no prior context exists, the default selection is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="1245" data-end="1258"&gt;Last 7 days&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This feature leverages historical data available through Sugar&amp;#39;s Enhanced Forecasting and is only available to customers with the Opportunities dataset. For customers without this dataset, the feature will not be functional.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv0j7tfp2" data-start="83" data-end="136"&gt;Add Transcription, Recording, and Summary fields on the Meetings module&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;We&amp;#39;re improving Meetings module in this release by adding transcription, recording and summary fields to the record view in view/edit modes.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm2"&gt;&lt;span&gt;Dynamic Record View Dashlet Rendering for Flex Relate Fields in Focus Drawer&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Enhanced the Focus Drawer experience by enabling dynamic rendering (new tab) of related records defined via flex relate (&amp;quot;Relate To&amp;quot;) fields within a Record View Dashlet. Users can now configure a dashlet to automatically display the appropriate related record based on the selected module and record in the flex relate field without needing to manually toggle between module tabs. This&amp;nbsp;improves context access and ensures only relevant record views are shown, improving usability and efficiency when working across related modules.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1i7u16d9e5"&gt;&lt;span&gt;&lt;span class="TextRun SCXW118627354 BCX0" lang="EN-US" data-contrast="auto"&gt;&lt;span class="NormalTextRun SCXW118627354 BCX0" data-ccp-parastyle="heading 2"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="NormalTextRun SCXW249156672 BCX0"&gt;&lt;/span&gt;&lt;/span&gt;Sugar REST API updates&lt;/h2&gt;
&lt;p&gt;This Sugar release introduces REST&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;&lt;code&gt;v11_27&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Dropdown Editor&lt;/h3&gt;
&lt;p&gt;Set details of dropdown.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;PUT &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/:dropdownName&lt;/pre&gt;
&lt;p&gt;Get details of dropdown that can be changed.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;GET &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/:dropdownName&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Create Dropdown DOM issue as a php-file in customer storage directory.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/create&lt;/pre&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Enhancements to&amp;nbsp;Package Builder APIs&amp;rsquo; error handling&lt;/h3&gt;
&lt;p&gt;We have improved our Package Builder API for clear and consistent error codes that are returned for known failure scenarios.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong data-renderer-mark="true"&gt;For Example:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="402"&gt;Error messages include actionable context (e.g., missing fields, invalid formats, unsupported package types)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="516"&gt;All existing error-prone areas are covered (e.g., input validation, external service failures, internal exceptions)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="637"&gt;Fallbacks or default behaviors are implemented where appropriate&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="707"&gt;Errors follow our API error response standard (e.g., HTTP status codes, message structure)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following endpoints were improved:&lt;/p&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/customizations&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package&lt;br /&gt;&lt;br /&gt;GET &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/id&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/remote&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/data&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/getRemotePackages&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Historical Deltas API&lt;/h3&gt;
&lt;p&gt;Retrieves historical delta changes for a module based on a timestamp and list of records.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/historically/delta&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1ff80k9i33"&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id="mcetoc_1ff80k9i33"&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h2 id="mcetoc_1ftl03hrg6"&gt;&lt;span style="font-size:inherit;"&gt;PHP Library Upgrades&lt;/span&gt;&lt;/h2&gt;
&lt;h3 id="mcetoc_1iuf1asts0"&gt;Upgrade PHP libs to the latest Minor versions without breaking changes&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;ve updated all minor and patch versions of our PHP libraries as declared in the composer. Since these are minor and patch updates, no breaking changes are expected. The updates primarily include newly generated models and improved PHP version compatibility.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;aws/aws-sdk-php             3.342.19 AWS SDK for PHP - Use Amazon Web Services in your PHP project
doctrine/dbal               3.9.4    Powerful PHP database abstraction layer (DBAL) with many features for da...
egulias/email-validator     4.0.4    A library for validating emails against several RFCs
guzzlehttp/guzzle           7.9.3    Guzzle is a PHP HTTP client library
laminas/laminas-escaper     2.16.0   Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs
league/oauth2-client        2.8.1    OAuth 2.0 Client Library
microsoft/microsoft-graph   2.29.0   The Microsoft Graph SDK for PHP
rector/rector               2.0.11   Instant Upgrade and Automated Refactoring of any PHP code
rlanvin/php-rrule           2.5.2    Lightweight and fast recurrence rules for PHP (RFC 5545)&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1gj2kiufk0"&gt;PHP-Parser&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;Sugar utilizes a&amp;nbsp;PHP library called&amp;nbsp;&lt;a href="https://github.com/nikic/PHP-Parser/tree/master"&gt;PHP-Parser&lt;/a&gt;&amp;nbsp;Its purpose is to simplify static code analysis and manipulation.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There are major internal changes in this library, if you use them, please refer to this library&amp;rsquo;s upgrade procedure&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;nikic/php-parser &amp;rarr; ^v5.4.0&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gj2kiufk0"&gt;&lt;/h3&gt;
&lt;div&gt;&lt;/div&gt;
&lt;h2 id="mcetoc_1ff80k9i33"&gt;&lt;span style="font-size:inherit;"&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;JS Library Upgrades&lt;/h2&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Low to Minimum Impact&amp;nbsp;Upgrade on various JS&amp;nbsp;libraries&lt;/h3&gt;
&lt;p&gt;We have performed a patch upgrade on several JavaScript libraries used within our codebase. These upgrades are considered low to minimum impact and should not affect Sugar developers in any meaningful way. Most of the components upgraded are used for internal build processes or tooling.&lt;/p&gt;
&lt;p&gt;There are no known breaking changes associated with these updates.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;@babel/core -&amp;gt; 7.27.4
@sugarcrm/ventana -&amp;gt; 1.1.19
handlebars -&amp;gt; 4.7.8-sugarcrm
babel-loader -&amp;gt; 9.2.1
bootstrap  -&amp;gt; 5.3.7
gulp -&amp;gt; 5.0.1
jquery-migrate -&amp;gt; 3.5.2
sinon -&amp;gt; 21.0.0
tailwindcss -&amp;gt; 3.4.17
underscore -&amp;gt; 1.13.7
webpack -&amp;gt; 5.99.9
eslint-plugin-import -&amp;gt; 2.31.0
cure53/DOMPurify -&amp;gt; 3.2.6
stylelint-config-standard -&amp;gt; 38.0.0&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm3"&gt;&lt;span&gt;DOMPurify JS Library&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="8778637e-7a32-4e7c-a539-f7b8b9aba45c" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="19" data-end="397" data-is-last-node="" data-is-only-node=""&gt;The DOMPurify library is now integrated as a standard dependency, replacing the previously used forked version. This update improves the maintainability and transparency of the codebase by aligning with standard package management practices. Functionality that relies on DOMPurify remains unchanged, and the library is properly bundled and referenced throughout the application.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1iipslfhq0"&gt;Supported Platforms Update&lt;/h2&gt;
&lt;h3 id="mcetoc_1j5hknrrm4"&gt;&lt;span style="font-size:inherit;"&gt;Support for OAuth 2.0 token-based authorization with Exchange Online and Sugar email functions&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="97" data-end="525"&gt;Sugar now supports sending emails via Microsoft Exchange Online using OAuth 2.0, in alignment with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://learn.microsoft.com/en-us/answers/questions/2202498/basic-authentication-for-smtp-deprecation"&gt;Microsoft&amp;rsquo;s announcement to permanently disable Basic Authentication for SMTP AUTH in Exchange Online starting September 2025.&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This enhancement ensures continued compatibility and improved security by allowing outbound email functionality to operate without relying on SMTP when using Microsoft OAuth 2.0.&lt;/p&gt;
&lt;p data-start="527" data-end="849"&gt;With this update, when an outbound email account is configured using Microsoft OAuth 2.0, Sugar will send emails through the Microsoft Graph API instead of SMTP. This change is specific to Exchange Online accounts using Microsoft OAuth and does not impact other connection types, such as username/password or Google OAuth.&lt;/p&gt;
&lt;p data-start="851" data-end="1247"&gt;To enable this functionality, administrators must register an application in Azure with the necessary API permissions (Mail.Send, Mail.Read, IMAP.AccessAsUser.All, offline_access) and configure the Microsoft connector in Sugar with the application&amp;#39;s client ID and secret. Once authorized, the email status will indicate successful authentication, allowing emails to be sent using the new method.&lt;/p&gt;
&lt;p data-start="1249" data-end="1596"&gt;This transition will occur automatically, requiring no action from customers. However, the shift to the Microsoft Graph API will require adjusting OAuth scopes,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;necessitating users to re-authorize their email accounts in Sugar&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This improvement has been backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1ff80k9i33"&gt;Configurability&amp;nbsp;updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1iv0j7tfp1"&gt;&lt;a id="Improved_Control_Over_Field_Updates_by_Proces_Definitions"&gt;&lt;/a&gt;Improved Control Over Field Updates by Process Definitions&lt;/h3&gt;
&lt;p&gt;We are enhancing the control over the fields that can be modified by Process Definitions. By default, only fields that are visible in the user interface will be eligible for updates. Fields not exposed on the frontend will no longer be modified automatically by these processes, in other words, if the field is available to be added to any UI it means it is exposed, doesn&amp;#39;t necessarily mean it has be in a Record View for example.&lt;/p&gt;
&lt;p data-start="74" data-end="312"&gt;For teams needing more specific behavior, it&amp;rsquo;s now possible to configure a denylist of fields that should not be updated by Process Definitions. This provides greater flexibility and precision in how automation interacts with your data. You can define this using a new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="346" data-end="361"&gt;$sugar_config&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;setting. For example, the configuration below prevents&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="417" data-end="435"&gt;field_to_block_1&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="440" data-end="458"&gt;field_to_block_2&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;ModuleName1&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;from being updated by Process Definitions:&lt;/p&gt;
&lt;p data-start="504" data-end="747"&gt;&lt;pre class="ui-code" data-mode="php"&gt;$sugar_config[&amp;#39;bpm_denylisted_fields&amp;#39;] = [
  &amp;#39;ModuleName1&amp;#39; =&amp;gt; [&amp;#39;field_to_block_1&amp;#39;, &amp;#39;field_to_block_2&amp;#39;],
  &amp;#39;ModuleName2&amp;#39; =&amp;gt; [&amp;#39;another_field_to_block&amp;#39;],
];
&lt;/pre&gt;&lt;/p&gt;
&lt;p data-start="641" data-end="780"&gt;&lt;strong data-start="641" data-end="650"&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;When this&amp;nbsp;sugar_config is set, only the specified fields will be blocked &amp;mdash; regardless of their visibility in the user interface.&lt;/p&gt;
&lt;h3 id="mcetoc_1f77psegv7"&gt;Sugar Config Settings&lt;/h3&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Setting Name&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Default&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Override Example&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;bpm_denylisted_fields&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;[]&lt;/td&gt;
&lt;td&gt;&lt;span&gt;$sugar_config['bpm_denylisted_fields'] = [ &amp;#39;ModuleName1&amp;#39; =&amp;gt; ['field_to_block_1', 'field_to_block_2'], &amp;#39;ModuleName2&amp;#39; =&amp;gt; ['another_field_to_block']];&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;This new config will be used to include specific fields in the &amp;#39;allowed fields to be modified by BPM&amp;#39;.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;features.&lt;span&gt;enableStrictContentSecurityPolicy&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;&lt;span&gt;$sugar_config['features']['enableStrictContentSecurityPolicy'] = true;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;If enabled, the application will enforce a strict Content Security Policy (CSP)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="mcetoc_1f77psegvb"&gt;Removed Functions / Libraries / Features&lt;/h2&gt;
&lt;h3 id="mcetoc_1j5hknrrm5"&gt;Removal of Legacy Modules Metadata Extension Path&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to modernize Sugar Core, this release removes support for the legacy&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;include/modules_override.php&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;file path used for module metadata customizations. This path was used in versions prior to Sugar 6.3.x but is no longer recommended or necessary. You&amp;nbsp;should be using the supported&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/smartlinks/developer_guide/architecture/extensions/include/"&gt;Extensions Framework&lt;/a&gt;&amp;nbsp;with the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="534" data-end="543"&gt;Include&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;extension for all metadata customizations.&lt;/p&gt;
&lt;p&gt;A new healthcheck&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;bucket &amp;#39;F&amp;#39;&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(manual customization) has been added to ensure compatibility to this version.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv0j7tfp2" data-start="83" data-end="136"&gt;Removal of&amp;nbsp;Unused Platform&lt;/h3&gt;
&lt;p&gt;As part of ongoing SugarCore cleanup efforts, the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;kiosk&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;platform has been removed to streamline with our products and services.&lt;/p&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Removing gulp-filter JS lib&lt;/h3&gt;
&lt;p&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;gulp-filter&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;library has been removed from the project after verifying it was only used in a single instance within&amp;nbsp;a Gulp task. Its functionality has been replaced with a native filter function applied directly within the stream. This change retains the original filtering logic and preserves the existing behavior and output of the task.&lt;/p&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Removing uglify-js and socket.io JS lib&lt;/h3&gt;
&lt;p&gt;Removed the legacy socket.io dependency from package.json as it was no longer used anywhere in the codebase. This also removed its transitive dependency uglify-js.&lt;/p&gt;
&lt;h2 id="mcetoc_1g4djj0su0"&gt;Data Changes&lt;/h2&gt;
&lt;h3 id="mcetoc_1gj9pc6si2"&gt;New Indexes for Calls and Meetings&lt;/h3&gt;
&lt;p&gt;To enhance the performance of queries involving Calls and Meetings using our FilterApi, we&amp;rsquo;ve added new indexes to the corresponding module definitions (vardefs). These indexes are designed to optimize data retrieval on such modules by our FilterApi:&lt;/p&gt;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&lt;code&gt;idx_calls_series_event_deleted&lt;/code&gt;&amp;nbsp;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&amp;#39;series_deleted&amp;#39;, &amp;#39;event_type&amp;#39;, &amp;#39;deleted&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-renderer-start-pos="1"&gt;&lt;code&gt;idx_calls_date_mod_rep_parent_id&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&amp;nbsp;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&amp;#39;date_modified&amp;#39;, &amp;#39;repeat_parent_id&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcetoc_1f77psegvl"&gt;Platform Updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Scheduler Jobs Now Run on a Consistent Timezone&lt;/h3&gt;
&lt;p data-start="224" data-end="596"&gt;In this release, we&amp;#39;ve implemented a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="261" data-end="289"&gt;long-awaited enhancement&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that brings consistency to scheduler job execution times. Previously, job execution depended on the timezone of the scheduler&amp;#39;s assigned user.&amp;nbsp;As Sugar automatically updates user timezones&amp;mdash;and admin users might share credentials across different locations&amp;mdash;this led to jobs running at&amp;nbsp;different times.&lt;/p&gt;
&lt;p data-start="598" data-end="771"&gt;With this update, scheduler jobs now run based on a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="650" data-end="686"&gt;centralized (UTC), consistent timezone&lt;/strong&gt;, eliminating timezone-related surprises and ensuring reliable, predictable behavior.&lt;/p&gt;
&lt;ul data-start="1098" data-end="1370"&gt;
&lt;li data-start="935" data-end="1013"&gt;
&lt;p data-start="1100" data-end="1247"&gt;In the Scheduler UI, execution times are shown in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="1150" data-end="1173"&gt;your local timezone&lt;/strong&gt;, so you always know exactly when it will trigger&amp;mdash;no mental math required.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1248" data-end="1370"&gt;
&lt;p data-start="1250" data-end="1370"&gt;Existing schedulers remain unchanged, but we now clearly display their scheduled times based on your profile&amp;rsquo;s timezone.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1j5hknrrm6"&gt;Enhanced SystemProcessLock Handling to Prevent Redundant Execution During Admin QRR&lt;/h3&gt;
&lt;p data-start="134" data-end="681"&gt;Resolved an issue where regular user requests could unintentionally trigger cascading expensive operations, such as metadata and language cache rebuilds, during or immediately after an admin-initiated Quick Repair &amp;amp; Rebuild (QRR). Previously, when cache files were deleted by an admin QRR, regular users encountering missing cache would queue behind a system-level lock and subsequently attempt to perform the same rebuild operations once the lock was released. This behavior caused unnecessary server load, timeouts, and a degraded user experience.&lt;/p&gt;
&lt;p data-start="683" data-end="1067"&gt;With this enhancement, the lock handling logic has been updated to prevent non-admin users from executing expensive operations when the lock cannot be acquired. Instead, such requests will now safely return without action, ensuring that only admin users can initiate these processes. This significantly reduces server load and improves system responsiveness during cache rebuilding scenarios.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This improvement has been backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1j5hknrrm7"&gt;Improved Deep Linking Support for SugarCRM on Mobile Devices&lt;/h3&gt;
&lt;p data-start="302" data-end="605"&gt;In this release, we have implemented improvements to the experience of opening Sugar links from mobile devices. This update addresses inconsistent behaviors when accessing CRM links from email or messages, ensuring a more predictable and seamless user journey on both Android and iOS platforms.&lt;/p&gt;
&lt;p data-start="612" data-end="633"&gt;&lt;strong data-start="612" data-end="633"&gt;What&amp;#39;s new:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="635" data-end="1391"&gt;
&lt;li data-start="635" data-end="869"&gt;
&lt;p data-start="637" data-end="869"&gt;&lt;strong data-start="637" data-end="664"&gt;Universal Link Support:&amp;nbsp;&lt;/strong&gt;SugarCRM mobile app now uses&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content/"&gt;Universal Links&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(iOS) and App Links (Android) to seamlessly redirect users from supported emails/messages directly into the app&amp;mdash;bypassing the mobile browser when possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="871" data-end="1043"&gt;
&lt;p data-start="873" data-end="1043"&gt;&lt;strong data-start="873" data-end="895"&gt;Smart App Prompts:&amp;nbsp;&lt;/strong&gt;Users without the SugarCRM mobile app installed are now prompted to download it from the App Store or Google Play when tapping a SugarCRM link.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1045" data-end="1212"&gt;
&lt;p data-start="1047" data-end="1212"&gt;&lt;strong data-start="1047" data-end="1083"&gt;Improved Workflow Link Behavior:&amp;nbsp;&lt;/strong&gt;Links generated from workflows (e.g., SugarBPM) now attempt to open in the app rather than defaulting to the mobile browser.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1214" data-end="1391"&gt;
&lt;p data-start="1216" data-end="1391"&gt;&lt;strong data-start="1216" data-end="1246"&gt;Browser Consistency Fixes:&amp;nbsp;&lt;/strong&gt;Addressed inconsistent behavior across third-party browsers (e.g., Edge, Chrome) especially on iPhone, ensuring reliable app-switch prompts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="2453" data-end="2679"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Support for this feature on custom or on-premise domains requires configuration changes and updates to the Mobile App to include those domains. Customers should contact support for implementation guidance.&lt;/p&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Enhanced Content Security Policy (CSP) for Improved Application Security&lt;/h3&gt;
&lt;p data-start="207" data-end="462"&gt;As part of our ongoing commitment to application security, we are introducing&amp;nbsp;&lt;strong data-start="287" data-end="327"&gt;Strict Content Security Policy (CSP)&lt;/strong&gt;. This policy significantly reduces the risk of malicious script execution by restricting the sources from which scripts can be loaded.&lt;/p&gt;
&lt;p data-start="464" data-end="531"&gt;To support this enhancement, we&amp;rsquo;ve refactored the frontend code to:&lt;/p&gt;
&lt;ul data-start="533" data-end="640"&gt;
&lt;li data-start="533" data-end="588"&gt;
&lt;p data-start="535" data-end="588"&gt;Remove inline event handlers and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="568" data-end="581"&gt;javascript:&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;URIs&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="589" data-end="640"&gt;
&lt;p data-start="591" data-end="640"&gt;Add&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="595" data-end="602"&gt;nonce&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;attributes to all&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="621" data-end="631"&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;elements&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="642" data-end="717"&gt;These changes ensure that only trusted scripts are executed in the browser.&lt;/p&gt;
&lt;p data-start="719" data-end="906"&gt;This feature is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="735" data-end="755"&gt;configurable via&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="756" data-end="768"&gt;App.config&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;code data-start="770" data-end="834"&gt;$sugar_config['features']['enableStrictContentSecurityPolicy']&lt;/code&gt;) and requires a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="851" data-end="885"&gt;QRR&lt;/strong&gt;&amp;nbsp;after being toggled.&lt;/p&gt;
&lt;p data-start="908" data-end="1101"&gt;The rollout will follow a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="934" data-end="953"&gt;phased approach&lt;/strong&gt;, beginning in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="968" data-end="988"&gt;report-only mode&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to monitor violations, with full enforcement to follow in both core and customization layers in future releases.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv32kek40"&gt;Declarative Metadata&amp;nbsp;Refactoring&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to make Sugar&amp;#39;s metadata fully declarative and serializable, this release includes the first phase of refactoring non-declarative components within Sugar Core. Previously, some metadata included conditional logic that needed refactoring. We&amp;rsquo;ve begun restructuring&amp;nbsp;some&amp;nbsp;areas to align with a declarative format, laying the groundwork for&amp;nbsp;the new Metadata generation we&amp;#39;ve discussed&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="/dev-club/m/event-recaps/3010"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm8"&gt;Replace the TCPDF with SugarPdf in MapsGenerator&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;ve updated&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="217" data-end="258"&gt;MapsGenerator.php::createPDF()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to use SugarPDF instead of directly using TCPDF.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm9"&gt;Added Backend Framework for Dropdown Classifications&lt;/h3&gt;
&lt;p data-start="130" data-end="389"&gt;In this release, we&amp;#39;ve introduced backend support for a generic dropdown classification framework as part of the Dropdown Editor enhancements. This replaces hardcoded logic (e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;sales_stage_dom&lt;/code&gt;) with a scalable, metadata-driven approach, enabling configurable mappings between dropdown values and classification categories..&lt;/p&gt;
&lt;p data-start="391" data-end="403"&gt;&lt;strong&gt;Key details:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="404" data-end="871"&gt;
&lt;li data-start="404" data-end="488"&gt;
&lt;p data-start="406" data-end="488"&gt;Classifications are defined using metadata and stored via the extension framework.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="489" data-end="669"&gt;
&lt;p data-start="491" data-end="517"&gt;A classification includes:&lt;/p&gt;
&lt;ul data-start="520" data-end="669"&gt;
&lt;li data-start="520" data-end="541"&gt;
&lt;p data-start="522" data-end="541"&gt;A unique identifier&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="544" data-end="602"&gt;
&lt;p data-start="546" data-end="602"&gt;Translatable classification options (&lt;code data-start="583" data-end="601"&gt;app_list_strings&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="605" data-end="669"&gt;
&lt;p data-start="607" data-end="669"&gt;Mappings between dropdown values and classification categories&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-start="670" data-end="794"&gt;
&lt;p data-start="672" data-end="794"&gt;No API or UI changes in this implementation &amp;mdash; a helper class is provided to handle loading and saving classification data.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="795" data-end="871"&gt;
&lt;p data-start="797" data-end="871"&gt;Designed to support future use with multiple classifications per dropdown.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="873" data-end="956"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This implementation does&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="904" data-end="911"&gt;not&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;expand classifications to all dropdowns yet,&amp;nbsp;&lt;code&gt;sales_stage_dom&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;only&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrma"&gt;Prevent Data Loss on Market MLP Uninstallation and upgrade&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;re addressing a critical issue where upgrading to Market MLP version 25.1 resulted in unintended data loss, specifically affecting connector setting configurations that were expected to persist through the uninstallation process. Additionally, the upgrade mechanism has been improved to ensure proper cleanup of residual files and directories from previous installations, which previously caused version conflicts, broken dependencies, and unexpected behavior in the new version. These enhancements ensure a smoother and more reliable upgrade experience..&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&amp;nbsp;&lt;span&gt;If on upgrade customer does not have active market license then the features would not show up after upgrade even if they have MLP installed.&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrmb"&gt;SugarLive&amp;nbsp;compatibility with the V2 console for Amazon Connect&lt;/h3&gt;
&lt;p&gt;For SugarLive, Sugar provides SugarServeSampleBot.zip which was only compatible with the Amazon Lex V1 console. However, in Amazon Connect, there is a warning about the Lex V1 console being discontinued after September 15, 2025. We have now implemented and updated the SugarServeSampleBot.zip file to make it compatible with the V2 console.&lt;/p&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;Bug Fixes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fixed an issue with the FilterApi that was returning extra fields in the response payload even&amp;nbsp;when filtering by using args&amp;nbsp;&lt;code&gt;fields.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fixed QRR Occasionally Fails to Rebuild Module/Class Cache, Causing Outages and UI Errors caused by race conditions during concurrent or overlapping QRR executions or user requests during cache rebuild.&lt;/li&gt;
&lt;li&gt;Fixed an issue for a specific scenario where user licenses were failing to update&lt;/li&gt;
&lt;li&gt;Fixed an issue that a&amp;nbsp;calculated field using the countConditional function in combination with a relate field (e.g. countConditional($notes,&amp;quot;name&amp;quot;,createList(&amp;quot;test&amp;quot;))) caused a &amp;ldquo;Too many tables&amp;rdquo; error when a large number of one-to-one relationships were created between modules&lt;/li&gt;
&lt;li&gt;Fixed an issue on&amp;nbsp;Uncaught TypeError: Sugarcrm\\Sugarcrm\\Security\\Escaper\\Escape::html() caught on upgrade from 14.0.1 to 25.1&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Fixed an issue where Sugar Automate licenses and UI elements disappear after manual license validation and only reappear when an online validation is performed.&lt;/li&gt;
&lt;li&gt;Fixed an issue with our silentUpgrader that, in some cases,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;the healthcheck was still executed during the upgrade&amp;nbsp;even if the instance was configured with&lt;/span&gt;&amp;nbsp;&amp;ldquo;Bypass Healthcheck&amp;rdquo; option enabled.&lt;/li&gt;
&lt;li&gt;Fixed an issue where&amp;nbsp;&lt;span&gt;People names (Contacts, Leads, Users) currently do not have a display option for how names are traditionally written in Chinese.&amp;nbsp;The LastFirst name format has been added to the default list to support proper name display across the product (e.g., Contacts list view, record view, Lead, and Assigned To fields)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue on the Case Summary Chart and Opportunity Metrics Chart (migrated from Sucrose to Chart.JS) where, if only one label was present, it&amp;nbsp;displayed at the bottom of the chart in a space too small to display it properly&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where&amp;nbsp;Users&amp;nbsp;were receiving a permission error when trying to use Doc Merge for Quote records and have {currency_name.iso4217} in the template.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where regular users triggered redundant rebuilds after admin QRR due to SystemProcessLock, causing server overload due to multiple expensive operations running simultaneously.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where Generating a Quote from the Revenue Line Item detail view doesn&amp;#39;t allow the generated Quote to be saved.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where Update Momentum of In-Progress Smart Guides&amp;#39; scheduler fails on NULL score or point values.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Fixed an issue where a single email failure due to a Laminas error could halt the entire import scheduler, leaving the email unread and causing repeated failures.&lt;/li&gt;
&lt;li&gt;Fixed an issue where&amp;nbsp;Developer user encounters error when editing Timeline. Developer only users will not see this option but users with Admin and Developer access will.&lt;/li&gt;
&lt;li&gt;Fixed an issue when a user has a profile date format that is not ISO compliant, the date outputted into a PDF is not valid in ISO format, Sugar was using current date but should interpret that date&amp;nbsp;correctly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Sugar Core Security Updates&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to keep Sugar clean, fast, reliable, and most importantly secure, we have updated&amp;nbsp;Sugar Core code in different areas of the application such as Package Scanner, Studio, Module Installer, UI rendering, API endpoints, Legacy SOAP API, File Uploads, Portal, PII Fields, ACL rules, Legacy Workflows, Smart Guides, HTTP Headers and BWC modules. Not only those changes but we&amp;#39;ve cleaned up and improve our logging messages around deprecations.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;Some of those security improvements&lt;/span&gt;&amp;nbsp;were backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gqa6vaoc8"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;h2 id="mcetoc_1hgt5tqlsa"&gt;Healthcheck&amp;nbsp;Updates&lt;/h2&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;ul&gt;
&lt;li&gt;Built a health scanner tool to detect cloud instances using&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;SugarMarket MLP&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;versions&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;&amp;lt; 2.2&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and send it to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;bucket F&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Built a healthcheck to detect and support&amp;nbsp;the removal of the legacy&amp;nbsp;&lt;code&gt;include/modules_override.php&lt;/code&gt;&amp;nbsp;file path and send it to&amp;nbsp;&lt;code&gt;bucket F&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Sugar 25.2&lt;/div&gt;
</description></item><item><title>Sugar 25.2 Customization Guide</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/1013/sugar-25-2-customization-guide/revision/2</link><pubDate>Mon, 29 Sep 2025 13:40:36 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:3307f36c-90a1-4f3c-b6a7-d6d6641bf5cb</guid><dc:creator>Rafael Fernandes</dc:creator><description>Revision 2 posted to Dev Tutorials by Rafael Fernandes on 9/29/2025 1:40:36 PM&lt;br /&gt;
&lt;p&gt;The purpose of this document is to provide insight to Sugar Developers for upgrading custom Sugar code, extensions, and integrations to the Sugar 25.2 release. This guide focuses on changes in&amp;nbsp;&lt;span&gt;Sugar 25.2&lt;/span&gt;&amp;nbsp;that could cause an immediate impact on Sugar customizations and integrations built for earlier Sugar versions.&lt;/p&gt;
&lt;p&gt;Please check out the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="/dev-club/m/event-recaps/3045"&gt;Sugar 25.2 Developer Webinar recording&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for more developer highlights.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;For Admin and End User release notes, please visit the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Release_Notes/25.2.0_Release_Notes/"&gt;Sugar 25.2.0 Release Notes&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1ff80k9i32"&gt;User Experience Updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;&lt;span style="font-size:inherit;"&gt;&lt;span class="TextRun SCXW218900661 BCX0" lang="EN" data-contrast="auto"&gt;&lt;span class="NormalTextRun SCXW218900661 BCX0" data-ccp-parastyle="heading 2"&gt;Modernized Dropdown Editor&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-start="232" data-end="471"&gt;In this release, we&amp;rsquo;ve modernized the Dropdown Editor UI, transitioning from BWC to Sidecar for a cleaner, more streamlined admin experience. Admins can now manage entire dropdown lists from a single view&amp;mdash;no more editing one row at a time.&lt;/p&gt;
&lt;p data-start="115" data-end="326"&gt;&lt;strong data-start="328" data-end="343"&gt;What&amp;rsquo;s new:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="115" data-end="326"&gt;
&lt;p&gt;&lt;strong data-start="492" data-end="516"&gt;Redesigned Interface&lt;/strong&gt;: New Sidecar layout with updated header, inline editing, and consistent list view styling. Supports dark mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="115" data-end="326"&gt;
&lt;p&gt;&lt;strong&gt;Formatting Flow Fix &amp;amp; Toggle Control:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Chart colors no longer drive dropdown styling; instead, formatting is now defined at the dropdown level and pushed to charts for consistency..&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="296" data-end="364"&gt;
&lt;p data-start="298" data-end="364"&gt;New&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="302" data-end="337"&gt;&amp;quot;Enable formatting in dropdown&amp;quot;&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;toggle added per dropdown.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="367" data-end="486"&gt;
&lt;p data-start="369" data-end="486"&gt;Toggle is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="379" data-end="401"&gt;enabled by default&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="407" data-end="424"&gt;sales_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="426" data-end="443"&gt;quote_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="445" data-end="462"&gt;case_status_dom&lt;/code&gt;, and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="468" data-end="485"&gt;lead_status_dom&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="489" data-end="580"&gt;
&lt;p data-start="491" data-end="580"&gt;When&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="496" data-end="508"&gt;disabled&lt;/strong&gt;, formatting is hidden in the UI but preserved and still used in charts.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="583" data-end="683"&gt;
&lt;p data-start="585" data-end="683"&gt;When&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="590" data-end="601"&gt;enabled&lt;/strong&gt;, users can customize styles (colors, fonts) via the conditional formatting panel.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="686" data-end="803"&gt;
&lt;p data-start="688" data-end="803"&gt;Chart colors fallback to the chart palette for items without formatting; white and black are excluded and replaced.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Conditional Formatting&lt;/strong&gt;: New Conditional Format Options control panel that allows&lt;span&gt;&amp;nbsp;users to customize the appearance of dropdown values using styles, icons, and colors&lt;/span&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The following stock/out-of-the-box dropdowns were predefined with conditional formatting for awareness and usability:&amp;nbsp;&lt;code&gt;sales_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;quote_stage_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;case_status_dom&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;lead_status_dom&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Formatting will be reflected as soon as saved on List View, Record View, Focus Drawer, Reports, Other components using the dropdown values, All views in sugar&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Even if user selects RGB, we only reflect Hex number in the input&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong data-start="630" data-end="646"&gt;Add New Rows&lt;/strong&gt;: Easily add multiple items with editable fields for Display Label and Classification (for&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="737" data-end="754"&gt;sales_stage_dom&lt;/code&gt;). Only completed rows are saved.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="790" data-end="897"&gt;
&lt;p&gt;&lt;strong data-start="790" data-end="809"&gt;Improved Search&lt;/strong&gt;: Real-time search across Item Name and Display Label fields with partial match support.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="900" data-end="1048"&gt;
&lt;p&gt;&lt;strong data-start="900" data-end="922"&gt;Restore to Default&lt;/strong&gt;: Revert dropdowns to their original out-of-the-box (OOTB) state, including default sort order. Available only for OOTB lists.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1051" data-end="1148"&gt;
&lt;p&gt;&lt;strong data-start="1051" data-end="1084"&gt;Bulk Editing &amp;amp; Reduced Clicks&lt;/strong&gt;: Edit multiple rows at once and save all changes in one action.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1151" data-end="1272"&gt;
&lt;p&gt;&lt;strong data-start="1151" data-end="1176"&gt;Sort by Display Label&lt;/strong&gt;: New sort button allows ascending/descending sort; order persists across sessions and is saved.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1275" data-end="1392"&gt;
&lt;p&gt;&lt;strong data-start="1275" data-end="1304"&gt;Inline Editing &amp;amp; Deletion&lt;/strong&gt;: Edit Display Label and Classification fields directly; delete items with confirmation.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1395" data-end="1487"&gt;
&lt;p&gt;&lt;strong data-start="1395" data-end="1423"&gt;Drag-and-Drop Reordering&lt;/strong&gt;: Rearrange items via drag-and-drop. Changes are saved on click.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1490" data-end="1579"&gt;
&lt;p&gt;&lt;strong data-start="1490" data-end="1516"&gt;Unsaved Change Warning&lt;/strong&gt;: Users are prompted before navigating away from unsaved edits.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Devs can still define&amp;nbsp;custom styles (e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;backgroundColor, textColor, icon, iconColor&lt;/code&gt;) for dropdown fields using extension framework (&lt;code&gt;custom/application/Ext/DropdownsStyle/dropdowns_style.ext.php&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;Adding support to Shared Drive files on Google Drive&lt;/h3&gt;
&lt;p data-start="232" data-end="471"&gt;The Google Drive connector for Sugar has been enhanced to support access to shared files. Users can now interact with files that are shared with them or located in Shared Drives. This includes the ability to set a Shared Drive as the root path, define variable file paths on record views, and seamlessly navigate through folders within Shared Drives. Additionally, users can switch between viewing files in &amp;quot;My Files,&amp;quot; &amp;quot;Shared with me,&amp;quot; and Shared Drives. If a user attempts to access a Shared Drive they do not have permission to view, a message will appear within the dashlet indicating the lack of access, with the option to navigate to an accessible area instead.&lt;/p&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;h3 id="mcetoc_1j5hknrrm0"&gt;Enable Row-Level Actions in List View Dashlets&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="e60a4390-8308-4251-a10b-5a6239bb908d" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="19" data-end="705" data-is-last-node="" data-is-only-node=""&gt;In this release, we&amp;#39;ve&amp;nbsp;added row-level actions on List view dashlets, enabling users to take immediate action on records directly within dashboards and Focus Drawers. This improvement aligns with Sugar&amp;rsquo;s goal of&amp;nbsp;UX efficiency, reducing clicks and streamlining workflows. The available actions now mirror those found in the standard module list views, using the familiar action menu design for consistency. The preview option has been intentionally excluded to suit the context of dashlets.&lt;/p&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="269059f9-331f-43d5-a7e5-913f02e20322" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="671" data-end="888" data-is-last-node="" data-is-only-node=""&gt;&lt;strong data-start="671" data-end="680"&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This functionality has been implemented as a new plugin and integrated into relevant views. Custom or extended views that include inline editing may require a review to ensure compatibility with this update.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm1"&gt;Opportunity&amp;nbsp;Delta&amp;nbsp;in List and Record Views&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="e60a4390-8308-4251-a10b-5a6239bb908d" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p&gt;To improve visibility into how opportunities evolve over time, we&amp;#39;ve introduced a new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;Change Over Time&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;dropdown labeling &amp;quot;Show changes&amp;quot; across Opportunity list views (module and subpanels), record views, and dashlets (LV &amp;amp; RV). This enables sellers and managers to quickly see cumulative changes without piecing together discrete audit log entries.&lt;/p&gt;
&lt;p data-start="518" data-end="535"&gt;&lt;strong data-start="518" data-end="535"&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="536" data-end="941"&gt;
&lt;li data-start="536" data-end="650"&gt;
&lt;p data-start="538" data-end="650"&gt;&lt;strong data-start="538" data-end="576"&gt;New Time-Based Comparison Dropdown&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;with four options:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="596" data-end="602"&gt;None&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="604" data-end="617"&gt;Last 7 days&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="619" data-end="633"&gt;Last 14 days&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="635" data-end="649"&gt;Last 30 days&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="651" data-end="736"&gt;
&lt;p data-start="653" data-end="736"&gt;Compares current opportunity data to its value at the end of the selected past day.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="737" data-end="835"&gt;
&lt;p data-start="739" data-end="765"&gt;Highlights value changes in::&lt;/p&gt;
&lt;ul data-start="768" data-end="835"&gt;
&lt;li data-start="768" data-end="787"&gt;
&lt;p data-start="770" data-end="787"&gt;&lt;strong data-start="770" data-end="787"&gt;Likely Amount&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="790" data-end="815"&gt;
&lt;p data-start="792" data-end="815"&gt;&lt;strong data-start="792" data-end="815"&gt;Expected Close Date&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="818" data-end="835"&gt;
&lt;p data-start="820" data-end="835"&gt;&lt;strong data-start="820" data-end="835"&gt;Sales Stage&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul data-start="965" data-end="1259"&gt;
&lt;li data-start="965" data-end="1068"&gt;
&lt;p data-start="967" data-end="1068"&gt;Selection is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="980" data-end="999"&gt;sticky per view&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(e.g., list view and record view can maintain independent settings).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1069" data-end="1189"&gt;
&lt;p data-start="1071" data-end="1189"&gt;When navigating using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="1097" data-end="1111"&gt;focus icon&lt;/strong&gt;, the source view&amp;rsquo;s selection is respected and carried to the target dashlets.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1190" data-end="1259"&gt;
&lt;p data-start="1192" data-end="1259"&gt;If no prior context exists, the default selection is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="1245" data-end="1258"&gt;Last 7 days&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This feature leverages historical data available through Sugar&amp;#39;s Enhanced Forecasting and is only available to customers with the Opportunities dataset. For customers without this dataset, the feature will not be functional.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv0j7tfp2" data-start="83" data-end="136"&gt;Add Transcription, Recording, and Summary fields on the Meetings module&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;We&amp;#39;re improving Meetings module in this release by adding transcription, recording and summary fields to the record view in view/edit modes.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm2"&gt;&lt;span&gt;Dynamic Record View Dashlet Rendering for Flex Relate Fields in Focus Drawer&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Enhanced the Focus Drawer experience by enabling dynamic rendering (new tab) of related records defined via flex relate (&amp;quot;Relate To&amp;quot;) fields within a Record View Dashlet. Users can now configure a dashlet to automatically display the appropriate related record based on the selected module and record in the flex relate field without needing to manually toggle between module tabs. This&amp;nbsp;improves context access and ensures only relevant record views are shown, improving usability and efficiency when working across related modules.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1i7u16d9e5"&gt;&lt;span&gt;&lt;span class="TextRun SCXW118627354 BCX0" lang="EN-US" data-contrast="auto"&gt;&lt;span class="NormalTextRun SCXW118627354 BCX0" data-ccp-parastyle="heading 2"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="NormalTextRun SCXW249156672 BCX0"&gt;&lt;/span&gt;&lt;/span&gt;Sugar REST API updates&lt;/h2&gt;
&lt;p&gt;This Sugar release introduces REST&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;&lt;code&gt;v11_27&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Dropdown Editor&lt;/h3&gt;
&lt;p&gt;Set details of dropdown.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;PUT &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/:dropdownName&lt;/pre&gt;
&lt;p&gt;Get details of dropdown that can be changed.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;GET &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/:dropdownName&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Create Dropdown DOM issue as a php-file in customer storage directory.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/dropdownEditor/create&lt;/pre&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Enhancements to&amp;nbsp;Package Builder APIs&amp;rsquo; error handling&lt;/h3&gt;
&lt;p&gt;We have improved our Package Builder API for clear and consistent error codes that are returned for known failure scenarios.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong data-renderer-mark="true"&gt;For Example:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="402"&gt;Error messages include actionable context (e.g., missing fields, invalid formats, unsupported package types)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="516"&gt;All existing error-prone areas are covered (e.g., input validation, external service failures, internal exceptions)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="637"&gt;Fallbacks or default behaviors are implemented where appropriate&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul class="ak-ul" data-indent-level="1"&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="707"&gt;Errors follow our API error response standard (e.g., HTTP status codes, message structure)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following endpoints were improved:&lt;/p&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/customizations&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package&lt;br /&gt;&lt;br /&gt;GET &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/id&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/remote&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/data&lt;br /&gt;&lt;br /&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/Administration/package/getRemotePackages&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gjpkca900"&gt;Historical Deltas API&lt;/h3&gt;
&lt;p&gt;Retrieves historical delta changes for a module based on a timestamp and list of records.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;POST &amp;lt;sugar instance&amp;gt;/rest/v11_27/historically/delta&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1ff80k9i33"&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id="mcetoc_1ff80k9i33"&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h2 id="mcetoc_1ftl03hrg6"&gt;&lt;span style="font-size:inherit;"&gt;PHP Library Upgrades&lt;/span&gt;&lt;/h2&gt;
&lt;h3 id="mcetoc_1iuf1asts0"&gt;Upgrade PHP libs to the latest Minor versions without breaking changes&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;ve updated all minor and patch versions of our PHP libraries as declared in the composer. Since these are minor and patch updates, no breaking changes are expected. The updates primarily include newly generated models and improved PHP version compatibility.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;aws/aws-sdk-php             3.342.19 AWS SDK for PHP - Use Amazon Web Services in your PHP project
doctrine/dbal               3.9.4    Powerful PHP database abstraction layer (DBAL) with many features for da...
egulias/email-validator     4.0.4    A library for validating emails against several RFCs
guzzlehttp/guzzle           7.9.3    Guzzle is a PHP HTTP client library
laminas/laminas-escaper     2.16.0   Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs
league/oauth2-client        2.8.1    OAuth 2.0 Client Library
microsoft/microsoft-graph   2.29.0   The Microsoft Graph SDK for PHP
rector/rector               2.0.11   Instant Upgrade and Automated Refactoring of any PHP code
rlanvin/php-rrule           2.5.2    Lightweight and fast recurrence rules for PHP (RFC 5545)&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1gj2kiufk0"&gt;PHP-Parser&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;Sugar utilizes a&amp;nbsp;PHP library called&amp;nbsp;&lt;a href="https://github.com/nikic/PHP-Parser/tree/master"&gt;PHP-Parser&lt;/a&gt;&amp;nbsp;Its purpose is to simplify static code analysis and manipulation.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There are major internal changes in this library, if you use them, please refer to this library&amp;rsquo;s upgrade procedure&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;pre&gt;nikic/php-parser &amp;rarr; ^v5.4.0&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gj2kiufk0"&gt;&lt;/h3&gt;
&lt;div&gt;&lt;/div&gt;
&lt;h2 id="mcetoc_1ff80k9i33"&gt;&lt;span style="font-size:inherit;"&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;JS Library Upgrades&lt;/h2&gt;
&lt;div class="content-scrollable-wrapper"&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Low to Minimum Impact&amp;nbsp;Upgrade on various JS&amp;nbsp;libraries&lt;/h3&gt;
&lt;p&gt;We have performed a patch upgrade on several JavaScript libraries used within our codebase. These upgrades are considered low to minimum impact and should not affect Sugar developers in any meaningful way. Most of the components upgraded are used for internal build processes or tooling.&lt;/p&gt;
&lt;p&gt;There are no known breaking changes associated with these updates.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;@babel/core -&amp;gt; 7.27.4
@sugarcrm/ventana -&amp;gt; 1.1.19
handlebars -&amp;gt; 4.7.8-sugarcrm
babel-loader -&amp;gt; 9.2.1
bootstrap  -&amp;gt; 5.3.7
gulp -&amp;gt; 5.0.1
jquery-migrate -&amp;gt; 3.5.2
sinon -&amp;gt; 21.0.0
tailwindcss -&amp;gt; 3.4.17
underscore -&amp;gt; 1.13.7
webpack -&amp;gt; 5.99.9
eslint-plugin-import -&amp;gt; 2.31.0
cure53/DOMPurify -&amp;gt; 3.2.6
stylelint-config-standard -&amp;gt; 38.0.0&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm3"&gt;&lt;span&gt;DOMPurify JS Library&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="8778637e-7a32-4e7c-a539-f7b8b9aba45c" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="19" data-end="397" data-is-last-node="" data-is-only-node=""&gt;The DOMPurify library is now integrated as a standard dependency, replacing the previously used forked version. This update improves the maintainability and transparency of the codebase by aligning with standard package management practices. Functionality that relies on DOMPurify remains unchanged, and the library is properly bundled and referenced throughout the application.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1iipslfhq0"&gt;Supported Platforms Update&lt;/h2&gt;
&lt;h3 id="mcetoc_1j5hknrrm4"&gt;&lt;span style="font-size:inherit;"&gt;Support for OAuth 2.0 token-based authorization with Exchange Online and Sugar email functions&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="97" data-end="525"&gt;Sugar now supports sending emails via Microsoft Exchange Online using OAuth 2.0, in alignment with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://learn.microsoft.com/en-us/answers/questions/2202498/basic-authentication-for-smtp-deprecation"&gt;Microsoft&amp;rsquo;s announcement to permanently disable Basic Authentication for SMTP AUTH in Exchange Online starting September 2025.&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This enhancement ensures continued compatibility and improved security by allowing outbound email functionality to operate without relying on SMTP when using Microsoft OAuth 2.0.&lt;/p&gt;
&lt;p data-start="527" data-end="849"&gt;With this update, when an outbound email account is configured using Microsoft OAuth 2.0, Sugar will send emails through the Microsoft Graph API instead of SMTP. This change is specific to Exchange Online accounts using Microsoft OAuth and does not impact other connection types, such as username/password or Google OAuth.&lt;/p&gt;
&lt;p data-start="851" data-end="1247"&gt;To enable this functionality, administrators must register an application in Azure with the necessary API permissions (Mail.Send, Mail.Read, IMAP.AccessAsUser.All, offline_access) and configure the Microsoft connector in Sugar with the application&amp;#39;s client ID and secret. Once authorized, the email status will indicate successful authentication, allowing emails to be sent using the new method.&lt;/p&gt;
&lt;p data-start="1249" data-end="1596"&gt;This transition will occur automatically, requiring no action from customers. However, the shift to the Microsoft Graph API will require adjusting OAuth scopes,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;necessitating users to re-authorize their email accounts in Sugar&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This improvement has been backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1ff80k9i33"&gt;Configurability&amp;nbsp;updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1iv0j7tfp1"&gt;&lt;a id="Improved_Control_Over_Field_Updates_by_Proces_Definitions"&gt;&lt;/a&gt;Improved Control Over Field Updates by Process Definitions&lt;/h3&gt;
&lt;p&gt;We are enhancing the control over the fields that can be modified by Process Definitions. By default, only fields that are visible in the user interface will be eligible for updates. Fields not exposed on the frontend will no longer be modified automatically by these processes, in other words, if the field is available to be added to any UI it means it is exposed, doesn&amp;#39;t necessarily mean it has be in a Record View for example.&lt;/p&gt;
&lt;p data-start="74" data-end="312"&gt;For teams needing more specific behavior, it&amp;rsquo;s now possible to configure a denylist of fields that should not be updated by Process Definitions. This provides greater flexibility and precision in how automation interacts with your data. You can define this using a new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="346" data-end="361"&gt;$sugar_config&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;setting. For example, the configuration below prevents&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="417" data-end="435"&gt;field_to_block_1&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="440" data-end="458"&gt;field_to_block_2&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;ModuleName1&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;from being updated by Process Definitions:&lt;/p&gt;
&lt;p data-start="504" data-end="747"&gt;&lt;pre class="ui-code" data-mode="php"&gt;$sugar_config[&amp;#39;bpm_denylisted_fields&amp;#39;] = [
  &amp;#39;ModuleName1&amp;#39; =&amp;gt; [&amp;#39;field_to_block_1&amp;#39;, &amp;#39;field_to_block_2&amp;#39;],
  &amp;#39;ModuleName2&amp;#39; =&amp;gt; [&amp;#39;another_field_to_block&amp;#39;],
];
&lt;/pre&gt;&lt;/p&gt;
&lt;p data-start="641" data-end="780"&gt;&lt;strong data-start="641" data-end="650"&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;When this&amp;nbsp;sugar_config is set, only the specified fields will be blocked &amp;mdash; regardless of their visibility in the user interface.&lt;/p&gt;
&lt;h3 id="mcetoc_1f77psegv7"&gt;Sugar Config Settings&lt;/h3&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Setting Name&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Default&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Override Example&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;bpm_denylisted_fields&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;[]&lt;/td&gt;
&lt;td&gt;&lt;span&gt;$sugar_config['bpm_denylisted_fields'] = [ &amp;#39;ModuleName1&amp;#39; =&amp;gt; ['field_to_block_1', 'field_to_block_2'], &amp;#39;ModuleName2&amp;#39; =&amp;gt; ['another_field_to_block']];&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;This new config will be used to include specific fields in the &amp;#39;allowed fields to be modified by BPM&amp;#39;.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;features.&lt;span&gt;enableStrictContentSecurityPolicy&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;&lt;span&gt;$sugar_config['features']['enableStrictContentSecurityPolicy'] = true;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;If enabled, the application will enforce a strict Content Security Policy (CSP)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="mcetoc_1f77psegvb"&gt;Removed Functions / Libraries / Features&lt;/h2&gt;
&lt;h3 id="mcetoc_1j5hknrrm5"&gt;Removal of Legacy Modules Metadata Extension Path&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to modernize Sugar Core, this release removes support for the legacy&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;include/modules_override.php&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;file path used for module metadata customizations. This path was used in versions prior to Sugar 6.3.x but is no longer recommended or necessary. You&amp;nbsp;should be using the supported&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/smartlinks/developer_guide/architecture/extensions/include/"&gt;Extensions Framework&lt;/a&gt;&amp;nbsp;with the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="534" data-end="543"&gt;Include&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;extension for all metadata customizations.&lt;/p&gt;
&lt;p&gt;A new healthcheck&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;bucket &amp;#39;F&amp;#39;&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(manual customization) has been added to ensure compatibility to this version.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv0j7tfp2" data-start="83" data-end="136"&gt;Removal of&amp;nbsp;Unused Platform&lt;/h3&gt;
&lt;p&gt;As part of ongoing SugarCore cleanup efforts, the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;kiosk&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;platform has been removed to streamline with our products and services.&lt;/p&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Removing gulp-filter JS lib&lt;/h3&gt;
&lt;p&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;gulp-filter&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;library has been removed from the project after verifying it was only used in a single instance within&amp;nbsp;a Gulp task. Its functionality has been replaced with a native filter function applied directly within the stream. This change retains the original filtering logic and preserves the existing behavior and output of the task.&lt;/p&gt;
&lt;h3 id="mcetoc_1fda51lpf4"&gt;Removing uglify-js and socket.io JS lib&lt;/h3&gt;
&lt;p&gt;Removed the legacy socket.io dependency from package.json as it was no longer used anywhere in the codebase. This also removed its transitive dependency uglify-js.&lt;/p&gt;
&lt;h2 id="mcetoc_1g4djj0su0"&gt;Data Changes&lt;/h2&gt;
&lt;h3 id="mcetoc_1gj9pc6si2"&gt;New Indexes for Calls and Meetings&lt;/h3&gt;
&lt;p&gt;To enhance the performance of queries involving Calls and Meetings using our FilterApi, we&amp;rsquo;ve added new indexes to the corresponding module definitions (vardefs). These indexes are designed to optimize data retrieval on such modules by our FilterApi:&lt;/p&gt;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&lt;code&gt;idx_calls_series_event_deleted&lt;/code&gt;&amp;nbsp;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&amp;#39;series_deleted&amp;#39;, &amp;#39;event_type&amp;#39;, &amp;#39;deleted&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-renderer-start-pos="1"&gt;&lt;code&gt;idx_calls_date_mod_rep_parent_id&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&amp;nbsp;
&lt;ul&gt;
&lt;li data-renderer-start-pos="1"&gt;&amp;#39;date_modified&amp;#39;, &amp;#39;repeat_parent_id&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcetoc_1f77psegvl"&gt;Platform Updates&lt;/h2&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Scheduler Jobs Now Run on a Consistent Timezone&lt;/h3&gt;
&lt;p data-start="224" data-end="596"&gt;In this release, we&amp;#39;ve implemented a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="261" data-end="289"&gt;long-awaited enhancement&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that brings consistency to scheduler job execution times. Previously, job execution depended on the timezone of the scheduler&amp;#39;s assigned user.&amp;nbsp;As Sugar automatically updates user timezones&amp;mdash;and admin users might share credentials across different locations&amp;mdash;this led to jobs running at&amp;nbsp;different times.&lt;/p&gt;
&lt;p data-start="598" data-end="771"&gt;With this update, scheduler jobs now run based on a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="650" data-end="686"&gt;centralized (UTC), consistent timezone&lt;/strong&gt;, eliminating timezone-related surprises and ensuring reliable, predictable behavior.&lt;/p&gt;
&lt;ul data-start="1098" data-end="1370"&gt;
&lt;li data-start="935" data-end="1013"&gt;
&lt;p data-start="1100" data-end="1247"&gt;In the Scheduler UI, execution times are shown in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="1150" data-end="1173"&gt;your local timezone&lt;/strong&gt;, so you always know exactly when it will trigger&amp;mdash;no mental math required.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1248" data-end="1370"&gt;
&lt;p data-start="1250" data-end="1370"&gt;Existing schedulers remain unchanged, but we now clearly display their scheduled times based on your profile&amp;rsquo;s timezone.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1j5hknrrm6"&gt;Enhanced SystemProcessLock Handling to Prevent Redundant Execution During Admin QRR&lt;/h3&gt;
&lt;p data-start="134" data-end="681"&gt;Resolved an issue where regular user requests could unintentionally trigger cascading expensive operations, such as metadata and language cache rebuilds, during or immediately after an admin-initiated Quick Repair &amp;amp; Rebuild (QRR). Previously, when cache files were deleted by an admin QRR, regular users encountering missing cache would queue behind a system-level lock and subsequently attempt to perform the same rebuild operations once the lock was released. This behavior caused unnecessary server load, timeouts, and a degraded user experience.&lt;/p&gt;
&lt;p data-start="683" data-end="1067"&gt;With this enhancement, the lock handling logic has been updated to prevent non-admin users from executing expensive operations when the lock cannot be acquired. Instead, such requests will now safely return without action, ensuring that only admin users can initiate these processes. This significantly reduces server load and improves system responsiveness during cache rebuilding scenarios.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This improvement has been backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1j5hknrrm7"&gt;Improved Deep Linking Support for SugarCRM on Mobile Devices&lt;/h3&gt;
&lt;p data-start="302" data-end="605"&gt;In this release, we have implemented improvements to the experience of opening Sugar links from mobile devices. This update addresses inconsistent behaviors when accessing CRM links from email or messages, ensuring a more predictable and seamless user journey on both Android and iOS platforms.&lt;/p&gt;
&lt;p data-start="612" data-end="633"&gt;&lt;strong data-start="612" data-end="633"&gt;What&amp;#39;s new:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="635" data-end="1391"&gt;
&lt;li data-start="635" data-end="869"&gt;
&lt;p data-start="637" data-end="869"&gt;&lt;strong data-start="637" data-end="664"&gt;Universal Link Support:&amp;nbsp;&lt;/strong&gt;SugarCRM mobile app now uses&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content/"&gt;Universal Links&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(iOS) and App Links (Android) to seamlessly redirect users from supported emails/messages directly into the app&amp;mdash;bypassing the mobile browser when possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="871" data-end="1043"&gt;
&lt;p data-start="873" data-end="1043"&gt;&lt;strong data-start="873" data-end="895"&gt;Smart App Prompts:&amp;nbsp;&lt;/strong&gt;Users without the SugarCRM mobile app installed are now prompted to download it from the App Store or Google Play when tapping a SugarCRM link.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1045" data-end="1212"&gt;
&lt;p data-start="1047" data-end="1212"&gt;&lt;strong data-start="1047" data-end="1083"&gt;Improved Workflow Link Behavior:&amp;nbsp;&lt;/strong&gt;Links generated from workflows (e.g., SugarBPM) now attempt to open in the app rather than defaulting to the mobile browser.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="1214" data-end="1391"&gt;
&lt;p data-start="1216" data-end="1391"&gt;&lt;strong data-start="1216" data-end="1246"&gt;Browser Consistency Fixes:&amp;nbsp;&lt;/strong&gt;Addressed inconsistent behavior across third-party browsers (e.g., Edge, Chrome) especially on iPhone, ensuring reliable app-switch prompts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="2453" data-end="2679"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Support for this feature on custom or on-premise domains requires configuration changes and updates to the Mobile App to include those domains. Customers should contact support for implementation guidance.&lt;/p&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Enhanced Content Security Policy (CSP) for Improved Application Security&lt;/h3&gt;
&lt;p data-start="207" data-end="462"&gt;As part of our ongoing commitment to application security, we are introducing&amp;nbsp;&lt;strong data-start="287" data-end="327"&gt;Strict Content Security Policy (CSP)&lt;/strong&gt;. This policy significantly reduces the risk of malicious script execution by restricting the sources from which scripts can be loaded.&lt;/p&gt;
&lt;p data-start="464" data-end="531"&gt;To support this enhancement, we&amp;rsquo;ve refactored the frontend code to:&lt;/p&gt;
&lt;ul data-start="533" data-end="640"&gt;
&lt;li data-start="533" data-end="588"&gt;
&lt;p data-start="535" data-end="588"&gt;Remove inline event handlers and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="568" data-end="581"&gt;javascript:&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;URIs&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="589" data-end="640"&gt;
&lt;p data-start="591" data-end="640"&gt;Add&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="595" data-end="602"&gt;nonce&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;attributes to all&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="621" data-end="631"&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;elements&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="642" data-end="717"&gt;These changes ensure that only trusted scripts are executed in the browser.&lt;/p&gt;
&lt;p data-start="719" data-end="906"&gt;This feature is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="735" data-end="755"&gt;configurable via&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="756" data-end="768"&gt;App.config&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;code data-start="770" data-end="834"&gt;$sugar_config['features']['enableStrictContentSecurityPolicy']&lt;/code&gt;) and requires a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="851" data-end="885"&gt;QRR&lt;/strong&gt;&amp;nbsp;after being toggled.&lt;/p&gt;
&lt;p data-start="908" data-end="1101"&gt;The rollout will follow a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="934" data-end="953"&gt;phased approach&lt;/strong&gt;, beginning in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="968" data-end="988"&gt;report-only mode&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to monitor violations, with full enforcement to follow in both core and customization layers in future releases.&lt;/p&gt;
&lt;h3 id="mcetoc_1iv32kek40"&gt;Declarative Metadata&amp;nbsp;Refactoring&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to make Sugar&amp;#39;s metadata fully declarative and serializable, this release includes the first phase of refactoring non-declarative components within Sugar Core. Previously, some metadata included conditional logic that needed refactoring. We&amp;rsquo;ve begun restructuring&amp;nbsp;some&amp;nbsp;areas to align with a declarative format, laying the groundwork for&amp;nbsp;the new Metadata generation we&amp;#39;ve discussed&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="/dev-club/m/event-recaps/3010"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm8"&gt;Replace the TCPDF with SugarPdf in MapsGenerator&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;ve updated&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code data-start="217" data-end="258"&gt;MapsGenerator.php::createPDF()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to use SugarPDF instead of directly using TCPDF.&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrm9"&gt;Added Backend Framework for Dropdown Classifications&lt;/h3&gt;
&lt;p data-start="130" data-end="389"&gt;In this release, we&amp;#39;ve introduced backend support for a generic dropdown classification framework as part of the Dropdown Editor enhancements. This replaces hardcoded logic (e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;sales_stage_dom&lt;/code&gt;) with a scalable, metadata-driven approach, enabling configurable mappings between dropdown values and classification categories..&lt;/p&gt;
&lt;p data-start="391" data-end="403"&gt;&lt;strong&gt;Key details:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-start="404" data-end="871"&gt;
&lt;li data-start="404" data-end="488"&gt;
&lt;p data-start="406" data-end="488"&gt;Classifications are defined using metadata and stored via the extension framework.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="489" data-end="669"&gt;
&lt;p data-start="491" data-end="517"&gt;A classification includes:&lt;/p&gt;
&lt;ul data-start="520" data-end="669"&gt;
&lt;li data-start="520" data-end="541"&gt;
&lt;p data-start="522" data-end="541"&gt;A unique identifier&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="544" data-end="602"&gt;
&lt;p data-start="546" data-end="602"&gt;Translatable classification options (&lt;code data-start="583" data-end="601"&gt;app_list_strings&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="605" data-end="669"&gt;
&lt;p data-start="607" data-end="669"&gt;Mappings between dropdown values and classification categories&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-start="670" data-end="794"&gt;
&lt;p data-start="672" data-end="794"&gt;No API or UI changes in this implementation &amp;mdash; a helper class is provided to handle loading and saving classification data.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-start="795" data-end="871"&gt;
&lt;p data-start="797" data-end="871"&gt;Designed to support future use with multiple classifications per dropdown.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="873" data-end="956"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This implementation does&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="904" data-end="911"&gt;not&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;expand classifications to all dropdowns yet,&amp;nbsp;&lt;code&gt;sales_stage_dom&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;only&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrma"&gt;Prevent Data Loss on Market MLP Uninstallation and upgrade&lt;/h3&gt;
&lt;p&gt;In this release, we&amp;#39;re addressing a critical issue where upgrading to Market MLP version 25.1 resulted in unintended data loss, specifically affecting connector setting configurations that were expected to persist through the uninstallation process. Additionally, the upgrade mechanism has been improved to ensure proper cleanup of residual files and directories from previous installations, which previously caused version conflicts, broken dependencies, and unexpected behavior in the new version. These enhancements ensure a smoother and more reliable upgrade experience..&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&amp;nbsp;&lt;span&gt;If on upgrade customer does not have active market license then the features would not show up after upgrade even if they have MLP installed.&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1j5hknrrmb"&gt;SugarLive&amp;nbsp;compatibility with the V2 console for Amazon Connect&lt;/h3&gt;
&lt;p&gt;For SugarLive, Sugar provides SugarServeSampleBot.zip which was only compatible with the Amazon Lex V1 console. However, in Amazon Connect, there is a warning about the Lex V1 console being discontinued after September 15, 2025. We have now implemented and updated the SugarServeSampleBot.zip file to make it compatible with the V2 console.&lt;/p&gt;
&lt;h3 id="mcetoc_1gb30ctl1e"&gt;Bug Fixes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fixed an issue with the FilterApi that was returning extra fields in the response payload even&amp;nbsp;when filtering by using args&amp;nbsp;&lt;code&gt;fields.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fixed QRR Occasionally Fails to Rebuild Module/Class Cache, Causing Outages and UI Errors caused by race conditions during concurrent or overlapping QRR executions or user requests during cache rebuild.&lt;/li&gt;
&lt;li&gt;Fixed an issue for a specific scenario where user licenses were failing to update&lt;/li&gt;
&lt;li&gt;Fixed an issue that a&amp;nbsp;calculated field using the countConditional function in combination with a relate field (e.g. countConditional($notes,&amp;quot;name&amp;quot;,createList(&amp;quot;test&amp;quot;))) caused a &amp;ldquo;Too many tables&amp;rdquo; error when a large number of one-to-one relationships were created between modules&lt;/li&gt;
&lt;li&gt;Fixed an issue on&amp;nbsp;Uncaught TypeError: Sugarcrm\\Sugarcrm\\Security\\Escaper\\Escape::html() caught on upgrade from 14.0.1 to 25.1&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Fixed an issue where Sugar Automate licenses and UI elements disappear after manual license validation and only reappear when an online validation is performed.&lt;/li&gt;
&lt;li&gt;Fixed an issue with our silentUpgrader that, in some cases,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;the healthcheck was still executed during the upgrade&amp;nbsp;even if the instance was configured with&lt;/span&gt;&amp;nbsp;&amp;ldquo;Bypass Healthcheck&amp;rdquo; option enabled.&lt;/li&gt;
&lt;li&gt;Fixed an issue where&amp;nbsp;&lt;span&gt;People names (Contacts, Leads, Users) currently do not have a display option for how names are traditionally written in Chinese.&amp;nbsp;The LastFirst name format has been added to the default list to support proper name display across the product (e.g., Contacts list view, record view, Lead, and Assigned To fields)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue on the Case Summary Chart and Opportunity Metrics Chart (migrated from Sucrose to Chart.JS) where, if only one label was present, it&amp;nbsp;displayed at the bottom of the chart in a space too small to display it properly&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where&amp;nbsp;Users&amp;nbsp;were receiving a permission error when trying to use Doc Merge for Quote records and have {currency_name.iso4217} in the template.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where regular users triggered redundant rebuilds after admin QRR due to SystemProcessLock, causing server overload due to multiple expensive operations running simultaneously.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where Generating a Quote from the Revenue Line Item detail view doesn&amp;#39;t allow the generated Quote to be saved.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Fixed an issue where Update Momentum of In-Progress Smart Guides&amp;#39; scheduler fails on NULL score or point values.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Fixed an issue where a single email failure due to a Laminas error could halt the entire import scheduler, leaving the email unread and causing repeated failures.&lt;/li&gt;
&lt;li&gt;Fixed an issue where&amp;nbsp;Developer user encounters error when editing Timeline. Developer only users will not see this option but users with Admin and Developer access will.&lt;/li&gt;
&lt;li&gt;Fixed an issue when a user has a profile date format that is not ISO compliant, the date outputted into a PDF is not valid in ISO format, Sugar was using current date but should interpret that date&amp;nbsp;correctly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1ij671fi12"&gt;Sugar Core Security Updates&lt;/h3&gt;
&lt;p&gt;As part of our ongoing efforts to keep Sugar clean, fast, reliable, and most importantly secure, we have updated&amp;nbsp;Sugar Core code in different areas of the application such as Package Scanner, Studio, Module Installer, UI rendering, API endpoints, Legacy SOAP API, File Uploads, Portal, PII Fields, ACL rules, Legacy Workflows, Smart Guides, HTTP Headers and BWC modules. Not only those changes but we&amp;#39;ve cleaned up and improve our logging messages around deprecations.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn"&gt;
&lt;div class="relative flex-col gap-1 md:gap-3"&gt;
&lt;div class="flex max-w-full flex-col grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="7e91f8eb-92d0-42d3-9eb4-8e0f56738542" data-message-model-slug="gpt-4o"&gt;
&lt;div&gt;
&lt;div class="markdown prose dark:prose-invert w-full break-words dark"&gt;
&lt;p data-start="1249" data-end="1596"&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;Some of those security improvements&lt;/span&gt;&amp;nbsp;were backported to&amp;nbsp;Sugar&amp;nbsp;25.1.x.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="mcetoc_1gqa6vaoc8"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;h2 id="mcetoc_1hgt5tqlsa"&gt;Healthcheck&amp;nbsp;Updates&lt;/h2&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;ul&gt;
&lt;li&gt;Built a health scanner tool to detect cloud instances using&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;SugarMarket MLP&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;versions&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;&amp;lt; 2.2&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and send it to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;bucket F&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Built a healthcheck to detect and support&amp;nbsp;the removal of the legacy&amp;nbsp;&lt;code&gt;include/modules_override.php&lt;/code&gt;&amp;nbsp;file path and send it to&amp;nbsp;&lt;code&gt;bucket F&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;div class="code-block sc-ezYOhE gBYPJJ"&gt;
&lt;p&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Sugar 25.2&lt;/div&gt;
</description></item><item><title>PHP 7.4 Warnings to PHP8.x Errors</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/896/php-7-4-warnings-to-php8-x-errors</link><pubDate>Wed, 10 Sep 2025 18:25:36 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:dca5875d-93ad-45ff-9416-62a2e4f1b912</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 6:25:36 PM&lt;br /&gt;
&lt;div class="flex flex-grow flex-col max-w-full"&gt;
&lt;div data-message-author-role="assistant" data-message-id="e2b43fa2-3f21-462c-a90a-1c802c969a4f"&gt;
&lt;div class="markdown prose w-full break-words dark:prose-invert dark"&gt;
&lt;h2 id="mcetoc_1hgqkj3ct0"&gt;PHP Warnings to Runtime Errors&lt;/h2&gt;
&lt;p&gt;In the transition from PHP 7.4 to PHP 8.x, notable changes have occurred with certain runtime errors that were previously treated as warnings by PHP. As of PHP 8.x, these errors have been elevated to runtime errors.&lt;/p&gt;
&lt;p&gt;Automated tools like Rector can do a lot when it comes to lexical scanning but runtime (&lt;span&gt;non-lexical and more closely tied to the logic and&amp;nbsp;data aspects of code execution&lt;/span&gt;) its help is limited.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Defensive coding techniques serve as a robust shield against runtime errors, empowering&amp;nbsp;you&amp;nbsp;to anticipate and prevent such issues.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In our research,&amp;nbsp;&lt;span&gt;we&amp;#39;ve identified several of these techniques designed to assist you in addressing and rectifying potential errors.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1hgqkj3ct1" class="mt-1 flex justify-start gap-3 empty:hidden"&gt;Importance of Sugar&amp;nbsp;logs&lt;/h2&gt;
&lt;p&gt;Logs are crucial to help you understand the services and customizations you develop and operate.&amp;nbsp;Not only can logs help you troubleshoot problems, but they can also help you understand your&amp;nbsp;customizations better, and sometimes provide you with unforeseen logic errors that your code might not expect.&amp;nbsp;Even after applying defensive coding, you may end up with errors in your logs that will not propagate to the requestor, that being a UI trigger or a cron job, so it is important to keep an eye on your logs.&lt;/p&gt;
&lt;p&gt;Some Sugar errors will not be noticeable by users but they&amp;#39;ll be logged in the sugarcrm.log. It is your responsibility as Sugar instance&amp;#39;s administrator to check the logs, understand the causes, and possibly provide fixes.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="mcetoc_1hgqkj3ct1" class="mt-1 flex justify-start gap-3 empty:hidden"&gt;Enable PHP logs&lt;/h2&gt;
&lt;p&gt;The starting point to identify such errors is to enable PHP logs.&amp;nbsp;&lt;span&gt;This foundational step will provide the necessary data (warnings/errors) so you can implement effective defensive coding to prevent them from happening.&lt;/span&gt;&lt;/p&gt;
&lt;div class="mt-1 flex justify-start gap-3 empty:hidden"&gt;Enable &lt;code&gt;E_WARNING&lt;/code&gt; and &lt;code&gt;E_DEPRECATION&lt;/code&gt; Log in to the PHP error log and test your code.&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Locate your &lt;code&gt;php.ini&lt;/code&gt; file. The location can vary based on your operating system and PHP installation. Common paths include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;C:\Program Files\PHP\php.ini&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Linux: &lt;code&gt;/etc/php/{version}/apache2/php.ini&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the &lt;code&gt;php.ini&lt;/code&gt; file in a text editor with administrative privileges.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search for the &lt;code&gt;error_reporting&lt;/code&gt; directive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the directive to include &lt;code&gt;E_WARNING&lt;/code&gt; and &lt;code&gt;E_DEPRECATED&lt;/code&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;error_reporting = E_ALL &amp;amp; ~E_NOTICE | E_WARNING | E_DEPRECATED
&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1hgqkj3ct1" class="mt-1 flex justify-start gap-3 empty:hidden"&gt;Understanding Rector&amp;#39;s Limitations&lt;/h2&gt;
&lt;p&gt;While Rector serves as a valuable tool for scanning code and conducting lexical analysis, it&amp;#39;s important to note its limitations regarding Sugar expressions used in calculated fields, SugarBPM, and similar contexts.&lt;/p&gt;
&lt;p&gt;During runtime, Sugar evaluates expressions using various methods, including PHP evaluation. This introduces the possibility of encountering exceptions&amp;nbsp;similar to those in native PHP code. Therefore, developers must review their expressions to ensure they are &amp;quot;code safe&amp;quot; for Sugar interpreter, for example, by checking if a variable is not empty before applying a function to it.&lt;/p&gt;
&lt;p&gt;Exceptions occurring during expression evaluation are logged in the &lt;code&gt;sugarcrm.log&lt;/code&gt; file. This log serves as a valuable resource for developers, facilitating the analysis and resolution of runtime issues within&amp;nbsp;Sugar.&lt;/p&gt;
&lt;h2 id="mcetoc_1hgqkj3ct1" class="mt-1 flex justify-start gap-3 empty:hidden"&gt;Applying defensive code to avoid Runtime Errors&lt;/h2&gt;
&lt;h3 id="mcetoc_1hgqkpl7g0"&gt;PHP Warning: count(): Parameter must be an array or an object that implements Countable in *&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;&lt;i&gt;safeCount&lt;/i&gt;&lt;/b&gt;&lt;b&gt; will take care of it&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;For Sugar, the best practice is to use &lt;code&gt;safeCount()&lt;/code&gt; instead of &lt;code&gt;count()&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1hgqkpl7g1"&gt;PHP Warning: A non-numeric value encountered*&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Occurs when invalid strings are coerced using operators expecting numbers (+ - * / ** % &amp;lt;&amp;lt; &amp;gt;&amp;gt; | &amp;amp; ^) or their assignment equivalents.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cast the values to int or float before the arithmetic operation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Check the values for &lt;i&gt;is_numeric&lt;/i&gt;&lt;i&gt;($value)&lt;/i&gt; and skip the calculation if the values are not numeric.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1hgqkpl7g2"&gt;PHP Warning: sizeof(): Parameter must be an array or an object that implements Countable in*&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;if you have code like &lt;code&gt;&lt;i&gt;$a = &lt;/i&gt;&lt;i&gt;sizeof&lt;/i&gt;&lt;i&gt;($b) + 1;&lt;/i&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;replace sizeof with &lt;code&gt;safeCount&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1hgqkpl7g3"&gt;PHP Warning: strlen() expects parameter 1 to be string, array given in*&lt;/h3&gt;
&lt;p&gt;Add defensive for example:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (is_string($some_value)) {
  $len = strlen($some_value);
} else { 
  // throw error or deal with your data
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1hgqkr1n54"&gt;&lt;br /&gt;PHP Warning: array_key_exists(): The first argument should be either a string or an integer in*&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Add defensive for example:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if ((is_string($haystack) || is_numeric($haystack)) &amp;amp;&amp;amp; array_key_exists($haystack, $myArray)) {
    // continue with your logic  
} else { 
    // throw error or deal with your data;
}
&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1hgqksl5u5"&gt;PHP Warning: strpos(): Empty needle in*&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Add defensive code, for example:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (isset($haystack) &amp;amp;&amp;amp; !empty($haystack) &amp;amp;&amp;amp; isset($needle) &amp;amp;&amp;amp; !empty($needle) &amp;amp;&amp;amp; strpos($haystack, $needle, $offset)) {
   // continue with your logic
} else { 
   // throw error or deal with your data;
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1hgqktv9u6"&gt;PHP Warning: array_intersect(): Expected parameter 1 to be an array, null given in*&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Add defensive for example:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (is_array($array) &amp;amp;&amp;amp; is_array($arrays)) {
   $intersect = array_intersect($array, $arrays);
} else { 
   // throw error or deal with your data;
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1hgqkpl7g3"&gt;PHP Warning: Creating default object from empty value*&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Add defensive for example:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;// Option 1: Skip execution branch if the value is not an object
$a = BeanFactory::newBean(&amp;#39;someweirdstuff&amp;#39;);
if (!($a instanceof SugarBean)) {
  return;
}
$a-&amp;gt;b = &amp;#39;c&amp;#39;;

// Option 2: In cases when the variable is used without being initialized - initialize it first
$a = new stdClass();
$a-&amp;gt; b = ‘c’;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1hgqkpl7g3"&gt;PHP Warning: DateTime::diff() expects parameter 1 to be DateTimeInterface, null given*&lt;/h3&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: DateTime::setTimezone() expects parameter 1 to be DateTimeZone, string given&lt;span class="heading-anchor-wrapper"&gt;&lt;span class="cc-1afrefi"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Add defensive for example:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$date = new \DateTime();
if ($otherDate instanceof \DateTimeInterface) {
  $diff = $date-&amp;gt;diff($otherDate);
}
&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: Division by zero&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Add defensive for example:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if ($rate != 0) {
  $result = $amount / $rate;
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: Illegal offset type in isset or empty; PHP Warning: Illegal offset type&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="1317"&gt;Array keys can only be int or string. Float, boolean and null get converted transparently (though it may be signal that something went wrong), but arrays, objects and resources don&amp;rsquo;t.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The solution is also defensive coding::&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (is_string($key) || is_int($key)) {
  $map[$key] = $value;
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: Illegal string offset&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="1317"&gt;Array keys can only be int or string. Float, boolean and null get converted transparently (though it may be signal that something went wrong), but arrays, objects and resources don&amp;rsquo;t.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The solution is also defensive coding::&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$a = ‘some_string’;
if (is_array($a)) {
  var_dump($a[‘key’]);
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: Use of undefined constant SOME_NAME - assumed &amp;#39;SOME_NAME&amp;#39;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;This may have multiple causes, the most common is string literal, not wrapped in quotes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The solution is to wrap these to quotes:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$status = $row[&amp;#39;status&amp;#39;];
$duration = $n . &amp;#39;hours&amp;#39;;
&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: fclose() expects parameter 1 to be resource, bool given&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The solution is defensive coding, but in case of resources it should be applied as close as possible to the place of its initialization&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$file = fopen($name, &amp;#39;r&amp;#39;);
if ($file === false) {
    // handle error, interrupt execution - whatever appropriate
}
// do something
fclose($file);&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: call_user_func() expects parameter 1 to be a valid callback, function &amp;#39;my_func_name&amp;#39; not found or invalid function name&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p data-renderer-start-pos="1317"&gt;The solution is also defensive coding:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (is_callable(&amp;#39;my_func_name&amp;#39;)) {
    call_user_func(&amp;#39;my_func_name&amp;#39;);
} else {
    // handle error
}
&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: implode(): Invalid arguments passed&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Happens when the second argument is not array:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The solution is also defensive coding:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (is_array($names)) {
    var_dump(implode(&amp;#39;, &amp;#39;, $names));
}

// or

$pieces = is_array($names) ? $names : [];
var_dump(implode(&amp;#39;, &amp;#39;, $pieces));&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: max(): Array must contain at least one element&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The solution is also defensive coding:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (safeCount($items) &amp;gt; 0) {
    $best = max($items);
} else {
    // handle error, assign some defaults, etc.
}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3&gt;PHP Warning: Attempt to assign property &amp;#39;team_id&amp;#39; of non-object&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Defensive coding - check if the object is really an instance of the class you assuming. Makes sense to perform this check as close to object initialization as possible (or at the beginning of the function if the object is passed as an argument)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (!($a instanceof SomeClass)) {
    // handle error, interrupt execution, etc.
}
$a-&amp;gt;b = 1;&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1hgqkpl7g3"&gt;PHP Warning: array_multisort(): Array sizes are inconsistent*&lt;/h3&gt;
&lt;h3 id="PHP-Warning:-DateTime::setTimezone()-expects-parameter-1-to-be-DateTimeZone,-string-given" data-renderer-start-pos="902"&gt;PHP Warning: array_combine(): Both parameters should have an equal number of elements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;These warnings have similar cause and solution. Make sure, that sorted arrays are of the same size&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (safeCount($apples) !== safeCount($oranges)) {
    // handle error
}
array_multisort($apples, $oranges);&lt;/pre&gt;&lt;/p&gt;
&lt;h3&gt;PHP Warning: array_multisort(): Argument #1 is expected to be an array or a sort flag&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Defensive coding. Assuming here that sorting flags and order are hardcoded, otherwise, check them with is_integer&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;if (!is_array($apples) || !is_array($oranges)) {
    // handle error
}
array_multisort($apples, SORT_ASC, SORT_STRING, $oranges, SORT_ASC, SORT_STRING);
&lt;/pre&gt;&lt;/p&gt;
&lt;h3&gt;PHP Warning: sprintf(): Too few arguments&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Carefully check that number of arguments to sprintf corresponds to number of placeholders in format string&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;echo sprintf(&amp;#39;%s is the capital of %s&amp;#39;, &amp;#39;London&amp;#39;); // error - 2 placeholders but 1 argument
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Executing Rector</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/836/executing-rector</link><pubDate>Wed, 10 Sep 2025 18:23:34 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:9077db19-d8a3-40ae-af68-ef3aae2e8fbd</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 6:23:34 PM&lt;br /&gt;
&lt;p&gt;Now that you have your Rector properly configured with your customization, either via MLP or custom code you can continue with a dry-run or run rector automatically.&lt;/p&gt;
&lt;h2 id="mcetoc_1guavo72a0"&gt;Dry-run Rector&lt;/h2&gt;
&lt;p&gt;Dry-run gives you the opportunity to see what Rector has found and will execute/change before it actually does it on your behalf.&lt;/p&gt;
&lt;p&gt;it&amp;#39;s time to dry-run rector by executing the following command.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;/path/to/RECTOR/vendor/rector/rector/bin/rector/bin/rector process --clear-cache --dry-run &amp;gt; php83.diff&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Dry run produces a very detailed output file (if you spooled it off to a file) containing the diff and the list of rector rules that were applied to make your code compatible with PHP 8.3 (which we configured it to)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;1 file with changes
===================

1) custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php:0

    ---------- begin diff ----------
@@ @@
 &amp;lt;?php 
  //WARNING: The contents of this file are auto-generated
- $this-&amp;gt;ss-&amp;gt;assign(&amp;quot;current_users&amp;quot;, count($active_users));
- $this-&amp;gt;ss-&amp;gt;assign(&amp;quot;user_count_param&amp;quot;, &amp;quot;user_count: &amp;#39;&amp;quot;.count($active_users).&amp;quot;&amp;#39;&amp;quot;);
+ $this-&amp;gt;ss-&amp;gt;assign(&amp;quot;current_users&amp;quot;, is_countable($active_users) ? count($active_users) : 0);
+ $this-&amp;gt;ss-&amp;gt;assign(&amp;quot;user_count_param&amp;quot;, &amp;quot;user_count: &amp;#39;&amp;quot;.(is_countable($active_users) ? count($active_users) : 0).&amp;quot;&amp;#39;&amp;quot;);
  
 ?&amp;gt;
    ----------- end diff -----------

Applied rules:
 * AddDefaultValueForUndefinedVariableRector (https://github.com/vimeo/psalm/blob/29b70442b11e3e66113935a2ee22e165a70c74a4/docs/fixing_code.md#possiblyundefinedvariable)
 * CountOnNullRector (https://3v4l.org/Bndc9)

 [OK] 1 file would have changed (dry-run) by Rector                                                                     &lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1gub0q34a0"&gt;Execute Rector&lt;/h2&gt;
&lt;p&gt;If your dry-run is clear or you are ready to let Rector auto-update your code, now it&amp;#39;s time&amp;nbsp;to get a second &lt;code&gt;git commit&lt;/code&gt; to &amp;quot;clear&amp;quot; the system so we know exactly what rector has done to our system.&lt;/p&gt;
&lt;h3 id="mcetoc_1gub12ta51"&gt;Prepare Git&lt;/h3&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;sh-3.2$ git status
On branch main
Changes not staged for commit:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
        modified:   config_override.php
        modified:   custom/application/Ext/Include/modules.ext.php

Untracked files:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to include in what will be committed)
        custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php
        custom/Extension/application/Ext/Include/orderMapping.php
        custom/clients/
        custom/modules/ActivityStream/
        custom/modules/Forecasts/Ext/clients/
        custom/modules/pmse_Project/

no changes added to commit (use &amp;quot;git add&amp;quot; and/or &amp;quot;git commit -a&amp;quot;)&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Commit&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;sh-3.2$ git add . &amp;amp;&amp;amp; git commit -am &amp;quot;Before Rector&amp;quot;
[master 33a18416] Before Rector
 422 files changed, 96500 insertions(+)
&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1gub14s0p2"&gt;Execute&lt;/h3&gt;
&lt;p&gt;Run the rector process once again, but without &lt;code&gt;--dry-run&lt;/code&gt; and &lt;code&gt;--clear-cache&lt;/code&gt; options as the cache is up to date (the process should look like this):&lt;/p&gt;
&lt;div class="code-block  css-1si5tit"&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;➜  sugar git:(master) /path/to/RECTOR/vendor/rector/rector/bin/rector/bin/rector process                                           
 10/30 [▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░]  33%&lt;/pre&gt;&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;*&amp;nbsp;This may take time, depending on the complexity of the code. In some cases, this can take more than an hour.&lt;/div&gt;
&lt;h3 id="mcetoc_1gub18vs83" class="code-block  css-1si5tit"&gt;After Execution (Git diff)&lt;/h3&gt;
&lt;div class="code-block  css-1si5tit"&gt;Once finished, Rector will provide&amp;nbsp;a similar report to the dry-run, but now those files have actually been modified in the filesystem.&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;By executing git status you should see all the changes ready to work on PHP 8.3 (or depending on the config you specified)&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;&lt;pre class="ui-code" data-mode="php"&gt;sh-3.2$ git status
On branch main
Changes not staged for commit:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
        modified:   custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php

no changes added to commit (use &amp;quot;git add&amp;quot; and/or &amp;quot;git commit -a&amp;quot;)&lt;/pre&gt;&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;&lt;/div&gt;
&lt;pre class="code-block  css-1si5tit"&gt;Congratulations, you now have your code compatible with PHP 8.3.&lt;/pre&gt;
&lt;p class="code-block  css-1si5tit"&gt;You can now commit&amp;nbsp;your new code as follows:&lt;/p&gt;
&lt;div class="code-block  css-1si5tit"&gt;&lt;pre class="ui-code" data-mode="text"&gt;sh-3.2$ git add . &amp;amp;&amp;amp; git commit -am &amp;quot;PHP 8.3 upgrade refactor&amp;quot;
[master 7ad3bf22] PHP 8.3 upgrade refactor
 1 files changed, 1 insertions(+), 1 deletions(-)&lt;/pre&gt;&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;&lt;/div&gt;
&lt;div class="code-block  css-1si5tit"&gt;
&lt;div class="content"&gt;
&lt;h2 id="mcetoc_1gudgk28f0"&gt;You can move on to the Keeping Compatibility Phase:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/838/keeping-compatibility"&gt;Keeping Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Prepare Rector for Customizations</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/837/prepare-rector-for-customizations</link><pubDate>Wed, 10 Sep 2025 18:21:05 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:0c3c010e-3789-4bb8-8641-fa07752f8e5f</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 6:21:05 PM&lt;br /&gt;
&lt;p&gt;If you are looking to make your&amp;nbsp;customizations, not packaged in an MLP,&amp;nbsp;compatible with different versions of PHP, this guide is for you.&lt;/p&gt;
&lt;p&gt;You will take your customization code and add to the Sugar instance&amp;nbsp;&lt;span&gt;where Rector is and git has been initialized.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You need to have a inventory of your assets&amp;nbsp;to create your git/rector config.&lt;/p&gt;
&lt;h3 id="mcetoc_1guaf95jk0"&gt;Prerequisites:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/828/rector-basic-setup"&gt;Basic Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Customization Assets Inventory (file path of your assets)&lt;/li&gt;
&lt;li&gt;A vanilla instance if you don&amp;#39;t have an inventory&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1guafg2ff1"&gt;Create your Customization Inventory&lt;/h3&gt;
&lt;p&gt;If you already have&amp;nbsp;your customization inventory, great! you can skip this section.&lt;/p&gt;
&lt;p&gt;There are a few different ways of achieving the same result, we are going to use a vanilla instance as our basis for git comparison, and your current &amp;quot;&lt;code&gt;deployed&lt;/code&gt;&amp;quot; instance with your code installed in the filesystem.&lt;/p&gt;
&lt;p&gt;In your vanilla instance, you will follow the steps from our &lt;a href="/dev-club/w/dev-tutorials/828/rector-basic-setup"&gt;Basic Setup&lt;/a&gt; and have git init and commit.&lt;/p&gt;
&lt;p&gt;Once you have that in place, (&lt;code&gt;git status&lt;/code&gt; should return empty results), you are good to go on copying all the files from your&amp;nbsp;&lt;span&gt;&lt;code&gt;deployed&lt;/code&gt;&amp;nbsp;&lt;/span&gt;instance to the&amp;nbsp;vanilla&amp;nbsp;one.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;sh-3.2$ cp -rf /path/to/current/sugar/* /path/to/vanila/sugar/&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;* it is important to have the exact same version of Sugar, otherwise, you&amp;#39;ll have &amp;quot;noise&amp;quot; in the vendors folder and other files that aren&amp;#39;t necessarily your custom code but Sugar release-related files and those can be ignored if found.&lt;/p&gt;
&lt;p&gt;After it has been copied over, execute&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;git status&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;commands to get your customization inventory:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;sh-3.2$ git status
On branch main
Untracked files:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to include in what will be committed)
        modules/FD_SignupFraudDetection/
        modules/FD_TransactionFraudDetect/

nothing added to commit but untracked files present (use &amp;quot;git add&amp;quot; to track)
&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1guah5gi50"&gt;Prepare your Rector config&lt;/h3&gt;
&lt;p&gt;Copy the paths, and paste them into the rector.php config file that was generated in the previous step, replacing the default paths in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="code css-z5oxh7"&gt;$rectorConfig-&amp;gt;paths([&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;array like this (you can ignore some of the files, like&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="code css-z5oxh7"&gt;*ext.php&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="code css-z5oxh7"&gt;*orderMapping.php&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;    $rectorConfig-&amp;gt;paths([
        &amp;#39;/path/to/sugar/modules/FD_SignupFraudDetection/&amp;#39;,
        &amp;#39;/path/to/sugar/modules/FD_TransactionFraudDetect/&amp;#39;,
    ]);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&amp;nbsp;Rector will generate a sample code with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;__DIR__,&lt;/code&gt;&amp;nbsp;that, however, translates to &amp;quot;current folder&amp;quot; which can be different than your Sugar install. As a rule of thumb, always use fully qualified names&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;&lt;strong&gt;without&lt;/strong&gt;&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;__DIR__&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Modify the &lt;code&gt;rector.php&lt;/code&gt; config file to upgrade the code to the newest supported PHP version (in our case, 8.3):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Specify PHP 8.3 as your phpVersion:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$rectorConfig-&amp;gt;phpVersion(PhpVersion::PHP_83);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;#39;d like to have only the incompatible rules between PHP 7.4 to PHP 8.3, you can use the following:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;    $rectorConfig-&amp;gt;rules([
        AddParamBasedOnParentClassMethodRector::class,
        SetStateToStaticRector::class,
        ArrayKeyExistsOnPropertyRector::class,
        ExportToReflectionFunctionRector::class,
        FilterVarToAddSlashesRector::class,
        MbStrrposEncodingArgumentPositionRector::class,
        MoneyFormatToNumberFormatRector::class,
        RealToFloatTypeCastRector::class,
        RestoreDefaultNullToNullableTypePropertyRector::class,
        StringifyStrNeedlesRector::class,
        GetClassOnNullRector::class,
        ListEachRector::class,
        ReplaceEachAssignmentWithKeyCurrentRector::class,
        ParseStrWithResultArgumentRector::class,
        StringifyDefineRector::class,
        WhileEachToForeachRector::class,
        ConsistentImplodeRector::class,
        CurlyToSquareBracketArrayStringRector::class,
        StaticCallOnNonStaticToInstanceCallRector::class,
    ]);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Add&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;safeCount&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;rule to your config if you are in a supported Sugar version&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;&amp;lt;?php
...
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
...
return static function (RectorConfig $rectorConfig): void {
...
    $rectorConfig-&amp;gt;ruleWithConfiguration(RenameFunctionRector::class, [
        &amp;#39;count&amp;#39; =&amp;gt; &amp;#39;safeCount&amp;#39;,
    ]);
...&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1guah5gi50"&gt;Full config file example (incompatibilities only)&lt;/h3&gt;
&lt;p&gt;By following those steps, you should have a rector.php similar to this to run&amp;nbsp;&lt;span&gt;incompatible rules between PHP 7.4 to PHP 8.3&lt;/span&gt;:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;&amp;lt;?php
declare(strict_types=1);

use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector;
use Rector\Php72\Rector\Assign\ListEachRector;
use Rector\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector;
use Rector\Php72\Rector\FuncCall\GetClassOnNullRector;
use Rector\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector;
use Rector\Php72\Rector\FuncCall\StringifyDefineRector;
use Rector\Php72\Rector\While_\WhileEachToForeachRector;
use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector;
use Rector\Php74\Rector\Double\RealToFloatTypeCastRector;
use Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector;
use Rector\Php74\Rector\FuncCall\FilterVarToAddSlashesRector;
use Rector\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector;
use Rector\Php74\Rector\FuncCall\MoneyFormatToNumberFormatRector;
use Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector;
use Rector\Php74\Rector\StaticCall\ExportToReflectionFunctionRector;
use Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector;
use Rector\Php80\Rector\ClassMethod\SetStateToStaticRector;
use Rector\Config\RectorConfig;
use Rector\ValueObject\PhpVersion;
use Rector\Php74\Rector\ArrayDimFetch\CurlyToSquareBracketArrayStringRector;
use Rector\Caching\ValueObject\Storage\MemoryCacheStorage;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
use Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector;

return static function (RectorConfig $rectorConfig): void {
    $rectorConfig-&amp;gt;paths([
        &amp;#39;/path/to/sugar/modules/FD_SignupFraudDetection/&amp;#39;,
        &amp;#39;/path/to/sugar/modules/FD_TransactionFraudDetect/&amp;#39;,
    ]);

    $rectorConfig-&amp;gt;cacheClass(MemoryCacheStorage::class);
    $rectorConfig-&amp;gt;disableParallel();
    $rectorConfig-&amp;gt;phpVersion(PhpVersion::PHP_83);

    $rectorConfig-&amp;gt;rules([
        AddParamBasedOnParentClassMethodRector::class,
        SetStateToStaticRector::class,
        ArrayKeyExistsOnPropertyRector::class,
        ExportToReflectionFunctionRector::class,
        FilterVarToAddSlashesRector::class,
        MbStrrposEncodingArgumentPositionRector::class,
        MoneyFormatToNumberFormatRector::class,
        RealToFloatTypeCastRector::class,
        RestoreDefaultNullToNullableTypePropertyRector::class,
        StringifyStrNeedlesRector::class,
        GetClassOnNullRector::class,
        ListEachRector::class,
        ReplaceEachAssignmentWithKeyCurrentRector::class,
        ParseStrWithResultArgumentRector::class,
        StringifyDefineRector::class,
        WhileEachToForeachRector::class,
        ConsistentImplodeRector::class,
        CurlyToSquareBracketArrayStringRector::class,
        StaticCallOnNonStaticToInstanceCallRector::class,
    ]);
    $rectorConfig-&amp;gt;ruleWithConfiguration(RenameFunctionRector::class, [
        &amp;#39;count&amp;#39; =&amp;gt; &amp;#39;safeCount&amp;#39;,
    ]);
};&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1gudgk28f0"&gt;You can move on to the&amp;nbsp;Execution Phase:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/836/executing-rector"&gt;Execute Rector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Prepare Rector for MLP/Addon</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/834/prepare-rector-for-mlp-addon</link><pubDate>Wed, 10 Sep 2025 18:20:03 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:6ad24d35-d4d0-410f-8bd4-16ba03823af4</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 6:20:03 PM&lt;br /&gt;
&lt;p&gt;If you are looking to make your Module Loadable Package (MLP), a.k.a Addon, compatible with different versions of PHP, this guide is for you.&lt;/p&gt;
&lt;p&gt;You will&amp;nbsp;install&amp;nbsp;an MLP zip file into Sugar&amp;#39;s instance where Rector is and git has been initialized, by doing that, Sugar will add the MLP in the filesystem and git will let us know what has changed.&lt;/p&gt;
&lt;h3 id="mcetoc_1guaf95jk0"&gt;Prerequisites:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/828/rector-basic-setup"&gt;Basic Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A module loadable package (zip file)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1guafg2ff1"&gt;Upload and Install your MLP&lt;/h3&gt;
&lt;p&gt;Upload and install your MLP as you normally would through Sugar&amp;#39;s UI or API. Sugar will copy your files per manifest definition and perform the database operations as well (which are irrelevant for Rector).&lt;/p&gt;
&lt;p&gt;After it has been installed, execute &lt;code&gt;git status&lt;/code&gt; commands to see which files were updated/added by Sugar:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;sh-3.2$ git status
On branch main
Changes not staged for commit:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
        modified:   config_override.php
        modified:   custom/application/Ext/Include/modules.ext.php

Untracked files:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to include in what will be committed)
        custom/Extension/application/Ext/Include/xxxx.php
        custom/Extension/application/Ext/Include/orderMapping.php
        custom/modules/ActivityStream/
        custom/modules/Forecasts/Ext/clients/
        custom/modules/pmse_Project/

no changes added to commit (use &amp;quot;git add&amp;quot; and/or &amp;quot;git commit -a&amp;quot;)&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1guah5gi50"&gt;Prepare your Rector config&lt;/h3&gt;
&lt;p&gt;Copy the fully qualified paths, and paste them into the rector.php config file that was generated in the previous step, replacing the default paths in &lt;code class="code css-z5oxh7"&gt;$rectorConfig-&amp;gt;paths([&lt;/code&gt; array like this (you can ignore some of the files, like &lt;code class="code css-z5oxh7"&gt;*ext.php&lt;/code&gt; and &lt;code class="code css-z5oxh7"&gt;*orderMapping.php&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;    $rectorConfig-&amp;gt;paths([
        &amp;#39;/path/to/sugar/custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php&amp;#39;,
        &amp;#39;/path/to/sugar/custom/clients/&amp;#39;,
        &amp;#39;/path/to/sugar/custom/modules/ActivityStream/&amp;#39;,
        &amp;#39;/path/to/sugar/custom/modules/Forecasts/Ext/clients/&amp;#39;,
        &amp;#39;/path/to/sugar/custom/modules/pmse_Project/&amp;#39;,
    ]);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&amp;nbsp;Rector will generate a sample code with &lt;code&gt;__DIR__,&lt;/code&gt;&amp;nbsp;that, however, translates to &amp;quot;current folder&amp;quot; which can be different than your Sugar install. As a rule of thumb, always use fully qualified names &lt;em&gt;&lt;strong&gt;without&lt;/strong&gt;&lt;/em&gt; the &lt;code&gt;__DIR__&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Modify the &lt;code&gt;rector.php&lt;/code&gt; config file to upgrade the code to the newest supported PHP version (in our case, 8.3):&lt;/p&gt;
&lt;p&gt;Remove/Comment:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;// register a single rule
$rectorConfig-&amp;gt;rule(InlineConstructorDefaultToPropertyRector::class);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Specify PHP 8.3 as your phpVersion:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$rectorConfig-&amp;gt;phpVersion(PhpVersion::PHP_83);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;#39;d like to have only the incompatible rules between PHP 7.4 to PHP 8.3, you can use the following:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;    $rectorConfig-&amp;gt;rules([
        AddParamBasedOnParentClassMethodRector::class,
        SetStateToStaticRector::class,
        ArrayKeyExistsOnPropertyRector::class,
        ExportToReflectionFunctionRector::class,
        FilterVarToAddSlashesRector::class,
        MbStrrposEncodingArgumentPositionRector::class,
        MoneyFormatToNumberFormatRector::class,
        RealToFloatTypeCastRector::class,
        RestoreDefaultNullToNullableTypePropertyRector::class,
        StringifyStrNeedlesRector::class,
        GetClassOnNullRector::class,
        ListEachRector::class,
        ReplaceEachAssignmentWithKeyCurrentRector::class,
        ParseStrWithResultArgumentRector::class,
        StringifyDefineRector::class,
        WhileEachToForeachRector::class,
        ConsistentImplodeRector::class,
        CurlyToSquareBracketArrayStringRector::class,
        StaticCallOnNonStaticToInstanceCallRector::class,
    ]);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;safeCount&lt;/code&gt; rule to your config if you are in a supported Sugar version&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;&amp;lt;?php
...
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
...
return static function (RectorConfig $rectorConfig): void {
...
    $rectorConfig-&amp;gt;ruleWithConfiguration(RenameFunctionRector::class, [
        &amp;#39;count&amp;#39; =&amp;gt; &amp;#39;safeCount&amp;#39;,
    ]);
...&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1guah5gi50"&gt;Full config file example (incompatibilities only)&lt;/h3&gt;
&lt;p&gt;By following those steps, you should have a rector.php similar to this to run&amp;nbsp;&lt;span&gt;incompatible rules between PHP 7.4 to PHP 8.3&lt;/span&gt;:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;&amp;lt;?php
declare(strict_types=1);

use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector;
use Rector\Php72\Rector\Assign\ListEachRector;
use Rector\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector;
use Rector\Php72\Rector\FuncCall\GetClassOnNullRector;
use Rector\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector;
use Rector\Php72\Rector\FuncCall\StringifyDefineRector;
use Rector\Php72\Rector\While_\WhileEachToForeachRector;
use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector;
use Rector\Php74\Rector\Double\RealToFloatTypeCastRector;
use Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector;
use Rector\Php74\Rector\FuncCall\FilterVarToAddSlashesRector;
use Rector\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector;
use Rector\Php74\Rector\FuncCall\MoneyFormatToNumberFormatRector;
use Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector;
use Rector\Php74\Rector\StaticCall\ExportToReflectionFunctionRector;
use Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector;
use Rector\Php80\Rector\ClassMethod\SetStateToStaticRector;
use Rector\Config\RectorConfig;
use Rector\ValueObject\PhpVersion;
use Rector\Php74\Rector\ArrayDimFetch\CurlyToSquareBracketArrayStringRector;
use Rector\Caching\ValueObject\Storage\MemoryCacheStorage;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
use Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector;

return static function (RectorConfig $rectorConfig): void {
    $rectorConfig-&amp;gt;cacheClass(MemoryCacheStorage::class);
    $rectorConfig-&amp;gt;disableParallel();
    $rectorConfig-&amp;gt;phpVersion(PhpVersion::PHP_83);

    $rectorConfig-&amp;gt;rules([
        AddParamBasedOnParentClassMethodRector::class,
        SetStateToStaticRector::class,
        ArrayKeyExistsOnPropertyRector::class,
        ExportToReflectionFunctionRector::class,
        FilterVarToAddSlashesRector::class,
        MbStrrposEncodingArgumentPositionRector::class,
        MoneyFormatToNumberFormatRector::class,
        RealToFloatTypeCastRector::class,
        RestoreDefaultNullToNullableTypePropertyRector::class,
        StringifyStrNeedlesRector::class,
        GetClassOnNullRector::class,
        ListEachRector::class,
        ReplaceEachAssignmentWithKeyCurrentRector::class,
        ParseStrWithResultArgumentRector::class,
        StringifyDefineRector::class,
        WhileEachToForeachRector::class,
        ConsistentImplodeRector::class,
        CurlyToSquareBracketArrayStringRector::class,
        StaticCallOnNonStaticToInstanceCallRector::class,
    ]);
    $rectorConfig-&amp;gt;ruleWithConfiguration(RenameFunctionRector::class, [
        &amp;#39;count&amp;#39; =&amp;gt; &amp;#39;safeCount&amp;#39;,
    ]);
};&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1gudgk28f0"&gt;You can move on to the&amp;nbsp;Execution&lt;span&gt;&amp;nbsp;&lt;/span&gt;Phase:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/836/executing-rector"&gt;Execute Rector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Prepare Rector for MLP/Addon</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/834/prepare-rector-for-mlp-addon/revision/20</link><pubDate>Wed, 10 Sep 2025 18:16:46 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:6ad24d35-d4d0-410f-8bd4-16ba03823af4</guid><dc:creator>Rafael Fernandes</dc:creator><description>Revision 20 posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 6:16:46 PM&lt;br /&gt;
&lt;p&gt;If you are looking to make your Module Loadable Package (MLP), a.k.a Addon, compatible with different versions of PHP, this guide is for you.&lt;/p&gt;
&lt;p&gt;You will&amp;nbsp;install&amp;nbsp;an MLP zip file into Sugar&amp;#39;s instance where Rector is and git has been initialized, by doing that, Sugar will add the MLP in the filesystem and git will let us know what has changed.&lt;/p&gt;
&lt;h3 id="mcetoc_1guaf95jk0"&gt;Prerequisites:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/828/rector-basic-setup"&gt;Basic Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A module loadable package (zip file)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mcetoc_1guafg2ff1"&gt;Upload and Install your MLP&lt;/h3&gt;
&lt;p&gt;Upload and install your MLP as you normally would through Sugar&amp;#39;s UI or API. Sugar will copy your files per manifest definition and perform the database operations as well (which are irrelevant for Rector).&lt;/p&gt;
&lt;p&gt;After it has been installed, execute &lt;code&gt;git status&lt;/code&gt; commands to see which files were updated/added by Sugar:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;sh-3.2$ git status
On branch main
Changes not staged for commit:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
        modified:   config_override.php
        modified:   custom/application/Ext/Include/modules.ext.php

Untracked files:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to include in what will be committed)
        custom/Extension/application/Ext/Include/xxxx.php
        custom/Extension/application/Ext/Include/orderMapping.php
        custom/modules/ActivityStream/
        custom/modules/Forecasts/Ext/clients/
        custom/modules/pmse_Project/

no changes added to commit (use &amp;quot;git add&amp;quot; and/or &amp;quot;git commit -a&amp;quot;)&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1guah5gi50"&gt;Prepare your Rector config&lt;/h3&gt;
&lt;p&gt;Copy the fully qualified paths, and paste them into the rector.php config file that was generated in the previous step, replacing the default paths in &lt;code class="code css-z5oxh7"&gt;$rectorConfig-&amp;gt;paths([&lt;/code&gt; array like this (you can ignore some of the files, like &lt;code class="code css-z5oxh7"&gt;*ext.php&lt;/code&gt; and &lt;code class="code css-z5oxh7"&gt;*orderMapping.php&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;    $rectorConfig-&amp;gt;paths([
        &amp;#39;/path/to/sugar/custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php&amp;#39;,
        &amp;#39;/path/to/sugar/custom/clients/&amp;#39;,
        &amp;#39;/path/to/sugar/custom/modules/ActivityStream/&amp;#39;,
        &amp;#39;/path/to/sugar/custom/modules/Forecasts/Ext/clients/&amp;#39;,
        &amp;#39;/path/to/sugar/custom/modules/pmse_Project/&amp;#39;,
    ]);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&amp;nbsp;Rector will generate a sample code with &lt;code&gt;__DIR__,&lt;/code&gt;&amp;nbsp;that, however, translates to &amp;quot;current folder&amp;quot; which can be different than your Sugar install. As a rule of thumb, always use fully qualified names &lt;em&gt;&lt;strong&gt;without&lt;/strong&gt;&lt;/em&gt; the &lt;code&gt;__DIR__&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Modify the &lt;code&gt;rector.php&lt;/code&gt; config file to upgrade the code to the newest supported PHP version (in our case, 8.3):&lt;/p&gt;
&lt;p&gt;Remove/Comment:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;// register a single rule
$rectorConfig-&amp;gt;rule(InlineConstructorDefaultToPropertyRector::class);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Specify PHP 8.3 as your phpVersion:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$rectorConfig-&amp;gt;phpVersion(PhpVersion::PHP_83);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;#39;d like to have only the incompatible rules between PHP 7.4 to PHP 8.3, you can use the following:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;    $rectorConfig-&amp;gt;rules([
        AddParamBasedOnParentClassMethodRector::class,
        SetStateToStaticRector::class,
        ArrayKeyExistsOnPropertyRector::class,
        ExportToReflectionFunctionRector::class,
        FilterVarToAddSlashesRector::class,
        MbStrrposEncodingArgumentPositionRector::class,
        MoneyFormatToNumberFormatRector::class,
        RealToFloatTypeCastRector::class,
        RestoreDefaultNullToNullableTypePropertyRector::class,
        StringifyStrNeedlesRector::class,
        GetClassOnNullRector::class,
        ListEachRector::class,
        ReplaceEachAssignmentWithKeyCurrentRector::class,
        ParseStrWithResultArgumentRector::class,
        StringifyDefineRector::class,
        WhileEachToForeachRector::class,
        ConsistentImplodeRector::class,
        CurlyToSquareBracketArrayStringRector::class,
        StaticCallOnNonStaticToInstanceCallRector::class,
    ]);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;safeCount&lt;/code&gt; rule to your config if you are in a supported Sugar version&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;&amp;lt;?php
...
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
...
return static function (RectorConfig $rectorConfig): void {
...
    $rectorConfig-&amp;gt;ruleWithConfiguration(RenameFunctionRector::class, [
        &amp;#39;count&amp;#39; =&amp;gt; &amp;#39;safeCount&amp;#39;,
    ]);
...&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1guah5gi50"&gt;Full config file example (incompatibilities only)&lt;/h3&gt;
&lt;p&gt;By following those steps, you should have a rector.php similar to this to run&amp;nbsp;&lt;span&gt;incompatible rules between PHP 7.4 to PHP 8.3&lt;/span&gt;:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;&amp;lt;?php
declare(strict_types=1);

use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector;
use Rector\Php72\Rector\Assign\ListEachRector;
use Rector\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector;
use Rector\Php72\Rector\FuncCall\GetClassOnNullRector;
use Rector\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector;
use Rector\Php72\Rector\FuncCall\StringifyDefineRector;
use Rector\Php72\Rector\While_\WhileEachToForeachRector;
use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector;
use Rector\Php74\Rector\Double\RealToFloatTypeCastRector;
use Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector;
use Rector\Php74\Rector\FuncCall\FilterVarToAddSlashesRector;
use Rector\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector;
use Rector\Php74\Rector\FuncCall\MoneyFormatToNumberFormatRector;
use Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector;
use Rector\Php74\Rector\StaticCall\ExportToReflectionFunctionRector;
use Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector;
use Rector\Php80\Rector\ClassMethod\SetStateToStaticRector;
use Rector\Config\RectorConfig;
use Rector\ValueObject\PhpVersion;
use Rector\Php74\Rector\ArrayDimFetch\CurlyToSquareBracketArrayStringRector;
use Rector\Caching\ValueObject\Storage\MemoryCacheStorage;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
use Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector;

return static function (RectorConfig $rectorConfig): void {
    $rectorConfig-&amp;gt;cacheClass(MemoryCacheStorage::class);
    $rectorConfig-&amp;gt;disableParallel();
    $rectorConfig-&amp;gt;phpVersion(PhpVersion::PHP_83);

    $rectorConfig-&amp;gt;rules([
        AddParamBasedOnParentClassMethodRector::class,
        SetStateToStaticRector::class,
        ArrayKeyExistsOnPropertyRector::class,
        ExportToReflectionFunctionRector::class,
        FilterVarToAddSlashesRector::class,
        MbStrrposEncodingArgumentPositionRector::class,
        MoneyFormatToNumberFormatRector::class,
        RealToFloatTypeCastRector::class,
        RestoreDefaultNullToNullableTypePropertyRector::class,
        StringifyStrNeedlesRector::class,
        GetClassOnNullRector::class,
        ListEachRector::class,
        ReplaceEachAssignmentWithKeyCurrentRector::class,
        ParseStrWithResultArgumentRector::class,
        StringifyDefineRector::class,
        WhileEachToForeachRector::class,
        ConsistentImplodeRector::class,
        CurlyToSquareBracketArrayStringRector::class,
        StaticCallOnNonStaticToInstanceCallRector::class,
    ]);
    $rectorConfig-&amp;gt;ruleWithConfiguration(RenameFunctionRector::class, [
        &amp;#39;count&amp;#39; =&amp;gt; &amp;#39;safeCount&amp;#39;,
    ]);
};&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1gudgk28f0"&gt;You can move on to the&amp;nbsp;Execution&lt;span&gt;&amp;nbsp;&lt;/span&gt;Phase:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/836/executing-rector"&gt;Execute Rector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Rector Basic Setup</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/828/rector-basic-setup</link><pubDate>Wed, 10 Sep 2025 18:08:36 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:050075a8-3243-4b82-89be-e8ba4c5bdd17</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 6:08:36 PM&lt;br /&gt;
&lt;p&gt;Refactoring your code required a very basic setup to make your life easier to track what Rector does to your code (if you use its auto-fix mode).&lt;/p&gt;
&lt;p&gt;Basic setup means installing software you will need such as Rector and Git. Git will help us to keep track of changes in your Sugar instance folder after we initialize it.&lt;/p&gt;
&lt;p&gt;Keep that info in mind if you are planing to use a pre-existing instance for this work, it will add a lot of extra files to it.&lt;/p&gt;
&lt;h2 id="mcetoc_1gtthusvo0"&gt;Step by Step&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Download a build of SugarCRM. Please use the one that is compatible with your MPL.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Install the build locally or use your own Sugar instance if you have one local&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;If you are using&amp;nbsp;Sugar &amp;gt;= 25.1&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;You must use Rector that is shipped with Sugar and located at vendor lib in your Sugar Instance folder. ex:&amp;nbsp;&lt;code&gt;&lt;span&gt;/path/to/sugar/&lt;/span&gt;&lt;span&gt;vendor/rector/rector/bin/rector&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;You might need to give +x permissions to execute rector.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chmod +x&amp;nbsp;&lt;span&gt;/path/to/sugar&lt;/span&gt;/vendor/rector/rector/bin/rector&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You may still&amp;nbsp;use newer Rector version for your convenience, just make sure you use composer in a different directory (see below) to avoid incompatibilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;If you are using Sugar &amp;lt; 25&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;You should leverage composer to install rector and its dependencies.&lt;/li&gt;
&lt;li&gt;It is important that you&amp;nbsp;&lt;strong&gt;DO NOT&lt;/strong&gt; install it in the Sugar&amp;#39;s directory, if you do you will get errors due to libraries incompatibilities, see &lt;a href="/dev-club/w/dev-tutorials/835/troubleshooting-rector"&gt;Troubleshooting Rector&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You can use the following commands to prepare rector to be used.&lt;/li&gt;
&lt;li&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;# install composer if you dont have it
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

# create a temp rector folder
mkdir /tmp/rector
cd /tmp/rector

# let composer decide which version works on your system
composer require rector/rector --dev

# or specify a specific version (if you need to)
composer require rector/rector:^1.2.3 --dev
&lt;/pre&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Initialize a git repository inside the instance folder: cd /path/to/sugar &amp;amp;&amp;amp; git init. It&amp;rsquo;s just convenient to use git to track the list of files and some other things.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Create /path/to/sugar/.gitignore file to skip the unneeded changes with the following contents:&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;&lt;pre class="ui-code" data-mode="text"&gt;cache
vendor
portal2
upload
upgrades
*.log
rector.php
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Commit the initial repository state&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;&lt;pre class="ui-code" data-mode="text"&gt;git add . &amp;amp;&amp;amp; git commit -am &amp;#39;init&amp;#39;
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;You might wanna keep your initial commit hash in case you need to reset&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;&lt;pre class="ui-code" data-mode="text"&gt;sh-3.2$ git log
commit 0ac2bd9c175f7bff9842dcd8179fc9ec67b7002f (HEAD -&amp;gt; main)
Author: Rafael Fernandes &amp;lt;rafael.fernandes@sugarcrm.com&amp;gt;
Date:   Mon Apr 17 10:40:56 2023 -0400
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1h96266pd0"&gt;&lt;span style="font-size:inherit;"&gt;Initialize Rector config (rector.php)&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;If you are using rector 0.15.x or less, you can use the following&lt;/span&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;rector-0.15.20/bin/rector init&lt;/pre&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;If you are using newer versions of Rector, you can use&lt;/span&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;/path/to/rector/bin/rector

 No &amp;quot;rector.php&amp;quot; config found. Should we generate it for you? [yes]:
 &amp;gt; yes

 [OK] The config is added now. Re-run command to make Rector do the work!&lt;/pre&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li class="code-block  css-1si5tit"&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;Agree to create a &lt;code&gt;rector.php&lt;/code&gt; file with the dummy config. It should generate a template file similar to this:&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;&lt;pre class="ui-code" data-mode="php"&gt;&amp;lt;?php

declare(strict_types=1);

use Rector\Config\RectorConfig;

return RectorConfig::configure()
    -&amp;gt;withPaths([
        __DIR__ . &amp;#39;/src&amp;#39;,
    ])
    // uncomment to reach your current PHP version
    // -&amp;gt;withPhpSets()
    -&amp;gt;withTypeCoverageLevel(0)
    -&amp;gt;withDeadCodeLevel(0)
    -&amp;gt;withCodeQualityLevel(0);&lt;/pre&gt;&lt;/span&gt;&lt;span style="font-size:inherit;"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1gtthusvo0"&gt;Reset your Repo to start over&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;You can always start over at your initial Git commit and undo all changes by Rector or yourself after that point.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Take the initial commit hash from git log and reset as follows:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;sh-3.2$ git log
commit 0ac2bd9c175f7bff9842dcd8179fc9ec67b7002f (HEAD -&amp;gt; main)
Author: Rafael Fernandes &amp;lt;rafael.fernandes@sugarcrm.com&amp;gt;
Date:   Mon Apr 17 10:40:56 2023 -0400

sh-3.2$ git status
On branch main
Changes not staged for commit:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
        modified:   vCard.php

no changes added to commit (use &amp;quot;git add&amp;quot; and/or &amp;quot;git commit -a&amp;quot;)

sh-3.2$ git reset --hard 0ac2bd9c175f7bff9842dcd8179fc9ec67b7002f
HEAD is now at 0ac2bd9c init

sh-3.2$ git clean -fd
Removing custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php
Removing custom/Extension/application/Ext/Include/orderMapping.php
Removing custom/modules/ActivityStream/
Removing custom/modules/Forecasts/Ext/clients/
Removing custom/modules/pmse_Project/

sh-3.2$ git status
On branch main
nothing to commit, working tree clean&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1gudgk28f0"&gt;You can move on to the Prepare Phase:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/834/prepare-rector-for-mlp-addon"&gt;Prepare Rector for MLP/Addon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/837/prepare-rector-for-customizations"&gt;Prepare Rector for Customizations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Rector Basic Setup</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/828/rector-basic-setup/revision/21</link><pubDate>Wed, 10 Sep 2025 18:06:28 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:050075a8-3243-4b82-89be-e8ba4c5bdd17</guid><dc:creator>Rafael Fernandes</dc:creator><description>Revision 21 posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 6:06:28 PM&lt;br /&gt;
&lt;p&gt;Refactoring your code required a very basic setup to make your life easier to track what Rector does to your code (if you use its auto-fix mode).&lt;/p&gt;
&lt;p&gt;Basic setup means installing software you will need such as Rector and Git. Git will help us to keep track of changes in your Sugar instance folder after we initialize it.&lt;/p&gt;
&lt;p&gt;Keep that info in mind if you are planing to use a pre-existing instance for this work, it will add a lot of extra files to it.&lt;/p&gt;
&lt;h2 id="mcetoc_1gtthusvo0"&gt;Step by Step&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Download a build of SugarCRM. Please use the one that is compatible with your MPL.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Install the build locally or use your own Sugar instance if you have one local&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;If you are using&amp;nbsp;Sugar &amp;gt;= 25.1&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;You must use Rector that is shipped with Sugar and located at vendor lib in your Sugar Instance folder. ex:&amp;nbsp;&lt;code&gt;&lt;span&gt;/path/to/sugar/&lt;/span&gt;&lt;span&gt;vendor/rector/rector/bin/rector&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;You might need to give +x permissions to execute rector.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chmod +x&amp;nbsp;&lt;span&gt;/path/to/sugar&lt;/span&gt;/vendor/rector/rector/bin/rector&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You may still download newer versions of Rector from its &lt;a href="https://github.com/rectorphp/rector/releases"&gt;release repository&lt;/a&gt;, however, you&amp;#39;d need to follow their &lt;a href="https://github.com/rectorphp/rector?tab=readme-ov-file#install"&gt;installation instructions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;If you are using Sugar &amp;lt; 25&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;You should leverage composer to install rector and its dependencies.&lt;/li&gt;
&lt;li&gt;It is important that you&amp;nbsp;&lt;strong&gt;DO NOT&lt;/strong&gt; install it in the Sugar&amp;#39;s directory, if you do you will get errors due to libraries incompatibilities, see &lt;a href="/dev-club/w/dev-tutorials/835/troubleshooting-rector"&gt;Troubleshooting Rector&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You can use the following commands to prepare rector to be used.&lt;/li&gt;
&lt;li&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;# install composer if you dont have it
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

# create a temp rector folder
mkdir /tmp/rector
cd /tmp/rector

# let composer decide which version works on your system
composer require rector/rector --dev

# or specify a specific version (if you need to)
composer require rector/rector:^1.2.3 --dev
&lt;/pre&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Initialize a git repository inside the instance folder: cd /path/to/sugar &amp;amp;&amp;amp; git init. It&amp;rsquo;s just convenient to use git to track the list of files and some other things.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Create /path/to/sugar/.gitignore file to skip the unneeded changes with the following contents:&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;&lt;pre class="ui-code" data-mode="text"&gt;cache
vendor
portal2
upload
upgrades
*.log
rector.php
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;Commit the initial repository state&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;&lt;pre class="ui-code" data-mode="text"&gt;git add . &amp;amp;&amp;amp; git commit -am &amp;#39;init&amp;#39;
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;You might wanna keep your initial commit hash in case you need to reset&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;&lt;pre class="ui-code" data-mode="text"&gt;sh-3.2$ git log
commit 0ac2bd9c175f7bff9842dcd8179fc9ec67b7002f (HEAD -&amp;gt; main)
Author: Rafael Fernandes &amp;lt;rafael.fernandes@sugarcrm.com&amp;gt;
Date:   Mon Apr 17 10:40:56 2023 -0400
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1h96266pd0"&gt;&lt;span style="font-size:inherit;"&gt;Initialize Rector config (rector.php)&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;If you are using rector 0.15.x or less, you can use the following&lt;/span&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;rector-0.15.20/bin/rector init&lt;/pre&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;If you are using newer versions of Rector, you can use&lt;/span&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;/path/to/rector/bin/rector

 No &amp;quot;rector.php&amp;quot; config found. Should we generate it for you? [yes]:
 &amp;gt; yes


 [OK] The config is added now. Re-run command to make Rector do the work!&lt;/pre&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li class="code-block  css-1si5tit"&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;Agree to create a &lt;code&gt;rector.php&lt;/code&gt; file with the dummy config. It should generate a template file similar to this:&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style="font-size:inherit;"&gt;&lt;pre class="ui-code" data-mode="php"&gt;&amp;lt;?php

declare(strict_types=1);

use Rector\Config\RectorConfig;

return RectorConfig::configure()
    -&amp;gt;withPaths([
        __DIR__ . &amp;#39;/src&amp;#39;,
    ])
    // uncomment to reach your current PHP version
    // -&amp;gt;withPhpSets()
    -&amp;gt;withTypeCoverageLevel(0)
    -&amp;gt;withDeadCodeLevel(0)
    -&amp;gt;withCodeQualityLevel(0);&lt;/pre&gt;&lt;/span&gt;&lt;span style="font-size:inherit;"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1gtthusvo0"&gt;Reset your Repo to start over&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-size:inherit;"&gt;You can always start over at your initial Git commit and undo all changes by Rector or yourself after that point.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Take the initial commit hash from git log and reset as follows:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;sh-3.2$ git log
commit 0ac2bd9c175f7bff9842dcd8179fc9ec67b7002f (HEAD -&amp;gt; main)
Author: Rafael Fernandes &amp;lt;rafael.fernandes@sugarcrm.com&amp;gt;
Date:   Mon Apr 17 10:40:56 2023 -0400

sh-3.2$ git status
On branch main
Changes not staged for commit:
  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
        modified:   vCard.php

no changes added to commit (use &amp;quot;git add&amp;quot; and/or &amp;quot;git commit -a&amp;quot;)

sh-3.2$ git reset --hard 0ac2bd9c175f7bff9842dcd8179fc9ec67b7002f
HEAD is now at 0ac2bd9c init

sh-3.2$ git clean -fd
Removing custom/Extension/application/Ext/Include/BuildingBlock_HelloWorldDashlet.php
Removing custom/Extension/application/Ext/Include/orderMapping.php
Removing custom/modules/ActivityStream/
Removing custom/modules/Forecasts/Ext/clients/
Removing custom/modules/pmse_Project/

sh-3.2$ git status
On branch main
nothing to commit, working tree clean&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1gudgk28f0"&gt;You can move on to the Prepare Phase:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/834/prepare-rector-for-mlp-addon"&gt;Prepare Rector for MLP/Addon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/dev-club/w/dev-tutorials/837/prepare-rector-for-customizations"&gt;Prepare Rector for Customizations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Troubleshooting Rector</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/835/troubleshooting-rector</link><pubDate>Wed, 10 Sep 2025 17:52:31 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:1a4c0389-d769-4db8-b43c-4cb703e645ab</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 5:52:31 PM&lt;br /&gt;
&lt;h2 id="mcetoc_1guavb70f0"&gt;Troubleshooting&lt;/h2&gt;
&lt;p&gt;Depending on how large your files are or your local environment has in terms of resources, Rector can fail.&lt;/p&gt;
&lt;p&gt;Here are few workarounds you can use to troubleshoot and ensure it runs.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1guavb70f1"&gt;Uncaught Error: Class &amp;quot;PHPStan\DependencyInjection\ContainerFactory&amp;quot; not found&lt;/h3&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;[09-Sep-2025 20:09:26 GMT] PHP Fatal error:  Uncaught Error: Class &amp;quot;PHPStan\DependencyInjection\ContainerFactory&amp;quot; not found in /var/www/html/sugar/vendor/rector/rector/src/NodeTypeResolver/DependencyInjection/PHPStanServicesFactory.php:49
Stack trace:
#0 [internal function]: Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory-&amp;gt;__construct()
#1 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(843): ReflectionClass-&amp;gt;newInstanceArgs(Array)
#2 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(706): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;build(&amp;#39;Rector\\NodeType...&amp;#39;)
#3 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(653): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolve(&amp;#39;Rector\\NodeType...&amp;#39;, Array)
#4 /var/www/html/sugar/vendor/rector/rector/src/DependencyInjection/LazyContainerFactory.php(273): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;make(&amp;#39;Rector\\NodeType...&amp;#39;)
#5 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(806): Rector\DependencyInjection\LazyContainerFactory::Rector\DependencyInjection\{closure}(Object(Rector\Config\RectorConfig), Array)
#6 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(706): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;build(Object(Closure))
#7 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(653): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolve(&amp;#39;Rector\\NodeType...&amp;#39;, Array)
#8 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(946): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;make(&amp;#39;Rector\\NodeType...&amp;#39;)
#9 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(872): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolveClass(Object(ReflectionParameter))
#10 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(837): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolveDependencies(Array)
#11 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(706): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;build(&amp;#39;Rector\\StaticRe...&amp;#39;)
#12 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(653): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolve(&amp;#39;Rector\\StaticRe...&amp;#39;, Array)
#13 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(946): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;make(&amp;#39;Rector\\StaticRe...&amp;#39;)
#14 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(872): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolveClass(Object(ReflectionParameter))
#15 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(837): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolveDependencies(Array)
#16 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(706): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;build(&amp;#39;Rector\\Autoload...&amp;#39;)
#17 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(653): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolve(&amp;#39;Rector\\Autoload...&amp;#39;, Array)
#18 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(946): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;make(&amp;#39;Rector\\Autoload...&amp;#39;)
#19 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(872): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolveClass(Object(ReflectionParameter))
#20 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(837): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolveDependencies(Array)
#21 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(277): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;build(&amp;#39;Rector\\Console\\...&amp;#39;)
#22 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(806): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;RectorPrefix202411\Illuminate\Container\{closure}(Object(Rector\Config\RectorConfig), Array)
#23 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(706): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;build(Object(Closure))
#24 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(653): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolve(&amp;#39;Rector\\Console\\...&amp;#39;, Array)
#25 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(496): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;make(&amp;#39;Rector\\Console\\...&amp;#39;)
#26 [internal function]: RectorPrefix202411\Illuminate\Container\Container-&amp;gt;RectorPrefix202411\Illuminate\Container\{closure}()
#27 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/ContextualBindingBuilder.php(72): iterator_to_array(Object(RectorPrefix202411\Illuminate\Container\RewindableGenerator))
#28 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Util.php(40): RectorPrefix202411\Illuminate\Container\ContextualBindingBuilder-&amp;gt;RectorPrefix202411\Illuminate\Container\{closure}(Object(Rector\Config\RectorConfig))
#29 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(922): RectorPrefix202411\Illuminate\Container\Util::unwrapIfClosure(Object(Closure), Object(Rector\Config\RectorConfig))
#30 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(872): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolvePrimitive(Object(ReflectionParameter))
#31 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(837): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolveDependencies(Array)
#32 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(706): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;build(&amp;#39;Rector\\Console\\...&amp;#39;)
#33 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(653): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolve(&amp;#39;Rector\\Console\\...&amp;#39;, Array)
#34 /var/www/html/sugar/vendor/rector/rector/src/DependencyInjection/LazyContainerFactory.php(244): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;make(&amp;#39;Rector\\Console\\...&amp;#39;)
#35 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(806): Rector\DependencyInjection\LazyContainerFactory::Rector\DependencyInjection\{closure}(Object(Rector\Config\RectorConfig), Array)
#36 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(706): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;build(Object(Closure))
#37 /var/www/html/sugar/vendor/rector/rector/vendor/illuminate/container/Container.php(663): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;resolve(&amp;#39;RectorPrefix202...&amp;#39;)
#38 /var/www/html/sugar/vendor/rector/rector/bin/rector.php(129): RectorPrefix202411\Illuminate\Container\Container-&amp;gt;get(&amp;#39;RectorPrefix202...&amp;#39;)
#39 /var/www/html/sugar/vendor/rector/rector/bin/rector(5): require_once(&amp;#39;/var/www/html/s...&amp;#39;)
#40 {main}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re running Sugar &amp;lt; 25, you likely installed rector in the sugar&amp;#39;s install directory (where ./vendor is) causing incompatibilities.&lt;/p&gt;
&lt;p&gt;just uninstall rector and install it anywhere else.&lt;/p&gt;
&lt;h3 id="mcetoc_1guavb70f1"&gt;&lt;span&gt;Child process timed out after 120 seconds&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;&lt;pre class="ui-code" data-mode="text"&gt; [ERROR] Could not process                                                                                              
         &amp;quot;/path/to/sugar/rector-0.15.20/vendor/symplify/easy-parallel/src/ValueObject/ParallelProcess.php&amp;quot;  
         file, due to:                                                                                                  
         &amp;quot;Child process timed out after 120 seconds&amp;quot;. On line: 108                                                      
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;try to&amp;nbsp;add the following to the end of&amp;nbsp;rector.php:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$rectorConfig-&amp;gt;parallel(900);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1guavb70f1"&gt;Reached system errors count limit of 50, exiting...&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;&lt;pre class="ui-code" data-mode="text"&gt; [ERROR] Could not process some files, due to:                                                                          
         &amp;quot;Reached system errors count limit of 50, exiting...&amp;quot;.                                                         
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;try to&amp;nbsp;add the following to the end of&amp;nbsp;rector.php:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="php"&gt;$rectorConfig-&amp;gt;disableParallel();
&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Integrations for Product Devs</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/708/integrations-for-product-devs</link><pubDate>Wed, 10 Sep 2025 15:09:30 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:e64c91c4-03e5-454f-bbd7-3beb25519ac3</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 3:09:30 PM&lt;br /&gt;
&lt;table style="margin-left:auto;margin-right:auto;width:100%;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#approach"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/1.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#rest"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/7612.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#common"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/3.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#performance"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/4.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="approach"&gt;&lt;/a&gt;Integration Approaches&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/1.png" /&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;The Sugar platform is extremely powerful, flexible, and allows for easy integration. This flexibility means that determining the&amp;nbsp;best approach for your use case scenario is the key to success.&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Push/Pull from Sugar via REST API&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline;"&gt;Recommended&lt;/span&gt; approach&lt;/li&gt;
&lt;li&gt;Developers have full control over integration&lt;/li&gt;
&lt;li&gt;Integration can be stopped and systems will continue to work&lt;/li&gt;
&lt;li&gt;Retry mechanism: Resume integrations at any time (identify deltas through &amp;quot;last_update_time&amp;quot; flags)&lt;/li&gt;
&lt;li&gt;The time delay from/to Sugar is minimal&lt;/li&gt;
&lt;li&gt;Sugar REST API offers almost everything you need,&amp;nbsp;and can be further extended via customization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Push to Sugar, Sugar pushes to an external queue in realtime&lt;span&gt;&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;Use Sugar Events to trigger through Logic Hooks&lt;/li&gt;
&lt;li&gt;Requires&amp;nbsp;Logic Hooks (code customization) to be created and deployed&lt;/li&gt;
&lt;li&gt;Does not offer a &amp;quot;retry mechanism&amp;quot;, if a logic hook fails to deliver to a queue or contact an external system&lt;/li&gt;
&lt;li&gt;Synchronous in nature, requires a response from an external system&lt;/li&gt;
&lt;li&gt;Needs to handle dependencies manually (contact can only be created after account)&lt;/li&gt;
&lt;li&gt;Good to have a middleware/queue in front of Sugar to receive those event triggers to&amp;nbsp;reliably&amp;nbsp;manage peak loads&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Realtime: Embed your app into Sugar&amp;nbsp;through &lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/User_Interface/Dashlets/"&gt;Dashlets&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Simplest approach&lt;/li&gt;
&lt;li&gt;Often requires web user-based authentication&lt;/li&gt;
&lt;li&gt;All load/traffic goes directly to your system,&amp;nbsp;which makes your system the performance bottleneck&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="rest"&gt;&lt;/a&gt;REST API Webservices&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/7612.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;Accessing your data when you want is good. Accessing your data however you want is better. &lt;/span&gt;&lt;span&gt;When you decide to build an integration with Sugar, you&amp;rsquo;ll need an easy way to access and interact with the data stored in Sugar. The REST (&lt;/span&gt;&lt;span&gt;representational state transfer)&lt;/span&gt;&lt;span&gt;&amp;nbsp;API (application programming interface) is perfect for this.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can also leverage our&amp;nbsp;live&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://rest.apidocs.sugarcrm.com/"&gt;Sugar RESTful API Collection&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sugar REST API Overview
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Fully RESTfull (GET, POST, PUT, DELETE)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;OAuth2 token based&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;All fields and modules are available through the API automatically&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Metadata API provides everything you need to know about your Sugar instance.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Topics
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/new-tutorial-intro-to-the-sugar-rest-api"&gt;Intro to the Sugar REST API&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/how-sugar-rest-api-versioning-works"&gt;How Sugar REST API versioning works&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/"&gt;RestAPI Developer Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/sugarcrm-cookbook---the-school-of-rest---part-1"&gt;SugarCRM Cookbook - The School of REST - Part 1&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/"&gt;REST APIs Endpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Extending_Endpoints/"&gt;How to add your own custom endpoints to the REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/API_Exceptions/"&gt;API Exceptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Best_Practices/"&gt;Best practices when integrating and migrating Sugar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="common"&gt;&lt;/a&gt;Common&amp;nbsp;APIs&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/3.png" /&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;It is important to understand our most commonly used APIs and their functions within Sugar. Your integration might need data within Sugar&amp;#39;s module structure that could potentially change over time, so you can use Metadata for it as well as CRUD onto Sugar.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Metadata
&lt;ul&gt;
&lt;li&gt;Endpoint with current system&amp;#39;s structure&lt;/li&gt;
&lt;li&gt;Modules, fields, relationships, layouts&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Use carefully, its payload can be MBs in size, cache as much as possible&lt;/li&gt;
&lt;li&gt;GET /metadata (use filters as much as possible)&lt;/li&gt;
&lt;li&gt;It does not retrieve labels for dropdowns, use language API to do so&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/3-tips-for-using-the-sugar-metadata-api"&gt;3 Tips for using the Sugar Metadata API&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Language
&lt;ul&gt;
&lt;li&gt;Endpoint to retrieve labels&lt;/li&gt;
&lt;li&gt;Dropdown list values&lt;/li&gt;
&lt;li&gt;Any language-specific content&lt;/li&gt;
&lt;li&gt;GET /lang/&amp;lt;language_code&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CRUD (Create, Read, Update, Delete) records&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_GET"&gt;Read&lt;/a&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;GET /&amp;lt;module&amp;gt;/:record&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/module_POST"&gt;Create&lt;/a&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;POST /&amp;lt;module&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_PUT"&gt;Update&lt;/a&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;PUT&amp;nbsp;&lt;span&gt;/&amp;lt;module&amp;gt;/:record&lt;/span&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_DELETE"&gt;Delete&lt;/a&gt;:&amp;nbsp;&lt;code&gt;DELETE&amp;nbsp;/&amp;lt;module&amp;gt;/:record&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulefilter_POST"&gt;Filter&lt;/a&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;POST /&amp;lt;module&amp;gt;/filter&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;You can use&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulefilter_GET"&gt;GET&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;as well&lt;/li&gt;
&lt;li&gt;POST solves the issue when a query is too long and hits HTTP limits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecordlinklink_name_GET"&gt;Retrieve records related to another module&lt;/a&gt;:&amp;nbsp;&lt;code&gt;GET /&amp;lt;module&amp;gt;/:record/link/:link_name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecordlinklink_nameremote_id_POST"&gt;Creates a relationship to a pre-existing record:&lt;/a&gt;&lt;code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;POST /&amp;lt;module&amp;gt;/:record/link/:link_name/:remote_id&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="performance"&gt;&lt;/a&gt;Performance and Best Practices&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/4.png" /&gt;&lt;/h1&gt;
&lt;div class="row"&gt;
&lt;div class="col-xs-12 col-md-9"&gt;
&lt;p&gt;As you deep dive into Sugar APIs,&amp;nbsp;you will see how easy it is to integrate to pull and push data from and to Sugar and your systems, however, you must consider performance so your integration doesn&amp;#39;t become a bottleneck between those systems.&lt;/p&gt;
&lt;p&gt;You should consider the following guidelines when implementing your integrations:&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Use Sugar&amp;#39;s sync_key to link to remote IDs
&lt;ul&gt;
&lt;li&gt;Sugar provides in every module a sync_key field&lt;/li&gt;
&lt;li&gt;sync_key is a unique identifier for that module that links back to your external system&amp;#39;s generated key&lt;/li&gt;
&lt;li&gt;For example, account ID from ERP is AC0001, set sync_key (pass as a parameter in the POST) and sugar will create a record in account with sync_key=AC0001&lt;/li&gt;
&lt;li&gt;Next patch/update, sugar will check &amp;quot;where sync_key=&amp;#39;AC0001&amp;#39;&amp;quot; and update it (it is indexed)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Sugar Integrate Upsert APIs
&lt;ul&gt;
&lt;li&gt;Sugar&amp;#39;s &amp;quot;update or insert&amp;quot; API based on sync_key&lt;/li&gt;
&lt;li&gt;Simplifies a common operation - if record exists, update it, otherwise create it (GET followed by a&amp;nbsp;PUT or POST)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_PATCH"&gt;Upsert by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_GET"&gt;Get by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_DELETE"&gt;Delete by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulerecord_idsync_key_field_namesync_key_field_value_PATCH"&gt;Set a sync on existing record (without sync_key)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulelhs_sync_key_field_namelhs_sync_key_field_valuelinklink_namerhs_sync_key_field_namerhs_sync_key_field_value_POST"&gt;Create relationship based on sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulelhs_sync_key_field_namelhs_sync_key_field_valuelinklink_namerhs_sync_key_field_namerhs_sync_key_field_value_DELETE"&gt;Delete relationship based on sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong data-start="1409" data-end="1418"&gt;Note:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This endpoint is engineered for maximum performance and omits several ACL and validation checks.&lt;/li&gt;
&lt;li&gt;&lt;strong data-start="1522" data-end="1536"&gt;Important:&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;For security reasons, the Upsert API is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong data-start="1577" data-end="1611"&gt;restricted to Admin users only&lt;/strong&gt;&amp;mdash;non-admin users will be denied access.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Sugar Bulk API
&lt;ul&gt;
&lt;li&gt;An endpoint that encapsulates multiple API calls into one single HTTP request&lt;/li&gt;
&lt;li&gt;Requests are executed synchronously (one after the other)&lt;/li&gt;
&lt;li&gt;Reducing network latency between calls&lt;/li&gt;
&lt;li&gt;Combined with Upsert provides the best performance&lt;/li&gt;
&lt;li&gt;Careful not to add too many requests to it&lt;/li&gt;
&lt;li&gt;Start small (5 to 10 requests) and increase until you reach its max capacity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Polling and filtering time-based with Filter API
&lt;ul&gt;
&lt;li&gt;Integration gateway (or your external system) asks Sugar every X minutes&lt;/li&gt;
&lt;li&gt;Request Filter API using &lt;code&gt;max_num&lt;/code&gt; (max number of records), offset and fields&lt;/li&gt;
&lt;li&gt;The response will provide the data as well as &lt;code&gt;next_offset&lt;/code&gt; (offset can be seen as pagination)&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;next_offset&lt;/code&gt; is -1, no more records to go through&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Initial Data Load into Sugar&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Use Bulk + Upsert to achieve data loads&lt;/li&gt;
&lt;li&gt;Millions&amp;nbsp;of records may take a little while to process, plan ahead (over the weekend)&lt;/li&gt;
&lt;li&gt;Use multi-thread processes to load (for example 20 threads sending 20 bulk upsert requests)&lt;/li&gt;
&lt;li&gt;Careful with&amp;nbsp;&lt;a href="https://support.sugarcrm.com/Resources/Sugar_Cloud_Policy_Guide/#Rate_Limits"&gt;Sugar Cloud rate limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;It&amp;#39;s always a good idea to let Sugar Support know you&amp;#39;re data loading, they will be considerate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Understanding PHP limitations&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;PHP is single-threaded with limited resources to process your request&lt;/li&gt;
&lt;li&gt;If you send too much data using bulk, you may run out of memory for that request&lt;/li&gt;
&lt;li&gt;Push to the limits but give some room for PHP to finish the request gracefully&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Pagination for millions of records by using OFFSET will degrade performance
&lt;ul&gt;
&lt;li&gt;We rely on DB to find, sort and limit records&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/swlh/sql-pagination-you-are-probably-doing-it-wrong-d0f2719cc166"&gt;Nice article &lt;/a&gt;explaining how this works in details&lt;/li&gt;
&lt;li&gt;Instead of offsets, store the last processed ID and use it in the WHERE clause and sort by ID&lt;/li&gt;
&lt;li&gt;Example
&lt;ul&gt;
&lt;li&gt;Instead of: (/v11_25/Leads?max_num=1000&amp;amp;&lt;strong&gt;offset=4619000&lt;/strong&gt;&amp;amp;order_by=id)&lt;/li&gt;
&lt;li&gt;Do this&amp;nbsp;(/v11_25/Leads?&lt;strong&gt;filter=[{"id": {"$gt": &amp;rdquo;&amp;lt;last_id&amp;gt;"}}]&lt;/strong&gt;&amp;amp;max_num=1000&amp;amp;order_by=id)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you need to add a date, make sure to add it as part of the filter and order&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p id="mcetoc_1f5uvkqns0"&gt;&lt;span style="font-size:150%;"&gt;You&amp;#39;re up and running, but don&amp;#39;t stop now - this is just the beginning of your journey. &lt;/span&gt;&lt;/p&gt;
&lt;p id="mcetoc_1g9jl704d0"&gt;&lt;span style="font-size:150%;"&gt;&lt;strong&gt;Now it&amp;#39;s time for&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/676/customization-for-product-devs"&gt;Customization for Product Devs&lt;/a&gt;.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Integrations for Devs</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/698/integrations-for-devs</link><pubDate>Wed, 10 Sep 2025 15:09:03 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:df5ca10a-d211-4e03-9873-9d48feb7b645</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/10/2025 3:09:03 PM&lt;br /&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="#approach"&gt;&lt;img alt=" " src="/resized-image/__size/269x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.42.25-AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="#rest"&gt;&lt;img alt=" " src="/resized-image/__size/270x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.43.33-AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="#common"&gt;&lt;img alt=" " src="/resized-image/__size/270x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.43.48-AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="#performance"&gt;&lt;img alt=" " src="/resized-image/__size/268x140/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.44.04-AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="approach"&gt;&lt;/a&gt;Integration Approaches&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;The Sugar platform is extremely powerful, flexible, and allows for easy integration. This flexibility means that determining the&amp;nbsp;best approach for your use case scenario is the key to success.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/269x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.42.25-AM.png" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Push/Pull from Sugar via REST API&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline;"&gt;Recommended&lt;/span&gt; approach&lt;/li&gt;
&lt;li&gt;Developers have full control over integration&lt;/li&gt;
&lt;li&gt;Integration can be stopped and systems will continue to work&lt;/li&gt;
&lt;li&gt;Retry mechanism: Resume integrations at any time (identify deltas through &amp;quot;last_update_time&amp;quot; flags)&lt;/li&gt;
&lt;li&gt;The time delay from/to Sugar is minimal&lt;/li&gt;
&lt;li&gt;Sugar REST API offers almost everything you need,&amp;nbsp;and can be further extended via customization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Push to Sugar, Sugar pushes to an external queue in realtime&lt;span&gt;&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;Use Sugar Events to trigger through Logic Hooks&lt;/li&gt;
&lt;li&gt;Requires&amp;nbsp;Logic Hooks (code customization) to be created and deployed&lt;/li&gt;
&lt;li&gt;Does not offer a &amp;quot;retry mechanism&amp;quot;, if a logic hook fails to deliver to a queue or contact an external system&lt;/li&gt;
&lt;li&gt;Synchronous in nature, requires a response from an external system&lt;/li&gt;
&lt;li&gt;Needs to handle dependencies manually (contact can only be created after account)&lt;/li&gt;
&lt;li&gt;Good to have a middleware/queue in front of Sugar to receive those event triggers to&amp;nbsp;reliably&amp;nbsp;manage peak loads&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Realtime: Embed your app into Sugar&amp;nbsp;through &lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/User_Interface/Dashlets/"&gt;Dashlets&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Simplest approach&lt;/li&gt;
&lt;li&gt;Often requires web user-based authentication&lt;/li&gt;
&lt;li&gt;All load/traffic goes directly to your system,&amp;nbsp;which makes your system the performance bottleneck&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="rest"&gt;&lt;/a&gt;REST API Webservices&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;Accessing your data when you want is good. Accessing your data however you want is better. &lt;/span&gt;&lt;span&gt;When you decide to build an integration with Sugar, you&amp;rsquo;ll need an easy way to access and interact with the data stored in Sugar. The REST (&lt;/span&gt;&lt;span&gt;representational state transfer)&lt;/span&gt;&lt;span&gt;&amp;nbsp;API (application programming interface) is perfect for this.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can also leverage our&amp;nbsp;live&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://rest.apidocs.sugarcrm.com/"&gt;Sugar RESTful API Collection&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sugar REST API Overview
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Fully RESTfull (GET, POST, PUT, DELETE)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;OAuth2 token based&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;All fields and modules are available through the API automatically&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Metadata API provides everything you need to know about your Sugar instance.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/270x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.43.33-AM.png" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Topics
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/b/dev-blog/posts/new-tutorial-intro-to-the-sugar-rest-api"&gt;Intro to the Sugar REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/dev-club/b/dev-blog/posts/how-sugar-rest-api-versioning-works"&gt;How Sugar REST API versioning works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/"&gt;RestAPI Developer Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/dev-club/b/dev-blog/posts/sugarcrm-cookbook---the-school-of-rest---part-1"&gt;School of REST blog series&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/"&gt;REST APIs Endpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Extending_Endpoints/"&gt;How to add your own custom endpoints to the REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/API_Exceptions/"&gt;API Exceptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Best_Practices/"&gt;Best practices when integrating and migrating Sugar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="common"&gt;&lt;/a&gt;Common&amp;nbsp;APIs&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;It is important to understand our most commonly used APIs and their functions within Sugar. Your integration might need data within Sugar&amp;#39;s module structure that could potentially change over time, so you can use Metadata for it as well as CRUD onto Sugar.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/270x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.43.48-AM.png" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Metadata
&lt;ul&gt;
&lt;li&gt;Endpoint with current system&amp;#39;s structure&lt;/li&gt;
&lt;li&gt;Modules, fields, relationships, layouts&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Use carefully, its payload can be MBs in size, cache as much as possible&lt;/li&gt;
&lt;li&gt;GET /metadata (use filters as much as possible)&lt;/li&gt;
&lt;li&gt;It does not retrieve labels for dropdowns, use language API to do so&lt;/li&gt;
&lt;li&gt;&lt;a href="/dev-club/b/dev-blog/posts/3-tips-for-using-the-sugar-metadata-api"&gt;3 Tips for using the Sugar Metadata API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Language
&lt;ul&gt;
&lt;li&gt;Endpoint to retrieve labels&lt;/li&gt;
&lt;li&gt;Dropdown list values&lt;/li&gt;
&lt;li&gt;Any language-specific content&lt;/li&gt;
&lt;li&gt;GET /lang/&amp;lt;language_code&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CRUD (Create, Read, Update, Delete) records
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_GET"&gt;Read&lt;/a&gt;: &lt;code&gt;GET /&amp;lt;module&amp;gt;/:record&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/module_POST"&gt;Create&lt;/a&gt;: &lt;code&gt;POST /&amp;lt;module&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_PUT"&gt;Update&lt;/a&gt;: &lt;code&gt;PUT&amp;nbsp;&lt;span&gt;/&amp;lt;module&amp;gt;/:record&lt;/span&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_DELETE"&gt;Delete&lt;/a&gt;: &lt;code&gt;DELETE&amp;nbsp;/&amp;lt;module&amp;gt;/:record&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulefilter_POST"&gt;Filter&lt;/a&gt;: &lt;code&gt;POST /&amp;lt;module&amp;gt;/filter&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;You can use &lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulefilter_GET"&gt;GET&lt;/a&gt; as well&lt;/li&gt;
&lt;li&gt;POST solves the issue when a query is too long and hits HTTP limits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecordlinklink_name_GET"&gt;Retrieve records related to another module&lt;/a&gt;:&amp;nbsp;&lt;code&gt;GET /&amp;lt;module&amp;gt;/:record/link/:link_name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecordlinklink_nameremote_id_POST"&gt;Creates a relationship to a pre-existing record:&lt;/a&gt;&lt;code&gt; POST /&amp;lt;module&amp;gt;/:record/link/:link_name/:remote_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="performance"&gt;&lt;/a&gt;Performance and Best Practices&lt;/h1&gt;
&lt;div class="row"&gt;
&lt;div class="col-xs-12 col-md-9"&gt;
&lt;p&gt;As you deep dive into Sugar APIs,&amp;nbsp;you will see how easy it is to integrate to pull and push data from and to Sugar and your systems, however, you must consider performance so your integration doesn&amp;#39;t become a bottleneck between those systems.&lt;/p&gt;
&lt;p&gt;You should consider the following guidelines when implementing your integrations:&lt;/p&gt;
&lt;p&gt;&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/268x140/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.44.04-AM.png" /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Use Sugar&amp;#39;s sync_key to link to remote IDs
&lt;ul&gt;
&lt;li&gt;Sugar provides in every module a sync_key field&lt;/li&gt;
&lt;li&gt;sync_key is a unique identifier for that module that links back to your external system&amp;#39;s generated key&lt;/li&gt;
&lt;li&gt;For example, account ID from ERP is AC0001, set sync_key (pass as a parameter in the POST) and sugar will create a record in account with sync_key=AC0001&lt;/li&gt;
&lt;li&gt;Next patch/update, sugar will check &amp;quot;where sync_key=&amp;#39;AC0001&amp;#39;&amp;quot; and update it (it is indexed)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Sugar Integrate Upsert APIs
&lt;ul&gt;
&lt;li&gt;Sugar&amp;#39;s &amp;quot;update or insert&amp;quot; API based on sync_key&lt;/li&gt;
&lt;li&gt;Simplifies a common operation - if record exists, update it, otherwise create it (GET followed by a&amp;nbsp;PUT or POST)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_PATCH"&gt;Upsert by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_GET"&gt;Get by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_DELETE"&gt;Delete by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulerecord_idsync_key_field_namesync_key_field_value_PATCH"&gt;Set a sync on existing record (without sync_key)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulelhs_sync_key_field_namelhs_sync_key_field_valuelinklink_namerhs_sync_key_field_namerhs_sync_key_field_value_POST"&gt;Create relationship based on sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulelhs_sync_key_field_namelhs_sync_key_field_valuelinklink_namerhs_sync_key_field_namerhs_sync_key_field_value_DELETE"&gt;Delete relationship based on sync_key&lt;/a&gt;&lt;br /&gt;
&lt;ul&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong data-start="1409" data-end="1418"&gt;Note:&lt;/strong&gt; This endpoint is engineered for maximum performance and omits several ACL and validation checks.&lt;/li&gt;
&lt;li&gt;&lt;strong data-start="1522" data-end="1536"&gt;Important:&lt;/strong&gt; For security reasons, the Upsert API is &lt;strong data-start="1577" data-end="1611"&gt;restricted to Admin users only&lt;/strong&gt;&amp;mdash;non-admin users will be denied access.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Sugar Bulk API
&lt;ul&gt;
&lt;li&gt;An endpoint that encapsulates multiple API calls into one single HTTP request&lt;/li&gt;
&lt;li&gt;Requests are executed synchronously (one after the other)&lt;/li&gt;
&lt;li&gt;Reducing network latency between calls&lt;/li&gt;
&lt;li&gt;Combined with Upsert provides the best performance&lt;/li&gt;
&lt;li&gt;Careful not to add too many requests to it&lt;/li&gt;
&lt;li&gt;Start small (5 to 10 requests) and increase until you reach its max capacity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Polling and filtering time-based with Filter API
&lt;ul&gt;
&lt;li&gt;Integration gateway (or your external system) asks Sugar every X minutes&lt;/li&gt;
&lt;li&gt;Request Filter API using &lt;code&gt;max_num&lt;/code&gt; (max number of records), offset and fields&lt;/li&gt;
&lt;li&gt;The response will provide the data as well as &lt;code&gt;next_offset&lt;/code&gt; (offset can be seen as pagination)&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;next_offset&lt;/code&gt; is -1, no more records to go through&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Initial Data Load into Sugar&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Use Bulk + Upsert to achieve data loads&lt;/li&gt;
&lt;li&gt;Millions&amp;nbsp;of records may take a little while to process, plan ahead (over the weekend)&lt;/li&gt;
&lt;li&gt;Use multi-thread processes to load (for example 20 threads sending 20 bulk upsert requests)&lt;/li&gt;
&lt;li&gt;Careful with&amp;nbsp;&lt;a href="https://support.sugarcrm.com/Resources/Sugar_Cloud_Policy_Guide/#Rate_Limits"&gt;Sugar Cloud rate limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;It&amp;#39;s always a good idea to let Sugar Support know you&amp;#39;re data loading, they will be considerate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Understanding PHP limitations&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;PHP is single-threaded with limited resources to process your request&lt;/li&gt;
&lt;li&gt;If you send too much data using bulk, you may run out of memory for that request&lt;/li&gt;
&lt;li&gt;Push to the limits but give some room for PHP to finish the request gracefully&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Pagination for millions of records by using OFFSET will degrade performance
&lt;ul&gt;
&lt;li&gt;We rely on DB to find, sort and limit records&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/swlh/sql-pagination-you-are-probably-doing-it-wrong-d0f2719cc166"&gt;Nice article&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/a&gt;explaining how this works in details&lt;/li&gt;
&lt;li&gt;Instead of offsets, store the last processed ID and use it in the WHERE clause and sort by ID&lt;/li&gt;
&lt;li&gt;Example
&lt;ul&gt;
&lt;li&gt;Instead of: (/v11_25/Leads?max_num=1000&amp;amp;&lt;strong&gt;offset=4619000&lt;/strong&gt;&amp;amp;order_by=id)&lt;/li&gt;
&lt;li&gt;Do this&amp;nbsp;(/v11_25/Leads?&lt;strong&gt;filter=[{"id": {"$gt": &amp;rdquo;&amp;lt;last_id&amp;gt;"}}]&lt;/strong&gt;&amp;amp;max_num=1000&amp;amp;order_by=id)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you need to add a date, make sure to add it as part of the filter and order&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3 id="mcetoc_1f5uvkqns0"&gt;&lt;span style="font-size:inherit;"&gt;You&amp;#39;re up and running, but don&amp;#39;t stop now - this is just the beginning of your journey. Now it&amp;#39;s time to &lt;a href="/dev-club/dev-onboarding/w/documentation/686/customization-for-devs"&gt;Customizations&lt;/a&gt;.&lt;/span&gt;&lt;/h3&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Sugar Developer Tools</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/199/sugar-developer-tools</link><pubDate>Tue, 02 Sep 2025 12:42:34 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:fbf7a97e-8cca-438d-9c57-dafa57c14fdf</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 9/2/2025 12:42:34 PM&lt;br /&gt;
&lt;h2 style="color:#444444;font-size:24px;margin-bottom:10px;"&gt;Sugar Test Tools&lt;/h2&gt;
&lt;p style="margin-bottom:20px;"&gt;Certain Sugar Test Tools are only made available to existing Sugar Customers and Sugar Partners and their use is subject to the included Test Tools License.&amp;nbsp; You are &lt;strong&gt;required&lt;/strong&gt; to have a &lt;strong&gt;Github account&lt;/strong&gt; and to be a &lt;strong&gt;current&lt;/strong&gt; &lt;strong&gt;Sugar Customer or SugarCRM Partner&lt;/strong&gt; in order to have your request for access approved.&amp;nbsp; These tools are stored in private Github repositories, this means you need to log into your Github account whenever you want to access them.&lt;/p&gt;
&lt;p style="margin-bottom:20px;"&gt;Requesting access is easy, all you need to do is send an e-mail to &lt;a href="mailto:developers@sugarcrm.com"&gt;developers@sugarcrm.com&lt;/a&gt;&amp;nbsp;with your request.&amp;nbsp; Please allow at least 2 business days for your request to be approved.&lt;/p&gt;
&lt;h4 style="color:#444444;margin-bottom:10px;"&gt;&lt;a style="color:#136dc7;" href="https://github.com/sugarcrm-developers/unit-tests" rel="nofollow"&gt;Sugar Unit Tests&lt;/a&gt;&lt;/h4&gt;
&lt;p style="margin-bottom:20px;"&gt;These unit tests contain both server side (PHP) and client side (JavaScript) test suites. PHP unit tests are implemented in &lt;a style="color:#136dc7;" href="https://phpunit.de/" rel="nofollow noopener noreferrer" target="_blank"&gt;PHPUnit&lt;/a&gt;. JavaScript tests are implemented using Jasmine.&lt;/p&gt;
&lt;h4 style="color:#444444;margin-bottom:10px;"&gt;&lt;a style="color:#136dc7;" href="https://github.com/sugarcrm-developers/performance" rel="nofollow"&gt;Sugar Performance Tests&lt;/a&gt;&lt;/h4&gt;
&lt;p style="margin-bottom:20px;"&gt;This package contains the JMeter based load testing tool for Sugar that is used to measure Sugar performance and validate deployment architecture.&lt;/p&gt;
&lt;h2 style="color:#444444;font-size:24px;margin-bottom:10px;"&gt;Open Source Tools&lt;/h2&gt;
&lt;p style="margin-bottom:20px;"&gt;SugarCRM is committed to an open, dynamic, and scalable Sugar Developer community.&amp;nbsp; For this reason, we have made a variety of tools available as open source software to our community.&amp;nbsp; We encourage contributions from outside SugarCRM because our goal is to foster a vibrant developer community that works to meets needs of all Sugar Developers.&lt;/p&gt;
&lt;h4 style="color:#444444;margin-bottom:10px;"&gt;&lt;a style="color:#136dc7;" href="https://github.com/sugarcrm/sidecardevtools" rel="nofollow noopener noreferrer" target="_blank"&gt;Sidecar Dev Tools&lt;/a&gt;&lt;/h4&gt;
&lt;p style="margin-bottom:20px;"&gt;Sidecar Dev Tools is a Google Chrome devtools extension that will help you to be more productive when developing Sugar front end code.&lt;/p&gt;
&lt;h4 style="color:#444444;margin-bottom:10px;"&gt;&lt;a style="color:#136dc7;" href="https://github.com/sugarcrm/tidbit" rel="nofollow noopener noreferrer" target="_blank"&gt;Tidbit&lt;/a&gt;&lt;/h4&gt;
&lt;p style="margin-bottom:20px;"&gt;Tidbit is a random record data generator for Sugar. By optimizing the communications with the database, large amounts of data can be inserted into the system automatically.&lt;/p&gt;
&lt;h4 style="color:#444444;margin-bottom:10px;"&gt;&lt;a style="color:#136dc7;" href="https://github.com/sugarcrm-developers/BuildingBlocks" rel="nofollow"&gt;Sugar Integration Building Blocks&lt;/a&gt;&lt;/h4&gt;
&lt;p style="margin-bottom:20px;"&gt;Common components to accelerate development of Sugar integrations. This project is focused on the needs of SugarCRM Technology partners that want to quickly build Sugar integrations that are portable, upgrade safe, and Sugar Cloud friendly.&lt;/p&gt;
&lt;h4 style="color:#444444;margin-bottom:10px;"&gt;&lt;a style="color:#136dc7;" href="https://github.com/sugarcrm/candybean" rel="nofollow noopener noreferrer" target="_blank"&gt;Candybean&lt;/a&gt;&lt;/h4&gt;
&lt;p style="margin-bottom:20px;"&gt;Candybean is a Java based framework for building GUI automation tests that Sugar QA leverages and maintains for building functional automation for Sugar.&amp;nbsp; It supports the Selenium WebDriver.&lt;/p&gt;
&lt;h4 style="color:#444444;margin-bottom:10px;"&gt;Sucrose&lt;/h4&gt;
&lt;p style="margin-bottom:20px;"&gt;Sugar&amp;rsquo;s Chart Library based on D3 and derived from NVD3.&amp;nbsp; Used to build charts for Business Applications.&lt;/p&gt;
&lt;h4 style="color:#444444;margin-bottom:10px;"&gt;&lt;a style="color:#136dc7;" href="https://github.com/sugarcrm/sugar-rest-harness" rel="nofollow noopener noreferrer" target="_blank"&gt;The Sugar REST Harness&lt;/a&gt;&lt;/h4&gt;
&lt;p style="margin-bottom:20px;"&gt;SugarCRM Professional Services has created a REST API test automation framework that they use to test their Enterprise CRM projects.&amp;nbsp; This is a great tool for Sugar Developers looking to build a better automated test practice.&amp;nbsp; It supports a variety of &lt;a style="color:#136dc7;" href="https://github.com/sugarcrm/sugar-rest-harness/tree/master/lib/Formatters" rel="nofollow noopener noreferrer" target="_blank"&gt;output formats&lt;/a&gt; which makes it an ideal Sugar REST API test tool to integrate with a DevOps/CI process.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: Development Best Practices&lt;/div&gt;
</description></item><item><title>Sugar 25.2 Customization Guide</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/1013/sugar-25-2-customization-guide/revision/1</link><pubDate>Fri, 15 Aug 2025 13:15:26 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:3307f36c-90a1-4f3c-b6a7-d6d6641bf5cb</guid><dc:creator>Rafael Fernandes</dc:creator><description>Revision 1 posted to Dev Tutorials by Rafael Fernandes on 8/15/2025 1:15:26 PM&lt;br /&gt;
&lt;p&gt;Sugar 25.2 Customization Guide&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Integrations for Devs</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/698/integrations-for-devs/revision/28</link><pubDate>Thu, 14 Aug 2025 17:27:02 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:df5ca10a-d211-4e03-9873-9d48feb7b645</guid><dc:creator>Rafael Fernandes</dc:creator><description>Revision 28 posted to Dev Tutorials by Rafael Fernandes on 8/14/2025 5:27:02 PM&lt;br /&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="#approach"&gt;&lt;img alt=" " src="/resized-image/__size/269x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.42.25-AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="#rest"&gt;&lt;img alt=" " src="/resized-image/__size/270x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.43.33-AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="#common"&gt;&lt;img alt=" " src="/resized-image/__size/270x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.43.48-AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="#performance"&gt;&lt;img alt=" " src="/resized-image/__size/268x140/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.44.04-AM.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="approach"&gt;&lt;/a&gt;Integration Approaches&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;The Sugar platform is extremely powerful, flexible, and allows for easy integration. This flexibility means that determining the&amp;nbsp;best approach for your use case scenario is the key to success.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/269x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.42.25-AM.png" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Push/Pull from Sugar via REST API&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline;"&gt;Recommended&lt;/span&gt; approach&lt;/li&gt;
&lt;li&gt;Developers have full control over integration&lt;/li&gt;
&lt;li&gt;Integration can be stopped and systems will continue to work&lt;/li&gt;
&lt;li&gt;Retry mechanism: Resume integrations at any time (identify deltas through &amp;quot;last_update_time&amp;quot; flags)&lt;/li&gt;
&lt;li&gt;The time delay from/to Sugar is minimal&lt;/li&gt;
&lt;li&gt;Sugar REST API offers almost everything you need,&amp;nbsp;and can be further extended via customization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Push to Sugar, Sugar pushes to an external queue in realtime&lt;span&gt;&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;Use Sugar Events to trigger through Logic Hooks&lt;/li&gt;
&lt;li&gt;Requires&amp;nbsp;Logic Hooks (code customization) to be created and deployed&lt;/li&gt;
&lt;li&gt;Does not offer a &amp;quot;retry mechanism&amp;quot;, if a logic hook fails to deliver to a queue or contact an external system&lt;/li&gt;
&lt;li&gt;Synchronous in nature, requires a response from an external system&lt;/li&gt;
&lt;li&gt;Needs to handle dependencies manually (contact can only be created after account)&lt;/li&gt;
&lt;li&gt;Good to have a middleware/queue in front of Sugar to receive those event triggers to&amp;nbsp;reliably&amp;nbsp;manage peak loads&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Realtime: Embed your app into Sugar&amp;nbsp;through &lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/User_Interface/Dashlets/"&gt;Dashlets&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Simplest approach&lt;/li&gt;
&lt;li&gt;Often requires web user-based authentication&lt;/li&gt;
&lt;li&gt;All load/traffic goes directly to your system,&amp;nbsp;which makes your system the performance bottleneck&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="rest"&gt;&lt;/a&gt;REST API Webservices&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;Accessing your data when you want is good. Accessing your data however you want is better. &lt;/span&gt;&lt;span&gt;When you decide to build an integration with Sugar, you&amp;rsquo;ll need an easy way to access and interact with the data stored in Sugar. The REST (&lt;/span&gt;&lt;span&gt;representational state transfer)&lt;/span&gt;&lt;span&gt;&amp;nbsp;API (application programming interface) is perfect for this.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can also leverage our&amp;nbsp;live&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://rest.apidocs.sugarcrm.com/"&gt;Sugar RESTful API Collection&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sugar REST API Overview
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Fully RESTfull (GET, POST, PUT, DELETE)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;OAuth2 token based&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;All fields and modules are available through the API automatically&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Metadata API provides everything you need to know about your Sugar instance.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/270x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.43.33-AM.png" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Topics
&lt;ul&gt;
&lt;li&gt;&lt;a href="/dev-club/b/dev-blog/posts/new-tutorial-intro-to-the-sugar-rest-api"&gt;Intro to the Sugar REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/dev-club/b/dev-blog/posts/how-sugar-rest-api-versioning-works"&gt;How Sugar REST API versioning works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/"&gt;RestAPI Developer Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/dev-club/b/dev-blog/posts/sugarcrm-cookbook---the-school-of-rest---part-1"&gt;School of REST blog series&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/"&gt;REST APIs Endpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Extending_Endpoints/"&gt;How to add your own custom endpoints to the REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/API_Exceptions/"&gt;API Exceptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Best_Practices/"&gt;Best practices when integrating and migrating Sugar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="common"&gt;&lt;/a&gt;Common&amp;nbsp;APIs&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;It is important to understand our most commonly used APIs and their functions within Sugar. Your integration might need data within Sugar&amp;#39;s module structure that could potentially change over time, so you can use Metadata for it as well as CRUD onto Sugar.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/270x141/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.43.48-AM.png" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Metadata
&lt;ul&gt;
&lt;li&gt;Endpoint with current system&amp;#39;s structure&lt;/li&gt;
&lt;li&gt;Modules, fields, relationships, layouts&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Use carefully, its payload can be MBs in size, cache as much as possible&lt;/li&gt;
&lt;li&gt;GET /metadata (use filters as much as possible)&lt;/li&gt;
&lt;li&gt;It does not retrieve labels for dropdowns, use language API to do so&lt;/li&gt;
&lt;li&gt;&lt;a href="/dev-club/b/dev-blog/posts/3-tips-for-using-the-sugar-metadata-api"&gt;3 Tips for using the Sugar Metadata API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Language
&lt;ul&gt;
&lt;li&gt;Endpoint to retrieve labels&lt;/li&gt;
&lt;li&gt;Dropdown list values&lt;/li&gt;
&lt;li&gt;Any language-specific content&lt;/li&gt;
&lt;li&gt;GET /lang/&amp;lt;language_code&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CRUD (Create, Read, Update, Delete) records
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_GET"&gt;Read&lt;/a&gt;: &lt;code&gt;GET /&amp;lt;module&amp;gt;/:record&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/module_POST"&gt;Create&lt;/a&gt;: &lt;code&gt;POST /&amp;lt;module&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_PUT"&gt;Update&lt;/a&gt;: &lt;code&gt;PUT&amp;nbsp;&lt;span&gt;/&amp;lt;module&amp;gt;/:record&lt;/span&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_DELETE"&gt;Delete&lt;/a&gt;: &lt;code&gt;DELETE&amp;nbsp;/&amp;lt;module&amp;gt;/:record&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulefilter_POST"&gt;Filter&lt;/a&gt;: &lt;code&gt;POST /&amp;lt;module&amp;gt;/filter&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;You can use &lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulefilter_GET"&gt;GET&lt;/a&gt; as well&lt;/li&gt;
&lt;li&gt;POST solves the issue when a query is too long and hits HTTP limits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecordlinklink_name_GET"&gt;Retrieve records related to another module&lt;/a&gt;:&amp;nbsp;&lt;code&gt;GET /&amp;lt;module&amp;gt;/:record/link/:link_name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecordlinklink_nameremote_id_POST"&gt;Creates a relationship to a pre-existing record:&lt;/a&gt;&lt;code&gt; POST /&amp;lt;module&amp;gt;/:record/link/:link_name/:remote_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="performance"&gt;&lt;/a&gt;Performance and Best Practices&lt;/h1&gt;
&lt;div class="row"&gt;
&lt;div class="col-xs-12 col-md-9"&gt;
&lt;p&gt;As you deep dive into Sugar APIs,&amp;nbsp;you will see how easy it is to integrate to pull and push data from and to Sugar and your systems, however, you must consider performance so your integration doesn&amp;#39;t become a bottleneck between those systems.&lt;/p&gt;
&lt;p&gt;You should consider the following guidelines when implementing your integrations:&lt;/p&gt;
&lt;p&gt;&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/268x140/__key/communityserver-wikis-components-files/00-00-00-00-14/Screen-Shot-2022_2D00_07_2D00_14-at-11.44.04-AM.png" /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Use Sugar&amp;#39;s sync_key to link to remote IDs
&lt;ul&gt;
&lt;li&gt;Sugar provides in every module a sync_key field&lt;/li&gt;
&lt;li&gt;sync_key is a unique identifier for that module that links back to your external system&amp;#39;s generated key&lt;/li&gt;
&lt;li&gt;For example, account ID from ERP is AC0001, set sync_key (pass as a parameter in the POST) and sugar will create a record in account with sync_key=AC0001&lt;/li&gt;
&lt;li&gt;Next patch/update, sugar will check &amp;quot;where sync_key=&amp;#39;AC0001&amp;#39;&amp;quot; and update it (it is indexed)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Sugar Integrate Upsert APIs
&lt;ul&gt;
&lt;li&gt;Sugar&amp;#39;s &amp;quot;update or insert&amp;quot; API based on sync_key&lt;/li&gt;
&lt;li&gt;Simplifies a common operation - if record exists, update it, otherwise create it (GET followed by a&amp;nbsp;PUT or POST)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_PATCH"&gt;Upsert by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_GET"&gt;Get by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_DELETE"&gt;Delete by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulerecord_idsync_key_field_namesync_key_field_value_PATCH"&gt;Set a sync on existing record (without sync_key)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulelhs_sync_key_field_namelhs_sync_key_field_valuelinklink_namerhs_sync_key_field_namerhs_sync_key_field_value_POST"&gt;Create relationship based on sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulelhs_sync_key_field_namelhs_sync_key_field_valuelinklink_namerhs_sync_key_field_namerhs_sync_key_field_value_DELETE"&gt;Delete relationship based on sync_key&lt;/a&gt;&lt;br /&gt;
&lt;ul&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Sugar Bulk API
&lt;ul&gt;
&lt;li&gt;An endpoint that encapsulates multiple API calls into one single HTTP request&lt;/li&gt;
&lt;li&gt;Requests are executed synchronously (one after the other)&lt;/li&gt;
&lt;li&gt;Reducing network latency between calls&lt;/li&gt;
&lt;li&gt;Combined with Upsert provides the best performance&lt;/li&gt;
&lt;li&gt;Careful not to add too many requests to it&lt;/li&gt;
&lt;li&gt;Start small (5 to 10 requests) and increase until you reach its max capacity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Polling and filtering time-based with Filter API
&lt;ul&gt;
&lt;li&gt;Integration gateway (or your external system) asks Sugar every X minutes&lt;/li&gt;
&lt;li&gt;Request Filter API using &lt;code&gt;max_num&lt;/code&gt; (max number of records), offset and fields&lt;/li&gt;
&lt;li&gt;The response will provide the data as well as &lt;code&gt;next_offset&lt;/code&gt; (offset can be seen as pagination)&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;next_offset&lt;/code&gt; is -1, no more records to go through&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Initial Data Load into Sugar&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Use Bulk + Upsert to achieve data loads&lt;/li&gt;
&lt;li&gt;Millions&amp;nbsp;of records may take a little while to process, plan ahead (over the weekend)&lt;/li&gt;
&lt;li&gt;Use multi-thread processes to load (for example 20 threads sending 20 bulk upsert requests)&lt;/li&gt;
&lt;li&gt;Careful with&amp;nbsp;&lt;a href="https://support.sugarcrm.com/Resources/Sugar_Cloud_Policy_Guide/#Rate_Limits"&gt;Sugar Cloud rate limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;It&amp;#39;s always a good idea to let Sugar Support know you&amp;#39;re data loading, they will be considerate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Understanding PHP limitations&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;PHP is single-threaded with limited resources to process your request&lt;/li&gt;
&lt;li&gt;If you send too much data using bulk, you may run out of memory for that request&lt;/li&gt;
&lt;li&gt;Push to the limits but give some room for PHP to finish the request gracefully&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Pagination for millions of records by using OFFSET will degrade performance
&lt;ul&gt;
&lt;li&gt;We rely on DB to find, sort and limit records&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/swlh/sql-pagination-you-are-probably-doing-it-wrong-d0f2719cc166"&gt;Nice article&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/a&gt;explaining how this works in details&lt;/li&gt;
&lt;li&gt;Instead of offsets, store the last processed ID and use it in the WHERE clause and sort by ID&lt;/li&gt;
&lt;li&gt;Example
&lt;ul&gt;
&lt;li&gt;Instead of: (/v11_25/Leads?max_num=1000&amp;amp;&lt;strong&gt;offset=4619000&lt;/strong&gt;&amp;amp;order_by=id)&lt;/li&gt;
&lt;li&gt;Do this&amp;nbsp;(/v11_25/Leads?&lt;strong&gt;filter=[{"id": {"$gt": &amp;rdquo;&amp;lt;last_id&amp;gt;"}}]&lt;/strong&gt;&amp;amp;max_num=1000&amp;amp;order_by=id)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you need to add a date, make sure to add it as part of the filter and order&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3 id="mcetoc_1f5uvkqns0"&gt;&lt;span style="font-size:inherit;"&gt;You&amp;#39;re up and running, but don&amp;#39;t stop now - this is just the beginning of your journey. Now it&amp;#39;s time to &lt;a href="/dev-club/dev-onboarding/w/documentation/686/customization-for-devs"&gt;Customizations&lt;/a&gt;.&lt;/span&gt;&lt;/h3&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Integrations for Product Devs</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/708/integrations-for-product-devs/revision/12</link><pubDate>Thu, 14 Aug 2025 17:26:46 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:e64c91c4-03e5-454f-bbd7-3beb25519ac3</guid><dc:creator>Rafael Fernandes</dc:creator><description>Revision 12 posted to Dev Tutorials by Rafael Fernandes on 8/14/2025 5:26:46 PM&lt;br /&gt;
&lt;table style="margin-left:auto;margin-right:auto;width:100%;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#approach"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/1.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#rest"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/7612.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#common"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/3.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#performance"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/4.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="approach"&gt;&lt;/a&gt;Integration Approaches&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/1.png" /&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;The Sugar platform is extremely powerful, flexible, and allows for easy integration. This flexibility means that determining the&amp;nbsp;best approach for your use case scenario is the key to success.&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Push/Pull from Sugar via REST API&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline;"&gt;Recommended&lt;/span&gt; approach&lt;/li&gt;
&lt;li&gt;Developers have full control over integration&lt;/li&gt;
&lt;li&gt;Integration can be stopped and systems will continue to work&lt;/li&gt;
&lt;li&gt;Retry mechanism: Resume integrations at any time (identify deltas through &amp;quot;last_update_time&amp;quot; flags)&lt;/li&gt;
&lt;li&gt;The time delay from/to Sugar is minimal&lt;/li&gt;
&lt;li&gt;Sugar REST API offers almost everything you need,&amp;nbsp;and can be further extended via customization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Push to Sugar, Sugar pushes to an external queue in realtime&lt;span&gt;&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;Use Sugar Events to trigger through Logic Hooks&lt;/li&gt;
&lt;li&gt;Requires&amp;nbsp;Logic Hooks (code customization) to be created and deployed&lt;/li&gt;
&lt;li&gt;Does not offer a &amp;quot;retry mechanism&amp;quot;, if a logic hook fails to deliver to a queue or contact an external system&lt;/li&gt;
&lt;li&gt;Synchronous in nature, requires a response from an external system&lt;/li&gt;
&lt;li&gt;Needs to handle dependencies manually (contact can only be created after account)&lt;/li&gt;
&lt;li&gt;Good to have a middleware/queue in front of Sugar to receive those event triggers to&amp;nbsp;reliably&amp;nbsp;manage peak loads&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Realtime: Embed your app into Sugar&amp;nbsp;through &lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/User_Interface/Dashlets/"&gt;Dashlets&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Simplest approach&lt;/li&gt;
&lt;li&gt;Often requires web user-based authentication&lt;/li&gt;
&lt;li&gt;All load/traffic goes directly to your system,&amp;nbsp;which makes your system the performance bottleneck&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="rest"&gt;&lt;/a&gt;REST API Webservices&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/7612.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;Accessing your data when you want is good. Accessing your data however you want is better. &lt;/span&gt;&lt;span&gt;When you decide to build an integration with Sugar, you&amp;rsquo;ll need an easy way to access and interact with the data stored in Sugar. The REST (&lt;/span&gt;&lt;span&gt;representational state transfer)&lt;/span&gt;&lt;span&gt;&amp;nbsp;API (application programming interface) is perfect for this.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can also leverage our&amp;nbsp;live&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://rest.apidocs.sugarcrm.com/"&gt;Sugar RESTful API Collection&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sugar REST API Overview
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Fully RESTfull (GET, POST, PUT, DELETE)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;OAuth2 token based&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;All fields and modules are available through the API automatically&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Metadata API provides everything you need to know about your Sugar instance.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Topics
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/new-tutorial-intro-to-the-sugar-rest-api"&gt;Intro to the Sugar REST API&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/how-sugar-rest-api-versioning-works"&gt;How Sugar REST API versioning works&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/"&gt;RestAPI Developer Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/sugarcrm-cookbook---the-school-of-rest---part-1"&gt;SugarCRM Cookbook - The School of REST - Part 1&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/"&gt;REST APIs Endpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Extending_Endpoints/"&gt;How to add your own custom endpoints to the REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/API_Exceptions/"&gt;API Exceptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Best_Practices/"&gt;Best practices when integrating and migrating Sugar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="common"&gt;&lt;/a&gt;Common&amp;nbsp;APIs&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/3.png" /&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;It is important to understand our most commonly used APIs and their functions within Sugar. Your integration might need data within Sugar&amp;#39;s module structure that could potentially change over time, so you can use Metadata for it as well as CRUD onto Sugar.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Metadata
&lt;ul&gt;
&lt;li&gt;Endpoint with current system&amp;#39;s structure&lt;/li&gt;
&lt;li&gt;Modules, fields, relationships, layouts&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Use carefully, its payload can be MBs in size, cache as much as possible&lt;/li&gt;
&lt;li&gt;GET /metadata (use filters as much as possible)&lt;/li&gt;
&lt;li&gt;It does not retrieve labels for dropdowns, use language API to do so&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/3-tips-for-using-the-sugar-metadata-api"&gt;3 Tips for using the Sugar Metadata API&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Language
&lt;ul&gt;
&lt;li&gt;Endpoint to retrieve labels&lt;/li&gt;
&lt;li&gt;Dropdown list values&lt;/li&gt;
&lt;li&gt;Any language-specific content&lt;/li&gt;
&lt;li&gt;GET /lang/&amp;lt;language_code&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CRUD (Create, Read, Update, Delete) records&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_GET"&gt;Read&lt;/a&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;GET /&amp;lt;module&amp;gt;/:record&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/module_POST"&gt;Create&lt;/a&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;POST /&amp;lt;module&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_PUT"&gt;Update&lt;/a&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;PUT&amp;nbsp;&lt;span&gt;/&amp;lt;module&amp;gt;/:record&lt;/span&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecord_DELETE"&gt;Delete&lt;/a&gt;:&amp;nbsp;&lt;code&gt;DELETE&amp;nbsp;/&amp;lt;module&amp;gt;/:record&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulefilter_POST"&gt;Filter&lt;/a&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;POST /&amp;lt;module&amp;gt;/filter&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;You can use&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulefilter_GET"&gt;GET&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;as well&lt;/li&gt;
&lt;li&gt;POST solves the issue when a query is too long and hits HTTP limits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecordlinklink_name_GET"&gt;Retrieve records related to another module&lt;/a&gt;:&amp;nbsp;&lt;code&gt;GET /&amp;lt;module&amp;gt;/:record/link/:link_name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/rest_api/Endpoints/modulerecordlinklink_nameremote_id_POST"&gt;Creates a relationship to a pre-existing record:&lt;/a&gt;&lt;code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;POST /&amp;lt;module&amp;gt;/:record/link/:link_name/:remote_id&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="performance"&gt;&lt;/a&gt;Performance and Best Practices&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/4.png" /&gt;&lt;/h1&gt;
&lt;div class="row"&gt;
&lt;div class="col-xs-12 col-md-9"&gt;
&lt;p&gt;As you deep dive into Sugar APIs,&amp;nbsp;you will see how easy it is to integrate to pull and push data from and to Sugar and your systems, however, you must consider performance so your integration doesn&amp;#39;t become a bottleneck between those systems.&lt;/p&gt;
&lt;p&gt;You should consider the following guidelines when implementing your integrations:&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Use Sugar&amp;#39;s sync_key to link to remote IDs
&lt;ul&gt;
&lt;li&gt;Sugar provides in every module a sync_key field&lt;/li&gt;
&lt;li&gt;sync_key is a unique identifier for that module that links back to your external system&amp;#39;s generated key&lt;/li&gt;
&lt;li&gt;For example, account ID from ERP is AC0001, set sync_key (pass as a parameter in the POST) and sugar will create a record in account with sync_key=AC0001&lt;/li&gt;
&lt;li&gt;Next patch/update, sugar will check &amp;quot;where sync_key=&amp;#39;AC0001&amp;#39;&amp;quot; and update it (it is indexed)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Sugar Integrate Upsert APIs
&lt;ul&gt;
&lt;li&gt;Sugar&amp;#39;s &amp;quot;update or insert&amp;quot; API based on sync_key&lt;/li&gt;
&lt;li&gt;Simplifies a common operation - if record exists, update it, otherwise create it (GET followed by a&amp;nbsp;PUT or POST)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_PATCH"&gt;Upsert by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_GET"&gt;Get by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulesync_key_field_namesync_key_field_value_DELETE"&gt;Delete by sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulerecord_idsync_key_field_namesync_key_field_value_PATCH"&gt;Set a sync on existing record (without sync_key)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulelhs_sync_key_field_namelhs_sync_key_field_valuelinklink_namerhs_sync_key_field_namerhs_sync_key_field_value_POST"&gt;Create relationship based on sync_key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/Integratemodulelhs_sync_key_field_namelhs_sync_key_field_valuelinklink_namerhs_sync_key_field_namerhs_sync_key_field_value_DELETE"&gt;Delete relationship based on sync_key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Sugar Bulk API
&lt;ul&gt;
&lt;li&gt;An endpoint that encapsulates multiple API calls into one single HTTP request&lt;/li&gt;
&lt;li&gt;Requests are executed synchronously (one after the other)&lt;/li&gt;
&lt;li&gt;Reducing network latency between calls&lt;/li&gt;
&lt;li&gt;Combined with Upsert provides the best performance&lt;/li&gt;
&lt;li&gt;Careful not to add too many requests to it&lt;/li&gt;
&lt;li&gt;Start small (5 to 10 requests) and increase until you reach its max capacity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Polling and filtering time-based with Filter API
&lt;ul&gt;
&lt;li&gt;Integration gateway (or your external system) asks Sugar every X minutes&lt;/li&gt;
&lt;li&gt;Request Filter API using &lt;code&gt;max_num&lt;/code&gt; (max number of records), offset and fields&lt;/li&gt;
&lt;li&gt;The response will provide the data as well as &lt;code&gt;next_offset&lt;/code&gt; (offset can be seen as pagination)&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;next_offset&lt;/code&gt; is -1, no more records to go through&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Initial Data Load into Sugar&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Use Bulk + Upsert to achieve data loads&lt;/li&gt;
&lt;li&gt;Millions&amp;nbsp;of records may take a little while to process, plan ahead (over the weekend)&lt;/li&gt;
&lt;li&gt;Use multi-thread processes to load (for example 20 threads sending 20 bulk upsert requests)&lt;/li&gt;
&lt;li&gt;Careful with&amp;nbsp;&lt;a href="https://support.sugarcrm.com/Resources/Sugar_Cloud_Policy_Guide/#Rate_Limits"&gt;Sugar Cloud rate limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;It&amp;#39;s always a good idea to let Sugar Support know you&amp;#39;re data loading, they will be considerate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Understanding PHP limitations&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;PHP is single-threaded with limited resources to process your request&lt;/li&gt;
&lt;li&gt;If you send too much data using bulk, you may run out of memory for that request&lt;/li&gt;
&lt;li&gt;Push to the limits but give some room for PHP to finish the request gracefully&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Pagination for millions of records by using OFFSET will degrade performance
&lt;ul&gt;
&lt;li&gt;We rely on DB to find, sort and limit records&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/swlh/sql-pagination-you-are-probably-doing-it-wrong-d0f2719cc166"&gt;Nice article &lt;/a&gt;explaining how this works in details&lt;/li&gt;
&lt;li&gt;Instead of offsets, store the last processed ID and use it in the WHERE clause and sort by ID&lt;/li&gt;
&lt;li&gt;Example
&lt;ul&gt;
&lt;li&gt;Instead of: (/v11_25/Leads?max_num=1000&amp;amp;&lt;strong&gt;offset=4619000&lt;/strong&gt;&amp;amp;order_by=id)&lt;/li&gt;
&lt;li&gt;Do this&amp;nbsp;(/v11_25/Leads?&lt;strong&gt;filter=[{"id": {"$gt": &amp;rdquo;&amp;lt;last_id&amp;gt;"}}]&lt;/strong&gt;&amp;amp;max_num=1000&amp;amp;order_by=id)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you need to add a date, make sure to add it as part of the filter and order&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p id="mcetoc_1f5uvkqns0"&gt;&lt;span style="font-size:150%;"&gt;You&amp;#39;re up and running, but don&amp;#39;t stop now - this is just the beginning of your journey. &lt;/span&gt;&lt;/p&gt;
&lt;p id="mcetoc_1g9jl704d0"&gt;&lt;span style="font-size:150%;"&gt;&lt;strong&gt;Now it&amp;#39;s time for&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/676/customization-for-product-devs"&gt;Customization for Product Devs&lt;/a&gt;.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>On-Boarding Product Devs</title><link>https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/675/on-boarding-product-devs</link><pubDate>Thu, 14 Aug 2025 17:26:21 GMT</pubDate><guid isPermaLink="false">5c521d64-519d-47a6-9065-134618b211bf:1efceeda-d037-49ba-b530-b71c0b94e9e6</guid><dc:creator>Rafael Fernandes</dc:creator><description>Current Revision posted to Dev Tutorials by Rafael Fernandes on 8/14/2025 5:26:21 PM&lt;br /&gt;
&lt;table style="margin-left:auto;margin-right:auto;width:100%;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#register"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/4645.Guides.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#setup"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/5008.Guides-_2800_2_2900_.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#api"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/8357.Dev-Onboarding-_2800_Rafa-822_2900_.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align:center;"&gt;&lt;a href="#learn"&gt;&lt;img style="height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/8422.Guides-_2800_4_2900_.png" /&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="register"&gt;&lt;/a&gt;Register&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/4645.Guides.png" /&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;What steps should a partner or customer follow in order to onboard themselves or a colleague as a new Sugar Developer with access to Sugar development resources?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Read below to learn more about how to provide your new developers with this&amp;nbsp;role.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/691/register-with-sugarclub"&gt;Register with SugarClub&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a Product Developer you can download our software and have instances hosted locally on your servers. To do so, you need to access Sugar On-Site software downloads and mobile development tools, which&amp;nbsp;is controlled using the&amp;nbsp;&lt;a href="/community-help/a/knowledge-base/KB94/how-to-resolve-access-issues-in-sugarclub#mcetoc_1fptmfsu54"&gt;&lt;b&gt;Download Manager Authorised Contact&lt;/b&gt;&amp;nbsp;role&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="setup"&gt;&lt;/a&gt;Set-up&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/5008.Guides-_2800_2_2900_.png" /&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;In order to get access to the Sugar code, you need to be either a SugarCRM customer or a partner.&amp;nbsp;&amp;nbsp;To learn more about our partner program and to apply, please check out our&amp;nbsp;&lt;a href="https://www.sugarcrm.com/partners/software-partners" rel="nofollow noopener noreferrer" target="_blank"&gt;website&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Partners also get access to Sugar cloud instances that they can use to quickly get started. See the Partner Portal for more information about accessing SugarCloud.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/695/demo-builder-tools"&gt;Demo Builder&lt;/a&gt;&amp;nbsp;provides you with your&amp;nbsp;Sugar Cloud Development environment
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/partner-club/sugar-labs/w/sugar-labs-guides/330/start-with-the-demo-builder"&gt;Start with the Demo Builder&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://www.youtube.com/watch?v=p97QUlxw528"&gt;Create a Demo&lt;/a&gt;&amp;nbsp;(Video)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://www.youtube.com/watch?v=PB6MS6LIGAg&amp;amp;feature=youtu.be"&gt;Demo Builder Guru Meditation (Video)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Finally, please take time to review our &lt;a href="https://support.sugarcrm.com/Resources/Developer_Policy/"&gt;Developer Policy&lt;/a&gt; before you begin coding.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1f4f3gg1d0"&gt;&lt;span&gt;&lt;a id="api"&gt;&lt;/a&gt;First API Call&lt;img class="align-right" style="float:right;height:auto;" alt=" " src="/resized-image/__size/2000x0/__key/communityserver-wikis-components-files/00-00-00-00-14/8357.Dev-Onboarding-_2800_Rafa-822_2900_.png" /&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;Web Services APIs are the fastest way to get started with custom Sugar development. The&amp;nbsp;Sugar API documentation is built into every Sugar instance, including Sugar&amp;#39;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/" rel="nofollow"&gt;REST API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Check out the&amp;nbsp;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/" rel="nofollow"&gt;Sugar Developer Guide&lt;/a&gt;&amp;nbsp;for more details on how to access Sugar REST APIs and to get started using them. You can also check out the&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/sugarcrm-cookbook---the-school-of-rest---part-1"&gt;The School of REST&lt;/a&gt;&amp;nbsp;blog series and the&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/m/uncon-archive/929"&gt;Tutorial: Intro to the Sugar REST v10 API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also leverage our&amp;nbsp;live&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://rest.apidocs.sugarcrm.com/"&gt;Sugar RESTful API Collection&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Making your first API call to Sugar is as easy as 1, 2, 3:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/#Authentication"&gt;Step 1: Authenticate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Cookbook/Web_Services/REST_API/Bash/How_to_Manipulate_Records_CRUD/"&gt;Step 2:&amp;nbsp;Manipulate your records&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Integration/Web_Services/REST_API/Endpoints/"&gt;Step 3: Know your Endpoints&lt;br /&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;a id="learn"&gt;&lt;/a&gt;Learn&lt;img class="align-right" style="float:right;" alt=" " src="/resized-image/__size/510x200/__key/communityserver-wikis-components-files/00-00-00-00-14/8422.Guides-_2800_4_2900_.png" /&gt;&lt;/h1&gt;
&lt;p&gt;Getting the most out of Sugar means understanding what can be done via the Sugar Administration panel. We highly recommend Sugar Developers learn about Sugar Administration. Both Sugar Developer and Sugar Administrator training programs are available from Sugar University. There are self-paced videos as well as options for live developer webinars and in-person training.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/"&gt;Sugar Developer Guide&lt;/a&gt;&amp;nbsp;and the&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog"&gt;Dev Blog&lt;/a&gt;&amp;nbsp;are other primary training sources.&lt;/li&gt;
&lt;li&gt;There are a lot of great Sugar resources out there in the community as well, so&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/b/dev-blog/posts/sugarcrm-cookbook---the-school-of-rest---part-1"&gt;check out this blog post&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to find many of them.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;You should also get familiar with the&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/199/sugar-developer-tools"&gt;Sugar Developer Tools&lt;/a&gt;&amp;nbsp;that SugarCRM and the Sugar Developer community provide for you. We have tools that help you build, test, debug, and deploy your Sugar solutions even faster.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How will you know when you are ready to rock the Sugar Developer universe? When you get certified! You can learn more about our&amp;nbsp;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/learn/b/sugar-certification"&gt;Sugar Certification&lt;/a&gt;&amp;nbsp;program&amp;nbsp;and the&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/learn/b/sugar-certification/posts/sugar-developer-specialist"&gt;Sugar Developer Specialist&lt;/a&gt;&amp;nbsp;exam at&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/learn/"&gt;Training &amp;amp; Certification&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Get involved with our&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/f/questions-answers"&gt;Sugar Community&lt;/a&gt;&amp;nbsp;by posting your questions and get answers from a broad audience of experts&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1 id="mcetoc_1g6ls8l4i0"&gt;&lt;strong&gt;&lt;a id="Resources-Downloads"&gt;&lt;/a&gt;Resources &amp;amp; Downloads&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;Use the additional resources and downloads below to further support your onboarding.&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SugarClub&lt;/td&gt;
&lt;td&gt;
&lt;ul&gt;
&lt;li&gt;SugarClub&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/"&gt;DevClub&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/f/questions-answers"&gt;Dev Answers &amp;amp; Best Practices&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth.sugarcrm.com/register"&gt;Register with Sugar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://info.sugarcrm.com/developer-content-signup"&gt;Sign up to receive the latest Sugar Developer News&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sugar&amp;nbsp;Portals&lt;/td&gt;
&lt;td&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/"&gt;Support Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.sugarondemand.com/"&gt;Case Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://store.sugarcrm.com/downloads"&gt;Software Downloads Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://store.sugarcrm.com/dashboard"&gt;Portal Dashboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://store.sugarcrm.com/dashboard"&gt;Get your License Keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://demobuilder.sugarcrmdemo.com/"&gt;DemoBuilder Cloud Environment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sugar&amp;nbsp;Architecture&lt;/td&gt;
&lt;td&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Architecture/"&gt;Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Introduction"&gt;Introduction to Sugar Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Introduction/Development_Methodology/"&gt;Development Methodology&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/SmartLinks/Developer_Guide/Architecture/Extensions"&gt;Extensions Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.sugarcrm.com/Resources/Supported_Platforms/"&gt;Supported Platforms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p id="mcetoc_1f5uvkqns0"&gt;&lt;span style="font-size:150%;"&gt;You&amp;#39;re up and running, but don&amp;#39;t stop now - this is just the beginning of your journey.&lt;/span&gt;&lt;/p&gt;
&lt;p id="mcetoc_1g9jjo1t30"&gt;&lt;span style="font-size:150%;"&gt;&lt;strong&gt;Now it&amp;#39;s time to&amp;nbsp;&lt;a href="https://sugarclub.sugarai.com/dev-club/w/dev-tutorials/677/low-code-no-code-for-product-devs"&gt;Low Code No Code for Product Devs&lt;/a&gt;.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item></channel></rss>