function stats_generate()
 {
     log_write("debug", "phone_home", "Executing stats_generate()");
     /*
     	Application Details
     */
     $this->stats["app_name"] = $GLOBALS["config"]["app_name"];
     $this->stats["app_version"] = $GLOBALS["config"]["app_version"];
     /*
     	Server Details
     */
     $this->stats["server_app"] = $_SERVER["SERVER_SOFTWARE"];
     $this->stats["server_php"] = phpversion();
     /*
     	Configuration Information
     */
     // account information
     $this->stats["subscription_support"] = sql_get_singlevalue("SELECT value FROM config WHERE name='SUBSCRIPTION_SUPPORT' LIMIT 1");
     $this->stats["subscription_id"] = sql_get_singlevalue("SELECT value FROM config WHERE name='SUBSCRIPTION_ID' LIMIT 1");
     // check account ID
     if (!$this->stats["subscription_id"]) {
         // if no subscription id exists, then this must be running the open source version
         // we should create an ID so we can track requests from this server, we do this by
         // aggregating the server name and the instance name (if any) however we then hash
         // for privacy reasons.
         $this->stats["subscription_id"] = md5($_SERVER["SERVER_NAME"] . $_SESSION["user"]["instance"]["id"]);
         $sql_obj = new sql_query();
         $sql_obj->string = "UPDATE config SET value='" . $this->stats["subscription_id"] . "' WHERE name='SUBSCRIPTION_ID' LIMIT 1";
         $sql_obj->execute();
         log_write("debug", "phone_home", "Generated new subscription ID of " . $this->stats["subscription_id"] . "");
     }
     return 1;
 }
 function get_tax_details($id)
 {
     log_debug("taxes_manage_soap", "Executing get_tax_details({$id})");
     if (user_permissions_get("accounts_taxes_view")) {
         $obj_tax = new tax();
         // sanitise input
         $obj_tax->id = @security_script_input_predefined("int", $id);
         if (!$obj_tax->id || $obj_tax->id == "error") {
             throw new SoapFault("Sender", "INVALID_INPUT");
         }
         // verify that the ID is valid
         if (!$obj_tax->verify_id()) {
             throw new SoapFault("Sender", "INVALID_ID");
         }
         // load data from DB for this tax
         if (!$obj_tax->load_data()) {
             throw new SoapFault("Sender", "UNEXPECTED_ACTION_ERROR");
         }
         // to save SOAP users from having to do another lookup to find out what the chart name is,
         // we fetch the details here
         if ($obj_tax->data["chartid"]) {
             $obj_tax->data["chartid_label"] = sql_get_singlevalue("SELECT CONCAT_WS('--', code_chart, description) as value FROM account_charts WHERE id='" . $obj_tax->data["chartid"] . "'");
         }
         // return data
         $return = array($obj_tax->data["name_tax"], $obj_tax->data["taxrate"], $obj_tax->data["chartid"], $obj_tax->data["chartid_label"], $obj_tax->data["taxnumber"], $obj_tax->data["description"]);
         return $return;
     } else {
         throw new SoapFault("Sender", "ACCESS_DENIED");
     }
 }
Exemple #3
0
 function page_output()
 {
     // fetch variables
     $this->id = @security_script_input('/^[0-9]*$/', $_GET["id"]);
     // create customer object
     $this->obj_customer = new customer();
     $this->obj_customer->id = $this->id;
     // define the navigation menu
     $this->obj_menu_nav = new menu_nav();
     $this->obj_menu_nav->add_item("Customer's Details", "page=customers/view.php&id=" . $this->id . "", TRUE);
     if (sql_get_singlevalue("SELECT value FROM config WHERE name='MODULE_CUSTOMER_PORTAL' LIMIT 1") == "enabled") {
         $this->obj_menu_nav->add_item("Portal Options", "page=customers/portal.php&id=" . $this->id . "");
     }
     $this->obj_menu_nav->add_item("Customer's Journal", "page=customers/journal.php&id=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Attributes", "page=customers/attributes.php&id_customer=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Orders", "page=customers/orders.php&id_customer=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Invoices", "page=customers/invoices.php&id=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Credit", "page=customers/credit.php&id_customer=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Services", "page=customers/services.php&id=" . $this->id . "");
     if ($this->obj_customer->verify_reseller() == 1) {
         $this->obj_menu_nav->add_item("Reseller's Customers", "page=customers/reseller.php&id_customer=" . $this->obj_customer->id . "");
     }
     if (user_permissions_get("customers_write")) {
         $this->obj_menu_nav->add_item("Delete Customer", "page=customers/delete.php&id=" . $this->id . "");
     }
     // required pages
     $this->requires["javascript"][] = "include/customers/javascript/addedit_customers.js";
     $this->requires["javascript"][] = "include/customers/javascript/addedit_customer_contacts.js";
     $this->requires["css"][] = "include/customers/css/addedit_customer.css";
 }
 function page_output()
 {
     // fetch variables
     $this->id = @security_script_input('/^[0-9]*$/', $_GET["id"]);
     $this->journalid = @security_script_input('/^[0-9]*$/', $_GET["journalid"]);
     $this->action = @security_script_input('/^[a-z]*$/', $_GET["action"]);
     $this->type = @security_script_input('/^[a-z]*$/', $_GET["type"]);
     // create customer object
     $this->obj_customer = new customer();
     $this->obj_customer->id = $this->id;
     // define the navigiation menu
     $this->obj_menu_nav = new menu_nav();
     $this->obj_menu_nav->add_item("Customer's Details", "page=customers/view.php&id=" . $this->id . "");
     if (sql_get_singlevalue("SELECT value FROM config WHERE name='MODULE_CUSTOMER_PORTAL' LIMIT 1") == "enabled") {
         $this->obj_menu_nav->add_item("Portal Options", "page=customers/portal.php&id=" . $this->id . "");
     }
     $this->obj_menu_nav->add_item("Customer's Journal", "page=customers/journal.php&id=" . $this->id . "", TRUE);
     $this->obj_menu_nav->add_item("Customer's Attributes", "page=customers/attributes.php&id_customer=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Orders", "page=customers/orders.php&id_customer=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Invoices", "page=customers/invoices.php&id=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Credit", "page=customers/credit.php&id_customer=" . $this->id . "");
     $this->obj_menu_nav->add_item("Customer's Services", "page=customers/services.php&id=" . $this->id . "");
     if ($this->obj_customer->verify_reseller() == 1) {
         $this->obj_menu_nav->add_item("Reseller's Customers", "page=customers/reseller.php&id_customer=" . $this->obj_customer->id . "");
     }
     $this->obj_menu_nav->add_item("Delete Customer", "page=customers/delete.php&id=" . $this->id . "");
 }
Exemple #5
0
 function render_html()
 {
     print "<h3>PROGRAM MANUALS</h3>";
     print "<p>This page lists all the program manual available for download.</p>";
     // standard user guide
     print "<br><br>";
     print "<h3>USER GUIDE</h3>";
     print "<p>If you are looking for information about using and configuring the Amberdms Billing System, download the user guide using the button below</p>";
     print "<a class=\"button\" target=\"new\" href=\"help/manual/amberdms_billing_system_userguide.pdf\">Download User Guide</a><br>";
     print "<br>";
     // administration guides
     // (these options differ depending on where you are running the application)
     $subscription_support = sql_get_singlevalue("SELECT value FROM config WHERE name='SUBSCRIPTION_SUPPORT' LIMIT 1");
     switch ($subscription_support) {
         case "hosted":
         case "hosted_phone":
             print "<br><br>";
             print "<h3>SYSADMIN MANUALS</h3>";
             print "<p>You are running the hosted version of the Amberdms Billing System, so no installation or upgrades are required. However, if you are interested in deploying the Amberdms Billing System open source releases elsewhere, you can download the installation manuals from our website.</p>";
             print "<a target=\"new\" class=\"button\" href=\"http://www.amberdms.com/?cms=opensource_billing\">Installation Instructions</a><br>";
             print "<br>";
             break;
         case "none":
         case "opensource":
         default:
             print "<br><br>";
             print "<h3>SYSADMIN MANUALS</h3>";
             print "<p>The following manuals are intended for system administrators installing or upgrading this program.</p>";
             print "<a class=\"button\" target=\"new\" href=\"help/manual/amberdms_billing_system_install_supported.pdf\">Installation on Supported Platforms</a><br><br>";
             print "<a class=\"button\" target=\"new\" href=\"help/manual/amberdms_billing_system_install_manual.pdf\">Manual Installation for Unsupported Platforms</a><br>";
             print "<br>";
             break;
     }
     // developer manual
     print "<br><br>";
     print "<h3>DEVELOPER MANUALS</h3>";
     print "<p>The following manual are intended for developers who want to write their own programs capable of talking to the Amberdms Billing\n\t\t\tSystem via the SOAP API, people interested in the code internals of the Amberdms Billing System or engineers wanting to intergrate\n\t\t\ttheir monitoring scripts with the service usage functions.</p>";
     print "<a class=\"button\" target=\"new\" href=\"help/manual/amberdms_billing_system_SOAP_API.pdf\">SOAP API Developer Documentation</a><br>";
     print "<br>";
     print "<a class=\"button\" target=\"new\" href=\"help/manual/amberdms_billing_system_service_usage_collectors.pdf\">Service Usage Collectors/Integration Documentation</a><br>";
     print "<br>";
     // mailing lists
     print "<br><br>";
     print "<h3>MAILING LISTS</h3>";
     print "<p>The following mailing lists may also be useful for finding out additional information, upcomming product features or dicussing\n\t\t\tdevelopment details with the Amberdms programmers.</p>";
     print "<table cellpadding=\"5\">";
     print "<tr>";
     print "<td><b>General User Discussion List</b></td>";
     print "<td><a class=\"button\" href=\"http://lists.amberdms.com/mailman/listinfo/amberdms-bs\">Sign Up</a></td>";
     print "<td><a class=\"button\" href=\"http://lists.amberdms.com/pipermail/amberdms-bs/\">Archives</a></td>";
     print "</tr>";
     print "<tr>";
     print "<td><b>Developers Mailing List</b></td>";
     print "<td><a class=\"button\" href=\"http://lists.amberdms.com/mailman/listinfo/amberdms-bs-devel\">Sign Up</a></td>";
     print "<td><a class=\"button\" href=\"http://lists.amberdms.com/pipermail/amberdms-bs-devel/\">Archives</a></td>";
     print "</tr>";
     print "</table>";
 }
function quotes_calc_duedate($date)
{
    log_debug("inc_quotes_details", "Executing quotes_calc_duedate({$date})");
    // get the terms
    $terms = sql_get_singlevalue("SELECT value FROM config WHERE name='ACCOUNTS_TERMS_DAYS'");
    // break up the date, and reconfigure
    $date_array = explode("-", $date);
    $timestamp = mktime(0, 0, 0, $date_array[1], $date_array[2] + $terms, $date_array[0]);
    // generate the date
    return date("Y-m-d", $timestamp);
}
Exemple #7
0
 function check_requirements()
 {
     // verify that customer exists
     if (!$this->obj_customer->verify_id()) {
         log_write("error", "page_output", "The requested customer (" . $this->obj_customer->id . ") does not exist - possibly the customer has been deleted.");
         return 0;
     }
     // ensure that the portal module is enabled
     if (sql_get_singlevalue("SELECT value FROM config WHERE name='MODULE_CUSTOMER_PORTAL' LIMIT 1") != "enabled") {
         log_write("error", "page_output", "MODULE_CUSTOMER_PORTAL is disabled, enable it if you wish to adjust customer portal configuration options.");
         return 0;
     }
     return 1;
 }
Exemple #8
0
 function page_output()
 {
     // fetch key service details
     $this->id = @security_script_input('/^[0-9]*$/', $_GET["id"]);
     $this->service_type = sql_get_singlevalue("SELECT service_types.name as value FROM services LEFT JOIN service_types ON service_types.id = services.typeid WHERE services.id='" . $this->id . "' LIMIT 1");
     // define the navigiation menu
     $this->obj_menu_nav = new menu_nav();
     $this->obj_menu_nav->add_item("Service Details", "page=services/view.php&id=" . $this->id . "");
     $this->obj_menu_nav->add_item("Service Plan", "page=services/plan.php&id=" . $this->id . "");
     if ($this->service_type == "bundle") {
         $this->obj_menu_nav->add_item("Bundle Components", "page=services/bundles.php&id=" . $this->id . "", TRUE);
     }
     $this->obj_menu_nav->add_item("Service Journal", "page=services/journal.php&id=" . $this->id . "");
     if (user_permissions_get("services_write")) {
         $this->obj_menu_nav->add_item("Delete Service", "page=services/delete.php&id=" . $this->id . "");
     }
 }
