Reload a field from the controller.php - sugarcrm

I have a logic hook that changes a field's value, but in order to see the change I need to refresh the whole page... I want it to refresh the field in real time.
So I'm trying to accomplish something like this(https://suitecrm.com/suitecrm/forum/suitecrm-7-0-discussion/21178-refresh-sub-panel-values-when-another-sub-panel-is-updated#75194)
But instead of reloading a whole subpanel through the controller.php I'm trying to find a way to refresh a single field.
Can anyone advise what method needs to be used to reload a field?
For example to reload a subpanel it's
**
showSubPanel('SUBPANEL_NAME',null,true);
**
But what is the JS method that reload a single field?

To refresh a detailView through a logic hook, you can use an easy "hack".
In your logic_hook, place the following js code as your last "echo" before your return or exit:
echo "<script>location.reload();</script>";
Simple but effective.

I couldn't find any built-in functions in suiteCRM that do it, spent a lot of time going through the chrome debugger, but nothing worked.
Here's a video that explains what's happening and the actual code example
https://youtu.be/ebuwWZoSYCk
You need to get the new status from the back-end then update the field inside the controller.php with:
document.querySelector('div[type="enum"][field="$field_to_update"]').innerHTML = "$inventory_status_c";
The whole controller.php file example is here, it all makes sense if you watch the 5 minute video:
class un_inventoryController extends SugarController {
/**
*
*/
function action_SubPanelViewer() {
require_once 'include/SubPanel/SubPanelViewer.php';
// only if this is creation of new sale under accounts, refresh the screen so the salerow subpanel will be refreshed too
if ( array_key_exists('module', $_REQUEST) && array_key_exists('subpanel', $_REQUEST) && array_key_exists('action', $_REQUEST) &&
$_REQUEST['module'] == 'un_inventory' && $_REQUEST['subpanel'] == "un_inventory_leads_1" && $_REQUEST['action'] == "SubPanelViewer") {
write_to_log(array("request" => $_REQUEST), "all conditions filled, custom controller called", true);
// Get the ID of the inventory unit so we can fetch the new status_c field and update the field right away (otherwise we'll have to refresh the page
$inventory = BeanFactory::getBean($_REQUEST["module"], $_REQUEST["record"]);
$inventory_status_c = ucwords(str_replace("_", " ", $inventory->status_c));
$field_to_update = "status_c";
$js=<<<EOQ
<script>
// Update the status
$( document ).ready(function() {
document.querySelector('div[type="enum"][field="$field_to_update"]').innerHTML = "$inventory_status_c";
});
</script>
EOQ;
echo $js;
}
}
}
?>

Related

ag-Grid set filter and sort model without triggering event

I am updating sort & filter models via api:
this.gridApi.setFilterModel(filterModels);
this.gridApi.setSortModel(sortModels);
The problem with this is I have a server request bound to the change even of both sort & filter so when user changes then the data is updated. This means when I change model on code like restoring a state or resetting the filters it causes multiple requests.
Is there a way to update the filter/sort model without triggering the event?
I see there is a ColumnEventType parameter but couldn't see how it works. Can I specify some variable that I can look for inside my event handlers to get them to ignore calls that are not generated from user?
I am trying to manage URL state so when url query params change my code sets the models in the grids but this ends up causing the page to reload multiple times because the onFilter and onSort events get called when the model is set and there is no way I can conceive to prevent this.
At the time, you are going to have to manage this yourself, ie, just before you call the setModel, somehow flag this in a shared part of your app (maybe a global variable)
Then when you react to these events, check the estate of this, to guess where it came from.
Note that at the moment, we have added source to the column events, but they are not yet for the model events, we are planning to add them though, but we have no ETA
Hope this helps
I had to solve similar issue. I found solution which working for my kind of situation. Maybe this help someone.
for (let j = 0; j < orders.length; j++) {
const sortModelEntry = orders[j];
if (typeof sortModelEntry.property === 'string') {
const column: Column = this.gridColumnApi.getColumn(sortModelEntry.property);
if (column && ! column.getColDef().suppressSorting) {
column.setSort(sortModelEntry.direction.toLowerCase());
column.setSortedAt(j);
}
}
this.gridApi.refreshHeader();
Where orders is array of key-value object where key is name of column and value is sorting directive (asc/desc).
Set filter without refresh was complicated
for (let j = 0; j < filters.length; j++) {
const filterModelEntry = filters[j];
if (typeof filterModelEntry.property === 'string') {
const column: Column = this.gridColumnApi.getColumn(filterModelEntry.property);
if (column && ! column.getColDef().suppressFilter) {
const filter: any = this.gridApi.getFilterApi(filterModelEntry.property);
filter['filter'] = filterModelEntry.command;
filter['defaultFilter'] = filterModelEntry.command;
filter['eTypeSelector'].value = filterModelEntry.command;
filter['filterValue'] = filterModelEntry.value;
filter['filterText'] = filterModelEntry.value;
filter['eFilterTextField'].value = filterModelEntry.value;
column.setFilterActive(true);
}
}
}
Attributes in filter:
property - name of column
command - filter action (contains, equals, ...)
value - value used in filter
For anyone else looking for a solution to this issue in Nov 2020, tapping into onFilterModified() might help. This gets called before onFilterChanged() so setting a value here (eg. hasUserManuallyChangedTheFilters = false, etc.) and checking the same in the filter changed event is a possible workaround. Although, I haven't found anything similar for onSortChanged() event, one that gets called before the sorting is applied to the grid.
I am not sure any clean way to achieve this but I noticed that FilterChangedEvent has "afterFloatingFilter = false" only if filterModel was updated from ui.
my workaround is as below
onFilterChanged = event:FilterChangedEvent) => {
if(event.afterFloatingFilter === undefined) return;
console.log("SaveFilterModel")
}

