Jump to content
twhiting9275

Open Invoices widget - Updated for 7.1

Recommended Posts

Posted (edited)

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
  • Thanks 1

Share this post


Link to post
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. :?:

Share this post


Link to post
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.

Share this post


Link to post
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:

  • Like 1

Share this post


Link to post
Share on other sites

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

  • Like 1

Share this post


Link to post
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. :?:

  • Like 1

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

I get a HTTP 500 error. I cant see anything in the error.log.

 

Any Idea?

Edited by J-B

Share this post


Link to post
Share on other sites

Some Whitespace was the error!

 

Thanks for this Widget!

Edited by J-B

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Parse error: syntax error, unexpected '<<' (T_SL) in /home/admin/domains/domain.com/public_html/whmcs/modules/widgets/OpenInvoices.php on line 103

 

Any help to fix this?

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
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;
}
}
}

  • Thanks 1

Share this post


Link to post
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.

Share this post


Link to post
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.

Share this post


Link to post
Share on other sites

WHMCS 7.2.3

 

I created and uploaded OpenInvoices.php but it doesn't appear.

Is there something more I need to do? Or is 7.2.3 not supported?

 

Thanks

Share this post


Link to post
Share on other sites

It does work on v7.2.3. Just need a little tweak on the layout but it works like a charm! ^_^

Thank you, guys!

Share this post


Link to post
Share on other sites
4 hours ago, Patty said:

It does work on v7.2.3. Just need a little tweak on the layout but it works like a charm! ^_^

Thank you, guys!

What tweaks did you need to use to get this working?

Share this post


Link to post
Share on other sites

Like less space between columns so it won't need the horizontal scroll bar... Things like that. ;)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×

Important Information

By using this site, you agree to our Terms of Use & Guidelines