Exemple #9
0
 function page_output()
 {
     $this->obj_serviceform = new services_form_delete();
     $this->obj_serviceform->serviceid = @security_script_input('/^[0-9]*$/', $_GET["id"]);
     // define the navigiation menu
     $this->obj_menu_nav = new menu_nav();
     $this->obj_menu_nav->add_item("Service Details", "page=services/view.php&id=" . $this->obj_serviceform->serviceid . "");
     $this->obj_menu_nav->add_item("Service Plan", "page=services/plan.php&id=" . $this->obj_serviceform->serviceid . "");
     if (sql_get_singlevalue("SELECT service_types.name as value FROM services LEFT JOIN service_types ON service_types.id = services.typeid WHERE services.id='" . $this->obj_serviceform->serviceid . "' LIMIT 1") == "bundle") {
         $this->obj_menu_nav->add_item("Bundle Components", "page=services/bundles.php&id=" . $this->obj_serviceform->serviceid . "");
     }
     if (in_array(sql_get_singlevalue("SELECT service_types.name as value FROM services LEFT JOIN service_types ON service_types.id = services.typeid WHERE services.id='" . $this->obj_serviceform->serviceid . "' LIMIT 1"), array("phone_single", "phone_tollfree", "phone_trunk"))) {
         $this->obj_menu_nav->add_item("Call Rate Override", "page=services/cdr-override.php&id=" . $this->obj_serviceform->serviceid . "");
     }
     $this->obj_menu_nav->add_item("Service Journal", "page=services/journal.php&id=" . $this->obj_serviceform->serviceid . "");
     $this->obj_menu_nav->add_item("Delete Service", "page=services/delete.php&id=" . $this->obj_serviceform->serviceid . "", TRUE);
 }
 function execute()
 {
     //check mail module and add data to array
     $name = "Mail";
     $location = "Mail.php";
     $status = @(include 'Mail.php');
     $this->table_array[$name]["location"] = $location;
     $this->table_array[$name]["status"] = $status;
     //check mail mime module and add data to array
     $name = "Mail Mime";
     $location = "Mail/mime.php";
     $status = @(include 'Mail/mime.php');
     $this->table_array[$name]["location"] = $location;
     $this->table_array[$name]["status"] = $status;
     //check mysql dump module and add data to array
     $name = "MySQL Dump";
     $location = sql_get_singlevalue("SELECT value FROM config WHERE name = 'APP_MYSQL_DUMP' LIMIT 1");
     $status = file_exists($location);
     $this->table_array[$name]["location"] = $location;
     $this->table_array[$name]["status"] = $status;
     //check html to pdf module and add data to array
     $name = "WK HTML to PDF";
     $location = sql_get_singlevalue("SELECT value FROM config WHERE name = 'APP_WKHTMLTOPDF' LIMIT 1");
     $status = file_exists($location);
     $this->table_array[$name]["location"] = $location;
     $this->table_array[$name]["status"] = $status;
     //check pdf latex module and add data to array
     $name = "PDF LaTeX";
     $location = sql_get_singlevalue("SELECT value FROM config WHERE name = 'APP_PDFLATEX' LIMIT 1");
     $status = file_exists($location);
     $this->table_array[$name]["location"] = $location;
     $this->table_array[$name]["status"] = $status;
     //check if temp directory has write permissions and add data to array
     $name = "Temp Directory - Write";
     $location = sql_get_singlevalue("SELECT value FROM config WHERE name = 'PATH_TMPDIR' LIMIT 1");
     $status = is_writable($location);
     $this->table_array[$name]["location"] = $location;
     $this->table_array[$name]["status"] = $status;
     //check if temp directory has read permissions and add data to array
     $name = "Temp Directory - Read";
     $status = is_readable($location);
     $this->table_array[$name]["location"] = $location;
     $this->table_array[$name]["status"] = $status;
 }
 function page_output()
 {
     $this->obj_rate_table = new cdr_rate_table_rates();
     // fetch variables
     $this->obj_rate_table->id = @security_script_input('/^[0-9]*$/', $_GET["id"]);
     $this->obj_rate_table->id_rate = @security_script_input('/^[0-9]*$/', $_GET["id_rate"]);
     if (!$this->obj_rate_table->id_rate) {
         // check for prefix
         $prefix = @security_script_input('/^[0-9]*$/', $_GET["prefix"]);
         if (!empty($prefix)) {
             $this->obj_rate_table->id_rate = sql_get_singlevalue("SELECT id as value FROM cdr_rate_tables_values WHERE id_rate_table='" . $this->obj_rate_table->id . "' AND rate_prefix='" . $prefix . "' LIMIT 1");
         }
     }
     // define the navigiation menu
     $this->obj_menu_nav = new menu_nav();
     $this->obj_menu_nav->add_item("Rate Table Details", "page=services/cdr-rates-view.php&id=" . $this->obj_rate_table->id . "");
     $this->obj_menu_nav->add_item("Rate Table Items", "page=services/cdr-rates-items.php&id=" . $this->obj_rate_table->id . "", TRUE);
     $this->obj_menu_nav->add_item("Delete Rate Table", "page=services/cdr-rates-delete.php&id=" . $this->obj_rate_table->id . "");
 }
 function page_output()
 {
     // get selected employee
     $this->employeeid = @security_script_input('/^[0-9]*$/', $_GET["employeeid"]);
     if ($this->employeeid) {
         // save to session vars
         $_SESSION["form"]["timereg"]["employeeid"] = $this->employeeid;
     } else {
         // load from session vars
         if (isset($_SESSION["form"]["timereg"]["employeeid"])) {
             $this->employeeid = $_SESSION["form"]["timereg"]["employeeid"];
         }
     }
     // get selected date
     $this->date = @security_script_input('/^\\S*$/', $_GET["date"]);
     if (!$this->date) {
         // try alternative input syntax
         $this->date = @security_script_input_predefined("date", $_GET["date_yyyy"] . "-" . $_GET["date_mm"] . "-" . $_GET["date_dd"]);
         if ($this->date == "error") {
             $this->date = NULL;
         }
     }
     if ($this->date) {
         // save to session vars
         $_SESSION["timereg"]["date"] = $this->date;
     } else {
         if ($_SESSION["timereg"]["date"]) {
             // load from session vars
             $this->date = $_SESSION["timereg"]["date"];
         } else {
             // use today's date
             $this->date = date("Y-m-d");
         }
     }
     $this->date_split = explode("-", $this->date);
     // define the navigiation menu
     $this->obj_menu_nav = new menu_nav();
     $this->obj_menu_nav->add_item("Weekview", "page=timekeeping/timereg.php&year=" . time_calculate_yearnum($this->date) . "&weekofyear=" . time_calculate_weeknum($this->date) . "");
     $this->obj_menu_nav->add_item("Day View", "page=timekeeping/timereg-day.php&date=" . $this->date . "", TRUE);
     // get future booking config option
     $this->config_timesheet_booktofuture = sql_get_singlevalue("SELECT value FROM config WHERE name='TIMESHEET_BOOKTOFUTURE'");
 }
 function get_product_details($id)
 {
     log_debug("products_manage_soap", "Executing get_product_details({$id})");
     if (user_permissions_get("products_view")) {
         $obj_product = new product();
         // sanitise input
         $obj_product->id = @security_script_input_predefined("int", $id);
         if (!$obj_product->id || $obj_product->id == "error") {
             throw new SoapFault("Sender", "INVALID_INPUT");
         }
         // verify that the ID is valid
         if (!$obj_product->verify_id()) {
             throw new SoapFault("Sender", "INVALID_ID");
         }
         // load data from DB for this product
         if (!$obj_product->load_data()) {
             throw new SoapFault("Sender", "UNEXPECTED_ACTION_ERROR");
         }
         // to save SOAP clients from having to do another lookup to find the vendor name,
         // we fetch it now.
         if ($obj_product->data["vendorid"]) {
             $obj_product->data["vendorid_label"] = sql_get_singlevalue("SELECT name_vendor as value FROM vendors WHERE id='" . $obj_product->data["vendorid"] . "'");
         }
         // to save SOAP clients from having to do another lookup to find the account_sales and account_purchase
         // account names, we look them up now
         if ($obj_product->data["account_sales"]) {
             $obj_product->data["account_sales_label"] = sql_get_singlevalue("SELECT CONCAT_WS('--', code_chart, description) as value FROM account_charts WHERE id='" . $obj_product->data["account_sales"] . "'");
         }
         if ($obj_product->data["account_purchase"]) {
             $obj_product->data["account_purchase_label"] = sql_get_singlevalue("SELECT CONCAT_WS('--', code_chart, description) as value FROM account_charts WHERE id='" . $obj_product->data["account_purchase"] . "'");
         }
         // return data
         $return = array($obj_product->data["code_product"], $obj_product->data["name_product"], $obj_product->data["units"], $obj_product->data["details"], $obj_product->data["price_cost"], $obj_product->data["price_sale"], $obj_product->data["date_start"], $obj_product->data["date_end"], $obj_product->data["date_current"], $obj_product->data["quantity_instock"], $obj_product->data["quantity_vendor"], $obj_product->data["vendorid"], $obj_product->data["vendorid_label"], $obj_product->data["code_product_vendor"], $obj_product->data["account_sales"], $obj_product->data["account_sales_label"], $obj_product->data["account_purchase"], $obj_product->data["account_purchase_label"], $obj_product->data["discount"]);
         return $return;
     } else {
         throw new SoapFault("Sender", "ACCESS_DENIED");
     }
 }
Exemple #14
0
 function page_output()
 {
     // get selected employee
     $this->employeeid = @security_script_input('/^[0-9]*$/', $_GET["employeeid"]);
     if ($this->employeeid) {
         // save to session vars
         $_SESSION["form"]["timereg"]["employeeid"] = $this->employeeid;
     } else {
         // load from session vars
         if (isset($_SESSION["form"]["timereg"]["employeeid"]) && $_SESSION["form"]["timereg"]["employeeid"] != NULL) {
             $this->employeeid = $_SESSION["form"]["timereg"]["employeeid"];
         }
     }
     // get the chosen year + week
     $this->date_selected_year = @security_script_input('/^[0-9]*$/', $_GET["year"]);
     $this->date_selected_weekofyear = @security_script_input('/^[0-9]*$/', $_GET["weekofyear"]);
     if (!$this->date_selected_year) {
         if (!empty($_SESSION["timereg"]["year"])) {
             $this->date_selected_year = $_SESSION["timereg"]["year"];
         } else {
             $this->date_selected_year = date("Y");
         }
     }
     if (!$this->date_selected_weekofyear) {
         if (!empty($_SESSION["timereg"]["weekofyear"])) {
             $this->date_selected_weekofyear = $_SESSION["timereg"]["weekofyear"];
         } else {
             $this->date_selected_weekofyear = time_calculate_weeknum();
         }
     }
     // save to session vars
     $_SESSION["timereg"]["year"] = $this->date_selected_year;
     $_SESSION["timereg"]["weekofyear"] = $this->date_selected_weekofyear;
     // get future booking config option
     $this->config_timesheet_booktofuture = sql_get_singlevalue("SELECT value FROM config WHERE name='TIMESHEET_BOOKTOFUTURE'");
 }
 function generate_pdf()
 {
     log_debug("template_engine_htmltopdf", "Executing generate_pdf()");
     //unset($_SESSION);
     /*
     	Generate temporary file for template
     */
     // generate unique tmp filename
     $tmp_filename = file_generate_name("/tmp/amberdms_billing_system");
     // write out template data
     if (!($handle = fopen("{$tmp_filename}.html", "w"))) {
         log_write("error", "template_engine_htmltopdf", "Failed to create temporary file ({$tmp_filename}.html) with template data");
         return 0;
     }
     $directory_prefix = $tmp_filename . "_";
     //$directory_prefix = "https://devel-web-tom.local.amberdms.com/development/amberdms/oss-amberdms-bs/trunk/templates/ar_invoice/ar_invoice_htmltopdf_telcostyle/";
     foreach ((array) $this->processed as $key => $processed_row) {
         $this->processed[$key] = str_replace("(tmp_filename)", $directory_prefix, $processed_row);
     }
     //print(implode("",$this->processed));
     //log_debug_render();
     //exit();
     foreach ($this->processed as $line) {
         if (fwrite($handle, $line) === FALSE) {
             log_write("error", "template_engine_htmltopdf", "Error occured whilst writing file ({$tmp_filename}.tex)");
             return 0;
         }
     }
     fclose($handle);
     /*
     	Create Directory for HTML data files & copy them over
     */
     $tmp_data_directory = $tmp_filename . "_html_data";
     mkdir($tmp_filename . "_html_data", 0700);
     $data_directory_items = glob($this->template_directory . "/html_data/*");
     foreach ((array) $data_directory_items as $data_dir_file) {
         $filename = basename($data_dir_file);
         $new_file_path = $tmp_data_directory . "/" . $filename;
         copy($data_dir_file, $new_file_path);
     }
     /*
     	Process with wkhtmltopdf
     
     	Note: In future, this may be extended to support alternative HTML to PDF rendering engines
     */
     // get the current directory so that we can change back after switching to the tmp directory
     $run_directory = getcwd();
     $app_wkhtmltopdf = sql_get_singlevalue("SELECT value FROM config WHERE name='APP_WKHTMLTOPDF' LIMIT 1");
     if (!is_executable($app_wkhtmltopdf)) {
         log_write("error", "process", "You have selected a template that requires the wkhtmltopdf engine, however {$app_wkhtmltopdf} does not exist or is not executable by your webserver process");
         return 0;
     }
     chdir("/tmp");
     // TODO: fix this to be a configurable value
     exec("{$app_wkhtmltopdf} -B 5mm -L 5mm -R 5mm -T 5mm {$tmp_filename}.html {$tmp_filename}.pdf 2>&1", $output);
     foreach ($output as $line) {
         log_debug("template_engine_htmltopdf", "wkhtmltopdf: {$line}");
     }
     if (in_array("wkhtmltopdf: cannot connect to X server", $output)) {
         log_debug("template_engine_htmltopdf", "Known fault on older systems, unable to run wkhtmltopdf without X server instance");
         /*
         	On older distribution versions of wkhtmltopdf, the shipped version of wkhtmltopdf/QT doesn't
         	work without a running X server.
         
         	This was fixed by patching both QT and wkhtmltopdf in later releases, however most distributions
         	don't appear to go to the extend of shipping this patched QT version. On RHEL platforms (as of RHEL 4/5/6), there is
         	no wkhtmltopdf, so we just ship a good package, on Ubuntu (as of 10.04 lts) where is the more limited package
         	we execute a wrapper script which runs a short lived Xorg session in a virtual framebuffer.
         
         	It's not perfect, in testing the PDFs are rendering nicely, apart from the page size being incorrect, regardless what paramaters
         	are passed to it - hence, we give the user a notification warning, so they know why the invoices are weird and how to fix it.
         
         	If we have numerious problems with a popular platform, then it will be worth Amberdms building packages for that platform, but
         	it's not a small effort.
         
         	TODO: Maybe we should have an external/natives/ static binary directory for some key essentials for common architectures?
         */
         $install_directory = dirname(__FILE__);
         $legacy_wrapper_cmd = "{$install_directory}/../../external/legacy/wkhtmltopdf_x11wrap.sh {$tmp_filename}.html {$tmp_filename}.pdf 2>&1";
         log_debug("template_engine_htmltopdf", "Executing {$legacy_wrapper_cmd}");
         $output = array();
         exec($legacy_wrapper_cmd, $output);
         foreach ($output as $line) {
             log_debug("template_engine_htmltopdf", "wkhtmltopdf (legacy wrapper): {$line}");
         }
         log_write("notification", "template_engine_htmltopdf", "Warning: Your server has a older/limited version of wkhtmltopdf installed, this can cause some performance and page-size rendering issues. If these cause you major issues, consider obtaining the static binary version and adjusting the configured executable path. A static version can be found on the wkhtmltopdf developer's website at http://code.google.com/p/wkhtmltopdf/downloads/list ");
     }
     /*
     	check that a PDF was generated
     */
     if (file_exists("{$tmp_filename}.pdf")) {
         log_debug("template_engine_htmltopdf", "Temporary PDF {$tmp_filename}.pdf generated");
         // import file data into memory
         $this->output = file_get_contents("{$tmp_filename}.pdf");
         // remove temporary files from disk
         @unlink("{$tmp_filename}");
         @unlink("{$tmp_filename}.aux");
         @unlink("{$tmp_filename}.log");
         @unlink("{$tmp_filename}.html");
         @unlink("{$tmp_filename}.pdf");
         // cleanup texlive home directory
         system("rm -rf " . $tmp_filename . "_html_data");
         // return back to original directory
         chdir($run_directory);
         return 1;
     } else {
         log_write("error", "template_engine_htmltopdf", "Unable to use wkhtmltopdf ({$app_wkhtmltopdf}) to generate PDF file");
         // return back to original directory
         chdir($run_directory);
         return 0;
     }
 }
     $data["password_confirm"] = @security_form_input_predefined("any", "password_confirm", 4, "");
     if ($data["password"] != $data["password_confirm"]) {
         $_SESSION["error"]["message"][] = "Customer passwords do not match.";
         $_SESSION["error"]["password-error"] = 1;
         $_SESSION["error"]["password_confirm-error"] = 1;
     }
 }
 /*
 	Error Handling
 */
 // verify valid customer ID
 if (!$obj_customer->verify_id()) {
     log_write("error", "process", "The customer you have attempted to edit - " . $obj_customer->id . " - does not exist in this system.");
 }
 // make sure the module is enabled
 if (sql_get_singlevalue("SELECT value FROM config WHERE name='MODULE_CUSTOMER_PORTAL' LIMIT 1") != "enabled") {
     log_write("error", "page_output", "MODULE_CUSTOMER_PORTAL is disabled, enable it if you wish to adjust customer portal configuration options.");
 }
 if (error_check()) {
     $_SESSION["error"]["form"]["customer_portal"] = "failed";
     header("Location: ../index.php?page=customers/portal.php");
     exit(0);
 }
 /*
 	Process Data
 */
 // update portal
 $obj_customer->auth_changepwd($data["password"]);
 // display updated details
 header("Location: ../index.php?page=customers/portal.php&id=" . $obj_customer->id);
 exit(0);
 function render_html()
 {
     log_debug("invoice_form_export", "Executing render_html()");
     // download form
     print "<table width=\"100%\" class=\"table_highlight\"><tr><td>";
     print "<table cellpadding=\"5\" width=\"100%\"><tr>";
     /*
     	TODO: good place to add an icon
     
     		print "<td valign=\"top\">";
     			print "pdf_icon_here";
     		print "</td>";
     */
     print "<td width=\"100%\">";
     print "<h3>Download PDF:</h3>";
     print "<form method=\"" . $this->obj_form_download->method . "\" action=\"" . $this->obj_form_download->action . "\">";
     print "<br><br>";
     $this->obj_form_download->render_field("invoice_mark_as_sent");
     print "<br>";
     $this->obj_form_download->render_field("formname");
     $this->obj_form_download->render_field("id_invoice");
     $this->obj_form_download->render_field("submit");
     print "</form>";
     print "</td>";
     print "</tr></table>";
     print "</td></tr></table>";
     print "<br><br>";
     // email form
     print "<table width=\"100%\" class=\"table_highlight\"><tr><td>";
     print "<table cellpadding=\"5\" width=\"100%\"><tr>";
     /*
     	TODO: good place to add an icon
     
     		print "<td valign=\"top\">";
     			print "email_icon_here";
     		print "</td>";
     */
     print "<td width=\"100%\">";
     print "<h3>Email PDF:</h3>";
     // check if we are permitted to send emails
     if (sql_get_singlevalue("SELECT value FROM config WHERE name='EMAIL_ENABLE'") == "enabled") {
         print "<form method=\"" . $this->obj_form_email->method . "\" action=\"" . $this->obj_form_email->action . "\">";
         print "<table width=\"100%\">";
         $this->obj_form_email->render_row("sender");
         $this->obj_form_email->render_row("subject");
         $this->obj_form_email->render_row("email_to");
         $this->obj_form_email->render_row("email_cc");
         $this->obj_form_email->render_row("email_bcc");
         $this->obj_form_email->render_row("email_message");
         print "</table>";
         $this->obj_form_email->render_field("formname");
         $this->obj_form_email->render_field("id_invoice");
         $this->obj_form_email->render_field("submit");
         print "</form>";
     } else {
         format_msgbox("locked", "<p>The ability to email PDFs has been disabled by the administrator.</p>");
     }
     print "</td>";
     print "</tr></table>";
     print "</td></tr></table>";
 }
