From ba19b1c569b8ad8b5f776e5a2cbf7b6d2fa8d34b Mon Sep 17 00:00:00 2001 From: Focuslinkstech <45756999+Focuslinkstech@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:30:24 +0100 Subject: [PATCH] `Added new functionality to Invoice class: generate invoice PDF, save to database, and send email. Also, updated language file with new translations.` --- system/autoload/Invoice.php | 208 +++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 84 deletions(-) diff --git a/system/autoload/Invoice.php b/system/autoload/Invoice.php index 96f558e6..70dd1561 100644 --- a/system/autoload/Invoice.php +++ b/system/autoload/Invoice.php @@ -88,84 +88,49 @@ class Invoice }, $template); } - public static function sendInvoice($userId, $status = "Unpaid") + /** + * Send invoice to user + * + * @param int $userId + * @param array $invoice + * @param array $bills + * @param string $status + * @param string $invoiceNo + * @return bool + */ + + public static function sendInvoice($userId, $invoice = [], $bills = [], $status = "Unpaid", $invoiceNo = "INV-" . Package::_raid()) { global $config, $root_path, $UPLOAD_PATH; - if (empty($config['currency_code'])) { - $config['currency_code'] = '$'; - } + // Set default currency code + $config['currency_code'] ??= '$'; $account = ORM::for_table('tbl_customers')->find_one($userId); + self::validateAccount($account); - if (!$account) { - _log("Failed to send invoice: User not found"); - sendTelegram("Failed to send invoice: User not found"); - return false; - } - - $invoice = ORM::for_table("tbl_transactions")->where("username", $account->username)->find_one(); - + // Fetch invoice if not provided + $invoice = $invoice ?: ORM::for_table("tbl_transactions")->where("username", $account->username)->find_one(); if (!$invoice) { - _log("Failed to send invoice: Transaction not found"); - sendTelegram("Failed to send invoice: Transaction not found"); - return false; + throw new Exception("Transaction not found for user: {$userId}"); } - [$additionalBills, $add_cost] = User::getBills($account->id); - - $invoiceItems = [ - [ - 'description' => $invoice->plan_name, - 'details' => 'Monthly Subscription', - 'amount' => (float) $invoice->price - ] - ]; - $subtotal = (float) $invoice->price; - - if ($add_cost > 0 && $invoice->routers != 'balance') { - foreach ($additionalBills as $description => $amount) { - if (is_numeric($amount)) { - $invoiceItems[] = [ - 'description' => $description, - 'details' => 'Additional Bill', - 'amount' => (float) $amount - ]; - $subtotal += (float) $amount; - } else { - _log("Invalid bill amount for {$description}: {$amount}"); - } - } + // Get additional bills if not provided + if (empty($bills)) { + [$bills, $add_cost] = User::getBills($account->id); } - $tax_rate = (float) ($config['tax_rate'] ?? 0); + $invoiceItems = self::generateInvoiceItems($invoice, $bills, $add_cost); + $subtotal = array_sum(array_column($invoiceItems, 'amount')); $tax = $config['enable_tax'] ? Package::tax($subtotal) : 0; - $total = ($tax > 0) ? $subtotal + $tax : $subtotal + $tax; + $tax_rate = $config['tax_rate'] ?? 0; + $total = $subtotal + $tax; - $token = User::generateToken($account->id, 1); - if (!empty($token['token'])) { - $tur = ORM::for_table('tbl_user_recharges') - ->where('customer_id', $account->id) - ->where('namebp', $invoice->plan_name); + $payLink = self::generatePaymentLink($account, $invoice, $status); + $logo = self::getCompanyLogo($UPLOAD_PATH, $root_path); - switch ($status) { - case 'Paid': - $tur->where('status', 'on'); - break; - default: - $tur->where('status', 'off'); - break; - } - $turResult = $tur->find_one(); - $payLink = $turResult ? '?_route=home&recharge=' . $turResult['id'] . '&uid=' . urlencode($token['token']) : '?_route=home'; - } else { - $payLink = '?_route=home'; - } - - $UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH); - $logo = (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) ? $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png?' . time() : $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.default.png'; $invoiceData = [ - 'invoice' => "INV-" . Package::_raid(), + 'invoice' => $invoiceNo, 'fullname' => $account->fullname, 'email' => $account->email, 'address' => $account->address, @@ -182,31 +147,90 @@ class Invoice 'payment_link' => $payLink ]; - if (!isset($invoiceData['bill_rows']) || empty($invoiceData['bill_rows'])) { - _log("Invoice Error: Bill rows data is empty."); + if (empty($invoiceData['bill_rows'])) { + throw new Exception("Bill rows data is empty."); } $filename = self::generateInvoice($invoiceData); - - if ($filename) { - $pdfPath = "system/uploads/invoices/{$filename}"; - - try { - Message::sendEmail( - $account->email, - "Invoice for Account {$account->fullname}", - "Please find your invoice attached", - $pdfPath - ); - return true; - } catch (\Exception $e) { - _log("Failed to send invoice email: " . $e->getMessage()); - sendTelegram("Failed to send invoice email: " . $e->getMessage()); - return false; - } + if (!$filename) { + throw new Exception("Failed to generate invoice PDF"); } - return false; + $pdfPath = "system/uploads/invoices/{$filename}"; + self::saveToDatabase($filename, $account->id, $invoiceData, $total); + + try { + Message::sendEmail( + $account->email, + "Invoice for Account {$account->fullname}", + "Please find your invoice attached", + $pdfPath + ); + return true; + } catch (\Exception $e) { + throw new Exception("Failed to send invoice email: " . $e->getMessage()); + } + } + + private static function validateAccount($account) + { + if (!$account) { + throw new Exception("User not found"); + } + if (!$account->email || !filter_var($account->email, FILTER_VALIDATE_EMAIL)) { + throw new Exception("Invalid user email"); + } + } + + private static function generateInvoiceItems($invoice, $bills, $add_cost) + { + $items = [ + [ + 'description' => $invoice->plan_name, + 'details' => 'Monthly Subscription', + 'amount' => (float) $invoice->price + ] + ]; + + if ($add_cost > 0 && $invoice->routers != 'balance') { + foreach ($bills as $description => $amount) { + if (is_numeric($amount)) { + $items[] = [ + 'description' => $description, + 'details' => 'Additional Bill', + 'amount' => (float) $amount + ]; + } else { + _log("Invalid bill amount for {$description}: {$amount}"); + } + } + } + return $items; + } + + private static function generatePaymentLink($account, $invoice, $status) + { + $token = User::generateToken($account->id, 1); + if (empty($token['token'])) { + return '?_route=home'; + } + + $tur = ORM::for_table('tbl_user_recharges') + ->where('customer_id', $account->id) + ->where('namebp', $invoice->plan_name); + + $tur->where('status', $status === 'Paid' ? 'on' : 'off'); + $turResult = $tur->find_one(); + + return $turResult ? '?_route=home&recharge=' . $turResult['id'] . '&uid=' . urlencode($token['token']) : '?_route=home'; + } + + private static function getCompanyLogo($UPLOAD_PATH, $root_path) + { + $UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH); + return file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png') ? + $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png?' . time() : + $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.default.png'; } private static function generateBillRows($items, $currency, $subtotal, $tax_rate, $tax, $total) @@ -247,6 +271,22 @@ class Invoice return $html; } - + private static function saveToDatabase($filename, $customer_id, $invoiceData, $total) + { + $invoice = ORM::for_table('tbl_invoices')->create(); + $invoice->number = $invoiceData['invoice']; + $invoice->customer_id = $customer_id; + $invoice->fullname = $invoiceData['fullname']; + $invoice->email = $invoiceData['email']; + $invoice->address = $invoiceData['address']; + $invoice->status = $invoiceData['status']; + $invoice->due_date = $invoiceData['due_date']; + $invoice->filename = $filename; + $invoice->amount = $total; + $invoice->data = json_encode($invoiceData); + $invoice->created_at = date('Y-m-d H:i:s'); + $invoice->save(); + return $invoice->id; + } }