What approach should be made to successfully use a specific variable?

I have a function were in I need to update multiple dropdown lists.
Since I want to remove manual interventions, I want the test to automatically decide which data to be used.
I have 2 sets of array/variable on my main tests.
arrDefinition1 = ['Section;Sec1', 'Domain;test1', 'Process layer;test1', 'Skill;test1'];
arrDefinition2 = ['Section;Sec2', 'Domain;test2', 'Process layer;test2', 'Skill;test2'];
If the current value of the dropdown list is "Sec1" then arrDefinition2 will be used and vise-versa.
But due to promises, my IF condition was executed first before the IT and the variable that contains the value of getText contains "undefined", which means arrDefinition2 will always be used.
it('should click the Edit icon of the first row of Activity table', function() {
waitForElement(5, elmFirstRow, true);
editFirstRow.click();
waitForElement(5, element(by.id('section')), true);
element(by.id('section')).getText().then(function (tmpText) {
tempObject.strSection = tmpText;
});
});
if (tempObject.strSection == 'Sec1') {
console.log('first');
fillUpForm(0, tempObject.arrDefinition2, 'UpdateMDef');
} else {
console.log('second');
fillUpForm(0, tempObject.arrDefinition1, 'UpdateMDef');
}
Note: The function fillUpForm consists of IT functions.
Any suggestions are very much appreciated.

Get Line Items in an Invoice logic hook in SuiteCRM

