Jump to content

Open Invoices widget - Updated for 7.1


Recommended Posts

As you may have noticed, the open invoices widget was removed from whmcs in 7.1. While you can find and copy the old widget (hint, it's in your backup, under whmcs/addons/widgets/), the best approach is to write this in a forward thinking way. Rather than use sql_query functions that may not work with php 7.x , and probably won't work with future versions of WHMCS, this widget was written to mimic the functionality of the old while utilizing Capsule, which (hopefully) won't be going away any time soon.

 

Copy and paste the following into a php file in whmcs/modules/widgets/ . I used openinvoices.php , but you can use whatever you like.

If you run into problems, let me know, but this is pretty straightforward stuff.

<?php
//open invoices widget , rewritten for 7.1
//courtesy of https://www.whmcs.guru
//version 1.0.1

use Illuminate\Database\Capsule\Manager as Capsule;
add_hook('AdminHomeWidgets', 1, function() {
   return new InvoiceWidget();
});

/**
* Updated invoices widget
*/
class InvoiceWidget extends \WHMCS\Module\AbstractWidget
{
   protected $title = 'Open Invoices';
   protected $description = 'An overview of Open Invoices.';
   protected $weight = 150;
   protected $columns = 1;
   protected $cache = false;
   protected $cacheExpiry = 120;
   protected $requiredPermission = 'View Income Totals';

   public function getData()
   {
       return array();
   }

   public function generateOutput($data)
   {


       foreach (Capsule::table('tblinvoices') ->WHERE ('status', '=' , 'Unpaid')->get() as $invoice) {
           $invowner = $invoice->userid;
           $invid = $invoice->id;
           $invgenerated = $invoice->date;
           $invoicedue = $invoice->duedate;
           $invoiceamt = $invoice->total;
           $invoicemethod = $invoice->paymentmethod;
           $invlink = "invoices.php?action=edit&id=$invid";
           $userlink = "clientssummary.php?userid=$invowner";
           $invgenerated = $date = fromMySQLDate($invgenerated);
           $invoicedue = $date = fromMySQLDate($invoicedue);
           $invoicemethod = ucwords($invoicemethod);
           foreach(Capsule::table('tblclients')->WHERE ('id', '=', $invowner)->get() as $whoami)
           {
               $fname = $whoami->firstname;
               $lname = $whoami->lastname;
               $cname = $whoami->companyname;
               $theuser = "$fname $lname";
               $currencyid = $whoami->currency;
           }
           if (empty($currencyid))
           {
               //set to default currency
               $currencyid = '1';
           }
           foreach(Capsule::table('tblcurrencies')->WHERE ('id', '=', $currencyid)->get() as $currency)
           {
               $prefix = $currency->prefix;
               $suffix = $currency->suffix;

           }

           $theoutput  .= "
           <div class=\"row\">
           <div class=\"col-xs-1\">
           <div class=\"item\">
           <a href=\"$invlink\">$invid</a>
           </div>
           </div>
           <div class=\"col-sm-2\">
           <div class=\"item\">
           <a href=\"$userlink\">$theuser</a>
           </div>
           </div>
           <div class=\"col-xs-3\">
           <div class=\"item\">
           $invgenerated 
           </div>
           </div>

           <div class=\"col-xs-3\">
           <div class=\"item\">
           $invoicedue 
           </div>
           </div>

           <div class=\"col-xs-2\">
           <div class=\"item\">
           $prefix$invoiceamt $suffix
           </div>
           </div>

           <div class=\"col-sm-1\">
           <div class=\"item\">
           $invoicemethod
           </div>
           </div>

           </div>";
       }
return <<<EOF
<div class="widget-content-padded">

       <div class="row">

        <div class="col-xs-1">
        <div class="item">
        <strong>ID</strong>
        </div>
        </div>

        <div class="col-sm-2">
        <div class="item">
        <strong>User</strong>
        </div>
        </div>

        <div class="col-xs-3">
        <div class="item">
        <strong>Generated</strong>
        </div>
        </div>

        <div class="col-xs-3">
        <div class="item">
        <strong>Due Date</strong>
        </div>
        </div>

        <div class="col-xs-2">
        <div class="item">
        <strong>Amount</strong>
        </div>
        </div>

        <div class="col-sm-1">
        <div class="item">
        <strong>Method</strong>
        </div>
        </div>
       </div>
       $theoutput
       <br />

       <a href="invoices.php?status=Unpaid" class="btn btn-info btn-sm">View All »</a>
       </br>
       <p align = "center">Widget courtesy of <a href="https://www.whmcs.guru/"> WHMCS Guru</a></p>

</div>
EOF;
   }
}

Edited by twhiting9275
Link to comment
Share on other sites

i'm getting an error when adding it to our v7.1.1 dev...

 

Parse error: syntax error, unexpected end of file in /modules/widgets/openinvoices.php on line 149

if I replace your return block with the return block from the Hello World hook in the widget developer page, the dashboard loads with the widget containing "Hello World" - so I can only assume there is something in your return block that WHMCS doesn't like. :?:

Link to comment
Share on other sites

Original post updated. Turns out that I missed the unpaid status. Still haven't been able to find where you were able to find that error though. What version of php are you running? It's possible (though not likely) that's part of the issue.

thanks for the tweak, v1.0.1 is working without issues.

 

if it helps, I have a couple of suggested improvements..

 

1. wrap the return block of code with an IF statement to detect if $theoutput is empty (e.g there are no unpaid invoices)...

 

if (!empty($theoutput)) {
...
}

if there are no unpaid invoices, it seems pointless to output the table headings - and the above code will effectively minimise the widget to save space on the dashboard. :idea:

 

2. again to save space, I moved the button link to view the unpaid invoices to the header - you could do that by either adding a link to the widget title...

 

    protected $title = '<a href="invoices.php?status=Unpaid">Open Invoices</a>';

but that link might not be obvious to users, so you can add your previous button to the title (either as a button or link)...

 

    protected $title = 'Open Invoices <a href="invoices.php?status=Unpaid" class="btn btn-link btn-xs">View All »</a>';

 

dM9qxz8.png

 

one weird thing that I did spot is that it's very difficult to make these v7 widgets multilingual - with previous versions you could reference the $_adminlang, but with these widgets, it either ignores the value or it causes an error. :roll:

Link to comment
Share on other sites

  • WHMCS Technical Analyst

Great job on this @twhiting9275

 

One suggestion that might simplify the code somewhat is to use the model that's available for invoices (http://docs.whmcs.com/classes/7.1/WHMCS/Billing/Invoice.html). For example:

 

use WHMCS\Billing\Invoice;

$unpaidInvoices = Invoice::with('client')->unpaid()->get();
foreach ($unpaidInvoices as $invoice) {
   // ...
}

 

As well if you move that to the data method, you could take advantage of the caching.

 

@brian For language variables you should be using the translation helper available in WHMCS 6+: AdminLang::trans('some.variable')

 

-Eddy

Link to comment
Share on other sites

@brian For language variables you should be using the translation helper available in WHMCS 6+: AdminLang::trans('some.variable')

interesting - I don't ever recall reading about AdminLang::trans in any of the documentation or release notes... i'm familiar with Lang::trans, but wasn't aware there was an admin equivalent.

 

so if you wanted to change...

 

<strong>Due Date</strong>

you can't use the AdminLang within the output (as it causes an error), it seems you need to declare it as a variable first before the return and then use that variable later in the output...

 

$duedate = AdminLang::trans('fields.duedate');
<strong>$duedate</strong>

out of interest Eddy, what about $title - it seems to only accept a string and not a variable, so I can't see how you could make the widget title multilingual. :?:

Link to comment
Share on other sites

  • WHMCS Technical Analyst

The output is being built as a heredoc so you might find you can enclose it in curly braces {AdminLang::trans('xxx')} to have it work inline, otherwise defining as a variable first would be the only way.

 

For a title to use language variables, you would want to do that via a constructor method:

 

use AdminLang;

public function __construct()
{
   $this->title = AdminLang::trans('global.success');
}

 

HTH

-Eddy

Link to comment
Share on other sites

  • 3 weeks later...

If there's whitespace, that's going to be due to your browser's interpretation of the code itself, or how it's pasted into your editor. I literally just did a c&p from the code entry in my original post, and there's nothing there for whitespace.

 

That said, I've added this as an attachment to prevent that . Just change the extension from .txt to .php

open_invoices.txt

Link to comment
Share on other sites

  • 2 months later...

I removed whitespaces but now when I try access "edit admin roles" I'm getting

 

Catchable fatal error: Object of class Closure could not be converted to string in /home/admin/domains/domain.com/public_html/whmcs/admin/configadminroles.php on line 0

Link to comment
Share on other sites

if it helps, the version below is the one I have working in a v7.2beta dev, but it worked fine when it was v7.1 too...

 

 <?php
//open invoices widget , rewritten for 7.1
//courtesy of https://www.whmcs.guru
//version 1.0.1

use Illuminate\Database\Capsule\Manager as Capsule;
add_hook('AdminHomeWidgets', 1, function() {
   return new InvoiceWidget();
});

/**
* Updated invoices widget
*/
class InvoiceWidget extends \WHMCS\Module\AbstractWidget
{
   protected $title = 'Open Invoices <a href="invoices.php?status=Unpaid" class="btn btn-link btn-xs">View All »</a>';
   protected $description = 'An overview of Open Invoices.';
   protected $weight = 150;
   protected $columns = 1;
   protected $cache = false;
   protected $cacheExpiry = 120;
   protected $requiredPermission = 'View Income Totals';

   public function getData()
   {
       return array();
   }

   public function generateOutput($data)
   {


       foreach (Capsule::table('tblinvoices') ->WHERE ('status', '=' , 'Unpaid')->get() as $invoice) {
           $invowner = $invoice->userid;
           $invid = $invoice->id;
           $invgenerated = $invoice->date;
           $invoicedue = $invoice->duedate;
           $invoiceamt = $invoice->total;
           $invoicemethod = $invoice->paymentmethod;
           $invlink = "invoices.php?action=edit&id=$invid";
           $userlink = "clientssummary.php?userid=$invowner";
           $invgenerated = $date = fromMySQLDate($invgenerated);
           $invoicedue = $date = fromMySQLDate($invoicedue);
           $invoicemethod = ucwords($invoicemethod);
           foreach(Capsule::table('tblclients')->WHERE ('id', '=', $invowner)->get() as $whoami)
           {
               $fname = $whoami->firstname;
               $lname = $whoami->lastname;
               $cname = $whoami->companyname;
               $theuser = "$fname $lname";
               $currencyid = $whoami->currency;
           }
           if (empty($currencyid))
           {
               //set to default currency
               $currencyid = '1';
           }
           foreach(Capsule::table('tblcurrencies')->WHERE ('id', '=', $currencyid)->get() as $currency)
           {
               $prefix = $currency->prefix;
               $suffix = $currency->suffix;

           }

           $theoutput  .= "
           <div class=\"row\">
           <div class=\"col-xs-1\">
           <div class=\"item\">
           <a href=\"$invlink\">$invid</a>
           </div>
           </div>
           <div class=\"col-sm-2\">
           <div class=\"item\">
           <a href=\"$userlink\">$theuser</a>
           </div>
           </div>
           <div class=\"col-xs-3\">
           <div class=\"item\">
           $invgenerated 
           </div>
           </div>

           <div class=\"col-xs-3\">
           <div class=\"item\">
           $invoicedue 
           </div>
           </div>

           <div class=\"col-xs-2\">
           <div class=\"item\">
           $prefix$invoiceamt $suffix
           </div>
           </div>

           <div class=\"col-sm-1\">
           <div class=\"item\">
           $invoicemethod
           </div>
           </div>

           </div>";
       }

if (!empty($theoutput)) {
return <<<EOF
<div class="widget-content-padded">

       <div class="row">

           <div class="col-xs-1">
           <div class="item">
           <strong>ID</strong>
           </div>
           </div>

           <div class="col-sm-2">
           <div class="item">
           <strong>User</strong>
           </div>
           </div>

           <div class="col-xs-3">
           <div class="item">
           <strong>Generated</strong>
           </div>
           </div>

           <div class="col-xs-3">
           <div class="item">
           <strong>Due Date</strong>
           </div>
           </div>

           <div class="col-xs-2">
           <div class="item">
           <strong>Amount</strong>
           </div>
           </div>

           <div class="col-sm-1">
           <div class="item">
           <strong>Method</strong>
           </div>
           </div>
       </div>
       $theoutput
</div>
EOF;
}
}
}

Link to comment
Share on other sites

Widget is showing, but still when I try access Setup > Staff Management > Administrator Roles > ex. full administrator > edit i got

 

Catchable fatal error: Object of class Closure could not be converted to string in /home/admin/domains/domain/public_html/whmcs/admin/configadminroles.php on line 0

 

when I remove widget file from server I can edit admin roles, so there is some issue with widget code I guess.

Link to comment
Share on other sites

hmm, it's not something i'm seeing on v7.2b - what I am seeing is that I can't disable it by changing permissions... i'm sure that worked in v7.1 :?:

 

as a temporary fix, the v6 widget should still work in v7 - you'd just need to upload it and enable it for your admin role.

Link to comment
Share on other sites

  • 4 months later...
  • 3 weeks later...
  • 5 months later...

WHMCS 7.4.2

Some cosmetic changes (FireFox user) and weight adjustment, works for me. 

 

<?php
//open invoices widget , rewritten for 7.1
//courtesy of https://www.whmcs.com
//version 1.0.1

use Illuminate\Database\Capsule\Manager as Capsule;
add_hook('AdminHomeWidgets', 1, function() {
   return new InvoiceWidget();
});

/**
* Open Invoices widget
*/
class InvoiceWidget extends \WHMCS\Module\AbstractWidget
{
   protected $title = 'ICT Open Invoices <a href="invoices.php?status=Unpaid" class="btn btn-link btn-xs">View All »</a>';
   protected $description = 'An overview of Open Invoices.';
   protected $weight = 50;
   protected $columns = 1;
   protected $cache = false;
   protected $cacheExpiry = 120;
   protected $requiredPermission = 'View Income Totals';

   public function getData()
   {
       return array();
   }

   public function generateOutput($data)
   {


       foreach (Capsule::table('tblinvoices') ->WHERE ('status', '=' , 'Unpaid')->get() as $invoice) {
           $invowner = $invoice->userid;
           $invid = $invoice->id;
           $invgenerated = $invoice->date;
           $invoicedue = $invoice->duedate;
           $invoiceamt = $invoice->total;
           $invoicemethod = $invoice->paymentmethod;
           $invlink = "invoices.php?action=edit&id=$invid";
           $userlink = "clientssummary.php?userid=$invowner";
           $invgenerated = $date = fromMySQLDate($invgenerated);
           $invoicedue = $date = fromMySQLDate($invoicedue);
           $invoicemethod = ucwords($invoicemethod);
           foreach(Capsule::table('tblclients')->WHERE ('id', '=', $invowner)->get() as $whoami)
           {
               $fname = $whoami->firstname;
               $lname = $whoami->lastname;
               $cname = $whoami->companyname;
               $theuser = "$fname $lname";
               $currencyid = $whoami->currency;
           }
           if (empty($currencyid))
           {
               //set to default currency
               $currencyid = '1';
           }
           foreach(Capsule::table('tblcurrencies')->WHERE ('id', '=', $currencyid)->get() as $currency)
           {
               $prefix = $currency->prefix;
               $suffix = $currency->suffix;

           }

           $theoutput  .= "
           <div class=\"row\">
           <div class=\"col-xs-2\">
           <div class=\"item\">
           <a href=\"$invlink\">$invid</a>
           </div>
           </div>
           <div class=\"col-sm-4\">
           <div class=\"item\">
           <a href=\"$userlink\">$theuser</a>
           </div>
           </div>
           <div class=\"col-xs-3\">
           <div class=\"item\">
           $invgenerated 
           </div>
           </div>

           <div class=\"col-xs-3\">
           <div class=\"item\">
           $prefix$invoiceamt $suffix
           </div>
           </div>

           </div>";
       }

if (!empty($theoutput)) {
return <<<EOF
<div class="widget-content-padded">

       <div class="row">

           <div class="col-xs-2">
           <div class="item">
           <strong>ID</strong>
           </div>
           </div>
           
           <div class="col-sm-4">
           <div class="item">
           <strong>User</strong>
           </div>
           </div>

           <div class="col-xs-3">
           <div class="item">
           <strong>Generated</strong>
           </div>
           </div>

           <div class="col-xs-3">
           <div class="item">
           <strong>Amount</strong>
           </div>
           </div>
       </div>
       $theoutput
</div>
EOF;
}
}
}

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use & Guidelines and understand your posts will initially be pre-moderated