Answers.Body: <p>The way I ended up having to do this was simple, but a less-than-ideal UX. My UF is Drupal. </p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-05-26T12:35:55.363Z
Answers.LastActivityDate: 2016-05-26T12:35:55.363Z
Answers.UserDisplayName: chrisfromredfin
Answers.Body: <p>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.</p>
  
  <p>The root issue here is that the coding convention has relied too heavily on static functions. For example, if you grep the codebase for <code>CRM_Contribute_Form_Task_Invoice</code>, you'll find snippets like this one (from <code>CRM/Contribute/Form/Task/Invoice.php</code>):</p>
  
  <pre><code>CRM_Contribute_Form_Task_Invoice::printPDF($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);
  </code></pre>
  
  <p>If you could change that line to point to your own version of <code>printPDF()</code>, 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.</p>
  
  <pre><code>// Example: Use a service container
  Civi::service('invoice_printer')-&gt;print($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);
  
  // Example: Use a hook
  CRM_Utils_Hook::printInvoicePdf($contributionIDs, $params, $contactId, CRM_Core_DAO::$_nullObject);
  </code></pre>
  
  <p>Either of those formulations would make it possible for extensions to change the implementation of <code>printPDF()</code>. The bad news: either way, you'd to have to refactor the existing implementation to match a different coding convention.</p>
  
  <p>A few links that might be interesting on the general topic of statics, services, and dependency-injection:</p>
  
  <ul>
  <li><a href="https://www.imarc.com/blog/dependency-injection" rel="nofollow">https://www.imarc.com/blog/dependency-injection</a></li>
  <li><a href="http://fabien.potencier.org/what-is-dependency-injection.html" rel="nofollow">http://fabien.potencier.org/what-is-dependency-injection.html</a></li>
  <li><a href="https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_container" rel="nofollow">https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_container</a></li>
  <li><a href="https://civicrm.org/blog/totten/the-static-is-dead-long-live-the-static" rel="nofollow">https://civicrm.org/blog/totten/the-static-is-dead-long-live-the-static</a></li>
  </ul>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-05-24T14:58:52.073Z
Answers.Text: 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?
Answers.UserDisplayName: chrisfromredfin
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-05-23T22:21:22.647Z
Answers.LastActivityDate: 2016-05-23T22:21:22.647Z
Answers.UserDisplayName: Tim Otten
Answers.Body: <p>I was lead-astray by this post, combined with my own dumb thinking.
  I'm not sure why the hook I finally found would not work for OP's problem, but have not looked at the bulk action. I kind of suspect the existing hook would have worked as well.</p>
  <p>I wanted to override the invoice template and I traced <code>civicrm/contribute/invoice</code> to <code>printPDF()</code>.</p>
  <p>&quot;I want to override the template to download an invoice. Hmmm... I don't see any hooks to override printPDF... Ah, Tim says I should create a new hook in Core, Okay!&quot;</p>
  <p>Got through creating a new hook for <code>printPdfTemplateParams</code> to be raised each time before <code>printPDF()</code> calls <code>CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams);</code>.
  Then when I went to get more details on what I need to alter in the params and finally realized that <code>CRM_Core_BAO_MessageTemplate</code> raises <code>hook_civicrm_alterMailParams</code> before loading the template. Even if you are just downloading a PDF, not sending an email. Doh!</p>
  <p>If anyone wants the code for the new hook, I'm not submitting a PR at this time. <a href="https://github.com/ginkgomzd/civicrm-core/tree/hook-print-pdf-params" rel="nofollow noreferrer">https://github.com/ginkgomzd/civicrm-core/tree/hook-print-pdf-params</a></p>
  <p>Created a PR to update the dev docs for <code>hook_civicrm_alterMailParams</code>: <a href="https://lab.civicrm.org/documentation/docs/dev/-/merge_requests/1054" rel="nofollow noreferrer">https://lab.civicrm.org/documentation/docs/dev/-/merge_requests/1054</a></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-01-19T16:32:28.563Z
Answers.LastActivityDate: 2023-01-19T16:59:26.653Z
Answers.LastEditDate: 2023-01-19T16:59:26.653Z
Answers.UserDisplayName: ginkgoMZD
Body: <p>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. </p>
  
  <p>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.</p>
  
  <p>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).</p>
  
  <p>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.</p>
  
  <p>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). :)</p>
  
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2016-05-23T19:16:35.130Z
Comments.Text: Want to confirm what version you are (or want to be) using
Comments.UserDisplayName: petednz - fuzion
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2016-05-23T19:22:14.970Z
Comments.Text: We are currently one point release back; but 4.7 on Drupal 7.
Comments.UserDisplayName: chrisfromredfin
Tags: <developer><invoice>
Title: How to Override System Workflow message template variables, single and batch
ThreadText: # How to Override System Workflow message template variables, single and batch
  
  (Asked by: username_0 on 2016-05-23)
  
  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). :)
  
  **Tags:** developer, invoice
  
  > **Comments:**
  > **username_1**: Want to confirm what version you are (or want to be) using  
  > **username_0**: We are currently one point release back; but 4.7 on Drupal 7.  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_0 on 2016-05-26. Score: 1 | Accepted answer
  
  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.
  
  
  ---
  
  ### Answer by username_2 on 2016-05-23. Score: 1
  
  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:
  
  * <https://www.imarc.com/blog/dependency-injection>
  * <http://fabien.potencier.org/what-is-dependency-injection.html>
  * <https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_container>
  * <https://civicrm.org/blog/totten/the-static-is-dead-long-live-the-static>
  
  > **Comments:**
  > **username_0**: 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?  
  
  ---
  
  ### Answer by username_3 on 2023-01-19. Score: 1
  
  I was lead-astray by this post, combined with my own dumb thinking.
  I'm not sure why the hook I finally found would not work for OP's problem, but have not looked at the bulk action. I kind of suspect the existing hook would have worked as well.
  
  I wanted to override the invoice template and I traced `civicrm/contribute/invoice` to `printPDF()`.
  
  "I want to override the template to download an invoice. Hmmm... I don't see any hooks to override printPDF... Ah, Tim says I should create a new hook in Core, Okay!"
  
  Got through creating a new hook for `printPdfTemplateParams` to be raised each time before `printPDF()` calls `CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams);`.
  Then when I went to get more details on what I need to alter in the params and finally realized that `CRM_Core_BAO_MessageTemplate` raises `hook_civicrm_alterMailParams` before loading the template. Even if you are just downloading a PDF, not sending an email. Doh!
  
  If anyone wants the code for the new hook, I'm not submitting a PR at this time. <https://github.com/username_3/civicrm-core/tree/hook-print-pdf-params>
  
  Created a PR to update the dev docs for `hook_civicrm_alterMailParams`: <https://lab.civicrm.org/documentation/docs/dev/-/merge_requests/1054>
  
  
  ---
  
  