Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRM-21558 Add batch update via profile for cases #11411

Merged
merged 1 commit into from
Feb 18, 2018

Conversation

mattwire
Copy link
Contributor

@mattwire mattwire commented Dec 14, 2017

Overview

This adds "Update Multiple Cases" option to search tasks and allows you to batch update multiple cases in the same way as other entities.

Before

Not possible to batch update multiple cases via profile.

After

Select "Update multiple cases" from a search task and see:
localhost_8000_civicrm_case_search__qf_pickprofile_display true qfkey b35d8a28d086734fe945cb2911bbf73e_2307

The you see the profile:
localhost_8000_civicrm_case_search__qf_batch_display true qfkey b35d8a28d086734fe945cb2911bbf73e_2307


@eileenmcnaughton
Copy link
Contributor

@colemanw - this would be a good one for you to evaluate.... It probably would be possible to do as an extension too - although it would require some core tidy-ups to facilitate that

@colemanw
Copy link
Member

I think this is fine to go into core. @jamienovick can you ask someone on your team to review this please?

@mickadoo
Copy link
Contributor

@mattwire can you rebase to add the ticket number to the commit message?

Copy link
Contributor

@mickadoo mickadoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see how this change would be useful. I left a lot of comments on this, but so much of the code is boilerplate stuff from previous tasks. I can understand why you might not want to rewrite it. It's concerning there's so much duplicate code in the first place.

I tested this locally and it seems to work. One thing that might be worth noting is that when I created a custom fieldset for a certain case type, and then added that field to the profile used in batch editing, all cases will display the fields to edit the custom data, regardless of case type. However when you save the form only the correct case type will save the custom data.

*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
* $Id$
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is $Id$ supposed to be here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

parent::preProcess();

//get the contact read only fields to display.
$readOnlyFields = array_merge(array('sort_name' => ts('Name')),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For new PRs to master are we supposed to use the short array syntax [] since PHP 5.3 support is supposed to be dropped this month?


//get the contact read only fields to display.
$readOnlyFields = array_merge(array('sort_name' => ts('Name')),
CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a pretty long call could you make it a separate variable? It would also help explain what it is. You probably already checked, but is this variable accessible through Civi::settings, or any shorter method?

// initialize the task and row fields
parent::preProcess();

//get the contact read only fields to display.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after //

TRUE, NULL, FALSE, 'name', TRUE
)
);
//get the read only field data.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after //