Exemple #18
0
 function execute()
 {
     // load customer's general data
     $this->obj_customer->load_data();
     // establish a new table object
     $this->obj_table = new table();
     $this->obj_table->language = $_SESSION["user"]["lang"];
     $this->obj_table->tablename = "orders_list";
     // define all the columns and structure
     $this->obj_table->add_column("date", "date_ordered", "");
     $this->obj_table->add_column("standard", "type", "");
     $this->obj_table->add_column("standard", "item", "NONE");
     $this->obj_table->add_column("standard", "quantity", "");
     $this->obj_table->add_column("standard", "units", "");
     $this->obj_table->add_column("money", "amount", "");
     $this->obj_table->add_column("money", "price", "");
     $this->obj_table->add_column("percentage", "discount", "");
     $this->obj_table->add_column("standard", "description", "");
     // defaults
     $this->obj_table->columns = array("date_ordered", "type", "item", "quantity", "units", "price", "discount", "amount", "description");
     // define SQL structure
     $this->obj_table->sql_obj->prepare_sql_settable("customers_orders");
     $this->obj_table->sql_obj->prepare_sql_addfield("id_order", "id");
     $this->obj_table->sql_obj->prepare_sql_addfield("customid", "customid");
     $this->obj_table->sql_obj->prepare_sql_addwhere("id_customer = '" . $this->obj_customer->id . "'");
     $this->obj_table->sql_obj->prepare_sql_addorderby_desc("date_ordered");
     // run SQL query
     $this->obj_table->generate_sql();
     $this->obj_table->load_data_sql();
     // load service item data and optiosn
     for ($i = 0; $i < $this->obj_table->data_num_rows; $i++) {
         switch ($this->obj_table->data[$i]["type"]) {
             case "product":
                 // lookup product code + name
                 $this->obj_table->data[$i]["item"] = sql_get_singlevalue("SELECT CONCAT_WS(' -- ',code_product,name_product) AS value FROM products WHERE id='" . $this->obj_table->data[$i]["customid"] . "'");
                 break;
             case "service":
                 // lookup service name
                 $this->obj_table->data[$i]["item"] = sql_get_singlevalue("SELECT name_service AS value FROM services WHERE id='" . $this->obj_table->data[$i]["customid"] . "'");
                 break;
         }
     }
 }
 	does belong to a user.
 
 	This prevent a malicious user from using this page to fetch other files
 	belonging to other journals or users.
 */
 // get the ID of the journal from the file ID
 $customid = sql_get_singlevalue("SELECT customid as value FROM file_uploads WHERE id='{$fileid}'");
 if (!$customid) {
     $_SESSION["error"]["message"][] = "No record for this file found in the database.";
 } else {
     // make sure the custom ID of the file matches the journal ID
     if ($customid != $journalid) {
         $_SESSION["error"]["message"][] = "Error: File customid and journal ID do not match";
     } else {
         // make sure the journal entry belongs to a user
         $userid = sql_get_singlevalue("SELECT customid as value FROM journal WHERE journalname='users' AND id='{$journalid}'");
         if (!$userid) {
             $_SESSION["error"]["message"][] = "Unable to match the provided journal entry to a user journal.";
         }
     }
 }
 /*
 	Produce output - either output request file content,
 	or take the user to a message page to view errors.
 */
 if ($_SESSION["error"]["message"]) {
     header("Location: ../index.php?page=message.php");
     exit(0);
 } else {
     // output file data
     $file_obj = new file_storage();
 function render_html()
 {
     // heading
     print "<h3>CUSTOMER SERVICE HISTORY</h3>";
     print "<p>This page displays all the periods of this service, showing when the service was active and when it has been billed.</p>";
     $this->obj_customer->service_render_summarybox();
     if (!$this->obj_table->data_num_rows) {
         format_msgbox("info", "<p>This service does not have any history - this is normal when the service has just recently been added.</p>");
     } else {
         // get the unit name if applicable
         if (preg_match("/^[0-9]*\$/", $this->obj_customer->obj_service->data["units"])) {
             $unitname = sql_get_singlevalue("SELECT name as value FROM service_units WHERE id='" . $this->obj_customer->obj_service->data["units"] . "'");
         }
         // run through all the data rows to make custom changes
         for ($i = 0; $i < $this->obj_table->data_num_rows; $i++) {
             // make the invoice number a hyperlink
             if ($this->obj_table->data[$i]["code_invoice"] && user_permissions_get("accounts_ar_view")) {
                 $this->obj_table->data[$i]["code_invoice"] = "<a href=\"index.php?page=accounts/ar/invoice-view.php&id=" . $this->obj_table->data[$i]["invoiced_plan"] . "\">AR " . $this->obj_table->data[$i]["code_invoice"] . "</a>";
             }
             // tick the paid column if the invoice has been paid off completely
             if (!empty($this->obj_table->data[$i]["invoiced"])) {
                 if ($this->obj_table->data[$i]["amount_total"] == $this->obj_table->data[$i]["amount_paid"]) {
                     $this->obj_table->data[$i]["paid"] = 1;
                 }
             }
             // provide service-type specific options for displaying and querying usage.
             switch ($this->obj_table->data[$i]["service_type"]) {
                 case "phone_trunk":
                 case "phone_tollfree":
                 case "phone_single":
                     // display a "view call records" link which looks up the historical
                     // call data to fetch pricing
                     $this->obj_table->data[$i]["usage_summary"] = "<a href=\"index.php?page=customers/service-history-cdr.php&id_customer=" . $this->obj_customer->id . "&id_service_customer=" . $this->obj_customer->id_service_customer . "&id_service_period=" . $this->obj_table->data[$i]["id"] . "\">View Call Records</a>";
                     break;
                 case "data_traffic":
                 case "generic_with_usage":
                     if ($i == $this->obj_table->data_num_rows - 1) {
                         // if this is the most recent period, then add a check link next to the usage amount
                         $this->obj_table->data[$i]["usage_summary"] = $this->obj_table->data[$i]["usage_summary"] . " {$unitname} <a href=\"customers/services-checkusage-process.php?id_customer=" . $this->obj_customer->id . "&id_service_customer=" . $this->obj_customer->id_service_customer . "\">(get latest)</a>";
                     } else {
                         // displau usage amount
                         $this->obj_table->data[$i]["usage_summary"] = $this->obj_table->data[$i]["usage_summary"] . " {$unitname}";
                     }
                     break;
             }
             // end of service type
         }
         // display the table
         $this->obj_table->render_table_html();
         // display CSV/PDF download link
         print "<p align=\"right\"><a class=\"button_export\" href=\"index-export.php?mode=csv&page=customers/service-history.php&id_customer=" . $this->obj_customer->id . "&id_service_customer=" . $this->obj_customer->id_service_customer . "\">Export as CSV</a></p>";
         print "<p align=\"right\"><a class=\"button_export\" href=\"index-export.php?mode=pdf&page=customers/service-history.php&id_customer=" . $this->obj_customer->id . "&id_service_customer=" . $this->obj_customer->id_service_customer . "\">Export as PDF</a></p>";
     }
 }
 	does belong to a project.
 
 	This prevent a malicious user from using this page to fetch other files
 	belonging to other journals or projects.
 */
 // get the ID of the journal from the file ID
 $customid = sql_get_singlevalue("SELECT customid as value FROM file_uploads WHERE id='{$fileid}'");
 if (!$customid) {
     $_SESSION["error"]["message"][] = "No record for this file found in the database.";
 } else {
     // make sure the custom ID of the file matches the journal ID
     if ($customid != $journalid) {
         $_SESSION["error"]["message"][] = "Error: File customid and journal ID do not match";
     } else {
         // make sure the journal entry belongs to a project
         $projectid = sql_get_singlevalue("SELECT customid as value FROM journal WHERE journalname='projects' AND id='{$journalid}'");
         if (!$projectid) {
             $_SESSION["error"]["message"][] = "Unable to match the provided journal entry to a project journal.";
         }
     }
 }
 /*
 	Produce output - either output request file content,
 	or take the user to a message page to view errors.
 */
 if ($_SESSION["error"]["message"]) {
     header("Location: ../index.php?page=message.php");
     exit(0);
 } else {
     // output file data
     $file_obj = new file_storage();
<?php

/*
	timekeeping/ajax/populate_phases_dropdown.php

	Function called by timereg-day-edit.js to generate phases dropdown via ajax
*/
require "../../include/config.php";
require "../../include/amberphplib/main.php";
if (user_permissions_get('timekeeping')) {
    $product_id = @security_script_input_predefined("int", $_GET['project_id']);
    $timereg_id = @security_script_input_predefined("int", $_GET['timereg_id']);
    $selected = @security_script_input_predefined("any", $_GET['selected']);
    $edit = @security_script_input_predefined("any", $_GET['edit']);
    $option_string = "";
    $phase_id = sql_get_singlevalue("SELECT phaseid AS value FROM timereg WHERE id='" . $timereg_id . "' LIMIT 1");
    if ($edit == "true") {
        $option_string .= "<option>" . $edit . "  " . $selected . "  </option>";
        $selected = $phase_id;
    }
    $sql_obj = new sql_query();
    $sql_obj->string = "SELECT id, name_phase FROM project_phases WHERE projectid =" . $product_id . " ORDER BY name_phase";
    $sql_obj->execute();
    if ($sql_obj->num_rows()) {
        $sql_obj->fetch_array();
        foreach ($sql_obj->data as $data_row) {
            $option_string .= "<option value=\"" . $data_row['id'] . "\"";
            if ($data_row['id'] == $selected) {
                $option_string .= " selected=\"selected\"";
            }
            $option_string .= ">" . $data_row['name_phase'] . "</option>";
                $invoice_data_parts['values'][$index] = trim($value);
            }
            $invoice_data_parts['keys'][] = "(days_overdue)";
            $invoice_data_parts['values'][] = trim($days_overdue);
            //create email message
            $email_message = @security_form_input_predefined("any", "email_message", 0, "");
            $email_message = str_replace($invoice_data_parts['keys'], $invoice_data_parts['values'], $email_message);
            //create subject
            $subject = @security_form_input_predefined("any", "subject", 0, "");
            $subject = str_replace($invoice_data_parts['keys'], $invoice_data_parts['values'], $subject);
            //sender
            $from = @security_form_input_predefined("any", "sender", 0, "");
            if ($from == "user") {
                $from = user_information("contact_email");
            } else {
                $from = sql_get_singlevalue("SELECT value FROM config WHERE name='COMPANY_CONTACT_EMAIL'");
            }
            log_debug("EMAIL", "avout to send" . $i);
            // send email
            $obj_invoice->email_invoice($from, $to, "", "", $subject, $email_message);
            $_SESSION["notification"]["message"][] = "Reminder email for Invoice " . $obj_sql_invoice->data[0]["code_invoice"] . " was sent successfully.";
        }
    }
    //set error messages for emails that couldn't be sent
    for ($i = 0; $i < count($error_array); $i++) {
        $_SESSION["error"]["message"][] = "Reminder for Invoice " . $error_array[$i] . " was not sent as no email is set for the customer's default account.";
    }
    header("Location: ../../index.php?page=accounts/ar/account-statements.php");
    exit(0);
} else {
    // user does not have perms to view this page/isn't logged on
 function check_autopay_capable()
 {
     log_write("debug", "invoice_autopay", "Executing check_autopay_capable()");
     // fetch customer/vendor information
     if ($this->type_invoice == "ap") {
         $this->id_org = sql_get_singlevalue("SELECT vendorid as value FROM account_ar WHERE id='" . $this->id_invoice . "' LIMIT 1");
     } else {
         $this->id_org = sql_get_singlevalue("SELECT customerid as value FROM account_ar WHERE id='" . $this->id_invoice . "' LIMIT 1");
     }
     // check credit pool
     if ($this->type_invoice == "ap") {
         $credit = sql_get_singlevalue("SELECT SUM(amount_total) as value FROM vendors_credits WHERE id_vendor='" . $this->id_org . "' LIMIT 1");
     } else {
         $credit = sql_get_singlevalue("SELECT SUM(amount_total) as value FROM customers_credits WHERE id_customer='" . $this->id_org . "' LIMIT 1");
     }
     if ($credit > 0) {
         log_write("debug", "invoice_autopay", "There is credit ({$credit}) available for autopayments");
         $this->capable = 1;
         return 1;
     }
     // no autopayment sources
     return 0;
 }
 function action_update_records()
 {
     log_write("debug", "inc_cloud_route53", "Executing action_update_records()");
     // fetch the SOA before we get the records - that way if the records get updated between now and us querying them,
     // we won't have a race condition and miss them, as our SOA will be behind, and a subsequent run will
     // cause the domain to get updated. This is easier than dealing with table locks.
     $current_soa = sql_get_singlevalue("SELECT soa_serial as value FROM dns_domains WHERE id='" . $this->obj_domain->id . "' LIMIT 1");
     // Fetch all the records for this domain from AWS
     if (!$this->fetch_records_remote()) {
         return 0;
     }
     // Fetch all the records for this domain from NamdManager
     if (!$this->fetch_records_local()) {
         return 0;
     }
     // Generate a batch job for AWS with the differences to be applied
     if (!$this->action_sync_records()) {
         return 0;
     }
     // Update SOA for cloud zone map table
     $obj_sql = new sql_query();
     $obj_sql->string = "UPDATE cloud_zone_map SET soa_serial='{$current_soa}' WHERE id_name_server='" . $this->obj_name_server->id . "' AND id_domain='" . $this->obj_domain->id . "' LIMIT 1";
     $obj_sql->execute();
     log_write("debug", "inc_cloud_route53", "Successfully updated to domain version {$current_soa}");
     $this->changelog->log_post('server', "Successfully updated domain \"" . $this->obj_domain->data["domain_name"] . "\" in Route53");
 }
function invoice_form_export_process($type, $returnpage_error, $returnpage_success)
{
    log_debug("inc_invoices_forms", "Executing invoice_form_export_process({$type}, {$returnpage_error}, {$returnpage_success})");
    /*
    	Start the invoice
    */
    $invoice = new invoice();
    $invoice->type = $type;
    /*
    	Fetch all form data
    */
    // get the ID for an edit
    $invoice->id = @security_form_input_predefined("int", "id_invoice", 1, "");
    // general details
    $data["formname"] = @security_form_input_predefined("any", "formname", 1, "");
    if ($data["formname"] == "invoice_export_email") {
        // send email
        $data["sender"] = @security_form_input_predefined("any", "sender", 1, "");
        $data["subject"] = @security_form_input_predefined("any", "subject", 1, "");
        $data["email_to"] = @security_form_input_predefined("multiple_email", "email_to", 1, "");
        $data["email_cc"] = @security_form_input_predefined("multiple_email", "email_cc", 0, "");
        $data["email_bcc"] = @security_form_input_predefined("multiple_email", "email_bcc", 0, "");
        $data["message"] = @security_form_input_predefined("any", "email_message", 1, "");
        // check if email sending is permitted
        if (sql_get_singlevalue("SELECT value FROM config WHERE name='EMAIL_ENABLE'") != "enabled") {
            log_write("error", "inc_invoices_process", "Sorry, the ability to email invoices has been disabled. Please contact your system administrator if you require this feature to be enabled.");
        }
    } else {
        // PDF download
        $data["invoice_mark_as_sent"] = @security_form_input_predefined("any", "invoice_mark_as_sent", 0, "");
    }
    // make sure that the invoice exists
    $sql_obj = new sql_query();
    $sql_obj->string = "SELECT id FROM `account_" . $invoice->type . "` WHERE id='" . $invoice->id . "'";
    $sql_obj->execute();
    if (!$sql_obj->num_rows()) {
        $_SESSION["error"]["message"][] = "The invoice you have attempted to edit - " . $invoice->id . " - does not exist in this system.";
    }
    //// ERROR CHECKING ///////////////////////
    /// if there was an error, go back to the entry page
    if (!empty($_SESSION["error"]["message"])) {
        header("Location: ../../index.php?page={$returnpage_error}&id=" . $invoice->id . "");
        exit(0);
    } else {
        if ($data["formname"] == "invoice_export_email") {
            /*
            	Generate a PDF of the invoice and email it to the customer
            */
            // stripslashes from the variables - by default all input variables are quoted for security reasons but
            // we don't want this going through to the email.
            $data["subject"] = stripslashes($data["subject"]);
            $data["message"] = stripslashes($data["message"]);
            // send email
            $invoice->load_data();
            $invoice->email_invoice($data["sender"], $data["email_to"], $data["email_cc"], $data["email_bcc"], $data["subject"], $data["message"]);
            $_SESSION["notification"]["message"][] = "Email sent successfully.";
        } else {
            /*
            	Mark invoice as being sent if user requests it
            */
            if ($data["invoice_mark_as_sent"]) {
                $sql_obj = new sql_query();
                $sql_obj->string = "UPDATE account_" . $invoice->type . " SET date_sent='" . date("Y-m-d") . "', sentmethod='manual' WHERE id='" . $invoice->id . "'";
                $sql_obj->execute();
            }
            /*
            	Provide PDF to user's browser
            */
            // generate PDF
            $invoice->load_data();
            $invoice->generate_pdf();
            // PDF headers
            if ($type == "quotes") {
                $filename = "/tmp/quote_" . $invoice->data["code_quote"] . ".pdf";
            } else {
                $filename = "/tmp/invoice_" . $invoice->data["code_invoice"] . ".pdf";
            }
            // required for IE, otherwise Content-disposition is ignored
            if (ini_get('zlib.output_compression')) {
                ini_set('zlib.output_compression', 'Off');
            }
            header("Pragma: public");
            // required
            header("Expires: 0");
            header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
            header("Cache-Control: private", false);
            // required for certain browsers
            header("Content-Type: application/pdf");
            header("Content-Disposition: attachment; filename=\"" . basename($filename) . "\";");
            header("Content-Transfer-Encoding: binary");
            // output the PDF
            print $invoice->obj_pdf->output;
            exit(0);
        }
        // display updated details
        header("Location: ../../index.php?page={$returnpage_success}&id=" . $invoice->id . "");
        exit(0);
    }
    // end if passed tests
}
function service_form_plan_process()
{
    log_debug("inc_services_process", "Executing service_form_plan_process()");
    /*
    	Fetch all form data
    */
    $id = @security_form_input_predefined("int", "id_service", 1, "");
    // general details
    $data["price"] = @security_form_input_predefined("money", "price", 0, "");
    $data["price_setup"] = @security_form_input_predefined("money", "price_setup", 0, "");
    $data["discount"] = @security_form_input_predefined("float", "discount", 0, "");
    $data["billing_cycle"] = @security_form_input_predefined("int", "billing_cycle", 1, "");
    $data["billing_mode"] = @security_form_input_predefined("int", "billing_mode", 1, "");
    // needed to handle errors, but not used
    $data["name_service"] = @security_form_input_predefined("any", "name_service", 0, "");
    // make sure that the service actually exists
    $sql_plan_obj = new sql_query();
    $sql_plan_obj->string = "SELECT services.typeid, service_types.name FROM services LEFT JOIN service_types ON service_types.id = services.typeid WHERE services.id='{$id}' LIMIT 1";
    $sql_plan_obj->execute();
    if (!$sql_plan_obj->num_rows()) {
        $_SESSION["error"]["message"][] = "The service you have attempted to edit - {$id} - does not exist in this system.";
    } else {
        $sql_plan_obj->fetch_array();
    }
    // fetch fields depending on the service type
    switch ($sql_plan_obj->data[0]["name"]) {
        case "generic_with_usage":
            $data["units"] = @security_form_input_predefined("any", "units", 1, "");
            $data["included_units"] = @security_form_input_predefined("int", "included_units", 0, "");
            $data["price_extraunits"] = @security_form_input_predefined("money", "price_extraunits", 0, "");
            $data["usage_mode"] = @security_form_input_predefined("int", "usage_mode", 1, "");
            $data["alert_80pc"] = @security_form_input_predefined("any", "alert_80pc", 0, "");
            $data["alert_100pc"] = @security_form_input_predefined("any", "alert_100pc", 0, "");
            $data["alert_extraunits"] = @security_form_input_predefined("any", "alert_extraunits", 0, "");
            break;
        case "licenses":
            $data["units"] = @security_form_input_predefined("any", "units", 1, "");
            $data["included_units"] = @security_form_input_predefined("int", "included_units", 0, "");
            $data["price_extraunits"] = @security_form_input_predefined("money", "price_extraunits", 0, "");
            break;
        case "time":
            $data["units"] = @security_form_input_predefined("int", "units", 1, "");
            $data["included_units"] = @security_form_input_predefined("int", "included_units", 0, "");
            $data["price_extraunits"] = @security_form_input_predefined("money", "price_extraunits", 0, "");
            // force data usage/time to be incrementing
            $data["usage_mode"] = sql_get_singlevalue("SELECT id as value FROM service_usage_modes WHERE name='incrementing' LIMIT 1");
            $data["alert_80pc"] = @security_form_input_predefined("any", "alert_80pc", 0, "");
            $data["alert_100pc"] = @security_form_input_predefined("any", "alert_100pc", 0, "");
            $data["alert_extraunits"] = @security_form_input_predefined("any", "alert_extraunits", 0, "");
            break;
        case "data_traffic":
            // general data traffic options
            $data["units"] = @security_form_input_predefined("int", "units", 1, "");
            // force data usage/time to be incrementing
            $data["usage_mode"] = sql_get_singlevalue("SELECT id as value FROM service_usage_modes WHERE name='incrementing' LIMIT 1");
            // loop through all the traffic types
            $data["data_traffic_caps"] = array();
            $obj_sql_traffic_types = new sql_query();
            $obj_sql_traffic_types->string = "SELECT id FROM traffic_types";
            $obj_sql_traffic_types->execute();
            $obj_sql_traffic_types->num_rows();
            // will always be at least one, need for loop
            for ($i = 0; $i < $obj_sql_traffic_types->data_num_rows; $i++) {
                $cap = array();
                if (@security_form_input_predefined("checkbox", "traffic_cap_" . $i . "_active", 0, "") || $_POST["traffic_cap_" . $i . "_id"] == "1") {
                    // fetch traffic cap details
                    $cap["id_traffic_type"] = @security_form_input_predefined("int", "traffic_cap_" . $i . "_id", 1, "");
                    $cap["name"] = @security_form_input_predefined("any", "traffic_cap_" . $i . "_name", 0, "");
                    $cap["mode"] = @security_form_input_predefined("any", "traffic_cap_" . $i . "_mode", 0, "");
                    $cap["units_included"] = @security_form_input_predefined("int", "traffic_cap_" . $i . "_units_included", 0, "");
                    $cap["units_price"] = @security_form_input_predefined("money", "traffic_cap_" . $i . "_units_price", 0, "");
                    // additional checks
                    if ($cap["mode"] != "unlimited" && $cap["mode"] != "capped") {
                        log_write("error", "inc_services_process", "A data type must either be disabled or marked as capped vs unlimited");
                        error_flag_field("traffic_cap_" . $i);
                    }
                    $data["data_traffic_caps"][] = $cap;
                }
            }
            unset($obj_sql_traffic_types);
            // alert configuration
            $data["alert_80pc"] = @security_form_input_predefined("any", "alert_80pc", 0, "");
            $data["alert_100pc"] = @security_form_input_predefined("any", "alert_100pc", 0, "");
            $data["alert_extraunits"] = @security_form_input_predefined("any", "alert_extraunits", 0, "");
            break;
        case "phone_single":
            $data["id_rate_table"] = @security_form_input_predefined("int", "id_rate_table", 1, "");
            break;
        case "phone_tollfree":
            $data["id_rate_table"] = @security_form_input_predefined("int", "id_rate_table", 1, "");
            $data["phone_trunk_included_units"] = @security_form_input_predefined("int", "phone_trunk_included_units", 1, "");
            $data["phone_trunk_price_extra_units"] = @security_form_input_predefined("money", "phone_trunk_price_extra_units", 0, "");
            break;
        case "phone_trunk":
            $data["id_rate_table"] = @security_form_input_predefined("int", "id_rate_table", 1, "");
            $data["phone_ddi_included_units"] = @security_form_input_predefined("int", "phone_ddi_included_units", 1, "");
            $data["phone_ddi_price_extra_units"] = @security_form_input_predefined("money", "phone_ddi_price_extra_units", 0, "");
            $data["phone_trunk_included_units"] = @security_form_input_predefined("int", "phone_trunk_included_units", 1, "");
            $data["phone_trunk_price_extra_units"] = @security_form_input_predefined("money", "phone_trunk_price_extra_units", 0, "");
            break;
    }
    // convert checkbox input
    if ($data["alert_80pc"]) {
        $data["alert_80pc"] = 1;
    }
    if ($data["alert_100pc"]) {
        $data["alert_100pc"] = 1;
    }
    //// ERROR CHECKING ///////////////////////
    /// if there was an error, go back to the entry page
    if ($_SESSION["error"]["message"]) {
        $_SESSION["error"]["form"]["service_plan"] = "failed";
        header("Location: ../index.php?page=services/plan.php&id={$id}");
        exit(0);
    } else {
        /*
        	Begin Transaction
        */
        $sql_obj = new sql_query();
        $sql_obj->trans_begin();
        /*
        	Update plan details
        */
        $sql_obj = new sql_query();
        switch ($sql_plan_obj->data[0]["name"]) {
            case "time":
                $sql_obj->string = "UPDATE services SET " . "active='1', " . "price='" . $data["price"] . "', " . "price_setup='" . $data["price_setup"] . "', " . "discount='" . $data["discount"] . "', " . "units='" . $data["units"] . "', " . "price_extraunits='" . $data["price_extraunits"] . "', " . "included_units='" . $data["included_units"] . "', " . "billing_cycle='" . $data["billing_cycle"] . "', " . "billing_mode='" . $data["billing_mode"] . "', " . "usage_mode='" . $data["usage_mode"] . "', " . "alert_80pc='" . $data["alert_80pc"] . "', " . "alert_100pc='" . $data["alert_100pc"] . "', " . "alert_extraunits='" . $data["alert_extraunits"] . "' " . "WHERE id='{$id}'";
                $sql_obj->execute();
                break;
            case "data_traffic":
                // update service plan
                $sql_obj->string = "UPDATE services SET " . "active='1', " . "price='" . $data["price"] . "', " . "price_setup='" . $data["price_setup"] . "', " . "discount='" . $data["discount"] . "', " . "units='" . $data["units"] . "', " . "billing_cycle='" . $data["billing_cycle"] . "', " . "billing_mode='" . $data["billing_mode"] . "', " . "usage_mode='" . $data["usage_mode"] . "', " . "alert_80pc='" . $data["alert_80pc"] . "', " . "alert_100pc='" . $data["alert_100pc"] . "', " . "alert_extraunits='" . $data["alert_extraunits"] . "' " . "WHERE id='{$id}'";
                $sql_obj->execute();
                // delete existing service traffic caps
                $sql_obj->string = "DELETE FROM traffic_caps WHERE id_service='{$id}'";
                $sql_obj->execute();
                // update service traffic caps
                foreach ($data["data_traffic_caps"] as $cap) {
                    // add each traffic cap
                    $sql_obj->string = "INSERT INTO traffic_caps (id_service, id_traffic_type, mode, units_price, units_included) VALUES ('{$id}', '" . $cap["id_traffic_type"] . "', '" . $cap["mode"] . "', '" . $cap["units_price"] . "', '" . $cap["units_included"] . "')";
                    $sql_obj->execute();
                }
                break;
            case "generic_with_usage":
                $sql_obj->string = "UPDATE services SET " . "active='1', " . "price='" . $data["price"] . "', " . "price_setup='" . $data["price_setup"] . "', " . "discount='" . $data["discount"] . "', " . "units='" . $data["units"] . "', " . "price_extraunits='" . $data["price_extraunits"] . "', " . "included_units='" . $data["included_units"] . "', " . "billing_cycle='" . $data["billing_cycle"] . "', " . "billing_mode='" . $data["billing_mode"] . "', " . "usage_mode='" . $data["usage_mode"] . "', " . "alert_80pc='" . $data["alert_80pc"] . "', " . "alert_100pc='" . $data["alert_100pc"] . "', " . "alert_extraunits='" . $data["alert_extraunits"] . "' " . "WHERE id='{$id}'";
                $sql_obj->execute();
                break;
            case "licenses":
                $sql_obj->string = "UPDATE services SET " . "active='1', " . "price='" . $data["price"] . "', " . "price_setup='" . $data["price_setup"] . "', " . "discount='" . $data["discount"] . "', " . "units='" . $data["units"] . "', " . "price_extraunits='" . $data["price_extraunits"] . "', " . "included_units='" . $data["included_units"] . "', " . "billing_cycle='" . $data["billing_cycle"] . "', " . "billing_mode='" . $data["billing_mode"] . "' " . "WHERE id='{$id}'";
                $sql_obj->execute();
                break;
            case "phone_single":
                $sql_obj->string = "UPDATE services SET " . "active='1', " . "price='" . $data["price"] . "', " . "price_setup='" . $data["price_setup"] . "', " . "discount='" . $data["discount"] . "', " . "billing_cycle='" . $data["billing_cycle"] . "', " . "billing_mode='" . $data["billing_mode"] . "', " . "id_rate_table='" . $data["id_rate_table"] . "' " . "WHERE id='{$id}'";
                $sql_obj->execute();
                break;
            case "phone_trunk":
                // update basic details
                $sql_obj->string = "UPDATE services SET " . "active='1', " . "price='" . $data["price"] . "', " . "price_setup='" . $data["price_setup"] . "', " . "discount='" . $data["discount"] . "', " . "billing_cycle='" . $data["billing_cycle"] . "', " . "billing_mode='" . $data["billing_mode"] . "', " . "id_rate_table='" . $data["id_rate_table"] . "' " . "WHERE id='{$id}'";
                $sql_obj->execute();
                // delete old options (if any)
                $sql_obj->string = "DELETE FROM services_options\n\t\t\t\t\t\t\t\tWHERE option_type='service' \n\t\t\t\t\t\t\t\tAND option_type_id='" . $id . "' \n\t\t\t\t\t\t\t\tAND option_name IN ('phone_ddi_included_units',\n\t\t\t\t\t\t\t\t\t\t\t'phone_ddi_price_extra_units',\n\t\t\t\t\t\t\t\t\t\t\t'phone_trunk_included_units',\n\t\t\t\t\t\t\t\t\t\t\t'phone_trunk_price_extra_units')";
                $sql_obj->execute();
                // apply new options
                $sql_obj->string = "INSERT INTO services_options (option_type, option_type_id, option_name, option_value) VALUES ('service', '" . $id . "', 'phone_ddi_included_units', '" . $data["phone_ddi_included_units"] . "')";
                $sql_obj->execute();
                $sql_obj->string = "INSERT INTO services_options (option_type, option_type_id, option_name, option_value) VALUES ('service', '" . $id . "', 'phone_ddi_price_extra_units', '" . $data["phone_ddi_price_extra_units"] . "')";
                $sql_obj->execute();
                $sql_obj->string = "INSERT INTO services_options (option_type, option_type_id, option_name, option_value) VALUES ('service', '" . $id . "', 'phone_trunk_included_units', '" . $data["phone_trunk_included_units"] . "')";
                $sql_obj->execute();
                $sql_obj->string = "INSERT INTO services_options (option_type, option_type_id, option_name, option_value) VALUES ('service', '" . $id . "', 'phone_trunk_price_extra_units', '" . $data["phone_trunk_price_extra_units"] . "')";
                $sql_obj->execute();
                break;
            case "phone_tollfree":
                // update basic details
                $sql_obj->string = "UPDATE services SET " . "active='1', " . "price='" . $data["price"] . "', " . "price_setup='" . $data["price_setup"] . "', " . "discount='" . $data["discount"] . "', " . "billing_cycle='" . $data["billing_cycle"] . "', " . "billing_mode='" . $data["billing_mode"] . "', " . "id_rate_table='" . $data["id_rate_table"] . "' " . "WHERE id='{$id}'";
                $sql_obj->execute();
                // delete old options (if any)
                $sql_obj->string = "DELETE FROM services_options\n\t\t\t\t\t\t\t\tWHERE option_type='service' \n\t\t\t\t\t\t\t\tAND option_type_id='" . $id . "' \n\t\t\t\t\t\t\t\tAND option_name IN ('phone_trunk_included_units',\n\t\t\t\t\t\t\t\t\t\t\t'phone_trunk_price_extra_units')";
                $sql_obj->execute();
                // apply new options
                $sql_obj->string = "INSERT INTO services_options (option_type, option_type_id, option_name, option_value) VALUES ('service', '" . $id . "', 'phone_trunk_included_units', '" . $data["phone_trunk_included_units"] . "')";
                $sql_obj->execute();
                $sql_obj->string = "INSERT INTO services_options (option_type, option_type_id, option_name, option_value) VALUES ('service', '" . $id . "', 'phone_trunk_price_extra_units', '" . $data["phone_trunk_price_extra_units"] . "')";
                $sql_obj->execute();
                break;
            case "generic_no_usage":
            case "bundle":
            default:
                $sql_obj->string = "UPDATE services SET " . "active='1', " . "price='" . $data["price"] . "', " . "price_setup='" . $data["price_setup"] . "', " . "discount='" . $data["discount"] . "', " . "billing_cycle='" . $data["billing_cycle"] . "', " . "billing_mode='" . $data["billing_mode"] . "' " . "WHERE id='{$id}'";
                $sql_obj->execute();
                break;
        }
        /*
        	Update the Journal
        */
        journal_quickadd_event("services", $id, "Service plan configuration changed");
        /*
        	Commit
        */
        if (error_check()) {
            $sql_obj->trans_rollback();
            log_write("error", "process", "An error occured whilst attempting to update service plan information. No changes have been made.");
        } else {
            $sql_obj->trans_commit();
            log_write("notification", "process", "Service successfully updated.");
        }
        // display updated details
        header("Location: ../index.php?page=services/plan.php&id={$id}");
        exit(0);
    }
    // end if passed tests
}
 	does belong to an quote
 
 	This prevent a malicious user from using this page to fetch other files
 	belonging to other journals or quotes.
 */
 // get the ID of the journal from the file ID
 $customid = sql_get_singlevalue("SELECT customid as value FROM file_uploads WHERE id='{$fileid}'");
 if (!$customid) {
     $_SESSION["error"]["message"][] = "No record for this file found in the database.";
 } else {
     // make sure the custom ID of the file matches the journal ID
     if ($customid != $journalid) {
         $_SESSION["error"]["message"][] = "Error: File customid and journal ID do not match";
     } else {
         // make sure the journal entry belongs to an quote
         $quoteid = sql_get_singlevalue("SELECT customid as value FROM journal WHERE journalname='account_quotes' AND id='{$journalid}'");
         if (!$quoteid) {
             $_SESSION["error"]["message"][] = "Unable to match the provided journal entry to an quote/transaction journal.";
         }
     }
 }
 /*
 	Produce output - either output request file content,
 	or take the user to a message page to view errors.
 */
 if ($_SESSION["error"]["message"]) {
     header("Location: ../../index.php?page=message.php");
     exit(0);
 } else {
     // output file data
     $file_obj = new file_storage();
 function render_html()
 {
     // heading
     print "<h3>CUSTOMER LIST</h3><br><br>";
     print "<p>List of customers with direct debit enabled and a current balance</p>";
     // load options form
     // $this->obj_table_list->render_options_form();
     // display results
     if (!count($this->obj_table_list->columns)) {
         format_msgbox("important", "<p>Please select some valid options to display.</p>");
     } else {
         if (!$this->obj_table_list->data_num_rows) {
             format_msgbox("info", "<p>You currently have no customers in your database.</p>");
         } else {
             // phases link
             //			$structure = NULL;
             //			$structure["reseller_id"]["column"]	= "id";
             //			$this->obj_table_list->add_link("customer_reseller", "customers/view.php", $structure);
             // calculate all the totals and prepare processed values
             $this->obj_table_list->render_table_prepare();
             // display header row
             print "<table class=\"table_content\" cellspacing=\"0\" width=\"100%\">";
             print "<tr>";
             foreach ($this->obj_table_list->columns as $column) {
                 print "<td class=\"header\"><b>" . $this->obj_table_list->render_columns[$column] . "</b></td>";
             }
             //placeholder for links
             print "<td class=\"header\">&nbsp;</td>";
             print "</tr>";
             // display data
             for ($i = 0; $i < $this->obj_table_list->data_num_rows; $i++) {
                 $customer_id = $this->obj_table_list->data[$i]["id"];
                 $contact_id = sql_get_singlevalue("SELECT id AS value FROM customer_contacts WHERE customer_id = '" . $customer_id . "' AND role = 'accounts' LIMIT 1");
                 print "<tr>";
                 foreach ($this->obj_table_list->columns as $columns) {
                     print "<td valign=\"top\">";
                     //contact name
                     if ($columns == "name_contact") {
                         $value = sql_get_singlevalue("SELECT contact AS value FROM customer_contacts WHERE id = '" . $contact_id . "' LIMIT 1");
                         if ($value) {
                             print $value;
                         }
                     } else {
                         if ($columns == "contact_phone") {
                             $value = sql_get_singlevalue("SELECT detail AS value FROM customer_contact_records WHERE contact_id = '" . $contact_id . "' AND type = 'phone' LIMIT 1");
                             if ($value) {
                                 print $value;
                             }
                         } else {
                             if ($columns == "contact_mobile") {
                                 $value = sql_get_singlevalue("SELECT detail AS value FROM customer_contact_records WHERE contact_id = '" . $contact_id . "' AND type= 'mobile' LIMIT 1");
                                 if ($value) {
                                     print $value;
                                 }
                             } else {
                                 if ($columns == "contact_email") {
                                     $value = sql_get_singlevalue("SELECT detail AS value FROM customer_contact_records WHERE contact_id = '" . $contact_id . "' AND type= 'email' LIMIT 1");
                                     if ($value) {
                                         print $value;
                                     }
                                 } else {
                                     if ($columns == "contact_fax") {
                                         $value = sql_get_singlevalue("SELECT detail AS value FROM customer_contact_records WHERE contact_id = '" . $contact_id . "' AND type= 'fax' LIMIT 1");
                                         if ($value) {
                                             print $value;
                                         }
                                     } else {
                                         if ($this->obj_table_list->data_render[$i][$columns]) {
                                             //								print $columns;
                                             print $this->obj_table_list->data_render[$i][$columns];
                                         } else {
                                             print "&nbsp;";
                                         }
                                     }
                                 }
                             }
                         }
                     }
                     print "</td>";
                 }
                 //links
                 print "<td align=\"right\" nowrap >";
                 print "<a class=\"button_small\" href=\"index.php?page=customers/view.php&id=" . $this->obj_table_list->data[$i]["id"] . "\">" . lang_trans("details") . "</a> ";
                 print "<a class=\"button_small\" href=\"index.php?page=customers/attributes.php&id_customer=" . $this->obj_table_list->data[$i]["id"] . "\">" . lang_trans("tbl_lnk_attributes") . "</a> ";
                 print "<a class=\"button_small\" href=\"index.php?page=customers/orders.php&id_customer=" . $this->obj_table_list->data[$i]["id"] . "\">" . lang_trans("orders") . "</a> ";
                 print "<a class=\"button_small\" href=\"index.php?page=customers/invoices.php&id=" . $this->obj_table_list->data[$i]["id"] . "\">" . lang_trans("invoices") . "</a> ";
                 print "<a class=\"button_small\" href=\"index.php?page=customers/services.php&id=" . $this->obj_table_list->data[$i]["id"] . "\">" . lang_trans("services") . "</a> ";
                 print "</td>";
                 print "</tr>";
             }
             print "</table>";
             print "<br />";
             // display CSV/PDF download link
             print "<p align=\"right\"><a class=\"button_export\" style=\"font-weight: normal;\"  href=\"index-export.php?mode=csv&page=customers/customers-billing.php\">Export as CSV</a></p>";
             print "<p align=\"right\"><a class=\"button_export\" style=\"font-weight: normal;\" href=\"index-export.php?mode=pdf&page=customers/customers-billing.php\">Export as PDF</a></p>";
         }
     }
 }
function service_invoices_generate($customerid = NULL)
{
    log_debug("inc_services_invoicegen", "Executing service_invoices_generate({$customerid})");
    /*
    	Invoice Report Statistics
    */
    $invoice_stats = array();
    $invoice_stats["time_start"] = time();
    $invoice_stats["total"] = 0;
    $invoice_stats["total_failed"] = 0;
    /*
    	Run through all the customers
    */
    $sql_customers_obj = new sql_query();
    $sql_customers_obj->string = "SELECT id, code_customer, name_customer FROM customers";
    if ($customerid) {
        $sql_customers_obj->string .= " WHERE id='{$customerid}' LIMIT 1";
    }
    $sql_customers_obj->execute();
    if ($sql_customers_obj->num_rows()) {
        $sql_customers_obj->fetch_array();
        foreach ($sql_customers_obj->data as $customer_data) {
            /*
            	Fetch all periods belonging to this customer which need to be billed.
            */
            $sql_periods_obj = new sql_query();
            $sql_periods_obj->string = "SELECT " . "services_customers_periods.id, " . "services_customers_periods.rebill, " . "services_customers_periods.invoiceid, " . "services_customers_periods.invoiceid_usage, " . "services_customers_periods.date_start, " . "services_customers_periods.date_end, " . "services_customers.date_period_first, " . "services_customers.date_period_next, " . "services_customers.date_period_last, " . "services_customers.id as id_service_customer, " . "services_customers.serviceid " . "FROM services_customers_periods " . "LEFT JOIN services_customers ON services_customers.id = services_customers_periods.id_service_customer " . "WHERE " . "services_customers.customerid='" . $customer_data["id"] . "' " . "AND (invoiceid = '0' OR rebill = '1')" . "AND date_billed <= '" . date("Y-m-d") . "'";
            $sql_periods_obj->execute();
            if ($sql_periods_obj->num_rows()) {
                $sql_periods_obj->fetch_array();
                /*
                	BILL CUSTOMER
                
                	This customer has at least one service that needs to be billed. We need to create
                	a new invoice, and then process each service, adding the services to the invoice as 
                	items.
                */
                /*
                	Start Transaction
                
                	(one transaction per invoice)
                */
                $sql_obj = new sql_query();
                $sql_obj->trans_begin();
                /*
                	Create new invoice
                */
                $invoice = new invoice();
                $invoice->type = "ar";
                $invoice->prepare_code_invoice();
                $invoice->data["customerid"] = $customer_data["id"];
                $invoice->data["employeeid"] = 1;
                // set employee to the default internal user
                $invoice->prepare_date_shift();
                if (!$invoice->action_create()) {
                    log_write("error", "services_invoicegen", "Unexpected problem occured whilst attempting to create invoice.");
                    $sql_obj->trans_rollback();
                    return 0;
                }
                $invoiceid = $invoice->id;
                $invoicecode = $invoice->data["code_invoice"];
                unset($invoice);
                /*
                	Create Service Items
                						
                	We need to create an item for basic service plan - IE: the regular fixed fee, and another item for any
                	excess usage.
                */
                foreach ($sql_periods_obj->data as $period_data) {
                    /*
                    	TODO:
                    
                    	We should be able to re-bill usage here when needed with a clever cheat - if we load the periods to be billed,
                    	we can then ignore the plan item, and rebill the usage item.
                    */
                    $period_data["mode"] = "standard";
                    if ($period_data["rebill"]) {
                        if (!$period_data["invoiceid_usage"] && $period_data["invoiceid"]) {
                            // the selected period has been billed, but the usage has been flagged for rebilling - we need to *ignore* the base plan
                            // item and only bill for the usage range.
                            $period_data["mode"] = "rebill_usage";
                        }
                    }
                    // fetch service details
                    $obj_service = new service_bundle();
                    $obj_service->option_type = "customer";
                    $obj_service->option_type_id = $period_data["id_service_customer"];
                    if (!$obj_service->verify_id_options()) {
                        log_write("error", "customers_services", "Unable to verify service ID of " . $period_data["id_service_customer"] . " as being valid.");
                        return 0;
                    }
                    $obj_service->load_data();
                    $obj_service->load_data_options();
                    // ratio is used to adjust prices for partial periods
                    $ratio = 1;
                    if ($obj_service->data["billing_mode_string"] == "monthend" || $obj_service->data["billing_mode_string"] == "monthadvance" || $obj_service->data["billing_mode_string"] == "monthtelco") {
                        log_debug("services_invoicegen", "Invoice bills by month date");
                        /*
                        	Handle monthly billing
                        
                        	Normally, monthly billing is easy, however the very first billing period is special, as it may span a more than 1 month, or
                        	consist of less than the full month, depending on the operational mode.
                        
                        	SERVICE_PARTPERIOD_MODE == seporate
                        
                        		If a service is started on 2008-01-09, the end date of the billing period will be 2008-01-31, which is less
                        		than one month - 22 days.
                        
                        		We can calculate with:
                        
                        		( standard_cost / normal_month_num_days ) * num_days_in_partial_month == total_amount
                        
                        
                        	SERVICE_PARTPERIOD_MODE == merge
                        
                        		If a service is started on 2008-01-09, the end of the billing period will be 2008-02-29, which is 1 month + 21 day.
                        
                        		We can calculate with:
                        
                        		( standard_cost / normal_month_num_days ) * num_days_in_partial_month == extra_amount
                        		total_amount = (extra_amount + normal_amount)
                        
                        	Note: we do not increase included units for licenses service types, since these need to remain fixed and do not
                        	scale with the month.
                        */
                        // check if the period start date is not the first of the month - this means that the period
                        // is an inital partial period.
                        if (time_calculate_daynum($period_data["date_start"]) != "01") {
                            // very first billing month
                            log_write("debug", "services_invoicegen", "First billing month for this service, adjusting pricing to suit days.");
                            // figure out normal period length - rememeber, whilst service may be configured to align monthly, it needs to
                            // handle pricing calculations for variations such as month, year, etc.
                            // get the number of days of a regular period - this correctly handles, month, year, etc
                            // 1. generate the next period dates, this will be of regular length
                            // 2. calculate number of days
                            $tmp_dates = service_period_dates_generate($period_data["date_period_next"], $obj_service->data["billing_cycle_string"], $obj_service->data["billing_mode_string"]);
                            $regular_period_num_days = sql_get_singlevalue("SELECT DATEDIFF('" . $tmp_dates["end"] . "', '" . $tmp_dates["start"] . "') as value");
                            // process for short/long periods
                            //
                            if ($GLOBALS["config"]["SERVICE_PARTPERIOD_MODE"] == "seporate") {
                                log_write("debug", "services_invoicegen", "Adjusting for partial month period (SERVICE_PARTPERIOD_MODE == seporate)");
                                // work out the total number of days in partial month
                                $short_month_days_total = time_calculate_daynum(time_calculate_monthdate_last($period_data["date_start"]));
                                $short_month_days_short = $short_month_days_total - time_calculate_daynum($period_data["date_start"]);
                                log_write("debug", "services_invoicegen", "Short initial billing period of {$short_month_days_short} days");
                                // calculate correct base fee
                                $obj_service->data["price"] = $obj_service->data["price"] / $regular_period_num_days * $short_month_days_short;
                                // calculate ratio
                                $ratio = $short_month_days_short / $regular_period_num_days;
                                log_write("debug", "services_invoicegen", "Calculated service bill ratio of {$ratio} to handle short period.");
                            } else {
                                log_write("debug", "services_invoicegen", "Adjusting for extended month period (SERVICE_PARTPERIOD_MODE == merge");
                                // work out the number of days extra
                                $extra_month_days_total = time_calculate_daynum(time_calculate_monthdate_last($period_data["date_start"]));
                                $extra_month_days_extra = $extra_month_days_total - time_calculate_daynum($period_data["date_start"]);
                                log_debug("services_invoicegen", "{$extra_month_days_extra} additional days ontop of started billing period");
                                // calculate correct base fee
                                $obj_service->data["price"] = $obj_service->data["price"] / $regular_period_num_days * $extra_month_days_extra + $obj_service->data["price"];
                                // calculate ratio
                                $ratio = ($extra_month_days_extra + $extra_month_days_total) / $regular_period_num_days;
                                log_write("debug", "services_invoicegen", "Calculated service bill ratio of {$ratio} to handle extended period.");
                            }
                        }
                    }
                    /*
                    	Service Last Period Handling
                    
                    	If this is the last period for a service, it may be of a different link to the regular full period term
                    	- this effects both month and period based services.
                    */
                    if ($period_data["date_period_last"] != "0000-00-00") {
                        log_write("debug", "services_invoicegen", "Service has a final period date set (" . $period_data["date_period_last"] . ")");
                        if ($period_data["date_period_last"] == $period_data["date_end"] || time_date_to_timestamp($period_data["date_period_last"]) < time_date_to_timestamp($period_data["date_end"])) {
                            log_write("debug", "services_invoicegen", "Service is a final period, checking for time adjustment (if any)");
                            // fetch the regular end date
                            $orig_dates = service_period_dates_generate($period_data["date_start"], $obj_service->data["billing_cycle_string"], $obj_service->data["billing_mode_string"]);
                            if ($orig_dates["end"] != $period_data["date_end"]) {
                                // work out the total number of days
                                $time = NULL;
                                $time["start"] = time_date_to_timestamp($period_data["date_start"]);
                                $time["end_orig"] = time_date_to_timestamp($orig_dates["end"]);
                                $time["end_new"] = time_date_to_timestamp($period_data["date_end"]);
                                $time["orig_days"] = sprintf("%d", ($time["end_orig"] - $time["start"]) / 86400);
                                $time["new_days"] = sprintf("%d", ($time["end_new"] - $time["start"]) / 86400);
                                log_write("debug", "services_invoicegen", "Short initial billing period of " . $time["new_days"] . " days rather than expected " . $time["orig_days"] . "");
                                // calculate correct base fee
                                $obj_service->data["price"] = $obj_service->data["price"] / $time["orig_days"] * $time["new_days"];
                                // calculate ratio
                                $ratio = $time["new_days"] / $time["orig_days"];
                                log_write("debug", "services_invoicegen", "Calculated service bill ratio of {$ratio} to handle short period.");
                                unset($time);
                            } else {
                                log_write("debug", "services_invoicegen", "Final service period is regular size, no adjustment required.");
                            }
                        }
                    }
                    /*
                    	Service Base Plan Item
                    
                    	Note: There can be a suitation where we shouldn't charge for the base plan
                    	fee, when the period end date is older than the service first start date.
                    
                    	This can happen due to the migration mode, which creates a usage-only period
                    	before the actual plan starts properly.
                    */
                    if (time_date_to_timestamp($period_data["date_period_first"]) < time_date_to_timestamp($period_data["date_end"]) && $period_data["mode"] == "standard") {
                        // date of the period is newer than the start date
                        log_write("debug", "inc_service_invoicegen", "Generating base plan item for period " . $period_data["date_start"] . " to " . $period_data["date_end"] . "");
                        // start the item
                        $invoice_item = new invoice_items();
                        $invoice_item->id_invoice = $invoiceid;
                        $invoice_item->type_invoice = "ar";
                        $invoice_item->type_item = "service";
                        $itemdata = array();
                        // chart ID
                        $itemdata["chartid"] = $obj_service->data["chartid"];
                        // service ID
                        $itemdata["customid"] = $obj_service->id;
                        // no units apply
                        $itemdata["units"] = "";
                        // description
                        switch ($obj_service->data["typeid_string"]) {
                            case "phone_single":
                                $itemdata["description"] = addslashes($obj_service->data["name_service"]) . " from " . $period_data["date_start"] . " to " . $period_data["date_end"] . " (" . $obj_service->data["phone_ddi_single"] . ")";
                                if ($obj_service->data["description"]) {
                                    $itemdata["description"] .= "\n\n";
                                    $itemdata["description"] .= addslashes($obj_service->data["description"]);
                                }
                                break;
                            default:
                                $itemdata["description"] = addslashes($obj_service->data["name_service"]) . " from " . $period_data["date_start"] . " to " . $period_data["date_end"];
                                if ($obj_service->data["description"]) {
                                    $itemdata["description"] .= "\n\n";
                                    $itemdata["description"] .= addslashes($obj_service->data["description"]);
                                }
                                break;
                        }
                        // handle final periods
                        if ($period_data["date_period_last"] != "0000-00-00" && $period_data["date_start"] == $period_data["date_end"]) {
                            $itemdata["description"] = addslashes($obj_service->data["name_service"]) . " service terminated as of " . $period_data["date_end"] . "";
                        }
                        // is this service item part of a bundle? if it is, we shouldn't charge for the plan
                        if (!empty($obj_service->data["id_bundle_component"])) {
                            // no charge for the base plan, since this is handled by
                            // the bundle item itself
                            $itemdata["price"] = "0.00";
                            $itemdata["quantity"] = 1;
                            $itemdata["discount"] = "0";
                            // append description
                            $itemdata["description"] .= " (part of bundle)";
                        } else {
                            // amount
                            $itemdata["price"] = $obj_service->data["price"];
                            $itemdata["quantity"] = 1;
                            $itemdata["discount"] = $obj_service->data["discount"];
                        }
                        // create item
                        $invoice_item->prepare_data($itemdata);
                        $invoice_item->action_update();
                        unset($invoice_item);
                    } else {
                        log_write("debug", "inc_service_invoicegen", "Skipping base plan item generation, due to migration or rebill mode operation");
                    }
                    /*
                    	Service Usage Items
                    
                    	Create another item on the invoice for any usage, provided that the service type is a usage service
                    */
                    $period_usage_data = array();
                    /*
                    	Depending on the service type, we need to handle the usage period in one of two ways:
                    	1. Invoice usage for the current period that has just ended (periodend/monthend)
                    	2. Invoice usage for the period BEFORE the current period. (periodtelco/monthtelco)
                    */
                    if ($obj_service->data["billing_mode_string"] == "periodtelco" || $obj_service->data["billing_mode_string"] == "monthtelco") {
                        log_write("debug", "service_invoicegen", "Determining previous period for telco-style usage billing (if any)");
                        if ($period_data["mode"] == "standard") {
                            // fetch previous period (if any) - we can determine the previous by subtracting one day from the current period start date.
                            $tmp_date = explode("-", $period_data["date_start"]);
                            $period_usage_data["date_end"] = date("Y-m-d", mktime(0, 0, 0, $tmp_date[1], $tmp_date[2] - 1, $tmp_date[0]));
                        } elseif ($period_data["mode"] == "rebill_usage") {
                            // use the period as the usage period
                            log_write("debug", "service_invoicegen", "Using the selected period " . $period_usage_data["date_end"] . " for rebill_usage mode");
                            $period_usage_data["date_end"] = $period_data["date_end"];
                        }
                        // fetch period data to confirm previous period
                        $sql_obj = new sql_query();
                        $sql_obj->string = "SELECT id, date_start, date_end FROM services_customers_periods WHERE id_service_customer='" . $period_data["id_service_customer"] . "' AND date_end='" . $period_usage_data["date_end"] . "' LIMIT 1";
                        $sql_obj->execute();
                        if ($sql_obj->num_rows()) {
                            log_write("debug", "service_invoicegen", "Billing for seporate past usage period");
                            // there is a valid usage period
                            $period_usage_data["active"] = "yes";
                            // fetch dates
                            $sql_obj->fetch_array();
                            $period_usage_data["id_service_customer"] = $period_data["id_service_customer"];
                            $period_usage_data["id"] = $sql_obj->data[0]["id"];
                            $period_usage_data["date_start"] = $sql_obj->data[0]["date_start"];
                            $period_usage_data["date_end"] = $sql_obj->data[0]["date_end"];
                            // tracing
                            log_write("debug", "service_invoicegen", "Current period is (" . $period_data["date_start"] . " to " . $period_data["date_end"] . "), usage period is (" . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"] . ")");
                            // reset ratio
                            $ratio = 1;
                            /*
                            	TODO: This code is a replicant of the section further above for calculating partial
                            	periods and should really be functionalised as part of service usage continual improvements
                            */
                            // calculate usage abnormal period
                            if ($obj_service->data["billing_mode_string"] == "monthend" || $obj_service->data["billing_mode_string"] == "monthadvance" || $obj_service->data["billing_mode_string"] == "monthtelco") {
                                log_debug("services_invoicegen", "Usage period service bills by month date");
                                if (time_calculate_daynum($period_usage_data["date_start"]) != "01") {
                                    // very first billing month
                                    log_write("debug", "services_invoicegen", "First billing month for this usage period, adjusting pricing to suit days.");
                                    if ($GLOBALS["config"]["SERVICE_PARTPERIOD_MODE"] == "seporate") {
                                        log_write("debug", "services_invoicegen", "Adjusting for partial month period (SERVICE_PARTPERIOD_MODE == seporate)");
                                        // work out the total number of days
                                        $short_month_days_total = time_calculate_daynum(time_calculate_monthdate_last($period_usage_data["date_start"]));
                                        $short_month_days_short = $short_month_days_total - time_calculate_daynum($period_usage_data["date_start"]);
                                        log_write("debug", "services_invoicegen", "Short initial billing period of {$short_month_days_short} days");
                                        // calculate ratio
                                        $ratio = $short_month_days_short / $short_month_days_total;
                                        log_write("debug", "services_invoicegen", "Calculated service bill ratio of {$ratio} to handle short period.");
                                    } else {
                                        log_write("debug", "services_invoicegen", "Adjusting for extended month period (SERVICE_PARTPERIOD_MODE == merge");
                                        // work out the number of days extra
                                        $extra_month_days_total = time_calculate_daynum(time_calculate_monthdate_last($period_usage_data["date_start"]));
                                        $extra_month_days_extra = $extra_month_days_total - time_calculate_daynum($period_usage_data["date_start"]);
                                        log_debug("services_invoicegen", "{$extra_month_days_extra} additional days ontop of started billing period");
                                        // calculate ratio
                                        $ratio = ($extra_month_days_extra + $extra_month_days_total) / $extra_month_days_total;
                                        log_write("debug", "services_invoicegen", "Calculated service bill ratio of {$ratio} to handle extended period.");
                                    }
                                }
                            }
                            // end of calculate usage abnormal period
                            if ($period_data["date_period_last"] != "0000-00-00") {
                                log_write("debug", "services_invoicegen", "Service has a final period date set (" . $period_data["date_period_last"] . ")");
                                if ($period_data["date_period_last"] == $period_usage_data["date_end"] || time_date_to_timestamp($period_data["date_period_last"]) < time_date_to_timestamp($period_usage_data["date_end"])) {
                                    log_write("debug", "services_invoicegen", "Service is a final period, checking for time adjustment (if any)");
                                    // fetch the regular end date
                                    $orig_dates = service_period_dates_generate($period_usage_data["date_start"], $obj_service->data["billing_cycle_string"], $obj_service->data["billing_mode_string"]);
                                    if ($orig_dates["end"] != $period_usage_data["date_end"]) {
                                        // work out the total number of days
                                        $time = NULL;
                                        $time["start"] = time_date_to_timestamp($period_usage_data["date_start"]);
                                        $time["end_orig"] = time_date_to_timestamp($orig_dates["end"]);
                                        $time["end_new"] = time_date_to_timestamp($period_usage_data["date_end"]);
                                        $time["orig_days"] = sprintf("%d", ($time["end_orig"] - $time["start"]) / 86400);
                                        $time["new_days"] = sprintf("%d", ($time["end_new"] - $time["start"]) / 86400);
                                        log_write("debug", "services_invoicegen", "Short initial billing period of " . $time["new_days"] . " days rather than expected " . $time["orig_days"] . "");
                                        // calculate correct base fee
                                        $obj_service->data["price"] = $obj_service->data["price"] / $time["orig_days"] * $time["new_days"];
                                        // calculate ratio
                                        $ratio = $time["new_days"] / $time["orig_days"];
                                        log_write("debug", "services_invoicegen", "Calculated service bill ratio of {$ratio} to handle short period.");
                                        unset($time);
                                    } else {
                                        log_write("debug", "services_invoicegen", "Final service period is regular size, no adjustment required.");
                                    }
                                }
                            }
                        } else {
                            log_write("debug", "service_invoicegen", "Not billing for past usage, as this appears to be the first plan period so no usage can exist yet");
                        }
                    } else {
                        log_write("debug", "service_invoicegen", "Using plan period as data usage period (" . $period_data["date_start"] . " to " . $period_data["date_end"] . "");
                        // use current period
                        $period_usage_data["active"] = "yes";
                        $period_usage_data["id_service_customer"] = $period_data["id_service_customer"];
                        $period_usage_data["id"] = $period_data["id"];
                        $period_usage_data["date_start"] = $period_data["date_start"];
                        $period_usage_data["date_end"] = $period_data["date_end"];
                    }
                    /*
                    	Create usage items if there is a valid usage period
                    */
                    if (!empty($period_usage_data["active"])) {
                        log_write("debug", "service_invoicegen", "Creating usage items due to active usage period");
                        switch ($obj_service->data["typeid_string"]) {
                            case "generic_with_usage":
                                /*
                                	GENERIC_WITH_USAGE
                                
                                	This service is to be used for any non-traffic, non-time accounting service that needs to track usage. Examples of this
                                	could be counting the number of API requests, size of disk usage on a vhost, etc.
                                */
                                log_write("debug", "service_invoicegen", "Processing usage items for generic_with_usage");
                                /*
                                	Usage Item Basics
                                */
                                // start the item
                                $invoice_item = new invoice_items();
                                $invoice_item->id_invoice = $invoiceid;
                                $invoice_item->type_invoice = "ar";
                                $invoice_item->type_item = "service_usage";
                                $itemdata = array();
                                $itemdata["chartid"] = $obj_service->data["chartid"];
                                $itemdata["description"] = addslashes($obj_service->data["name_service"]) . " usage from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"];
                                $itemdata["customid"] = $obj_service->id;
                                $itemdata["discount"] = 0;
                                /*
                                	Adjust Included Units to handle partial or extended periods
                                */
                                if ($ratio != "1") {
                                    $obj_service->data["included_units"] = sprintf("%d", $obj_service->data["included_units"] * $ratio);
                                }
                                /*
                                	Fetch usage amount
                                */
                                $usage_obj = new service_usage_generic();
                                $usage_obj->id_service_customer = $period_usage_data["id_service_customer"];
                                $usage_obj->date_start = $period_usage_data["date_start"];
                                $usage_obj->date_end = $period_usage_data["date_end"];
                                if ($usage_obj->load_data_service()) {
                                    $usage_obj->fetch_usagedata();
                                    if ($usage_obj->data["total_byunits"]) {
                                        $usage = $usage_obj->data["total_byunits"];
                                    } else {
                                        $usage = $usage_obj->data["total"];
                                    }
                                }
                                unset($usage_obj);
                                /*
                                	Charge for the usage in units
                                */
                                $unitname = addslashes($obj_service->data["units"]);
                                if ($usage > $obj_service->data["included_units"]) {
                                    // there is excess usage that we can bill for.
                                    $usage_excess = $usage - $obj_service->data["included_units"];
                                    // set item attributes
                                    $itemdata["price"] = $obj_service->data["price_extraunits"];
                                    $itemdata["quantity"] = $usage_excess;
                                    $itemdata["units"] = $unitname;
                                    // description example:		Used 120 ZZ out of 50 ZZ included in plan
                                    //				Excess usage of 70 ZZ charged at $5.00 per ZZ
                                    $itemdata["description"] .= "\nUsed {$usage} {$unitname} out of " . $obj_service->data["included_units"] . " {$unitname} included in plan.";
                                    $itemdata["description"] .= "\nExcess usage of {$usage_excess} {$unitname} charged at " . $obj_service->data["price_extraunits"] . " per {$unitname}.";
                                } else {
                                    // description example:		Used 120 ZZ out of 50 ZZ included in plan
                                    $itemdata["description"] .= "\nUsed {$usage} {$unitname} out of " . $obj_service->data["included_units"] . " {$unitname} included in plan.";
                                }
                                /*
                                	Add the item to the invoice
                                */
                                $invoice_item->prepare_data($itemdata);
                                $invoice_item->action_update();
                                unset($invoice_item);
                                /*
                                	Update usage value for period - this summary value is visable on the service
                                	history page and saves having to query lots of records to generate period totals.
                                */
                                $sql_obj = new sql_query();
                                $sql_obj->string = "UPDATE services_customers_periods SET usage_summary='{$usage}' WHERE id='" . $period_usage_data["id"] . "' LIMIT 1";
                                $sql_obj->execute();
                                break;
                            case "licenses":
                                /*
                                	LICENSES
                                
                                	No data usage, but there is a quantity field for the customer's account to specify the
                                	quantity of licenses that they have.
                                */
                                log_write("debug", "service_invoicegen", "Processing usage items for licenses");
                                /*
                                	Usage Item Basics
                                */
                                // start the item
                                $invoice_item = new invoice_items();
                                $invoice_item->id_invoice = $invoiceid;
                                $invoice_item->type_invoice = "ar";
                                $invoice_item->type_item = "service_usage";
                                $itemdata = array();
                                $itemdata["chartid"] = $obj_service->data["chartid"];
                                $itemdata["description"] = addslashes($obj_service->data["name_service"]) . " usage from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"];
                                $itemdata["customid"] = $obj_service->id;
                                $itemdata["discount"] = 0;
                                /*
                                	Determine Additional Charges
                                */
                                // charge for any extra licenses
                                if ($obj_service->data["quantity"] > $obj_service->data["included_units"]) {
                                    // there is excess usage that we can bill for.
                                    $licenses_excess = $obj_service->data["quantity"] - $obj_service->data["included_units"];
                                    // set item attributes
                                    $itemdata["price"] = $obj_service->data["price_extraunits"] * $ratio;
                                    $itemdata["quantity"] = $licenses_excess;
                                    $itemdata["units"] = addslashes($obj_service->data["units"]);
                                    // description example:		10 licences included
                                    //				2 additional licenses charged at $24.00 each
                                    $itemdata["description"] .= "\n" . $obj_service->data["included_units"] . " " . $obj_service->data["units"] . " included";
                                    $itemdata["description"] .= "\n{$licenses_excess} additional " . $obj_service->data["units"] . " charged at " . $obj_service->data["price_extraunits"] . " each.";
                                } else {
                                    // description example:		10 licenses
                                    $itemdata["description"] .= "\n" . $period_data["quantity"] . " " . $period_data["units"] . ".";
                                }
                                /*
                                	Add the item to the invoice
                                */
                                $invoice_item->prepare_data($itemdata);
                                $invoice_item->action_update();
                                unset($invoice_item);
                                break;
                            case "time":
                                /*
                                	TIME
                                
                                	Simular to the generic usage type, but instead of units being a text field, units
                                	is an ID to the service_units table.
                                */
                                log_write("debug", "service_invoicegen", "Processing usage items for time traffic");
                                /*
                                	Usage Item Basics
                                */
                                // start the item
                                $invoice_item = new invoice_items();
                                $invoice_item->id_invoice = $invoiceid;
                                $invoice_item->type_invoice = "ar";
                                $invoice_item->type_item = "service_usage";
                                $itemdata = array();
                                $itemdata["chartid"] = $obj_service->data["chartid"];
                                $itemdata["description"] = addslashes($obj_service->data["name_service"]) . " usage from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"];
                                $itemdata["customid"] = $obj_service->id;
                                $itemdata["discount"] = 0;
                                /*
                                	Adjust Included Units to handle partial or extended periods
                                */
                                if ($ratio != "1") {
                                    $obj_service->data["included_units"] = sprintf("%d", $obj_service->data["included_units"] * $ratio);
                                }
                                /*
                                	Fetch usage amount
                                */
                                $usage_obj = new service_usage_generic();
                                $usage_obj->id_service_customer = $period_usage_data["id_service_customer"];
                                $usage_obj->date_start = $period_usage_data["date_start"];
                                $usage_obj->date_end = $period_usage_data["date_end"];
                                if ($usage_obj->load_data_service()) {
                                    $usage_obj->fetch_usagedata();
                                    if ($usage_obj->data["total_byunits"]) {
                                        $usage = $usage_obj->data["total_byunits"];
                                    } else {
                                        $usage = $usage_obj->data["total"];
                                    }
                                }
                                unset($usage_obj);
                                /*
                                	Charge for the usage in units
                                */
                                $unitname = sql_get_singlevalue("SELECT name as value FROM service_units WHERE id='" . $obj_service->data["units"] . "'");
                                if ($usage > $obj_service->data["included_units"]) {
                                    // there is excess usage that we can bill for.
                                    $usage_excess = $usage - $obj_service->data["included_units"];
                                    // set item attributes
                                    $itemdata["price"] = $obj_service->data["price_extraunits"];
                                    $itemdata["quantity"] = $usage_excess;
                                    $itemdata["units"] = $unitname;
                                    // description example:		Used 120 GB out of 50 GB included in plan
                                    //				Excess usage of 70 GB charged at $5.00 per GB
                                    $itemdata["description"] .= "\nUsed {$usage} {$unitname} out of " . $obj_service->data["included_units"] . " {$unitname} included in plan.";
                                    $itemdata["description"] .= "\nExcess usage of {$usage_excess} {$unitname} charged at " . $obj_service->data["price_extraunits"] . " per {$unitname}.";
                                } else {
                                    // description example:		Used 10 out of 50 included units
                                    $itemdata["description"] .= "\nUsed {$usage} {$unitname} out of " . $obj_service->data["included_units"] . " {$unitname} included in plan.";
                                }
                                /*
                                	Add the item to the invoice
                                */
                                $invoice_item->prepare_data($itemdata);
                                $invoice_item->action_update();
                                unset($invoice_item);
                                /*
                                	Update usage value for period - this summary value is visable on the service
                                	history page and saves having to query lots of records to generate period totals.
                                */
                                $sql_obj = new sql_query();
                                $sql_obj->string = "UPDATE services_customers_periods SET usage_summary='{$usage}' WHERE id='" . $period_usage_data["id"] . "' LIMIT 1";
                                $sql_obj->execute();
                                break;
                            case "data_traffic":
                                /*
                                	DATA_TRAFFIC
                                
                                	We make use of the service_usage_traffic logic to determine the usage across all IP
                                	addressess assigned to this customer and then bill accordingly.
                                */
                                log_write("debug", "service_invoicegen", "Processing usage items for time/data_traffic");
                                /*
                                	Fetch data traffic plan usage type
                                */
                                $unitname = sql_get_singlevalue("SELECT name as value FROM service_units WHERE id='" . $obj_service->data["units"] . "'");
                                /*
                                	Fetch usage amount - the returned usage structure will include breakdown of traffic
                                	by configured types.
                                */
                                $usage_obj = new service_usage_traffic();
                                $usage_obj->id_service_customer = $period_usage_data["id_service_customer"];
                                $usage_obj->date_start = $period_usage_data["date_start"];
                                $usage_obj->date_end = $period_usage_data["date_end"];
                                /*
                                	Fetch Traffic Caps & Details
                                
                                	Returns all the traffic cap types including overrides.
                                
                                	id_type,
                                	id_cap,
                                	type_name,
                                	type_label,
                                	cap_mode,
                                	cap_units_included,
                                	cap_units_price
                                */
                                $traffic_types_obj = new traffic_caps();
                                $traffic_types_obj->id_service = $obj_service->id;
                                $traffic_types_obj->id_service_customer = $period_usage_data["id_service_customer"];
                                $traffic_types_obj->load_data_traffic_caps();
                                $traffic_types_obj->load_data_override_caps();
                                /*
                                	Generate Traffic Bills
                                */
                                if ($usage_obj->load_data_service()) {
                                    $usage_obj->fetch_usage_traffic();
                                    foreach ($traffic_types_obj->data as $data_traffic_cap) {
                                        // Adjust Included Units to handle partial or extended periods
                                        if ($ratio != "1") {
                                            $data_traffic_cap["cap_units_included"] = sprintf("%d", $data_traffic_cap["cap_units_included"] * $ratio);
                                        }
                                        // if there is only a single traffic cap, we should make the traffic type name blank, since there's only going to be
                                        // one line item anyway.
                                        if ($traffic_types_obj->data_num_rows == 1) {
                                            $data_traffic_cap["type_name"] = "";
                                        }
                                        // if the any traffic type is zero and there are other traffic types, we should skip it, since most likely
                                        // the other traffic types provide everything expected.
                                        //
                                        if ($traffic_types_obj->data_num_rows > 1 && $data_traffic_cap["type_label"] == "*" && $usage_obj->data["total_byunits"]["*"] == 0) {
                                            continue;
                                        }
                                        // start service item
                                        $invoice_item = new invoice_items();
                                        $invoice_item->id_invoice = $invoiceid;
                                        $invoice_item->type_invoice = "ar";
                                        $invoice_item->type_item = "service_usage";
                                        $itemdata = array();
                                        $itemdata["chartid"] = $obj_service->data["chartid"];
                                        $itemdata["customid"] = $obj_service->id;
                                        // base details
                                        $itemdata["price"] = 0;
                                        $itemdata["quantity"] = 0;
                                        $itemdata["discount"] = 0;
                                        $itemdata["units"] = "";
                                        $itemdata["description"] = addslashes($obj_service->data["name_service"]) . " usage from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"];
                                        if ($data_traffic_cap["cap_mode"] == "unlimited") {
                                            // unlimited data cap, there will never be any excess traffic charges, so the line item
                                            // description should be purely for informative purposes.
                                            $itemdata["description"] .= "\nUnlimited " . addslashes($data_traffic_cap["type_name"]) . " traffic, total of " . $usage_obj->data["total_byunits"][$data_traffic_cap["type_label"]] . " {$unitname} used\n";
                                        } else {
                                            // capped traffic - check for excess changes, otherwise just report on how much traffic
                                            // that the customer used.
                                            // description example:		Used 10 GB out of 50 GB included in plan
                                            $itemdata["description"] .= "\nCapped " . addslashes($data_traffic_cap["type_name"]) . " traffic, used " . $usage_obj->data["total_byunits"][$data_traffic_cap["type_label"]] . " {$unitname} out of " . $data_traffic_cap["cap_units_included"] . " {$unitname} in plan.";
                                            // handle excess charges
                                            //
                                            if ($usage_obj->data["total_byunits"][$data_traffic_cap["type_label"]] > $data_traffic_cap["cap_units_included"]) {
                                                // there is excess usage that we can bill for.
                                                $usage_excess = $usage_obj->data["total_byunits"][$data_traffic_cap["type_label"]] - $data_traffic_cap["cap_units_included"];
                                                // set item attributes
                                                $itemdata["price"] = $data_traffic_cap["cap_units_price"];
                                                $itemdata["quantity"] = $usage_excess;
                                                $itemdata["units"] = $unitname;
                                                // description example:		Excess usage of 70 GB charged at $5.00 per GB
                                                $itemdata["description"] .= "\nExcess usage of {$usage_excess} {$unitname} charged at " . format_money($data_traffic_cap["cap_units_price"]) . " per {$unitname}.";
                                            }
                                        }
                                        // end of traffic cap mode
                                        // add trunk usage item
                                        //
                                        $invoice_item->prepare_data($itemdata);
                                        $invoice_item->action_update();
                                        unset($invoice_item);
                                    }
                                }
                                /*
                                	Update usage value for period - this summary value is visable on the service
                                	history page and saves having to query lots of records to generate period totals.
                                */
                                $sql_obj = new sql_query();
                                $sql_obj->string = "UPDATE services_customers_periods SET usage_summary='" . $usage_obj->data["total_byunits"]["total"] . "' WHERE id='" . $period_usage_data["id"] . "' LIMIT 1";
                                $sql_obj->execute();
                                unset($usage_obj);
                                unset($traffic_types_obj);
                                break;
                            case "phone_single":
                            case "phone_tollfree":
                            case "phone_trunk":
                                /*
                                	PHONE_* SERVICES
                                
                                	The phone services are special and contain multiple usage items, for:
                                	* Additional DDI numbers
                                	* Additional Trunks
                                
                                	There are also multiple items for the call charges, grouped into one
                                	item for each DDI.
                                */
                                log_write("debug", "service_invoicegen", "Processing usage items for phone_single/phone_tollfree/phone_trunk");
                                // setup usage object
                                $usage_obj = new service_usage_cdr();
                                $usage_obj->id_service_customer = $period_usage_data["id_service_customer"];
                                $usage_obj->date_start = $period_usage_data["date_start"];
                                $usage_obj->date_end = $period_usage_data["date_end"];
                                $usage_obj->load_data_service();
                                /*
                                	1. DDI CHARGES
                                
                                	We need to fetch the total number of DDIs and see if there are any excess
                                	charges due to overage of the allocated amount in the plan.
                                */
                                if ($obj_service->data["typeid_string"] == "phone_trunk" && $period_data["mode"] == "standard") {
                                    // start service item
                                    $invoice_item = new invoice_items();
                                    $invoice_item->id_invoice = $invoiceid;
                                    $invoice_item->type_invoice = "ar";
                                    $invoice_item->type_item = "service_usage";
                                    $itemdata = array();
                                    $itemdata["chartid"] = $obj_service->data["chartid"];
                                    $itemdata["customid"] = $obj_service->id;
                                    $itemdata["discount"] = 0;
                                    // fetch DDI usage
                                    $usage = $usage_obj->load_data_ddi();
                                    // determine excess usage charges
                                    if ($usage > $obj_service->data["phone_ddi_included_units"]) {
                                        // there is excess usage that we can bill for.
                                        $usage_excess = $usage - $obj_service->data["phone_ddi_included_units"];
                                        // set item attributes
                                        $itemdata["price"] = $obj_service->data["phone_ddi_price_extra_units"] * $ratio;
                                        log_write("debug", "DEBUG", "Ratio is {$ratio}, price is " . $itemdata["price"] . "");
                                        $itemdata["quantity"] = $usage_excess;
                                        $itemdata["units"] = "DDIs";
                                        if ($obj_service->data["phone_ddi_included_units"]) {
                                            $itemdata["description"] = $obj_service->data["phone_ddi_included_units"] . "x DDI numbers included in service plan plus additional " . $usage_excess . "x numbers from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"] . "";
                                        } else {
                                            // no included units, we use an alternative string format
                                            $itemdata["description"] = $usage_excess . "x DDI numbers from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"] . "";
                                        }
                                    } else {
                                        // no charge for this item
                                        $itemdata["description"] = $obj_service->data["phone_ddi_included_units"] . "x DDI numbers included in service plan from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"] . "";
                                    }
                                    // add trunk usage item
                                    $invoice_item->prepare_data($itemdata);
                                    $invoice_item->action_update();
                                    unset($invoice_item);
                                }
                                /*
                                	2. Trunk Charges
                                */
                                if (($obj_service->data["typeid_string"] == "phone_trunk" || $obj_service->data["typeid_string"] == "phone_tollfree") && $period_data["mode"] == "standard") {
                                    // fetch the number of trunks included in the plan, along with the number provided
                                    // we can then see if there are any excess charges for these
                                    // start service item
                                    $invoice_item = new invoice_items();
                                    $invoice_item->id_invoice = $invoiceid;
                                    $invoice_item->type_invoice = "ar";
                                    $invoice_item->type_item = "service_usage";
                                    $itemdata = array();
                                    $itemdata["chartid"] = $obj_service->data["chartid"];
                                    $itemdata["customid"] = $obj_service->id;
                                    $itemdata["discount"] = 0;
                                    // determine excess usage charges
                                    if ($obj_service->data["phone_trunk_quantity"] > $obj_service->data["phone_trunk_included_units"]) {
                                        // there is excess usage that we can bill for.
                                        $usage_excess = $obj_service->data["phone_trunk_quantity"] - $obj_service->data["phone_trunk_included_units"];
                                        // set item attributes
                                        $itemdata["price"] = $obj_service->data["phone_trunk_price_extra_units"] * $ratio;
                                        $itemdata["quantity"] = $usage_excess;
                                        $itemdata["units"] = "trunks";
                                        if ($obj_service->data["phone_trunk_included_units"]) {
                                            $itemdata["description"] = $obj_service->data["phone_trunk_included_units"] . "x trunks included in service plan plus additional " . $usage_excess . "x trunks from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"] . "";
                                        } else {
                                            // no included trunks, adjust string to suit.
                                            $itemdata["description"] = $usage_excess . "x trunks from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"] . "";
                                        }
                                    } else {
                                        // no charge for this item
                                        $itemdata["description"] = $obj_service->data["phone_trunk_included_units"] . "x trunks included in service plan from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"] . "";
                                    }
                                    // add trunk usage item
                                    $invoice_item->prepare_data($itemdata);
                                    $invoice_item->action_update();
                                    unset($invoice_item);
                                }
                                /*
                                									Call Charges
                                	Use CDR usage billing module to handle call charges.
                                */
                                $usage_obj = new service_usage_cdr();
                                $usage_obj->id_service_customer = $period_usage_data["id_service_customer"];
                                $usage_obj->date_start = $period_usage_data["date_start"];
                                $usage_obj->date_end = $period_usage_data["date_end"];
                                $billgroup_obj = new sql_query();
                                $billgroup_obj->string = "SELECT id, billgroup_name FROM cdr_rate_billgroups";
                                $billgroup_obj->execute();
                                $billgroup_obj->fetch_array();
                                if ($usage_obj->load_data_service()) {
                                    $usage_obj->fetch_usage_calls();
                                    foreach ($billgroup_obj->data as $data_billgroup) {
                                        foreach ($usage_obj->data_ddi as $ddi) {
                                            if ($usage_obj->data[$ddi][$data_billgroup["id"]]["charges"] > 0) {
                                                // start service item
                                                $invoice_item = new invoice_items();
                                                $invoice_item->id_invoice = $invoiceid;
                                                $invoice_item->type_invoice = "ar";
                                                $invoice_item->type_item = "service_usage";
                                                $itemdata = array();
                                                $itemdata["chartid"] = $obj_service->data["chartid"];
                                                $itemdata["customid"] = $obj_service->id;
                                                // extra service details
                                                $itemdata["id_service_customer"] = $period_usage_data["id_service_customer"];
                                                $itemdata["id_period"] = $period_usage_data["id"];
                                                // determine excess usage charges
                                                $itemdata["discount"] = 0;
                                                $itemdata["price"] = $usage_obj->data[$ddi][$data_billgroup["id"]]["charges"];
                                                $itemdata["quantity"] = "1";
                                                $itemdata["units"] = "";
                                                $itemdata["description"] = $data_billgroup["billgroup_name"] . " call charges for {$ddi} from " . $period_usage_data["date_start"] . " to " . $period_usage_data["date_end"] . "";
                                                $itemdata["cdr_billgroup"] = $data_billgroup["id"];
                                                // add trunk usage item
                                                $invoice_item->prepare_data($itemdata);
                                                $invoice_item->action_update();
                                                unset($invoice_item);
                                            } else {
                                                log_write("debug", "inc_service_invoicegen", "Excluding DDI {$ddi} from " . $data_billgroup["billgroup_name"] . " due to no charges for the current period");
                                            }
                                        }
                                    }
                                }
                                unset($usage_obj);
                                /*
                                	If enabled, generate the CDR output format for the current service usage and attach
                                	it to the invoice via the invoice journal.
                                
                                	This feature can be enabled/disabled on a per-customer per-service basis.
                                */
                                if ($obj_service->data["billing_cdr_csv_output"]) {
                                    log_write("debug", "inc_service_invoicegen", "Generating CDR export file and attaching to invoice journal");
                                    // generate the CSV formatted export.
                                    $cdr_options = array('id_customer' => $customer_data["id"], 'id_service_customer' => $period_usage_data["id_service_customer"], 'period_start' => $period_usage_data["date_start"], 'period_end' => $period_usage_data["date_end"]);
                                    $csv = new cdr_csv($cdr_options);
                                    if (!($cdr_output = $csv->getCSV())) {
                                        log_write("error", "inc_service_invoicegen", "Unable to generate CSV ouput for the configured range");
                                        return 0;
                                    }
                                    // create journal entry
                                    $journal = new journal_process();
                                    $journal->prepare_set_journalname("account_ar");
                                    $journal->prepare_set_customid($invoiceid);
                                    $journal->prepare_set_type("file");
                                    // we use the prefix "SERVICE:" to find the journal at invoice time
                                    $journal->prepare_set_title("SERVICE: Service CDR Export Attachment");
                                    // details can be anything (just a text block)
                                    $data["content"] = NULL;
                                    $data["content"] .= "Automatically exported CDR for service " . addslashes($obj_service->data["name_service"]) . "\n";
                                    $data["content"] .= "\n";
                                    $journal->prepare_set_content($data["content"]);
                                    $journal->action_update();
                                    // create journal entry
                                    $journal->action_lock();
                                    // lock entry to avoid users deleting it or breaking it
                                    // upload file as an attachment for the journal
                                    $file_obj = new file_storage();
                                    $file_obj->data["type"] = "journal";
                                    $file_obj->data["customid"] = $journal->structure["id"];
                                    $file_obj->data["file_name"] = "invoice_" . $invoicecode . "_service_CDR_export.csv";
                                    if (!$file_obj->action_update_var($cdr_output)) {
                                        log_write("error", "inc_service_invoicegen", "Unable to upload export CDR invoice to journal.");
                                    }
                                    unset($csv);
                                    unset($journal);
                                    unset($file_obj);
                                    unset($cdr_output);
                                }
                                break;
                            case "generic_no_usage":
                            case "bundle":
                                // nothing todo for these service types
                                log_write("debug", "service_invoicegen", "Not processing usage, this is a non-usage service type");
                                break;
                            default:
                                // we should always match all service types, even if we don't need to do anything
                                // in particular for that type.
                                die("Unable to process unknown service type: " . $obj_service->data["typeid_string"] . "");
                                break;
                        }
                        // end of processing usage
                    } else {
                        log_write("debug", "service_invoicegen", "Not billing for current usage, as this appears to be the first plan period so no usage can exist yet");
                    }
                    /*
                    	Set invoice ID for period - this prevents the period from being added to
                    	any other invoices and allows users to see which invoice it was billed under
                    */
                    // set for plan period
                    $sql_obj = new sql_query();
                    $sql_obj->string = "UPDATE services_customers_periods SET invoiceid='{$invoiceid}', rebill='0' WHERE id='" . $period_data["id"] . "' LIMIT 1";
                    $sql_obj->execute();
                    // set for usage period
                    if (!empty($period_usage_data["active"])) {
                        $sql_obj = new sql_query();
                        $sql_obj->string = "UPDATE services_customers_periods SET invoiceid_usage='{$invoiceid}' WHERE id='" . $period_usage_data["id"] . "' LIMIT 1";
                        $sql_obj->execute();
                    }
                }
                // end of processing periods
                /*
                	Only process orders and invoice
                	summary details if we had no errors above.
                */
                if (!error_check()) {
                    /*
                    	Process any customer orders
                    */
                    if ($GLOBALS["config"]["ORDERS_BILL_ONSERVICE"]) {
                        log_write("debug", "inc_service_invoicegen", "Checking for customer orders to add to service invoice");
                        $obj_customer_orders = new customer_orders();
                        $obj_customer_orders->id = $customer_data["id"];
                        if ($obj_customer_orders->check_orders_num()) {
                            log_write("debug", "inc_service_invoicegen", "Order items exist, adding them to service invoice");
                            $obj_customer_orders->invoice_generate($invoiceid);
                        }
                    } else {
                        log_write("debug", "inc_service_invoicegen", "Not checking for customer orders, ORDERS_BILL_ONSERVICE is disabled currently");
                    }
                    /*
                    	Update the invoice details + Ledger
                    
                    	Processes:
                    	- taxes
                    	- ledger
                    	- invoice summary
                    
                    	We use the invoice_items class to perform these tasks, but we don't need
                    	to define an item ID for the functions being used to work.
                    */
                    $invoice = new invoice_items();
                    $invoice->id_invoice = $invoiceid;
                    $invoice->type_invoice = "ar";
                    $invoice->action_update_tax();
                    $invoice->action_update_ledger();
                    $invoice->action_update_total();
                    unset($invoice);
                    /*
                    	Update period information with invoiceid
                    */
                    $sql_obj = new sql_query();
                    $sql_obj->string = "UPDATE services_customers_periods SET invoiceid='{$invoiceid}' WHERE id='" . $period_data["id"] . "'";
                    $sql_obj->execute();
                }
                // end if error check
                /*
                	Automatic Payments
                
                	Makes automatic invoice payments using sources such as customer credit pools, reoccuring credit card transactions
                	and other sources.
                */
                if ($GLOBALS["config"]["ACCOUNTS_AUTOPAY"]) {
                    log_write("debug", "inc_services_invoicegen", "Autopay Functionality Enabled, running appropiate functions for invoice ID {$invoiceid}");
                    $obj_autopay = new invoice_autopay();
                    $obj_autopay->id_invoice = $invoiceid;
                    $obj_autopay->type_invoice = "ar";
                    $obj_autopay->autopay();
                    unset($obj_autopay);
                }
                /*
                	Commit
                
                	Conduct final error check, before commiting the new invoice and sending the customer an email
                	if appropiate.
                
                	(we obviously don't want to email them if the invoice is getting rolled back!)
                */
                $sql_obj = new sql_query();
                if (error_check()) {
                    $sql_obj->trans_rollback();
                    log_write("error", "inc_services_invoicegen", "An error occured whilst creating service invoice. No changes have been made.");
                } else {
                    $sql_obj->trans_commit();
                    // invoice creation complete - remove any notifications made by the invoice functions and return
                    // our own notification
                    $_SESSION["notification"]["message"] = array();
                    log_write("notification", "inc_services_invoicegen", "New invoice {$invoicecode} for customer " . $customer_data["code_customer"] . " created");
                    /*
                    		Send the invoice to the customer as a PDF via email
                    */
                    $emailed = "unsent";
                    if (sql_get_singlevalue("SELECT value FROM config WHERE name='EMAIL_ENABLE'") == "enabled") {
                        if (sql_get_singlevalue("SELECT value FROM config WHERE name='ACCOUNTS_INVOICE_AUTOEMAIL'") == "enabled") {
                            // load completed invoice data
                            $invoice = new invoice();
                            $invoice->id = $invoiceid;
                            $invoice->type = "ar";
                            $invoice->load_data();
                            $invoice->load_data_export();
                            if ($invoice->data["amount_total"] > 0) {
                                // generate an email
                                $email = $invoice->generate_email();
                                // send email
                                $invoice->email_invoice("system", $email["to"], $email["cc"], $email["bcc"], $email["subject"], $email["message"]);
                                // complete
                                log_write("notification", "inc_services_invoicegen", "Invoice {$invoicecode} has been emailed to customer (" . $email["to"] . ")");
                            } else {
                                // complete - invoice is for $0, so don't want to email out
                                log_write("notification", "inc_services_invoicegen", "Invoice {$invoicecode} has not been emailed to the customer due to invoice being for \$0.");
                            }
                            $emailed = "emailed - " . $email["to"];
                            unset($invoice);
                        }
                    }
                    // end if email enabled
                }
                // end if commit successful
                /*
                	Review Status
                
                	Here we need to check whether invoicing succeded/failed for the selected customer and process accordingly - we
                	also want to re-set the error flag if running a batch mode, to enable other customers to still be invoiced.
                */
                if (error_check()) {
                    // an error occured
                    $invoice_stats["total_failed"]++;
                    $invoice_stats["failed"][] = array("code_customer" => $customer_data["code_customer"], "name_customer" => $customer_data["name_customer"], "code_invoice" => $invoicecode);
                    // clear the error strings if we are processing from CLI this will allow us to continue on
                    // with additional invoices.
                    if (!empty($_SESSION["mode"])) {
                        if ($_SESSION["mode"] == "cli") {
                            log_write("debug", "inc_services_invoicegen", "Processing from CLI, clearing error flag and continuing with additional invoices");
                            error_clear();
                        }
                    }
                } else {
                    // successful
                    $invoice_stats["total"]++;
                    $invoice_stats["generated"][] = array("code_customer" => $customer_data["code_customer"], "name_customer" => $customer_data["name_customer"], "code_invoice" => $invoicecode, "sent" => $emailed);
                }
                // end of success/stats review
            }
            // end of processing customers
        }
        // end of if customers exist
    } else {
        log_debug("inc_services_invoicegen", "No services assigned to customer {$customerid}");
    }
    /*
    	Write Invoicing Report
    	
    	This only takes place if no customer ID is provided, eg we have run a full automatic invoice generation report.
    */
    if ($customerid == NULL) {
        log_write("debug", "inc_service_invoicegen", "Generating invoice report for invoice generation process");
        /*
        	Invoice Stats Calculations
        */
        $invoice_stats["time"] = time() - $invoice_stats["time_start"];
        if ($invoice_stats["time"] == 0) {
            $invoice_stats["time"] = 1;
        }
        /*
        	Write Invoice Report
        */
        $invoice_report = array();
        $invoice_report[] = "Complete Invoicing Run Time:\t" . $invoice_stats["time"] . " seconds";
        $invoice_report[] = "Total Invoices Generated:\t" . $invoice_stats["total"];
        $invoice_report[] = "Failed Invoices/Customers:\t" . $invoice_stats["total_failed"];
        if (isset($invoice_stats["generated"])) {
            $invoice_report[] = "";
            $invoice_report[] = "Customers / Invoices Generated";
            foreach (array_keys($invoice_stats["generated"]) as $id) {
                $invoice_report[] = " * " . $invoice_stats["generated"][$id]["code_invoice"] . ": " . $invoice_stats["generated"][$id]["code_customer"] . " -- " . $invoice_stats["generated"][$id]["name_customer"];
                $invoice_report[] = "   [" . $invoice_stats["generated"][$id]["sent"] . "]";
            }
            $invoice_report[] = "";
        }
        if (isset($invoice_stats["failed"])) {
            $invoice_report[] = "";
            $invoice_report[] = "Failed Customers / Invoices";
            foreach (array_keys($invoice_stats["failed"]) as $id) {
                $invoice_report[] = " * " . $invoice_stats["failed"][$id]["code_customer"] . " -- " . $invoice_stats["failed"][$id]["name_customer"];
            }
            $invoice_report[] = "";
            $invoice_report[] = "Failed invoices will be attempted again on the following billing run.";
            $invoice_report[] = "";
        }
        $invoice_report[] = "Invoicing Run Complete";
        // display to debug log
        log_write("debug", "inc_service_invoicegen", "----");
        foreach ($invoice_report as $line) {
            // loop through invoice report lines
            log_write("debug", "inc_service_invoicegen", $line);
        }
        log_write("debug", "inc_service_invoicegen", "----");
        // email if appropiate
        if ($GLOBALS["config"]["ACCOUNTS_INVOICE_BATCHREPORT"] && ($invoice_stats["total"] > 0 || $invoice_stats["total_failed"] > 0)) {
            log_write("debug", "inc_service_invoicegen", "Emailing invoice generation report to " . $GLOBALS["config"]["ACCOUNTS_EMAIL_ADDRESS"] . "");
            /*
            	External dependency of Mail_Mime
            */
            if (!@(include_once 'Mail.php')) {
                log_write("error", "invoice", "Unable to find Mail module required for sending email");
                break;
            }
            if (!@(include_once 'Mail/mime.php')) {
                log_write("error", "invoice", "Unable to find Mail::Mime module required for sending email");
                break;
            }
            /*
            	Email the Report
            */
            $email_sender = $GLOBALS["config"]["ACCOUNTS_EMAIL_ADDRESS"];
            $email_to = $GLOBALS["config"]["ACCOUNTS_EMAIL_ADDRESS"];
            $email_subject = "Invoice Batch Process Report";
            $email_message = $GLOBALS["config"]["COMPANY_NAME"] . "\n\nInvoice Batch Process Report for " . time_format_humandate() . "\n\n";
            foreach ($invoice_report as $line) {
                $email_message .= $line . "\n";
            }
            // prepare headers
            $mail_headers = array('From' => $email_sender, 'Subject' => $email_subject);
            $mail_mime = new Mail_mime("\n");
            $mail_mime->setTXTBody($email_message);
            $mail_body = $mail_mime->get();
            $mail_headers = $mail_mime->headers($mail_headers);
            $mail =& Mail::factory('mail');
            $status = $mail->send($email_to, $mail_headers, $mail_body);
            if (PEAR::isError($status)) {
                log_write("error", "inc_service_invoicegen", "An error occured whilst attempting to send the batch report email: " . $status->getMessage() . "");
            } else {
                log_write("debug", "inc_service_invoicegen", "Successfully sent batch report email.");
            }
        }
        // end if email
    }
    // end if report
    return 1;
}