Via a logic hook I'm trying to update fields of my products, after an invoice has been saved.
What I understand so far is, that I need to get the invoice related AOS_Products_Quotes and from there I could get the products, update the required fields and save the products. Does that sound about right?
The logic hook is being triggered but relationships won't load.
function decrement_stocks ( $bean, $event, $arguments) {
//$bean->product_value_c = $bean->$product_unit_price * $bean->product_qty;
$file = 'custom/modules/AOS_Invoices/decrement.txt';
// Get the Invoice ID:
$sInvoiceID = $bean->id;
$oInvoice = new AOS_Invoices();
$oInvoice->retrieve($sInvoiceID);
$oInvoice->load_relationship('aos_invoices_aos_product_quotes');
$aProductQuotes = $oInvoice->aos_invoices_aos_product_quotes->getBeans();
/*
$aLineItemslist = array();
foreach ($oInvoice->aos_invoices_aos_product_quotes->getBeans() as $lineitem) {
$aLineItemslist[$lineitem->id] = $lineitem;
}
*/
$sBean = var_export($bean, true);
$sInvoice = var_export($oInvoice, true);
$sProductQuotes = var_export($aProductQuotes, true);
$current = $sProductQuotes . "\n\n\n------\n\n\n" . $sInvoice . "\n\n\n------\n\n\n" . $sBean;
file_put_contents($file, $current);
}
The invoice is being retrieved just fine. But either load_relationship isn't doing anything ($sInvoice isn't changing with or without it) and $aProductQuotes is Null.
I'm working on SuiteCRM 7.8.3 and tried it on 7.9.1 as well without success. What am I doing wrong?
I'm not familiar with SuiteCRM specifics, however I'd always suggest to check:
Return value of retrieve(): bean or null?
If null, then no bean with the given ID was found.
In such case $oInvoice would stay empty (Your comment suggests that's not the case here though)
Return value of load_relationship(): true (success) or false (failure, check logs)
And I do wonder, why don't you use $bean?
Instead you seem to receive another copy/reference of $bean (and calling it $oInvoice)? Why?
Or did you mean to receive a different type bean that is somehow connected to $bean?
Then its surely doesn't have the same id as $bean, unless you specifically coded it that way.

Trying to copy values into multiple embedded forms

I have a Symfony 1.4 application to allow users to enter data to a electrical appliance testing database. The page in questions consists of multiple embedded "new" forms so the user can submit many tests in one go. The form validates and saves correctly, but feedback is that it will be tedious to use.
As much of the data may be the same in each test (e.g. same date, same result, same person doing the testing), I would like the user to be able to fill in values in the top row, then click a button to fill the same information in the rows below. I'm pretty sure this would require javascript, but I don't have much experience.
I would appreciate any suggestions.
Many Thanks.
Well, I managed to figure it out without using javascript.
I put a button on the page
<input type="submit" name="copy_values" value="duplicate">
In the action for the page I included the code...
elseif (isset($_POST['copy_values'])) {
// get values from first embedded test
$newTests = $testList['new_tests'];
$testDate = $newTests[0]['et_date_tested'];
$testedBy = $newTests[0]['et_tester_id'];
$formOptions = array('test_date'=>$testDate, 'tester'=>$testedBy);
$this->form = new MultiTestForm(null, $formOptions);
}
$this->setTemplate('multiAdd');
... which takes the values from the widgets in the top row of the form and creates an array. This is passed as the options array to create a new form.
In the top level form class..
public function configure()
{
...
$subform = new sfForm();
for($i = 0;$i < sfConfig::get('app_new_test_rows'); $i ++)
{
$formToAdd = new TestsForMultiAddForm(null,$this->getOptions());
$subform->embedForm($i, $formToAdd);
}
$this->embedForm('new_tests', $subform);
}
...and in the embedded form class...
public function configure()
{
...
if ($this->getOption('test_date')) {
$this->setDefault('et_date_tested', $this->getOption('test_date'));
}
if ($this->getOption('tester')) {
$this->setDefault('et_tester_id', $this->getOption('tester'));
}
...
}
Not sure if this is the conventional way to approach the problem, but it works!

How to populate zend form field using session?

I am using sessions to populate a multi select box with options in my Zend application.
The user selects one or more options and fills in other fields on the form and then submits. If the user didn't select all of the options in the multi select then the form is displayed again but the multi select only has the options that the user did not select the last time. This process goes on until there are no more options from the multi select left to process.
Here is the code I use to get rid of the options that have already been processed so that they are not used to populate the multi select box:
if($form_successful){
// TODO remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
print_r($keyword_names);
if(is_array($keyword_names) && !empty($keyword_names)){
// save updated $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
// set flag to false so that we display form again
$form_successful = false;
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
print_r($keyword_names); displays the correct options, however when the form is loaded when the user submits, the multi select displays the options that were there from the begining ie the options the user has just selected and submitted are not being taken out of the multi select, it is only when the user submits the form again then the multi select box updates.
Appreciate the help.
Solved the issue by making use of URL parameters. Here is the code (might differ a lot from what I posted first because some big changes were made):
// after successful form submission
if($form_successful){
// remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
// save remaining $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
if(is_array($keyword_names) && !empty($keyword_names)){
// redirect to the same step again - to ensure that the form will reflect (in select lists) newly created AdGroup and/or Campaign
// GET parameteres ($params_array) provide a way to remember user's choice
$params_array = array();
if(!empty($post['match_type_id'])){
$params_array['match_type_id'] = $post['match_type_id'];
}
if(!empty($post['with_permutations'])){
$params_array['with_permutations'] = $post['with_permutations'];
}
if(!empty($ad_group_id)){
$params_array['ad_group_id'] = $ad_group_id;
}
$this_step_url = UrlUtils::assemble('', $this->getRequest()->getActionName(), $this->getRequest()->getControllerName(), $this->getRequest()->getModuleName(), $params_array);
$this->_redirect($this_step_url);
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
So you don't have any code about Zend_Form object here. How do you populate the form element? If you post your class code which extends Zend_Form (or any other code dials with your form) then I may help. But in any case you can populate your multiselectbox with setMultiOptions() method or addMultiOption() for each item in multiselectbox.