Jump to content

NETLINK

Members
  • Posts

    125
  • Joined

  • Last visited

  • Days Won

    7

Everything posted by NETLINK

  1. No problem! Nikko, just in case you are registering .de domains, Namecheap offer those for under €9 per year, and Namecheap does have an API for Blesta, although, I spotted a couple of bugs the other day. It should still be usable, though.
  2. Here ya go... https://github.com/NETLINK/Blesta-UniversalDomains
  3. Hi Nikko, I'll release my code when I get a chance, and I'll update this thread. All the best, Julian
  4. For reference: https://github.com/NETLINK/Blesta-Namesilo
  5. Thank you! I'm moving on to the admin functions now. I'm going to create an email template and add a link to the admin Account Actions that can be used to send the statements via email.
  6. Okay, I made some more improvements... The currency information is now retrieved via the Invoice::invoicedCurrencies() method. If a client has invoices in multiple currencies, then the client will be able to select the currency from the dropdown box shown in the screenshot. Client can select between 6 month, 12 month, or 24 month statements. Client can select whether to open the statement in the browser window or force a PDF download. Company name, address and telephone number are retrieved via Configure::get( "Blesta.company ). The client will see an alert for overdue invoices on the Account Statement window, with a Pay Now button. I think those are the main features. I'm attaching 2 screenshots. One is the Account Statement screen and the other is the rendered PDF statement. I'll update the repo shortly (20 minutes or so). If anyone wants to test it out on their own system, any feedback is welcome, of course.
  7. Thanks, Paul! Not yet, but I was actually planning to put some up. I just need to update the client view template, as I still had some stuff for debugging in there.
  8. Thanks! Yeah, it can definitely be improved. I just wanted to get it working and the ball rolling and I like the idea of collaboration, so, you're all welcome to share/collaborate/fork/commit/pull I put the plugin on to a new branch called "plugin": https://github.com/NETLINK/BlestaClientStatements
  9. I did. Just for the client section for now. I'll work on the admin section later. It needs a little bit more work - like, for example, maybe storing some of the settings in the database rather than a config file, and support for i18n and L10n and such, but it looks like it's working so far.
  10. I'm almost done integrating this with the client dashboard. I have it working right now, I just need to clean up the code and push it to the Git repo. Should have that done tonight. Then, you can give it a test drive.
  11. Yeah, I'll work on it when I get a moment. I can probably hook it into the client interface. The mail template will take longer, I think. For me, personally, the mail thing is more important because most of my clients never log in to Blesta. They get their invoices via email and then either pay by credit car or cheque. But I'll see what I can do about hooking the statement into the client menu. I don't think it should take too long to do that.
  12. Hello! Is there a way in Blesta to refresh a plugin without having to uninstall and reinstall? Like, when I'm developing a plugin... I make my changes, upload the files, but in order to see my changes, I seemingly have to uninstall and then reinstall the plugin. Is there a quicker way to see my changes live? Thanks!
  13. Okay, guys. Here we go... https://github.com/NETLINK/BlestaClientStatements I've tested it a bit and it seems to be working well. Right now, it's a standalone script. Would just have to figure out how to create the access key and maybe create an email template with the link and access hash included. It's an MD5 hash made up of: [ClientID][ClientFirstName ClientLastName (ClientCompany)][ClientEmailAddress][salt] Line 48: $md5 = md5( $d['id'] . $d['client'] . $d['email'] . $salt ); But obviously, it would be a lot better to have it integrated with Blesta, like a plugin, as Naja suggested. Looking forward to thoughts/ideas.
  14. I'll try and make this work for Blesta tonight. I'll put it on GitHub. If you all like it, perhaps the team can develop it into a core module.
  15. A few years ago, WHMCS didn't have client statements. Not sure if it does now. So I wrote this code and uploaded it to one of my servers. It's a set of custom mysql queries to calculate the amount owed, amount paid and amount of credits. Kind of like a simple bank statement - amount in and amount out, with balance. It takes all the information and uses FPDF to generate a downloadable PDF document. I then made an email template in WHMCS that included the link to the page on my server that served my script. I created MD5 hash from some of the user information, to check against, so that clients wouldn't have to log in when opening the link. When the link is openend, the user gets a download prompt from their browser, asking if they want to download or open the PDF document. The filename is "Statement_YYYY-mm-dd". All these variables can be changed easily, of course, including the date range. I'm attaching the code and a screenshot of what the statement looks like. It's definitely not perfect and probably has some flaws, but maybe it can be modified to work with Blesta. <?php ini_set( 'display_errors', 1 ); ## Path to logo $logo = 'logo_vector_2.jpg'; ## Company/organistion information # Name $org_name = 'Acme'; # Address $org_addr_1 = "Address 1"; $org_addr_2 = "Address 2"; $org_addr_3 = "Ireland"; # Contact information 1 $org_contact_1 = 'Phone: +353 xxxx'; # Tax ID $org_tax_id = "VAT ID: 1234567M"; ## Database information $dbhost = "example.com"; $dbuser = "xxx"; $dbpass = "xxx"; $dbname = "xxx"; $uid = isset( $_REQUEST['uid'] ) ? (int)$_REQUEST['uid'] : NULL; $key = isset( $_REQUEST['key'] ) ? $_REQUEST['key'] : NULL; if ( empty( $uid ) || empty( $key ) ) { die( 'Error: no user ID or access key supplied. The link you entered may have expired.' ); } $mysqli = new mysqli( $dbhost, $dbuser, $dbpass, $dbname ); if ( $mysqli->connect_errno ) { echo "Failed to connect to MySQL: ( " . $mysqli->connect_errno . " ) " . $mysqli->connect_error; exit; } $result = $mysqli->query( "SELECT *, CONCAT( firstname, ' ', lastname, ' (', companyname, ')' ) AS client FROM tblclients WHERE id='$uid' LIMIT 1" ); if ( $result->num_rows !== 1 ) { die( 'Error: we could not access any account data with the information that was provided.' ); } $d = $result->fetch_assoc(); $result->free_result(); $md5 = md5( $d['id'] . ' : ' . $d['client'] . ' : ' . $d['email'] ); if ( $key !== $md5 ) { die( 'Error: the access key provided could not be validated. The link you followed may have expired.' ); } $date = date( 'Y-m-d', mktime( 0, 0, 0, date( 'm' ), date( 'd' ), date( 'Y' ) -6 ) ); $result = $mysqli->query( "SELECT SUM(total) AS total FROM tblinvoices WHERE userid = '$uid' AND DATE_FORMAT(`date`, '%Y-%m-%d') < '$date' AND status != 'Cancelled'" ); $balforward = $result->fetch_array()[0]; $result->free_result(); $result = $mysqli->query( "SELECT SUM(amountin) FROM tblaccounts WHERE userid='$uid' AND DATE_FORMAT(`date`, '%Y-%m-%d') < '$date'" ); $balforward -= $result->fetch_array()[0]; $result->free_result(); $result = $mysqli->query( "SELECT SUM(amount) FROM tblcredit WHERE clientid='$uid' AND DATE_FORMAT(`date`, '%Y-%m-%d') < '$date'" ); $balforward += $result->fetch_array()[0]; $result->free_result(); $result = $mysqli->query( " (SELECT id, ( subtotal + tax ) AS `amount`, DATE_FORMAT(`date`, '%Y-%m-%d') AS `date`, 'Invoice' AS txtype FROM tblinvoices WHERE userid='$uid' AND `date` >= '$date' AND status != 'Cancelled' AND ( subtotal + tax ) != 0) UNION (SELECT id, amountin AS `amount`, DATE_FORMAT(`date`, '%Y-%m-%d') AS `date`, 'Payment' AS txtype FROM tblaccounts WHERE userid='$uid' AND `date` >= '$date') UNION (SELECT id, amount AS `amount`, DATE_FORMAT(`date`, '%Y-%m-%d') AS `date`, 'Credit' AS txtype FROM tblcredit WHERE clientid='$uid' AND `date` >= '$date') ORDER BY `date` ASC " ); $i = array(); $n = 0; $balance = $balforward; while( $r = $result->fetch_assoc() ) { $i[$n] = $r; if ( $r['txtype'] === 'Invoice' ) { $balance += $r['amount']; } else { $balance -= $r['amount']; } $i[$n]['balance'] = number_format( $balance, 2, '.', ',' ); $n++; } $result->free_result(); $mysqli->close(); define( 'FPDF_FONTPATH', '../includes/font/' ); require( '../includes/fpdf.php' ); $pdf = new FPDF(); //Column titles $header = array( 'Date', 'Type', 'ID', 'Amount', 'Balance' ); //Data loading //$data=$pdf->LoadData($i); //$pdf->AddPage(); //$pdf->BasicTable($header,$data); //$pdf->AddPage(); //$pdf->ImprovedTable($header,$data); $pdf->AddPage(); $pdf->Image( $logo, 0, 0 ); $pdf->SetFont( 'Arial', 'B', 10 ); $pdf->Cell( 120, 6, '', 0, 0, 'L' ); $pdf->Cell( 0, 6, $org_name, 0, 0, 'L' ); $pdf->Ln(); $pdf->SetFont( 'Arial', '', 10 ); $pdf->Cell( 120, 6, '', 0, 0, 'L' ); $pdf->Cell( 0, 6, $org_addr_1, 0, 0, 'L' ); $pdf->Ln(); $pdf->Cell( 120, 6, '', 0, 0, 'L' ); $pdf->Cell( 0, 6, $org_addr_2, 0, 0, 'L' ); $pdf->Ln(); $pdf->Cell( 120, 6, '', 0, 0, 'L' ); $pdf->Cell( 0, 6, $org_addr_3, 0, 0, 'L' ); $pdf->Ln( 8 ); $pdf->Cell( 120, 6, '', 0, 0, 'L' ); $pdf->Cell( 0, 6, $org_contact_1, 0, 0, 'L' ); $pdf->Ln(); $pdf->Cell( 120, 6, '', 0, 0, 'L' ); $pdf->Cell( 0, 6, $org_tax_id, 0, 0, 'L' ); $pdf->Ln( 18 ); $pdf->SetFont( 'Arial', '', 14 ); $pdf->Cell( 80, 6, 'Account Statement', 0, 0, 'L' ); $pdf->Cell( 80, 6, gmdate( 'Y-m-d'), 0, 0, 'R' ); $pdf->Ln(10); $pdf->SetFont( 'Arial', 'B', 10 ); $pdf->Cell( 0, 6, $d['client'] ); $pdf->Ln(); $pdf->SetFont( 'Arial', '', 10 ); $pdf->Cell( 0, 6, $d['address1'] . ', '. $d['city'] . ', ' . $d['state'] ); $pdf->Ln( 20 ); //$pdf->FancyTable($header,$data); $data = $i; //Colors, line width and bold font $pdf->SetFillColor( 90, 135, 190 ); $pdf->SetTextColor( 255 ); $pdf->SetDrawColor( 128, 0, 0 ); $pdf->SetLineWidth( .3 ); $pdf->SetFont( '', 'B', 11 ); //Header $w = array( 30, 40, 30, 40, 40 ); for ( $i = 0; $i < count( $header ); $i++ ) $pdf->Cell( $w[$i], 7, $header[$i], 1, 0, 'C', true ); $pdf->Ln(); //Color and font restoration $pdf->SetFillColor( 224, 235, 255 ); $pdf->SetTextColor( 0 ); $pdf->SetFont( '' ); //Data $fill = false; $pdf->Cell( 30, 6, $date, 0, 0, 'L', $fill ); $pdf->Cell( 40, 6, 'Balance Forward', 0, 0, 'C', $fill ); $pdf->Cell( 30, 6, '-', 0, 0, 'L', $fill ); $pdf->Cell( 40, 6, number_format( $balforward, 2, '.', ',' ), 0, 0, 'R', $fill ); $pdf->Cell( 40, 6, number_format( $balforward, 2, '.', ',' ), 0, 0, 'R', $fill ); $pdf->Ln(); foreach( $data as $row ) { $pdf->Cell( 30, 6, $row['date'], 0, 0, 'L', $fill ); $pdf->Cell (40, 6, $row['txtype'], 0, 0, 'C', $fill ); $pdf->Cell( 30, 6, $row['id'], 0, 0, 'L', $fill ); if ( $row['txtype'] === 'Payment' || $row['txtype'] === 'Credit' ) { $pdf->Cell( 40, 6, '-' . $row['amount'], 0, 0, 'R', $fill ); } else { $pdf->Cell( 40, 6, $row['amount'], 0, 0, 'R', $fill ); } $pdf->Cell( 40, 6, $row['balance'], 0, 0, 'R', $fill ); $pdf->Ln(); $fill =! $fill; } $pdf->Cell( array_sum( $w ), 0, '', 'T' ); $pdf->Ln( 20 ); $pdf->Cell( 80, 6, 'Your current account balance is:', 0, 0, 'L' ); $pdf->SetFont( 'Arial', 'B', 13 ); $pdf->Cell( 80, 6, number_format( $balance, 2, '.', ',' ), 0, 0, 'L' ); $pdf->Ln( 8 ); $pdf->SetFont( 'Arial', '', 8 ); $pdf->MultiCell( 150, 6, 'Please note this is not an official invoice. Errors and omissions excepted. Please refer to your invoice for transaction details and tax information. Please contact us with any questions.' ); $pdf->Ln(5); $pdf->Ln(1); header( "Cache-Control: no-cache, must-revalidate" ); // HTTP/1.1 header( "Expires: Sat, 26 Jul 1997 05:00:00 GMT" ); // Date in the past header( "Content-type: application/pdf" ); header( 'Content-Disposition: attachment; filename="Statement_' . gmdate( 'Y_m_d' ) . '.pdf"' ); $pdf->Output( 'Statement_' .gmdate( 'Y_m_d' ) . '.pdf', 'D' );
  16. I'm getting the same thing. I posted without a CSRF token because I was seeing if I could post the domain variable to the domain order form "domain=example.com". So, I get the CSRF check failing, but shouldn't there be an error message, instead of throwing an exception because of a missing file? I'm guessing if debugging was off, it would be just a blank page.
  17. Hello! Stripe has a "Description" field for transactions. I was wondering if it's possible to get a description entered for this field - for example a list of invoice numbers the transaction was used to pay.
  18. To elaborate further, it wasn't the module's fault that only one NS was returned. This was just how the domain was (mis)configured. However, adding this code in the module would prevent fatal errors in Blesta in this situation, as rare as it may be.
  19. The expected output returned by the module is a list of nameservers associated with the domain, which is converted into a PHP array: https://reseller.enom.com/interface.asp <?xml version="1.0" encoding="utf-8"?> <interface-response> <dns>ns1.example.com</dns> <dns>ns2.example.com</dns> <UseDNS></UseDNS> However, the actual result I got was just 1 nameserver, which resulted in a fatal PHP error: Invalid argument supplied for foreach() https://reseller.enom.com/interface.asp <?xml version="1.0" encoding="utf-8"?> <interface-response> <dns>ns1.example.com</dns> <UseDNS></UseDNS> I added "&& is_array( $data->dns )", which checks if $data->dns is an array, and prevents a fatal error in this scenario.
  20. The following change will prevent an error if, for some reason, Enom is only returning one nameserver, which happened me today. if ($response->status() == "OK") { $data = $response->response(); if ( isset( $data->dns ) && is_array( $data->dns ) ) { foreach ($data->dns as $ns) $vars->ns[] = $ns; } } Blesta Version 3.6.1 Enom (ver 2.2.1) File: components/modules/enom/enom.php Line: 1151
  21. You're right. But I'm not sure if it technically indicates it directly. See point 2.1 on the following resource: http://www.taxdonut.co.uk/tax/vat/vat-problems/common-vat-problems Also here: https://www.e-conomic.com/accountingsoftware/accounting-words/pro-forma-invoice And after a ruling by the Federal Finance Court of Germany (Az. V R 44/09), it is recommended to clearly state on pro-forma invoices that the document does not authorize any kind of pre-tax deductions. And here it is directly from HMRC (see very bottom in bold): http://www.hmrc.gov.uk/manuals/vatrecmanual/VATREC9010.htm I can't seem to find an official reference that's valid for all EU member states. Adding it to the terms would resolve the issue. But I think it has the potential to cause confusion if it were mentioned on both Pro Forma and VAT invoices. Perhaps an extra text area under settings for adding terms specific to PF invoices? Or maybe a separate text-area for each type of invoice. That way, it would give more flexibility and wouldn't have to be hardcoded. Since non-EU countries may use PF invoicing but not VAT.
×
×
  • Create New...