We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 44580
    • 189 Posts
    I have a requirement to create an invoice run process. It works like this:

    (all steps happen at the back-end)

    - Admin clicks on a button: "Generate Invoices"
    - This sets off a process that selects all users that are members of a particular group (from Moduser) and creates an invoice header and a standard invoice detail line. This is identical for each user for every run.
    - The Invoice header/ detail information is then displayed in a grid for review.
    - Admin can then selectively modify individual invoice records by adding additional lines (to cater for discounts etc). These lines are predetermined and are selected from a dropdown.
    - Any invoice line can be edited after generation to change default quantity and price.
    - Once the Admin is happy with the new invoice run, he clicks on a "Send invoices" button and the invoices are updated in the database and emailed out.

    I'm using MIXdb and I think I have all the pieces I need, but I don't know how to put it all together.

    I have my two buttons defined (in core/components/add/migxconfigs/grid/grid.config.inc.php):
    $gridactionbuttons['createinvoices']['text'] = "'Generate Invoices'";
    $gridactionbuttons['createinvoices']['handler'] = 'this.createInvoices';
    $gridactionbuttons['createinvoices']['scope'] = 'this';
    
    $gridfunctions['this.createInvoices'] = "
    createInvoices: function() {
    
        }
    ";
    
    $gridactionbuttons['sendinvoices']['text'] = "'Send Invoices'";
    $gridactionbuttons['sendinvoices']['handler'] = 'this.sendInvoices';
    $gridactionbuttons['sendinvoices']['scope'] = 'this';
    
    $gridfunctions['this.sendInvoices'] = "
    sendInvoices: function() {
    
        }
    ";

    At the moment all this does is display the buttons.

    I have written two snippets that I've used to test the functionality I need:

    Snippet1 (used to generate invoices):
    <?php
    //-----------------------------------------------
    // First, get all the relevant Parent details
    //-----------------------------------------------
    $modx->addPackage('add',MODX_CORE_PATH.'components/add/model/','add_');
    
    $c = $modx->newQuery('modUser');
    //$c->innerJoin ('modUserProfile','Profile');
    $c->leftJoin ('ParentDetails','ParentDetail','`ParentDetail`.`parent_id` = `modUser`.`id`');
    $c->leftJoin ('Items','Item','`ParentDetail`.`hourly_rate` = `Item`.`item_price`');
    $c->leftJoin ('modUserGroupMember','UserGroupMembers','`UserGroupMembers`.`member` = `modUser`.`id`');
    $c->innerJoin ('modUserGroup','UserGroup','`UserGroupMembers`.`user_group` = `UserGroup`.`id`');
    $c->leftJoin ('modUserGroupRole','UserGroupRole','`UserGroupMembers`.`role` = `UserGroupRole`.`id`');
    $c->where(array(
       'active' => true,
       'UserGroupMembers.user_group' => 3, // This is the Parent_ug
       'UserGroupMembers.role' => '1',
       ));
    $c->select (array('modUser.id','Item.id as id2','ParentDetail.hourly_rate'));
    
    $parents = $modx->getCollection('modUser',$c);
    
    //-----------------------------------------------
    // Next, get the invoice term details
    //-----------------------------------------------
    
    $d = $modx->newQuery('TermCalendar');
    $d->select('id,term,term_year');
    $d->where(array('from_date:>=' => strftime('%Y-%m-%d 00:00:00')));
    $d->sortby('from_date');
    $d->limit(1);
    
    $_term = $modx->getCollection('TermCalendar',$d);
    
    // Set the invoice date
    $invoice_date = date('Y-m-d');
    
    // Set the quantity
    $qty = 8;
    
    //-----------------------------------------------
    // Process the queries
    //-----------------------------------------------
    
    foreach ($_term as $t) {
       $term_desc = "Term ".$t->get('term').", ".$t->get('term_year');
       $term_id = $t->get('id');
    }
    
    // create invoices
    foreach ($parents as $p) {
       $addinvoice = $modx->newObject('Invoice');
       $addinvoice->fromArray(array(
          'user_id' => $p->get('id'),
          'term_id' => $term_id,
          'invoice_date' => $invoice_date,
          //'balance' => $p->get('hourly_rate') * $qty,
       ));
       $addinvoice->save();
    
       $pk = $addinvoice->getPrimaryKey();
    
       // create invoice line items
       $addline = $modx->newObject('InvoiceLines');
       $addline->fromArray(array(
          'invoice_id' => $pk,
          'item_id' => $p->get('id2'),
          'quantity' => $qty,
       ));
       $addline->save();
    }
    
    return;


    Snippet 2 (to finalise invoices and email them):
    <?php
    //-----------------------------------------------
    // This snippet formats invoices and emails them
    //-----------------------------------------------
    
    $modx->addPackage('add',MODX_CORE_PATH.'components/add/model/','add_');
    
    $c = $modx->newQuery('Invoice');
    $c->where(array(
       'due_date' => NULL, // For invoices not yet sent
       ));
    $invoices = $modx->getCollection('Invoice',$c);
    
    // Next, get the invoice term details
    $d = $modx->newQuery('TermCalendar');
    $d->select('id,term,term_year');
    $d->where(array('from_date:>=' => strftime('%Y-%m-%d 00:00:00')));
    $d->sortby('from_date');
    $d->limit(1);
    
    $_term = $modx->getCollection('TermCalendar',$d);
    foreach ($_term as $t) {
       $term_desc = "Term ".$t->get('term').", ".$t->get('term_year');
    }
    
    // Set various dates
    $date_sent = date('Y-m-d');
    $due_date = date('Y-m-d', strtotime("+30 days"));
    $inv_date_sent = date('d/m/Y');
    $inv_due_date = date('d/m/Y', strtotime("+30 days"));
    
    foreach ($invoices as $i) {
       $total = 0;
       $out_header ='';
       $out_line = '';
       $out_total = '';
       $out_footer = '';
       $inv = $modx->getObject('Invoice', array('id' => $i->get('id')));
       $user = $modx->getCollection('modUserProfile', array('internalKey' => $i->get('user_id')));
       foreach ($user as $u) {
       $user_name = $u->get('fullname');
       $user_address1 = $u->get('address');
       $user_address2 = $u->get('city');
       $user_email = $u->get('email');
       }
       $lines = $inv->getMany('InvoiceLines');
       foreach ($lines as $l) {
          $qty = $l->get('quantity');
          $it = $modx->getObject('Items', array('id' => $l->get('item_id')));
          $item_desc = $it->get('item_desc');
          $it_price = $it->get('item_price');
          $line_sum = $it_price * $qty;
          $total = $total + $line_sum;
          $out_line .= $modx->getChunk('InvoiceLineOdd',array(
          'line_desc' => $item_desc,
          'line_qty' => $qty,
          'line_price' => $it_price,
          'line_total' => $line_sum,
          ));
       }
       $inv->set('balance',$total);
       $inv->set('due_date',$due_date);
       $inv->set('date_sent',$date_sent);
       $inv->set('num_times_sent',1);
       if ($inv->save() == false) {
         echo 'Failed to save!';
       }
       $out_header = $modx->getChunk('InvoiceHeader',array(
       'inv_no' => $i->get('id'),
       'inv_due_date' => $inv_due_date,
       'inv_date_sent' => $inv_date_sent,
       'user_name' => $user_name,
       'user_address1' => $user_address1,
       'user_address2' => $user_address2,
       ));
       $out_total = $modx->getChunk('InvoiceTotals',array(
       'total' => $total,
       'total_disc' => $total - (($total * 5)/100),
       ));
       $out_footer = $modx->getChunk('InvoiceFooter',array());
    
       $message =  $out_header . $out_line . $out_total . $out_footer;
    
       //echo $message;
    
       // --- mail stuff ---
       $modx->getService('mail', 'mail.modPHPMailer');
       $modx->mail->set(modMail::MAIL_BODY,$message);
       $modx->mail->set(modMail::MAIL_FROM,'[email protected]');
       $modx->mail->set(modMail::MAIL_FROM_NAME,'HomeCourt Advantage');
       $modx->mail->set(modMail::MAIL_SUBJECT,'Invoice for ' . $term_desc);
       $modx->mail->address('to', $user_email);
       $modx->mail->address('reply-to','[email protected]');
       $modx->mail->setHTML(true);
       if (!$modx->mail->send()) {
           $modx->log(modX::LOG_LEVEL_ERROR,'An error occurred while trying to send the Invoice email: '.$modx->mail->mailer->ErrorInfo);
       }
       $modx->mail->reset();
    }
    
    return;


    Now I'm stuck. I want to assign the functionality of these snippets to the two buttons I've created. If I understand correctly I need to turn the snippets into processors and add some code to the button definition but I am unsure as to how to do this. Guidance would be appreciated.
      • 4172
      • 5,888 Posts
      the grid.config.inc.php - part would look like that:

      <?php
      
      $gridactionbuttons['createinvoices']['text'] = "'Generate Invoices'";
      $gridactionbuttons['createinvoices']['handler'] = 'this.createInvoices';
      $gridactionbuttons['createinvoices']['scope'] = 'this';
       
      $gridfunctions['this.createInvoices'] = "
      createInvoices: function() {
          var _this=this;
          Ext.Msg.confirm(_('warning') || '','This will create Ivoices. Are you sure?',function(e) {
              if (e == 'yes') {    
                  MODx.Ajax.request({
                      url: _this.config.url
                      ,params: {
                          action: 'mgr/migxdb/process'
                          ,processaction: 'createinvoices'                     
                          ,configs: _this.config.configs
                          ,resource_id: _this.config.resource_id
                          ,co_id: '[[+config.connected_object_id]]'                
                          ,reqConfigs: '[[+config.req_configs]]'
                      }
                      ,listeners: {
                          'success': {fn:function(r) {
                              _this.refresh();
                          },scope:_this}
                      }
                  });
              }
          }),this;           
          return true; 
          }
      ";
       
      $gridactionbuttons['sendinvoices']['text'] = "'Send Invoices'";
      $gridactionbuttons['sendinvoices']['handler'] = 'this.sendInvoices';
      $gridactionbuttons['sendinvoices']['scope'] = 'this';
       
      $gridfunctions['this.sendInvoices'] = "
      sendInvoices: function() {
          var _this=this;
          Ext.Msg.confirm(_('warning') || '','This will send Ivoices. Are you sure?',function(e) {
              if (e == 'yes') {    
                  MODx.Ajax.request({
                      url: _this.config.url
                      ,params: {
                          action: 'mgr/migxdb/process'
                          ,processaction: 'sendinvoices'                     
                          ,configs: _this.config.configs
                          ,resource_id: _this.config.resource_id
                          ,co_id: '[[+config.connected_object_id]]'                
                          ,reqConfigs: '[[+config.req_configs]]'
                      }
                      ,listeners: {
                          'success': {fn:function(r) {
                              _this.refresh();
                          },scope:_this}
                      }
                  });
              }
          }),this;           
          return true;  
          }
      ";
      


      then create the two processors under

      core/components/add/processors/mgr/default/createinvoices.php

      and

      core/components/add/processors/mgr/default/sendinvoices.php

      with your snippet-codes

      your might want to add some permission-checks at the top of the code
      and you will need a line like that at the end:

      return $modx->error->success();


      that should be all, what you need, for your additional grid action buttons




        -------------------------------

        you can buy me a beer, if you like MIGX

        http://webcmsolutions.de/migx.html

        Thanks!
        • 44580
        • 189 Posts
        Thanks Bruno. As ever, you've gone the extra mile. I'll check this out and report back.
          • 44234
          • 219 Posts
          Bruno,

          I don't have a MIGX package setup, just a custom MIGX Config. Is it still possible to add action buttons that run php scripts and if so how?

          Thanks
            Find me on Twitter, GitHub or Google+
            • 4172
            • 5,888 Posts
            you only need to create this files to have your own button and processor

            core/components/{yourpackage}/migxconfigs/grid/grid.config.inc.php
            and
            core/components/{yourpackage}/processors/mgr/default/{yourprocessor}.php

            and put {yourpackage} into the packageName - setting

            Then MIGX will find your custom-button and your processor
              -------------------------------

              you can buy me a beer, if you like MIGX

              http://webcmsolutions.de/migx.html

              Thanks!
              • 44234
              • 219 Posts
              Great thanks Bruno
                Find me on Twitter, GitHub or Google+