My client has advanced invoice requirements. In the past we had a custom Drupal module and a custom template file. Added a link "Print Invoice" to the individual contributions Form.tpl and then did a custom template file in Drupal using CiviCRM API to get the relevant data. For Batch, we added a new custom task and called our custom function in a loop.

But now, of course, CiviCRM has invoicing capability baked in, which is awesome! We edited the System Workflow Message but we had a lot of trouble using crmapi calls, and using Smarty. We also see that {php} is deprecated in Smarty 3.

So then I had the thought to just override the callback for civicrm/contribute/invoice. I did that by implementing hook_civicrm_xmlMenu() and providing a custom Menu.xml that uses my own class. My class just extends CRM_Contribute_Form_Task_Invoice and then I just pull in the full objects whose data I need in my own printPDF(). (Specifically, if it's an event, I want to show event start dates, etc - and if it's a membership I want to show them their membership expiration date, etc).

The problem is that the batch invoice processor seems to be hardcoded to use CRM_Contribute_Form_Task_Invoice, and I can't find a way to override it. I've tried the same approach (overriding civicrm/contribute/search in Menu.xml) but that barfs in so many places. I've looked at hook_preProcess and changing the _attributes, etc. I've tried adding my own custom task to the dropdown on the contribute search page, but then it barks about a missing Invoice.tpl for my custom implementation.

Long story short, I'm just not 100% sure on the best way to proceed, and of course I'd rather not just hack the core code, but would rather extend it somehow. We've had a past history of nervousness to upgrade CiviCRM, and I want to not feel like we're in that same boat after this upgrade (to Drupal 7 / CiviCRM 4.7). :)

  • Want to confirm what version you are (or want to be) using – petednz - fuzion May 23 '16 at 19:16
  • We are currently one point release back; but 4.7 on Drupal 7. – chrisfromredfin May 23 '16 at 19:22

The short answer is that you can't change that from an extension, but you can change it by patching core. Unfortunately, this requires a change in coding-convention which isn't a trivial one-line patch -- it requires understanding some other layers/concepts and changing multiple files.

The root issue here is that the coding convention has relied too heavily on static functions. For example, if you grep the codebase for CRM_Contribute_Form_Task_Invoice, you'll find snippets like this one (from CRM/Contribute/Form/Task/Invoice.php):

CRM_Contribute_Form_Task_Invoice::printPDF($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);

If you could change that line to point to your own version of printPDF(), you'd be ready to go. But it's hard-coded -- there's no way for an extension to change it. The good news: there are better patterns which are more flexible, e.g.

// Example: Use a service container
Civi::service('invoice_printer')->print($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);

// Example: Use a hook
CRM_Utils_Hook::printInvoicePdf($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);

Either of those formulations would make it possible for extensions to change the implementation of printPDF(). The bad news: either way, you'd to have to refactor the existing implementation to match a different coding convention.

A few links that might be interesting on the general topic of statics, services, and dependency-injection:

  • OK, that's understandable (to an extent :) ). Which makes me think, couldn't I somehow prototype this as a new action from the dropdown from the contribution search? If so, how might I actually do that? – chrisfromredfin May 24 '16 at 14:58

The way I ended up having to do this was simple, but a less-than-ideal UX. My UF is Drupal.

I used Views to build a view of contributions, to somewhat mimic the native contribution search. (Specifically I added in status of pending, and is pay later), to find contributions for which invoices likely needed to be generated. I grabbed a few less columns.

I then added in civicrm_entity to the mix, so that I could use Views Bulk Operations in Drupal. The action I chose was "pass the id's as arguments to a page," and then I wrote a custom menu callback for that page that grabs the ids and passes them into my overridden printPDF() call. That call natively supports being passed multiple ID's, so it "just worked" to print multiple invoices into a single PDF.

The downside is that my client needs to go to a special menu item I built for him when he wants to do batch invoicing, but that's livable in our particular use case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.