Updating a record from a list view using the action dropdown

I'm still on the learning curve as a SugarCRM developer. I've done significant amounts of development on the back end (logic hooks, database work, scheduled jobs) but am still learning the ropes on the front end. I could really use some help understanding how to make this happen.

We have a custom module that includes a status dropdown. A set of our users spend much of their day in the list view of this module. One of their key actions is to update this status field. I've been asked to enable updating this status directly from the list view, which would simplify their workflow a lot.

I modified the modules recordlist.php file to add the status options in the dropdown (see the attached image). So if they select "Mark Complete", the status field should be updated to "Complete." The column in the listview should update to refresh the new value as well.

How do I make it so Sugar is "listening" for this action and makes the appropriate update?

Does the update of the record itself happen in Javascript on the front end? Or does the action trigger a process/call on the backend, with PHP code ultimately updating the field and saving it?

Thanks for any help you can provide.

Parents
  • I am not sure you are approaching this in the most efficient way.

    The user can use the Edit on the list view to update each line one by one but the easiest would be to add the status to the mass-update, that way they can check the boxes on the left for all the lines to be updated to a given value and then just select the status to assign.

    See if these links help:

    Adding a field to Mass Update:

    http://support.sugarcrm.com/Knowledge_Base/Studio_and_Module_Builder/Adding_a_Field_to_Mass_Update/ 

    Alternatively, you could add a List View Action (not Row Action) to mass update the status to the value you want on a whole set of selected list-view records, effectively doing a one-click mass update.

    For example, in my Cases module I added an action to close as spam in

    custom/modules/Cases/clients/base/views/recordlist/recordlist.php

    in the actions array:

     

    array (
            'name' => 'close_as_spam',
            'type'=>'button',
            'label' => 'LBL_CLOSE_AS_SPAM_ACTION',
            'acl_action' => 'edit',
            'events' => array(
              'click' => 'list:closeasspam:fire',
            ),
          ),

    I then extended the controller and defined the code to execute when the event fires:

    custom/modules/Cases/clients/base/views/recordlist/recordlist.js

    ({
       extendsFrom: 'RecordlistView',

       initialize: function(options){
          this._super('initialize', [options]);
          //define the event for the close as spam action
          this.context.on('list:closeasspam:fire', this.closeAsSpamClicked, this);

       },
       //execute the update
       updateRecords: function(params){
         var self = this,
             url = app.api.buildURL('Cases/MassUpdate');
         app.api.call('update',url,params,{
           success: function(o){
             self.layout.context.reloadData({showAlerts: false});
             var massCollection = self.context.get('mass_collection');
             massCollection.reset();
           },
           error: function(e){
             app.alert.show('error_while_mass_update', {
               level:'error',
               title: app.lang.getAppString('ERR_INTERNAL_ERR_MSG'),
               messages: app.lang.getAppString('ERR_HTTP_500_TEXT')
             });
           },
         });
       },
       //set the values to update
       closeAsSpamClicked : function() {
         var ids = _.map(this.context.get('mass_collection').models, function(selected_model){return selected_model.id}),
             params = {
               'massupdate_params':{
                 'uid':ids,
                 'status':'Closed',
                 'resolution':'Closed as Spam',
                 'case_closed_reason_c':'Spam'
               }
             };
         this.updateRecords(params);
       },
    })

    Note that all this does is leverage the MassUpdate to set the status, resolution and case_closed_reason_c (custom field) without the users having to set those themselves.

     

    If you wish to go down the path of custom rowaction events

    in custom/modules/<yourModule>/clients/base/views/recordlist/recordlist.php

    you will have added an item in the rowactions array.

    You now need an event handler for your custom event in the controller

    in custom/modules/<yourModule>/clients/base/views/recordlist/recordlist.js

    and define what to do when the event is fired

    Hope this helps,
    FrancescaS

  • Thank you very much for your reply. 

    I know they could use Mass Update, but in this use case, they are only updated one at a time as a user completes another action outside of Sugar.

    Editing the record in the list view via the "edit" option does work correctly. However it requires 5 clicks to update a single record (click the action arrow, click edit, click the dropdown, click the value, click save). If I can get this working as I hope, it is only 2 clicks (click the action arrow, click the desired value). In a process which might be completed hundreds of times each day, saving the extra clicks will save a lot of time.

    I did manage to add the item in the rowactions array, and my event handler is now firing the javascript function. But the record isn't updating. Here is my recordlist.js so far:

    ({

        extendsFrom:'RecordListView',

        initialize:function(options){
            this._super("initialize",[options]);
            this.context.on('list:editcompleterow:fire',this.markItineraryComplete,this);
        },
       
        markItineraryComplete:function(){
            this.model.set("status_c", 'Complete');
            console.log("Save was attempted.");
        } 
    })
  • Francesca can correct me here as she is like the Sugar queen, but I think the problem is this:

    You are calling a "this.model.set" on a listview.  A listview isnt tied directly to the record in question and by extension, the record DB field itself.  A listview is merely a query result laid out in a row.  You're attempting to set the value of a returned query, not the actual record itself.  You can set the value of the row but that doesnt have any connection to the DB record.  Francesca's mass update hack is serving a purpose.  Mass update connects to the record in question and updates the values in the database.  You are simply attempting to change the text on the view.  

    I'm very partial to PHP whenever I can, but If you wanted to do this in 2 clicks, I would probably grab the GUID of the record in your function, pass it off to a php helper file through an AJAX call which updates the field through an SQL query or a bean->save() and still set the value of the list view.  the changed value on the listview is really kinda "fake" but gives the user the appearance that the value has changed.  The php helper file is what actually changes the DB value.  

  • You flatter me too much ted moriello, I am not a queen, merely a court jester at best 

    I believe you are correct that "this.model" is not the actual model on that row. (When in doubt I use console.log(this) to see what "this" really is.)

    If I remember correctly the rowaction should pass the model for the specific row as a parameter.

    You will also need to save the model explicitly:

    markItineraryComplete:function(model){
            model.set("status_c", 'Complete');
            model.save();
            console.log("Save was attempted.");
    } 

    I think you can also use a shorter version:

    markItineraryComplete:function(model){
            model.save({status_c: 'Complete'});
            console.log("Save was attempted.");
    } 

    You could add a confirmation to the user for good measure:

    markItineraryComplete:function(model){
            model.save({status_c: 'Complete'});
            app.alert.show('update_complete', {
              level: 'success',
              messages: 'Record marked as Complete',
              autoClose: true
            });
            console.log("Save was attempted.");
    } 

    Aaron Kerr Let us know if that works

    HTH,
    FrancescaS

     

  • Thanks so much for the reply. I spent this morning playing around, working to adjust the example you provided. About half an hour ago, I managed to get the following solution working:

    markItineraryComplete:function(model){ 
         var self = this,
               url = app.api.buildURL('itin1_Itinerary/'+model.id);
               params = {'status_c':'Complete'};
         app.api.call('update',url,params,{
              success: function(o){
                   model.set("status_c", 'Complete');
                   App.alert.show('message-id', {
                        level: 'success',
                        messages: 'Status Updated',
                        autoClose: true,
                        autoCloseDelay : 2200
                   });
              },
              error: function(e){
                   console.log("API Call Failed");
                   app.alert.show('error_while_row_update', {
                        level:'error',
                        title: app.lang.getAppString('ERR_INTERNAL_ERR_MSG'),
                        messages: app.lang.getAppString('ERR_HTTP_500_TEXT')
                   });
              },
         });
    },

    I was essentially making an API call, and then if it succeeded, I was also updating the model in the UI to reflect the change. However, I just tried the third example you provided, and it worked perfectly. I think I'll go with the option you provided, as it is cleaner from a code perspective and also responds a bit faster in the UI as it's not waiting for the API call to complete before updating the local model.

    Thanks again for the awesome assistance!

Reply
  • Thanks so much for the reply. I spent this morning playing around, working to adjust the example you provided. About half an hour ago, I managed to get the following solution working:

    markItineraryComplete:function(model){ 
         var self = this,
               url = app.api.buildURL('itin1_Itinerary/'+model.id);
               params = {'status_c':'Complete'};
         app.api.call('update',url,params,{
              success: function(o){
                   model.set("status_c", 'Complete');
                   App.alert.show('message-id', {
                        level: 'success',
                        messages: 'Status Updated',
                        autoClose: true,
                        autoCloseDelay : 2200
                   });
              },
              error: function(e){
                   console.log("API Call Failed");
                   app.alert.show('error_while_row_update', {
                        level:'error',
                        title: app.lang.getAppString('ERR_INTERNAL_ERR_MSG'),
                        messages: app.lang.getAppString('ERR_HTTP_500_TEXT')
                   });
              },
         });
    },

    I was essentially making an API call, and then if it succeeded, I was also updating the model in the UI to reflect the change. However, I just tried the third example you provided, and it worked perfectly. I think I'll go with the option you provided, as it is cleaner from a code perspective and also responds a bit faster in the UI as it's not waiting for the API call to complete before updating the local model.

    Thanks again for the awesome assistance!

Children
No Data