@@ -2233,6 +2233,14 @@ public static function buildProfile(
$form->add('text', $name, $title, $attributes, $required);
$form->addRule($name, ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger');
}
elseif ($fieldName == 'case_status') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use strict comparison here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a whole load like that - really the whole if/elseif/else would be better as a switch but I'll leave that for someone else :-)

@@ -2514,6 +2522,11 @@ public static function setProfileDefaults(
if ($component == 'Activity') {
self::setComponentDefaults($fields, $componentId, $component, $defaults);
}

//Handling Case Part of the batch profile
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after //

@@ -2514,6 +2522,11 @@ public static function setProfileDefaults(
if ($component == 'Activity') {
self::setComponentDefaults($fields, $componentId, $component, $defaults);
}

//Handling Case Part of the batch profile
if (CRM_Core_Permission::access('CiviCase') && $component == 'Case') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strict comparison

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is consistent with the rest of the code so I won't change for now

@@ -3160,7 +3173,7 @@ public static function encodeGroupType($coreTypes, $subTypes, $delim = CRM_Core_
*/
public static function setComponentDefaults(&$fields, $componentId, $component, &$defaults, $isStandalone = FALSE) {
if (!$componentId ||
!in_array($component, array('Contribute', 'Membership', 'Event', 'Activity'))
!in_array($component, array('Contribute', 'Membership', 'Event', 'Activity', 'Case'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This array is just getting longer, it might be better as another variable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something for the future :-)

$profiles = CRM_Core_BAO_UFGroup::getProfiles($types, TRUE);

if (empty($profiles)) {
CRM_Core_Session::setStatus(ts("You will need to create a Profile containing the %1 fields you want to edit before you can use Update multiple cases. Navigate to Administer CiviCRM >> CiviCRM Profile to configure a Profile. Consult the online Administrator documentation for more information.", array(1 => $types[0])), ts('Update multiple records error'), 'error');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This information is not up to date, when I tried this PR branch I needed to go to Administer > Customize Data and Screens > Profiles

@mattwire
Copy link
Contributor Author

@colemanw @mickadoo @eileenmcnaughton So most of the comments raised by @mickadoo are because this is copied from all the other class implementations. There is some common refactoring I could attempt here though I've only got another couple of hours in the budget. Please bear with me and I'll see if I can fit it in.

@eileenmcnaughton
Copy link
Contributor

@mattwire my priority for any tidy up is that you share code where possible. I'd rather see a small amount of change to improve code sharing than a large amount of addressing over-long lines. Also any code that can be REMOVED is great.

From a merger POV I'd rather see a unit test over stylistic code changes and I'm not sure that anyone has reviewed this from a UI-test / UI-makes sense point of view.

@mattwire mattwire force-pushed the CRM-21558_case_batch_task branch 4 times, most recently from 07a328d to 0654056 Compare January 17, 2018 16:10
Copy link
Contributor

@mickadoo mickadoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating this @mattwire. I know you probably want to get this functionality in and don't have time to refactor the whole codebase. I wasn't aware of the level of boilerplate code. I left some more comments, but if the tests pass and someone with more experience is happy with it (I see @eileenmcnaughton looked over it) then I think it's fine

*/
class CRM_Case_Form_Task extends CRM_Core_Form {
class CRM_Case_Form_Task extends CRM_Core_Form_Task {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a fairly big tree of form classes, but from the naming at least it makes sense that Case_Form_Task extends from Core_Form_Task

*
* @return array $defaults
*/
public function setDefaultValues() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it just calls the parent then it's not required

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

$params = $this->exportValues();

if (!isset($params['field'])) {
CRM_Core_Session::setStatus(ts("No updates have been saved."), ts('Not Saved'), 'alert');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Single quotes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

unset($value['case_type']);

//Get the case status
$value['status_id'] = (CRM_Utils_Array::value('case_status', $value)) ? $value['case_status'] : CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $key, 'status_id');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm harping on about these long lines, with such long function arguments I know it's hard to keep the lines short. That said, here's an alternative for this line

//Get the case status
$daoClass = 'CRM_Case_DAO_Case';
$caseStatus = CRM_Utils_Array::value('case_status', $value);
if (!$caseStatus) {
  // default to existing status ID
  $caseStatus = CRM_Core_DAO::getFieldValue($daoClass, $key, 'status_id');
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is some personal preference involved there - depending how you edit things longer or shorter likes can be more readable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. But happy to clean up code where possible, just limited as to how much time I can spend on clean up at this stage - some of this could be done as smaller commits once we've made this (big) change. @mickadoo Thanks for the code snippet, I've added it to this PR.

$case = CRM_Case_BAO_Case::add($value);

// add custom field values
if (!empty($value['custom']) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One place where I actually think the line is too short 😄 both conditions fit on this line comfortably

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
}

CRM_Core_Session::setStatus(ts("Your updates have been saved."), ts('Saved'), 'success');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double quotes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

* since its used for things like send email
*/
public function setContactIDs() {
$this->_contactIds = &CRM_Core_DAO::getContactIDsFromComponent($this->_entityIds,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_contactIds is not a property of this class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

CRM_Utils_System::setTitle($this->_title);

$this->addDefaultButtons(ts('Save'));
$this->_fields = CRM_Core_BAO_UFGroup::getFields($ufGroupId, FALSE, CRM_Core_Action::VIEW);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_fields is not a property of this class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

* @return array
* list of errors to be posted back to the form
*/
public static function formRule($fields) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not sure of the purpose of this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default it is pointless :-) However, it makes more sense once CRM_Core_Form_Task_PickProfile is used for other entities - eg. Contacts: https://github.com/civicrm/civicrm-core/blob/master/CRM/Contact/Form/Task/PickProfile.php#L133

We could improve this/make it consistent in a future PR when we clean up some of the other entities to use the shared classes

$this->set('ufGroupId', $params['uf_group_id']);

// also reset the batch page so it gets new values from the db
$this->controller->resetPage('Batch');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My IDE tells me the resetPage method doesn't exist. Is it just because it's not type-hinted correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I get a few of those (PHPStorm) but I see them elsewhere in the codebase too. If you have a way to "fix" them let me know, otherwise let's ignore it for now :-)

@mickadoo
Copy link
Contributor

@mattwire Just a general comment, I'm not sure about the convention for commits. I notice your commit message doesn't include the ticket number in it, should it be there?

Also, everything is squashed into a single commit. That makes it a bit harder to check things later on, since the commit message for all the changes won't give a good reason for any single change. It's also a bit harder when reviewing since I don't know what specific changes were made since my last review.

I know there are different opinions on this so I'm not saying you should definitely change it, just wondering how you feel about it, or the stance from the community in general.

Copy link
Contributor

@mickadoo mickadoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me 💯

There are still some warnings on codestyle from Jenkins

@eileenmcnaughton
Copy link
Contributor

eileenmcnaughton commented Jan 22, 2018

Agree re the JIRA in the commit message. WRT one vs many commits - my opinion is each commit should stand alone - if adding new code we don't want to see your drafts in years to come but if doing a refactor then as long as each step stands alone it can be much easier to read smaller commits. (ie. the final commits should reflect what will read well in the future)

@eileenmcnaughton
Copy link
Contributor

So interpretting the above to how it maps to a review of this PR

(CiviCRM Review Template DEL-1.0)

  • JIRA (r-jira)
    • PASS : The PR has a JIRA reference. (Or: it does not need one.)
  • Test results (r-test)
    • UNREVIEWED
  • Code quality (r-code)
    • PASS: @mickadoo has reviewed the code quality
  • Documentation (r-doc)
    • UNREVIEWED
  • Maintainability (r-maint)
    • UNREVIEWED
  • Run it (r-run)
    • UNREVIEWED
  • User impact (r-user)
    • UNREVIEWED
  • Technical impact (r-tech)
    • UNREVIEWED:
    • COMMENTS: This is mostly new code so should not cause regressions but there are some changes to case task form (which some testing would quickly establish the validity of) and some fields are removed from UFField::getAvailableFields which I don't know the impact of from my quick look at the code

/**
* Build all the data structures needed to build the form.
*/
public function preProcess() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole function could be a shared function couldn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole function could be a shared function couldn't it?
@eileenmcnaughton Not sure quite what you mean here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I must have thought it was on a child class

@mattwire
Copy link
Contributor Author

Comments from client:

Seems to work fine on the test site. No comments on the usability - matches the standard bulk update using profile approach.
Using a Profile is a good idea I think as it will provide some flexibility (I'd just been thinking of it from a status perspective).

@mattwire
Copy link
Contributor Author

COMMENTS: This is mostly new code so should not cause regressions but there are some changes to case task form (which some testing would quickly establish the validity of) and some fields are removed from UFField::getAvailableFields which I don't know the impact of from my quick look at the code

The fields are case specific fields - dates and case status - and act as a filter as to what fields can be edited in a batch update. I've tested that they all update correctly. I think there were also earlier issues which have been resolved by @yashodha with her batch fields PRs that have been merged over the last couple of months.

@mattwire
Copy link
Contributor Author

@colemanw Do you have time to do a quick review of this? (And merge if you are happy?)

@colemanw
Copy link
Member

Code has been thoroughly reviewed and runtime testing was performed by @mickadoo so I think we're good to merge.

@colemanw colemanw merged commit 555acb0 into civicrm:master Feb 18, 2018
@mattwire
Copy link
Contributor Author

@colemanw Thankyou :-)

@mlutfy mlutfy added this to the 4.7.32 milestone Mar 6, 2018
@eileenmcnaughton
Copy link
Contributor

@mattwire @colemanw @mickadoo this caused 2 regressions not picked up in review #11928 #11926

there was some identification of this risk in my summary of the analysis. It's probably worth thinking about how this could have been picked up
#11411 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants