infowindow = new google.maps.InfoWindow({}); } google.maps.event.addListener(item, 'click', function(event) { infowindow.close(); infowindow = new google.maps.InfoWindow({ content: content, position: event.latLng }); infowindow.open(map, item); }); } var customer_address = []; <?php $customer_addresses = array(); $customers = module_customer::get_customers(array('customer_id' => isset($_REQUEST['customer_id']) && (int) $_REQUEST['customer_id'] > 0 ? (int) $_REQUEST['customer_id'] : false), array('columns' => 'c.customer_id, c.customer_name')); foreach ($customers as $customer) { $address = module_address::get_address($customer['customer_id'], 'customer', 'physical'); if (!empty($address)) { $address_count = 0; $customer['full_address'] = ''; foreach (array('line_1', 'line_2', 'suburb', 'state', 'region', 'country', 'post_code') as $key) { if (!empty($address[$key])) { $address_count++; $customer['full_address'] .= $address[$key] . ', '; } $customer[$key] = $address[$key]; } if ($address_count > 1) { $customer['address_id'] = $address['address_id']; $customer['full_address'] = rtrim($customer['full_address'], ', ');
function process() { if ('plupload' == $_REQUEST['_process']) { if (!self::can_i('edit', 'Files') && !self::can_i('create', 'Files')) { die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Permission error."}, "id" : "id"}'); } @ob_end_clean(); // HTTP headers for no cache etc header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); // Settings $targetDir = _FILE_UPLOAD_PATH . "plupload"; //$targetDir = 'uploads'; $cleanupTargetDir = true; // Remove old files $maxFileAge = 5 * 3600; // Temp file age in seconds // 5 minutes execution time @set_time_limit(5 * 60); // Uncomment this one to fake upload time // usleep(5000); // Get parameters $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0; $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0; $fileName = isset($_REQUEST["plupload_key"]) ? $_REQUEST["plupload_key"] : ''; $fileName .= isset($_REQUEST["fileid"]) ? '-' . $_REQUEST["fileid"] : ''; $fileName = preg_replace('/[^a-zA-Z0-9-_]+/', '', $fileName); if (!$fileName) { die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "No plupload_key defined."}, "id" : "id"}'); } // Make sure the fileName is unique but only if chunking is disabled if ($chunks < 2 && file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName)) { $ext = strrpos($fileName, '.'); $fileName_a = substr($fileName, 0, $ext); $fileName_b = substr($fileName, $ext); $count = 1; while (file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b)) { $count++; } $fileName = $fileName_a . '_' . $count . $fileName_b; } $filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName; // Create target dir if (!file_exists($targetDir)) { @mkdir($targetDir); } // Remove old temp files if ($cleanupTargetDir) { if (!is_dir($targetDir) || !($dir = opendir($targetDir))) { die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}'); } while (($file = readdir($dir)) !== false) { $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file; // If temp file is current file proceed to the next if ($tmpfilePath == "{$filePath}.part") { continue; } // Remove temp file if it is older than the max age and is not the current file if (preg_match('/\\.part$/', $file) && filemtime($tmpfilePath) < time() - $maxFileAge) { @unlink($tmpfilePath); } } closedir($dir); } /// Open temp file if (!($out = @fopen("{$filePath}.part", $chunks ? "ab" : "wb"))) { die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}'); } if (!empty($_FILES)) { if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) { die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}'); } // Read binary input stream and append it to temp file if (!($in = @fopen($_FILES["file"]["tmp_name"], "rb"))) { die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}'); } } else { if (!($in = @fopen("php://input", "rb"))) { die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}'); } } while ($buff = fread($in, 4096)) { fwrite($out, $buff); } @fclose($out); @fclose($in); // Check if file has been uploaded if (!$chunks || $chunk == $chunks - 1) { // Strip the temp .part suffix off rename("{$filePath}.part", $filePath); } die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}'); } else { if ('download' == $_REQUEST['_process']) { @ob_end_clean(); $file_id = (int) $_REQUEST['file_id']; $file_data = $this->get_file($file_id); if (isset($file_data['file_url']) && strlen($file_data['file_url'])) { redirect_browser($file_data['file_url']); } else { if (is_file($file_data['file_path'])) { header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: private", false); //header("Content-Type: application/pdf"); header("Content-type: " . dtbaker_mime_type($file_data['file_name'], $file_data['file_path'])); header("Content-Disposition: attachment; filename=\"" . $file_data['file_name'] . "\";"); header("Content-Transfer-Encoding: binary"); header("Content-Length: " . filesize($file_data['file_path'])); //readfile($file_data['file_path']); $size = @readfile($file_data['file_path']); if (!$size) { echo file_get_contents($file_data['file_path']); } } else { echo 'Not found'; } } exit; } else { if ('save_file_popup' == $_REQUEST['_process']) { $file_id = $_REQUEST['file_id']; $file_path = false; $file_name = false; $options = unserialize(base64_decode($_REQUEST['options'])); // have we uploaded anything if (isset($_FILES['file_upload']) && is_uploaded_file($_FILES['file_upload']['tmp_name'])) { // copy to file area. $file_name = basename($_FILES['file_upload']['name']); if ($file_name) { $file_path = _FILE_UPLOAD_PATH . md5(time() . $file_name); if (move_uploaded_file($_FILES['file_upload']['tmp_name'], $file_path)) { // it worked. umm.. do something. } else { ?> <script type="text/javascript"> alert('Unable to save file. Please check permissions.'); </script> <?php // it didnt work. todo: display error. $file_path = false; $file_name = false; //set_error('Unable to save file'); } } } if (isset($_REQUEST['file_name']) && $_REQUEST['file_name']) { $file_name = $_REQUEST['file_name']; } if (!$file_path && !$file_name) { return false; } if (!$file_id || $file_id == 'new') { $file_data = array('file_id' => $file_id, 'owner_id' => (int) $_REQUEST['owner_id'], 'owner_table' => $_REQUEST['owner_table'], 'file_time' => time(), 'file_name' => $file_name, 'file_path' => $file_path); } else { // some fields we dont want to overwrite on existing files: $file_data = array('file_id' => $file_id, 'file_path' => $file_path, 'file_name' => $file_name); } // make sure we're saving a file we have access too. module_security::sanatise_data('file', $file_data); $file_id = update_insert('file_id', $file_id, 'file', $file_data); $file_data = $this->get_file($file_id); // we've updated from a popup. // this means we have to replace an existing file id with the updated output. // or if none exists on the page, we add a new one to the holder. $layout_type = isset($_REQUEST['layout']) && $_REQUEST['layout'] ? $_REQUEST['layout'] : 'gallery'; ?> <script type="text/javascript"> // check if it exists in parent window var new_html = '<?php echo addcslashes(preg_replace('/\\s+/', ' ', $this->print_file($file_id, $layout_type, true, $options)), "'"); ?> '; parent.new_file_added<?php echo $file_data['owner_table']; ?> _<?php echo $file_data['owner_id']; ?> (<?php echo $file_id; ?> ,'<?php echo $file_data['owner_table']; ?> ',<?php echo $file_data['owner_id']; ?> ,new_html); </script> <?php exit; } else { if ('save_file' == $_REQUEST['_process']) { $file_id = (int) $_REQUEST['file_id']; $file_path = false; $file_name = false; $file_url = ''; if (isset($_REQUEST['butt_del']) && self::can_i('delete', 'Files')) { if (module_form::confirm_delete('file_id', 'Really delete this file?')) { $ucm_file = new ucm_file($file_id); $ucm_file->delete(); set_message('File removed successfully'); } redirect_browser(module_file::link_open(false)); } else { $files_to_save = array(); // pump data in to here for multiple file uploads. // todo: stop people changing the "file_id" to another file they don't own. if (self::can_i('edit', 'Files') || self::can_i('create', 'Files')) { // have we uploaded anything $file_changed = false; if (isset($_REQUEST['plupload_key']) && isset($_REQUEST['plupload_file_name']) && is_array($_REQUEST['plupload_file_name']) && strlen(preg_replace('/[^a-zA-Z0-9-_]+/', '', basename($_REQUEST['plupload_key'])))) { $plupload_key = preg_replace('/[^a-zA-Z0-9-_]+/', '', basename($_REQUEST['plupload_key'])); foreach ($_REQUEST['plupload_file_name'] as $plupload_file_name_key => $file_name) { $plupload_file_name_key = preg_replace('/[^a-zA-Z0-9-_]+/', '', basename($plupload_file_name_key)); if ($plupload_key && $plupload_file_name_key && $file_name && is_file(_FILE_UPLOAD_PATH . 'plupload' . DIRECTORY_SEPARATOR . $plupload_key . '-' . $plupload_file_name_key)) { $file_path = _FILE_UPLOAD_PATH . time() . '-' . md5(time() . $file_name); if (rename(_FILE_UPLOAD_PATH . 'plupload' . DIRECTORY_SEPARATOR . $plupload_key . '-' . $plupload_file_name_key, $file_path)) { // it worked. umm.. do something. $file_changed = true; $files_to_save[] = array('file_path' => $file_path, 'file_name' => $file_name); } else { // it didnt work. todo: display error. $file_path = false; $file_name = false; set_error('Unable to save file via plupload.'); } } } } // the old file upload method, no plupload: if (!$file_changed && isset($_FILES['file_upload']) && is_uploaded_file($_FILES['file_upload']['tmp_name'])) { // copy to file area. $file_name = basename($_FILES['file_upload']['name']); if ($file_name) { $file_path = _FILE_UPLOAD_PATH . time() . '-' . md5(time() . $file_name); if (move_uploaded_file($_FILES['file_upload']['tmp_name'], $file_path)) { // it worked. umm.. do something. $file_changed = true; $files_to_save[] = array('file_path' => $file_path, 'file_name' => $file_name); } else { // it didnt work. todo: display error. $file_path = false; $file_name = false; set_error('Unable to save file'); } } } if (!$file_path && isset($_REQUEST['file_url']) && isset($_REQUEST['file_name'])) { $files_to_save[] = array('file_path' => '', 'file_url' => $_REQUEST['file_url'], 'file_name' => $_REQUEST['file_name']); } if (!$file_path && isset($_REQUEST['bucket'])) { $files_to_save[] = array('file_name' => $_REQUEST['file_name'], 'bucket' => 1); } // make sure we have a valid customer_id and job_id selected. $possible_customers = $possible_jobs = array(); if (class_exists('module_customer', false)) { $possible_customers = module_customer::get_customers(); } if (class_exists('module_job', false)) { $possible_jobs = module_job::get_jobs(); } $original_file_data = array(); if ($file_id > 0) { $original_file_data = self::get_file($file_id); if (!$original_file_data || $original_file_data['file_id'] != $file_id) { die('No permissions to update this file'); } } $new_file = false; if (!$file_id) { $file_data = array('file_id' => $file_id, 'bucket_parent_file_id' => isset($_REQUEST['bucket_parent_file_id']) ? (int) $_REQUEST['bucket_parent_file_id'] : false, 'customer_id' => isset($_REQUEST['customer_id']) ? (int) $_REQUEST['customer_id'] : false, 'job_id' => isset($_REQUEST['job_id']) ? (int) $_REQUEST['job_id'] : false, 'quote_id' => isset($_REQUEST['quote_id']) ? (int) $_REQUEST['quote_id'] : false, 'website_id' => isset($_REQUEST['website_id']) ? (int) $_REQUEST['website_id'] : false, 'status' => isset($_REQUEST['status']) ? $_REQUEST['status'] : false, 'pointers' => isset($_REQUEST['pointers']) ? $_REQUEST['pointers'] : false, 'description' => isset($_REQUEST['description']) ? $_REQUEST['description'] : false, 'file_time' => time()); if (!isset($possible_customers[$file_data['customer_id']])) { $file_data['customer_id'] = 0; } if (!isset($possible_jobs[$file_data['job_id']])) { $file_data['job_id'] = 0; } $new_file = true; } else { // some fields we dont want to overwrite on existing files: $file_data = array('file_id' => $file_id, 'bucket_parent_file_id' => isset($_REQUEST['bucket_parent_file_id']) ? (int) $_REQUEST['bucket_parent_file_id'] : false, 'pointers' => isset($_REQUEST['pointers']) ? $_REQUEST['pointers'] : false, 'customer_id' => isset($_REQUEST['customer_id']) ? (int) $_REQUEST['customer_id'] : false, 'job_id' => isset($_REQUEST['job_id']) ? (int) $_REQUEST['job_id'] : false, 'quote_id' => isset($_REQUEST['quote_id']) ? (int) $_REQUEST['quote_id'] : false, 'website_id' => isset($_REQUEST['website_id']) ? (int) $_REQUEST['website_id'] : false, 'status' => isset($_REQUEST['status']) ? $_REQUEST['status'] : false, 'description' => isset($_REQUEST['description']) ? $_REQUEST['description'] : false); if (!isset($possible_customers[$file_data['customer_id']]) && $file_data['customer_id'] != $original_file_data['customer_id']) { $file_data['customer_id'] = $original_file_data['customer_id']; } if ($file_data['job_id'] && !isset($possible_jobs[$file_data['job_id']]) && $file_data['job_id'] != $original_file_data['job_id']) { $file_data['job_id'] = $original_file_data['job_id']; } } $sub_bucket_fields = array('customer_id', 'job_id', 'quote_id', 'website_id'); if ($file_data['bucket_parent_file_id']) { // we're saving a sub bucket file, pull in the file data from the parent file. $parent_file = new ucm_file($file_data['bucket_parent_file_id']); $parent_file_data = $parent_file->get_data(); foreach ($sub_bucket_fields as $sub_bucket_field) { $file_data[$sub_bucket_field] = $parent_file_data[$sub_bucket_field]; } } if (!count($files_to_save)) { $files_to_save[] = array(); } foreach ($files_to_save as $id => $file_to_save) { $file_data_to_save = array_merge($file_data, $file_to_save); $files_to_save[$id]['file_id'] = update_insert('file_id', $file_data['file_id'], 'file', $file_data_to_save); $file_data['file_id'] = 0; // incease we're uploading multiple files if (isset($_POST['staff_ids_save']) && (int) $files_to_save[$id]['file_id'] > 0) { delete_from_db('file_user_rel', array('file_id'), array($files_to_save[$id]['file_id'])); if (isset($_POST['staff_ids']) && is_array($_POST['staff_ids'])) { foreach ($_POST['staff_ids'] as $staff_id) { $sql = "REPLACE INTO `" . _DB_PREFIX . "file_user_rel` SET "; $sql .= " `user_id` = " . (int) $staff_id; $sql .= ", `file_id` = " . (int) $files_to_save[$id]['file_id']; query($sql); } } } if ($files_to_save[$id]['file_id'] > 0 && isset($file_data_to_save['bucket']) && $file_data_to_save['bucket']) { // update certain fields of all the child files to match the parent bucket. $search = array('bucket_parent_file_id' => $files_to_save[$id]['file_id']); $sub_files = module_file::get_files($search); $vals = array(); foreach ($sub_bucket_fields as $field) { $vals[$field] = isset($file_data_to_save[$field]) ? $file_data_to_save[$field] : false; } foreach ($sub_files as $sub_file) { update_insert('file_id', $sub_file['file_id'], 'file', $vals); // and save the staff assignment manually too if (isset($_POST['staff_ids_save']) && (int) $sub_file['file_id'] > 0) { delete_from_db('file_user_rel', array('file_id'), array($sub_file['file_id'])); if (isset($_POST['staff_ids']) && is_array($_POST['staff_ids'])) { foreach ($_POST['staff_ids'] as $staff_id) { $sql = "REPLACE INTO `" . _DB_PREFIX . "file_user_rel` SET "; $sql .= " `user_id` = " . (int) $staff_id; $sql .= ", `file_id` = " . (int) $sub_file['file_id']; query($sql); } } } } } module_extra::save_extras('file', 'file_id', $files_to_save[$id]['file_id']); if ($file_changed) { $this->send_file_changed_notice($files_to_save[$id]['file_id'], $new_file); } // file changed } } if (module_file::can_i('create', 'File Comments')) { $this->save_file_comments($file_id); } if (isset($_REQUEST['delete_file_comment_id']) && $_REQUEST['delete_file_comment_id']) { $file_comment_id = (int) $_REQUEST['delete_file_comment_id']; $comment = get_single('file_comment', 'file_comment_id', $file_comment_id); if ($comment['create_user_id'] == module_security::get_loggedin_id() || module_file::can_i('delete', 'File Comments')) { $sql = "DELETE FROM `" . _DB_PREFIX . "file_comment` WHERE file_id = '" . (int) $file_id . "' AND file_comment_id = '{$file_comment_id}' "; $sql .= " LIMIT 1"; query($sql); } } if (isset($_REQUEST['butt_email']) && $_REQUEST['butt_email'] && module_file::can_i('edit', 'File Approval')) { redirect_browser($this->link_open_email($file_id)); } if (count($files_to_save)) { if (count($files_to_save) > 1) { $file_id = false; set_message(_l('%s Files saved successfully', count($files_to_save))); } else { set_message(_l('File saved successfully')); $file_id = $files_to_save[0]['file_id']; } } redirect_browser($this->link_open($file_id)); } } else { if ('delete_file_popup' == $_REQUEST['_process']) { $file_id = (int) $_REQUEST['file_id']; if (!$file_id || $file_id == 'new') { // cant delete a new file.. do nothing. } else { $file_data = $this->get_file($file_id); if (true) { //module_security::can_access_data('file',$file_data,$file_id)){ // delete the physical file. if (is_file($file_data['file_path'])) { unlink($file_data['file_path']); } // delete the db entry. delete_from_db('file', 'file_id', $file_id); // update ui with changes. ?> <script type="text/javascript"> var new_html = ''; parent.new_file_added<?php echo $file_data['owner_table']; ?> _<?php echo $file_data['owner_id']; ?> (<?php echo $file_id; ?> ,'<?php echo $file_data['owner_table']; ?> ',<?php echo $file_data['owner_id']; ?> ,new_html); </script> <?php } } exit; } } } } } }
})); if (count($invoice['job_ids'])) { $fieldset_data['elements'][] = array('title' => 'Linked Job', 'fields' => array(function () use(&$invoice) { foreach ($invoice['job_ids'] as $job_id) { if ((int) $job_id > 0) { echo module_job::link_open($job_id, true); echo "<br/>\n"; } } })); } $fieldset_data['elements'][] = array('title' => 'Customer', 'fields' => array(function () use(&$invoice) { if (!$invoice['customer_id'] || isset($_REQUEST['change_customer'])) { // allow them to pick customer. $c = array(); $customers = module_customer::get_customers(); foreach ($customers as $customer) { $c[$customer['customer_id']] = $customer['customer_name']; } echo print_select_box($c, 'customer_id', $invoice['customer_id']); if ($invoice['customer_id'] && module_customer::can_i('view', 'Customers')) { ?> <a href="<?php echo module_customer::link_open($invoice['customer_id'], false); ?> "><?php _e('Open'); ?> </a> <?php
$customer_type = module_customer::get_customer_type($current_customer_type_id); if ($customer_type && !empty($customer_type['type_name'])) { $page_type = $customer_type['type_name_plural']; $page_type_single = $customer_type['type_name']; } } if (!module_customer::can_i('view', $page_type)) { redirect_browser(_BASE_HREF); } $module->page_title = _l($page_type); $staff_members = module_user::get_staff_members(); $staff_member_rel = array(); foreach ($staff_members as $staff_member) { $staff_member_rel[$staff_member['user_id']] = $staff_member['name']; } $customers = module_customer::get_customers($search, array('as_resource' => true)); $header_buttons = array(); if (module_customer::can_i('create', $page_type)) { $header_buttons[] = array('url' => module_customer::link_open('new', false), 'title' => 'Create New ' . $page_type_single, 'type' => 'add'); } if (class_exists('module_import_export', false) && module_customer::can_i('view', 'Import ' . $page_type)) { $header_buttons[] = array('url' => module_import_export::import_link(array('callback' => 'module_customer::handle_import' . ($current_customer_type_id ? '_leads' : ''), 'name' => $page_type, 'return_url' => $_SERVER['REQUEST_URI'], 'group' => 'customer', 'fields' => array($page_type_single . ' ID' => 'customer_id', $page_type_single . ' Name' => 'customer_name', 'Credit' => 'credit', 'Address Line 1' => 'line_1', 'Address Line 2' => 'line_2', 'Address Suburb' => 'suburb', 'Address Country' => 'country', 'Address State' => 'state', 'Address Region' => 'region', 'Address Post Code' => 'post_code', 'Primary Contact First Name' => 'primary_user_name', 'Primary Contact Last Name' => 'primary_user_last_name', 'Primary Phone' => 'primary_user_phone', 'Primary Email' => 'primary_user_email', 'Primary Fax' => 'primary_user_fax', 'Primary Mobile' => 'primary_user_mobile', 'Primary Language' => 'primary_user_language', 'Invoice Prefix' => 'default_invoice_prefix', 'Tax Name' => 'default_tax_name', 'Tax Rate' => 'default_tax', 'Password' => 'password', 'User Role Name' => 'role', 'Notes' => 'notes', 'Staff' => 'customer_staff'), 'extra' => array(array('owner_table' => 'customer', 'owner_id' => 'customer_id'), array('owner_table' => 'user', 'owner_id' => 'primary_user_id')))), 'title' => 'Import ' . $page_type, 'type' => 'add'); } if (file_exists('includes/plugin_user/pages/contact_admin_list.php') && module_user::can_i('view', 'All ' . $page_type_single . ' Contacts', 'Customer', 'customer')) { $header_buttons[] = array('url' => module_user::link_open_contact(false), 'title' => 'View All Contacts'); } print_heading(array('main' => true, 'type' => 'h2', 'title' => $page_type, 'button' => $header_buttons)); ?> <form action="" method="post">
public static function get_invoice($invoice_id, $basic = false, $skip_permissions = false) { $invoice = array(); $invoice_id = (int) $invoice_id; if ((int) $invoice_id > 0) { // we check the cache to see if the 'full' copy of this invoice exists anywhere yet. // if it does $cache_key = self::_invoice_cache_key($invoice_id, array($invoice_id, $basic, $skip_permissions, isset($_REQUEST['customer_id']) ? $_REQUEST['customer_id'] : 0, isset($_REQUEST['job_id']) ? $_REQUEST['job_id'] : 0)); if ($cached_item = module_cache::get('invoice', $cache_key)) { return $cached_item; } $cache_key_full = self::_invoice_cache_key($invoice_id, array($invoice_id, false, $skip_permissions, isset($_REQUEST['customer_id']) ? $_REQUEST['customer_id'] : 0, isset($_REQUEST['job_id']) ? $_REQUEST['job_id'] : 0)); if ($cache_key_full != $cache_key && ($cached_item = module_cache::get('invoice', $cache_key_full))) { return $cached_item; } $cache_timeout = module_config::c('cache_objects', 60); if ($basic === 2) { // used in links. just want the invoice name really. // todo - cache. meh return get_single('invoice', 'invoice_id', $invoice_id); } else { $sql = "SELECT i.*"; $sql .= ", c.primary_user_id "; // AS user_id // DONE - change this to the invoice table. drop down to select invoice contact. auto select based on contacts role? $sql .= ", c.customer_name AS customer_name "; $sql .= ", GROUP_CONCAT(DISTINCT j.`website_id` SEPARATOR ',') AS website_ids"; // the website id(s) $sql .= ", GROUP_CONCAT(DISTINCT j.`job_id` SEPARATOR ',') AS job_ids"; // the job id(s) $sql .= ", j.customer_id AS new_customer_id "; $sql .= " FROM `" . _DB_PREFIX . "invoice` i "; $sql .= " LEFT JOIN `" . _DB_PREFIX . "invoice_item` ii USING (invoice_id) "; $sql .= " LEFT JOIN `" . _DB_PREFIX . "task` t ON ii.task_id = t.task_id"; $sql .= " LEFT JOIN `" . _DB_PREFIX . "job` j ON t.job_id = j.job_id"; $sql .= " LEFT JOIN `" . _DB_PREFIX . "customer` c ON i.customer_id = c.customer_id "; //$sql .= " LEFT JOIN `"._DB_PREFIX."user` u ON c.primary_user_id = u.user_id "; $sql .= " WHERE i.invoice_id = " . (int) $invoice_id; $sql .= " GROUP BY i.invoice_id"; $invoice = qa1($sql); if (isset($invoice['website_id']) && $invoice['website_id']) { $website_ids = explode(',', $invoice['website_ids']); if (!in_array($invoice['website_id'], $website_ids)) { $website_ids[] = $invoice['website_id']; $invoice['website_ids'] = implode(',', $website_ids); } } } if (isset($invoice['job_ids']) && strlen(trim($invoice['job_ids'])) > 0) { $invoice['job_ids'] = explode(',', $invoice['job_ids']); } else { $invoice['job_ids'] = array(); } // check permissions if ($invoice && isset($invoice['invoice_id']) && $invoice['invoice_id'] == $invoice_id) { switch (self::get_invoice_access_permissions()) { case _INVOICE_ACCESS_ALL: break; case _INVOICE_ACCESS_STAFF: if ($invoice['vendor_user_id'] != module_security::get_loggedin_id()) { if ($skip_permissions) { $invoice['_no_access'] = true; // set a flag for custom processing. we check for this when calling get_customer with the skip permissions argument. (eg: in the ticket file listing link) } else { $invoice = false; } } break; case _INVOICE_ACCESS_JOB: // only invoices from jobs! $has_invoice_access = false; $jobs = module_job::get_jobs(); foreach ($invoice['job_ids'] as $invoice_job_id) { if (isset($jobs[$invoice_job_id])) { $has_invoice_access = true; } } unset($jobs); if (!$has_invoice_access) { if ($skip_permissions) { $invoice['_no_access'] = true; // set a flag for custom processing. we check for this when calling get_customer with the skip permissions argument. (eg: in the ticket file listing link) } else { $invoice = false; } } break; case _INVOICE_ACCESS_CUSTOMER: // tie in with customer permissions to only get invoices from customers we can access. $customers = module_customer::get_customers(); $has_invoice_access = false; if (isset($customers[$invoice['customer_id']])) { $has_invoice_access = true; } unset($customers); /*foreach($customers as $customer){ // todo, if($invoice['customer_id'] == 0) // ignore this permission if($customer['customer_id']==$invoice['customer_id']){ $has_invoice_access = true; break; } }*/ if (!$has_invoice_access) { if ($skip_permissions) { $invoice['_no_access'] = true; // set a flag for custom processing. we check for this when calling get_customer with the skip permissions argument. (eg: in the ticket file listing link) } else { $invoice = false; } } break; } // print_r($invoice);exit; if (!$invoice) { return array(); } $original_invoice = $invoice; $invoice['taxes'] = get_multiple('invoice_tax', array('invoice_id' => $invoice_id), 'invoice_tax_id', 'exact', 'order'); // set the job id of the first job just for kicks if (isset($invoice['deposit_job_id']) && (int) $invoice['deposit_job_id'] > 0) { $invoice['job_ids'][] = $invoice['deposit_job_id']; } if (isset($invoice['website_ids'])) { $invoice['website_ids'] = explode(',', $invoice['website_ids']); } else { $invoice['website_ids'] = array(); } // incase teh customer id on this invoice changes: if (isset($invoice['new_customer_id']) && $invoice['new_customer_id'] > 0 && $invoice['new_customer_id'] != $invoice['customer_id']) { $invoice['customer_id'] = $invoice['new_customer_id']; update_insert('invoice_id', $invoice_id, 'invoice', array('customer_id' => $invoice['new_customer_id'])); } if ($invoice['customer_id'] > 0) { $customer_data = module_customer::get_customer($invoice['customer_id']); if ($customer_data && class_exists('module_company', false) && isset($invoice['company_id']) && !$invoice['company_id'] && isset($customer_data['company_ids']) && count($customer_data['company_ids']) == 1) { // check if this customer has a company. $invoice['company_id'] = key($customer_data['company_ids']); } } if ($basic === true) { module_cache::put('invoice', $cache_key, $invoice, $cache_timeout); return $invoice; } } } // not sure why this code was here, commenting it out for now until we need it. /*if(isset($invoice['customer_id']) && isset($invoice['job_id']) && $invoice['customer_id'] <= 0 && $invoice['job_id'] > 0){ $job_data = module_job::get_job($invoice['job_id'],false); $invoice['customer_id'] = $job_data['customer_id']; }*/ if (!$invoice || !is_array($invoice) || !isset($invoice['invoice_id']) || !$invoice['invoice_id']) { $customer_id = isset($_REQUEST['customer_id']) ? $_REQUEST['customer_id'] : 0; $job_id = isset($_REQUEST['job_id']) ? $_REQUEST['job_id'] : 0; $currency_id = module_config::c('default_currency_id', 1); if ($customer_id > 0) { // find a default website to use ? } else { if ($job_id > 0) { // only a job, no customer. set the customer id. $job_data = module_job::get_job($job_id, false); $customer_id = $job_data['customer_id']; $currency_id = $job_data['currency_id']; } } // work out an invoice number $invoice_number = self::new_invoice_number($customer_id); $invoice = array('invoice_id' => 'new', 'customer_id' => $customer_id, 'job_id' => $job_id, 'job_ids' => $job_id > 0 ? array($job_id) : array(), 'currency_id' => $currency_id, 'name' => $invoice_number, 'cached_total' => 0, 'discount_description' => $job_id > 0 && isset($job_data['discount_description']) ? $job_data['discount_description'] : _l('Discount:'), 'discount_amount' => $job_id > 0 && isset($job_data['discount_amount']) ? $job_data['discount_amount'] : 0, 'discount_type' => $job_id > 0 && isset($job_data['discount_type']) ? $job_data['discount_type'] : module_config::c('invoice_discount_type', _DISCOUNT_TYPE_BEFORE_TAX), 'tax_type' => module_config::c('invoice_tax_type', 0), 'date_create' => date('Y-m-d'), 'date_sent' => '', 'date_due' => date('Y-m-d', strtotime('+' . module_config::c('invoice_due_days', 30) . ' days')), 'date_paid' => '', 'hourly_rate' => module_config::c('hourly_rate', 60), 'status' => module_config::s('invoice_status_default', 'New'), 'user_id' => '', 'date_renew' => '', 'renew_invoice_id' => '', 'deposit_job_id' => 0, 'date_cancel' => '0000-00-00', 'total_amount_deposits' => 0, 'total_amount_deposits_tax' => 0, 'default_task_type' => module_config::c('default_task_type', _TASK_TYPE_HOURS_AMOUNT), 'overdue_email_auto' => module_config::c('overdue_email_auto', 0), 'renew_auto' => 0, 'renew_email' => 0, 'overdue' => false, 'invoice_template_print' => '', 'website_id' => '0', 'website_ids' => ''); $invoice['total_tax_rate'] = module_config::c('tax_percent', 10); $invoice['total_tax_name'] = module_config::c('tax_name', 'TAX'); $customer_data = false; if ($customer_id > 0) { $customer_data = module_customer::get_customer($customer_id); } if ($customer_data && $customer_data['customer_id'] && $customer_data['customer_id'] == $customer_id) { // is there a default invoice template for this customer? if (class_exists('module_extra', false)) { $extras = module_extra::get_extras(array('owner_table' => 'customer', 'owner_id' => $customer_id)); foreach ($extras as $e) { if ($e['extra_key'] == 'invoice_template_print') { $invoice['invoice_template_print'] = $e['extra']; } } } if ($customer_data['primary_user_id']) { $invoice['primary_user_id'] = $customer_data['primary_user_id']; } if (isset($customer_data['default_tax']) && $customer_data['default_tax'] >= 0) { $invoice['total_tax_rate'] = $customer_data['default_tax']; $invoice['total_tax_name'] = $customer_data['default_tax_name']; } } } // drag some details from the related job $first_job_id = 0; if (!(int) $invoice_id) { if (isset($invoice['job_ids']) && $invoice['job_ids']) { $first_job_id = current($invoice['job_ids']); } else { if (isset($invoice['job_id']) && $invoice['job_id']) { $first_job_id = $invoice['job_id']; // abckwards compatibility } else { $first_job_id = 0; } } if ($first_job_id > 0) { $job_data = module_job::get_job($first_job_id, false); $invoice['hourly_rate'] = $job_data['hourly_rate']; $invoice['taxes'] = $job_data['taxes']; //$invoice['total_tax_rate'] = $job_data['total_tax_rate']; //$invoice['total_tax_name'] = $job_data['total_tax_name']; } } // new support for multiple taxes if (!isset($invoice['taxes']) || !count($invoice['taxes']) && $invoice['total_tax_rate'] > 0) { $invoice['taxes'] = array(); if ($first_job_id > 0 && !(int) $invoice_id) { // taxes set above from job } else { $tax_rates = explode(',', $invoice['total_tax_rate']); $tax_names = explode(',', $invoice['total_tax_name']); foreach ($tax_rates as $tax_rate_id => $tax_rate_amount) { if ($tax_rate_amount > 0) { $invoice['taxes'][] = array('order' => 0, 'percent' => $tax_rate_amount, 'name' => isset($tax_names[$tax_rate_id]) ? $tax_names[$tax_rate_id] : $invoice['total_tax_name'], 'total' => 0, 'amount' => 0, 'discount' => 0, 'increment' => module_config::c('tax_multiple_increment', 0)); } } } } // work out total hours etc.. //$invoice['total_hours'] = 0; //$invoice['total_hours_completed'] = 0; //$invoice['total_hours_overworked'] = 0; $invoice['discount_amount_on_tax'] = 0; // used in job.php $invoice['total_sub_amount'] = 0; $invoice['total_sub_amount_taxable'] = 0; $invoice_items = self::get_invoice_items((int) $invoice['invoice_id'], $invoice); foreach ($invoice_items as $invoice_item) { if ($invoice_item['invoice_item_amount'] != 0) { // we have a custom amount for this invoice_item $invoice['total_sub_amount'] += $invoice_item['invoice_item_amount']; if ($invoice_item['taxable']) { $invoice['total_sub_amount_taxable'] += $invoice_item['invoice_item_amount']; if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL) { // tax calculated along the way (this isn't the recommended way, but was included as a feature request) // we add tax to each of the tax array items //$invoice['total_tax'] += round(($invoice_item['invoice_item_amount'] * ($invoice['total_tax_rate'] / 100)),module_config::c('currency_decimal_places',2)); foreach ($invoice['taxes'] as $invoice_tax_id => $invoice_tax) { if (!isset($invoice['taxes'][$invoice_tax_id]['total'])) { $invoice['taxes'][$invoice_tax_id]['total'] = 0; } $invoice['taxes'][$invoice_tax_id]['total'] += $invoice_item['invoice_item_amount']; $invoice['taxes'][$invoice_tax_id]['amount'] += round($invoice_item['invoice_item_amount'] * ($invoice_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); } } } } } //$invoice['final_modification'] = 0; // hack for discount modes - change this to just 'discount_amount' cos that is all that uses this variable. HERE // add any discounts. if ($invoice['discount_amount'] != 0) { if ($invoice['discount_type'] == _DISCOUNT_TYPE_AFTER_TAX) { // after tax discount :::::::::: // handled below. //$invoice['final_modification'] = -$invoice['discount_amount']; } else { if ($invoice['discount_type'] == _DISCOUNT_TYPE_BEFORE_TAX) { // before tax discount::::: //$invoice['final_modification'] = -$invoice['discount_amount']; // problem : this 'discount_amount_on_tax' calculation may not match the correct final discount calculation as per below if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL) { // tax calculated along the way. // we have discounted the 'total amount taxable' so that means we need to reduce the tax amount by that much as well. foreach ($invoice['taxes'] as $invoice_tax_id => $invoice_tax) { $this_tax_discount = round($invoice['discount_amount'] * ($invoice['taxes'][$invoice_tax_id]['percent'] / 100), module_config::c('currency_decimal_places', 2)); $invoice['discount_amount_on_tax'] += $this_tax_discount; if (!isset($invoice['taxes'][$invoice_tax_id]['total'])) { $invoice['taxes'][$invoice_tax_id]['total'] = 0; } $invoice['taxes'][$invoice_tax_id]['total'] -= $invoice['discount_amount']; $invoice['taxes'][$invoice_tax_id]['amount'] -= $this_tax_discount; $invoice['taxes'][$invoice_tax_id]['discount'] = $this_tax_discount; } } else { // we work out what the tax would have been if there was no applied discount // this is used in job.php $invoice['taxes_backup'] = $invoice['taxes']; $invoice['total_sub_amount_taxable_backup'] = $invoice['total_sub_amount_taxable']; $total_tax_before_discount = 0; foreach ($invoice['taxes'] as $invoice_tax_id => $invoice_tax) { $invoice['taxes'][$invoice_tax_id]['total'] = $invoice['total_sub_amount_taxable']; $invoice['taxes'][$invoice_tax_id]['amount'] = round($invoice['total_sub_amount_taxable'] * ($invoice_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); // here we adjust the 'total_sub_amount_taxable' to include the value from the previous calculation. // this is for multiple taxes that addup as they go (eg: Canada) if (isset($invoice_tax['increment']) && $invoice_tax['increment']) { $invoice['total_sub_amount_taxable'] += $invoice['taxes'][$invoice_tax_id]['amount']; } $total_tax_before_discount += $invoice['taxes'][$invoice_tax_id]['amount']; } $invoice['taxes'] = $invoice['taxes_backup']; $invoice['total_sub_amount_taxable'] = $invoice['total_sub_amount_taxable_backup']; } // remove the discount amount from the 'sub total' and the 'taxable total' but don't go negative on it. // remove the discount from any non-taxable portion first. $non_taxable_amount = $invoice['total_sub_amount'] - $invoice['total_sub_amount_taxable']; $non_taxable_discount = min($invoice['discount_amount'], $non_taxable_amount); $taxable_discount = $invoice['discount_amount'] - $non_taxable_discount; //echo "non tax $non_taxable_amount \n nontax discount: $non_taxable_discount \n tax discount: $taxable_discount \n";print_r($invoice);exit; $invoice['total_sub_amount'] -= $invoice['discount_amount']; $invoice['total_sub_amount_taxable'] -= $taxable_discount; // $invoice['total_sub_amount']-=$invoice['discount_amount']; // $invoice['total_sub_amount_taxable']-=$invoice['discount_amount']; } } } //$invoice['total_hours_remain'] = $invoice['total_hours'] - $invoice['total_hours_completed']; //$invoice['total_percent_complete'] = $invoice['total_hours'] > 0 ? round($invoice['total_hours_remain'] / $invoice['total_hours'],2) : 0; //if(isset($invoice['total_tax_rate'])){ if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL && isset($invoice['total_tax']) && $invoice['total_tax'] > 0) { // tax already calculated above. } else { if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_AT_END) { // tax needs to be calculated based on the total_sub_amount_taxable $previous_invoice_tax_id = false; foreach ($invoice['taxes'] as $invoice_tax_id => $invoice_tax) { $invoice['taxes'][$invoice_tax_id]['total'] = $invoice['total_sub_amount_taxable']; if (isset($invoice_tax['increment']) && $invoice_tax['increment'] && $previous_invoice_tax_id) { $invoice['taxes'][$invoice_tax_id]['total'] += $invoice['taxes'][$previous_invoice_tax_id]['amount']; } $invoice['taxes'][$invoice_tax_id]['amount'] = round($invoice['taxes'][$invoice_tax_id]['total'] * ($invoice_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); // here we adjust the 'total_sub_amount_taxable' to include the value from the previous calculation. // this is for multiple taxes that addup as they go (eg: Canada) $previous_invoice_tax_id = $invoice_tax_id; } //$invoice['total_tax'] = round(($invoice['total_sub_amount_taxable'] * ($invoice['total_tax_rate'] / 100)),module_config::c('currency_decimal_places',2)); } else { //$invoice['total_tax'] = 0; } } if (isset($invoice['tax_type']) && $invoice['tax_type'] == 1) { // hack! not completely correct, oh well. // todo - make this work with more than 1 tax rate. // $amount / 1.05 ( this is 1 + tax %) // this will only work if a single tax has been included. if (is_array($invoice['taxes']) && count($invoice['taxes']) > 1) { set_error('Included tax calculation only works with 1 tax rate'); } else { if (is_array($invoice['taxes']) && count($invoice['taxes'])) { reset($invoice['taxes']); $invoice_tax_id = key($invoice['taxes']); if (isset($invoice['taxes'][$invoice_tax_id])) { $taxable_amount = $invoice['total_sub_amount_taxable'] / (1 + $invoice['taxes'][$invoice_tax_id]['percent'] / 100); $invoice['taxes'][$invoice_tax_id]['amount'] = $invoice['total_sub_amount_taxable'] - $taxable_amount; $invoice['total_sub_amount'] = $invoice['total_sub_amount'] - $invoice['taxes'][$invoice_tax_id]['amount']; } } } } $invoice['total_tax'] = 0; foreach ($invoice['taxes'] as $invoice_tax_id => $invoice_tax) { $invoice['total_tax'] += $invoice_tax['amount']; } if (isset($total_tax_before_discount)) { $invoice['discount_amount_on_tax'] += $total_tax_before_discount - $invoice['total_tax']; } $invoice['total_amount'] = $invoice['total_sub_amount'] + $invoice['total_tax']; if ($invoice['discount_type'] == _DISCOUNT_TYPE_AFTER_TAX) { $invoice['total_amount'] -= $invoice['discount_amount']; } $invoice['total_amount'] = round($invoice['total_amount'], module_config::c('currency_decimal_places', 2)); $invoice['overdue'] = $invoice['date_due'] && $invoice['date_due'] != '0000-00-00' && (!$invoice['date_paid'] || $invoice['date_paid'] == '0000-00-00') && strtotime($invoice['date_due']) < strtotime(date('Y-m-d')); if ($basic === 1) { // so we don't go clearning cache and working out how much has been paid. // used in the finance module while displaying dashboard summary. return $invoice; } // find the user id if none exists. /*if($invoice['customer_id'] && !$invoice['user_id']){ $customer_data = module_customer::get_customer($invoice['customer_id']); if($customer_data && $customer_data['customer_id'] == $invoice['customer_id']){ if($customer_data['primary_user_id']){ $invoice['user_id'] = $customer_data['primary_user_id']; }else{ $customer_contacts = module_user::get_contacts(array('customer_id'=>$invoice['customer_id'])); foreach($customer_contacts as $contact){ // todo - search roles or something to find the accountant. $invoice['user_id'] = $contact['user_id']; break; } } } }*/ $paid = 0; /* START DEPOSITS */ $invoice['total_amount_deposits'] = 0; // calculate deposits separately. $invoice['total_amount_deposits_tax'] = 0; // calculate deposits separately. //module_cache::clear_cache(); // no longer clearnig cache, it does it in get_invoice_payments. //module_cache::clear('invoice'); foreach (self::get_invoice_payments($invoice_id) as $payment) { if ($payment['date_paid'] && $payment['date_paid'] != '0000-00-00') { if ($payment['payment_type'] == _INVOICE_PAYMENT_TYPE_DEPOSIT) { // what invoice did this payment come from? $deposit_invoice = module_invoice::get_invoice($payment['other_id']); if ($deposit_invoice && $deposit_invoice['invoice_id'] == $payment['other_id']) { $invoice['total_amount_deposits'] += min($deposit_invoice['total_amount'] - $deposit_invoice['total_tax'], $payment['amount'] - $deposit_invoice['total_tax']); $invoice['total_amount_deposits_tax'] += $deposit_invoice['total_tax']; } } else { $paid += $payment['amount']; } } } if ($invoice['total_amount_deposits'] > 0) { // we need to reduce the 'total_amount' of this invoice so it doesn't double up with the other paid deposit invoice $invoice['total_amount'] -= $invoice['total_amount_deposits']; } if ($invoice['total_amount_deposits_tax'] > 0) { //$invoice['total_tax'] -= $invoice['total_amount_deposits_tax']; // we need to reduce the 'total_amount' of this invoice so it doesn't double up with the other paid deposit invoice $invoice['total_amount'] -= $invoice['total_amount_deposits_tax']; } /* END DEPOSITS */ // any extra fees (eG: paypap fee?) $invoice['fees'] = self::get_fees($invoice_id, $invoice); foreach ($invoice['fees'] as $fee) { $invoice['total_amount'] += $fee['total']; } // dont go negative on payments: $invoice['total_amount_paid'] = max(0, min($invoice['total_amount'], $paid)); $invoice['total_amount_credit'] = 0; if ($invoice['total_amount'] > 0 && $paid > $invoice['total_amount']) { // raise a credit against this customer for the difference. $invoice['total_amount_credit'] = round($paid - $invoice['total_amount'], 2); //echo $invoice['total_amount_overpaid'];exit; } if ($invoice['total_amount'] != $invoice['cached_total']) { if ((int) $invoice_id > 0) { update_insert('invoice_id', $invoice_id, 'invoice', array('cached_total' => $invoice['total_amount'])); } $invoice['cached_total'] = $invoice['total_amount']; } $invoice['total_amount_due'] = round($invoice['total_amount'] - $invoice['total_amount_paid'], module_config::c('currency_decimal_places', 2)); if ($invoice['date_cancel'] != '0000-00-00') { $invoice['total_amount_due'] = 0; } // a special addition for deposit invoices. if (isset($invoice['deposit_job_id']) && $invoice['deposit_job_id']) { // we find out how much deposit has actually been paid // and how much is remaining that hasn't been allocated to any other invoices $invoice['deposit_remaining'] = 0; if ($invoice['total_amount_paid'] > 0) { $invoice['deposit_remaining'] = $invoice['total_amount_paid']; $payments = get_multiple('invoice_payment', array('payment_type' => _INVOICE_PAYMENT_TYPE_DEPOSIT, 'other_id' => $invoice['invoice_id'])); foreach ($payments as $payment) { $invoice['deposit_remaining'] = $invoice['deposit_remaining'] - $payment['amount']; } } } // save our database cache values: if ((int) $invoice_id > 0) { foreach (array('total_amount', 'total_amount_due') as $cacheable_item) { if (isset($invoice[$cacheable_item]) && (!isset($original_invoice) || !isset($original_invoice['c_' . $cacheable_item]) || $original_invoice['c_' . $cacheable_item] != $invoice[$cacheable_item])) { // cacheable items can be the same name or prefixed with c_ update_insert('invoice_id', $invoice_id, 'invoice', array("c_{$cacheable_item}" => $invoice[$cacheable_item])); $invoice["c_{$cacheable_item}"] = $invoice[$cacheable_item]; } } } if (isset($cache_key)) { module_cache::put('invoice', $cache_key, $invoice, $cache_timeout); } return $invoice; }
public static function get_job($job_id, $full = true, $skip_permissions = false) { $job_id = (int) $job_id; if ($job_id <= 0) { $job = array(); } else { $cache_key = self::_job_cache_key($job_id, array($job_id, $full, $skip_permissions)); if ($cached_item = module_cache::get('job', $cache_key)) { return $cached_item; } $cache_key_full = self::_job_cache_key($job_id, array($job_id, true, $skip_permissions)); if ($cache_key_full != $cache_key && ($cached_item = module_cache::get('job', $cache_key_full))) { return $cached_item; } $cache_timeout = module_config::c('cache_objects', 60); $job = get_single("job", "job_id", $job_id); } // check permissions if ($job && isset($job['job_id']) && $job['job_id'] == $job_id) { switch (self::get_job_access_permissions()) { case _JOB_ACCESS_ALL: break; case _JOB_ACCESS_ASSIGNED: // only assigned jobs! $has_job_access = false; if ($job['user_id'] == module_security::get_loggedin_id()) { $has_job_access = true; break; } $tasks = module_job::get_tasks($job['job_id']); foreach ($tasks as $task) { if ($task['user_id'] == module_security::get_loggedin_id()) { $has_job_access = true; break; } } unset($tasks); if (!$has_job_access) { if ($skip_permissions) { $job['_no_access'] = true; // set a flag for custom processing. we check for this when calling get_customer with the skip permissions argument. (eg: in the ticket file listing link) } else { $job = false; } } break; case _JOB_ACCESS_CUSTOMER: // tie in with customer permissions to only get jobs from customers we can access. $customers = module_customer::get_customers(); $has_job_access = false; if (isset($customers[$job['customer_id']])) { $has_job_access = true; } /*foreach($customers as $customer){ // todo, if($job['customer_id'] == 0) // ignore this permission if($customer['customer_id']==$job['customer_id']){ $has_job_access = true; break; } }*/ unset($customers); if (!$has_job_access) { if ($skip_permissions) { $job['_no_access'] = true; // set a flag for custom processing. we check for this when calling get_customer with the skip permissions argument. (eg: in the ticket file listing link) } else { $job = false; } } break; } if ($job) { $job['taxes'] = get_multiple('job_tax', array('job_id' => $job_id), 'job_tax_id', 'exact', 'order'); } } if (!$full) { // unserialize our cached staff_total_grouped key (and other cache keys?) // this is used in finance.php line 1053 $job['staff_total_grouped'] = array(); if (isset($job['c_staff_total_grouped']) && strlen($job['c_staff_total_grouped'])) { $job['staff_total_grouped'] = @unserialize($job['c_staff_total_grouped']); } if (isset($cache_key)) { module_cache::put('job', $cache_key, $job, $cache_timeout); } return $job; } if (!$job) { $customer_id = 0; if (isset($_REQUEST['customer_id']) && $_REQUEST['customer_id']) { // $customer_id = (int) $_REQUEST['customer_id']; // find default website id to use. if (isset($_REQUEST['website_id'])) { $website_id = (int) $_REQUEST['website_id']; } else { } } $default_job_name = module_config::c('job_default_new_name', ''); if (module_config::c('job_name_incrementing', 0)) { $job_number = module_config::c('job_name_incrementing_next', 1); // see if there is an job number matching this one. $this_job_number = $job_number; do { $jobs = get_multiple('job', array('name' => $this_job_number)); //'customer_id'=>$customer_id, if (!count($jobs)) { $job_number = $this_job_number; } else { $this_job_number++; } } while (count($jobs)); module_config::save_config('job_name_incrementing_next', $job_number); $default_job_name = $job_number . $default_job_name; } $job = array('job_id' => 'new', 'customer_id' => $customer_id, 'website_id' => isset($_REQUEST['website_id']) ? $_REQUEST['website_id'] : 0, 'hourly_rate' => module_config::c('hourly_rate', 60), 'name' => $default_job_name, 'date_quote' => date('Y-m-d'), 'date_start' => module_config::c('job_allow_quotes', 0) ? '' : date('Y-m-d'), 'date_due' => '', 'date_completed' => '', 'date_renew' => '', 'user_id' => module_security::get_loggedin_id(), 'renew_job_id' => '', 'status' => module_config::s('job_status_default', 'New'), 'type' => module_config::s('job_type_default', 'Website Design'), 'currency_id' => module_config::c('default_currency_id', 1), 'auto_task_numbers' => '0', 'default_task_type' => module_config::c('default_task_type', _TASK_TYPE_HOURS_AMOUNT), 'description' => '', 'quote_id' => 0, 'discount_description' => _l('Discount:'), 'discount_amount' => 0, 'discount_type' => module_config::c('invoice_discount_type', _DISCOUNT_TYPE_BEFORE_TAX)); if (isset($_REQUEST['from_quote_id']) && (int) $_REQUEST['from_quote_id']) { $quote = module_quote::get_quote($_REQUEST['from_quote_id']); $job = array_merge($job, $quote); $job['date_quote'] = $quote['date_create']; $job['date_start'] = date('Y-m-d'); $job['quote_id'] = (int) $_REQUEST['from_quote_id']; } // some defaults from the db. $job['total_tax_rate'] = module_config::c('tax_percent', 10); $job['total_tax_name'] = module_config::c('tax_name', 'TAX'); if ($customer_id > 0) { $customer_data = module_customer::get_customer($customer_id, false, true); if ($customer_data && isset($customer_data['default_tax']) && $customer_data['default_tax'] >= 0) { $job['total_tax_rate'] = $customer_data['default_tax']; $job['total_tax_name'] = $customer_data['default_tax_name']; } } } // new support for multiple taxes if (!isset($job['taxes']) || !count($job['taxes']) && $job['total_tax_rate'] > 0) { $job['taxes'] = array(); $tax_rates = explode(',', $job['total_tax_rate']); $tax_names = explode(',', $job['total_tax_name']); foreach ($tax_rates as $tax_rate_id => $tax_rate_amount) { if ($tax_rate_amount > 0) { $job['taxes'][] = array('order' => 0, 'percent' => $tax_rate_amount, 'name' => isset($tax_names[$tax_rate_id]) ? $tax_names[$tax_rate_id] : $job['total_tax_name'], 'total' => 0, 'amount' => 0, 'discount' => 0, 'increment' => module_config::c('tax_multiple_increment', 0)); } } } if ($job) { // work out total hours etc.. $job['total_hours'] = 0; $job['total_hours_completed'] = 0; $job['total_hours_overworked'] = 0; $job['total_sub_amount'] = 0; $job['total_sub_amount_taxable'] = 0; $job['total_sub_amount_unbillable'] = 0; $job['total_sub_amount_invoicable'] = 0; $job['total_sub_amount_invoicable_taxable'] = 0; $job['total_amount_invoicable'] = 0; $job['total_tasks_remain'] = 0; $job['total_amount'] = 0; $job['total_amount_paid'] = 0; $job['total_amount_invoiced'] = 0; $job['total_amount_invoiced_deposit'] = 0; $job['total_amount_todo'] = 0; $job['total_amount_outstanding'] = 0; $job['total_amount_due'] = 0; $job['total_hours_remain'] = 0; $job['total_percent_complete'] = isset($job['total_percent_complete']) ? $job['total_percent_complete'] : 0; $job['total_tax'] = 0; $job['total_tax_invoicable'] = 0; $job['invoice_discount_amount'] = 0; $job['invoice_discount_amount_on_tax'] = 0; $job['total_amount_discounted'] = 0; // new feature to invoice incompleted tasks $job['uninvoiced_task_ids'] = array(); // new staff expenses/totals $job['staff_hourly_rate'] = $job['hourly_rate']; $job['staff_total_hours'] = 0; $job['staff_total_hours_completed'] = 0; $job['staff_total_hours_overworked'] = 0; $job['staff_total_sub_amount'] = 0; $job['staff_total_sub_amount_unbillable'] = 0; $job['staff_total_amount'] = 0; $job['staff_total_grouped'] = array(); // total staff expenses grouped by individual staff members. $job['total_net_amount'] = 0; // after the staff expense is taken away. if ($job_id > 0) { $non_hourly_job_count = $non_hourly_job_completed = 0; $tasks = self::get_tasks($job['job_id']); $job_percentage_complete_averages = array(); foreach ($tasks as $task_id => $task) { // new support for different task types if (!isset($task['manual_task_type']) || $task['manual_task_type'] < 0) { $task['manual_task_type'] = $job['default_task_type']; } if (module_config::c('job_task_log_all_hours', 1)) { // jobs have to be marked fully_completd. if (!$task['fully_completed']) { $job['total_tasks_remain']++; } } else { if ($task['amount'] != 0 && $task['completed'] <= 0) { $job['total_tasks_remain']++; } else { if ($task['hours'] > 0 && $task['completed'] < $task['hours']) { $job['total_tasks_remain']++; } } } $tasks[$task_id]['sum_amount'] = 0; if ($task['amount'] != 0) { // we have a custom amount for this task. // do we multiply it by qty (stored in hours?) if ($task['manual_task_type'] == _TASK_TYPE_QTY_AMOUNT) { $tasks[$task_id]['sum_amount'] = $task['amount'] * $task['hours']; } else { $tasks[$task_id]['sum_amount'] = $task['amount']; } } if ($task['manual_task_type'] == _TASK_TYPE_QTY_AMOUNT && $task['hours'] > 0 && $task['amount'] == 0) { $tasks[$task_id]['sum_amount'] = $task['hours'] * $job['hourly_rate']; } if ($task['manual_task_type'] == _TASK_TYPE_HOURS_AMOUNT && $task['hours'] > 0) { $job['total_hours'] += $task['hours']; $task_completed_hours = min($task['hours'], $task['completed']); if ($task['fully_completed']) { // hack to record that we have worked 100% of this task. $task_completed_hours = $task['hours']; } $job['total_hours_completed'] += $task_completed_hours; if ($task['completed'] > $task['hours']) { $job['total_hours_overworked'] += $task['completed'] - $task['hours']; } else { if ($task['completed'] > 0) { // underworked hours $job['total_hours_overworked'] += $task['completed'] - $task['hours']; } } if ($task['amount'] <= 0) { $tasks[$task_id]['sum_amount'] = $task['hours'] * $job['hourly_rate']; } } else { // it's a non-hourly task. // work out if it's completed or not. $non_hourly_job_count++; if ($task['fully_completed']) { $non_hourly_job_completed++; } } if (!$task['invoiced'] && $task['billable']) { $job['uninvoiced_task_ids'][] = $task_id; } if (!$task['invoiced'] && $task['billable'] && (module_config::c('job_task_log_all_hours', 1) || $task['hours'] > 0 && $task['completed'] > 0 && $task['completed'] >= $task['hours'] || $task['hours'] <= 0 && $task['fully_completed'])) { /*if(module_config::c('job_task_log_all_hours',1)){*/ // a task has to be marked "fully_completeD" before it will be invoiced. if ($task['fully_completed']) { $job['total_sub_amount_invoicable'] += $tasks[$task_id]['sum_amount']; if ($task['taxable']) { if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL) { foreach ($job['taxes'] as $job_tax_id => $job_tax) { $job['total_tax_invoicable'] += round($tasks[$task_id]['sum_amount'] * ($job_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); } } else { $job['total_sub_amount_invoicable_taxable'] += $tasks[$task_id]['sum_amount']; } } } /*}else{ $job['total_sub_amount_invoicable'] += $tasks[$task_id]['sum_amount']; if($task['taxable']){ if(module_config::c('tax_calculate_mode',_TAX_CALCULATE_AT_END)==_TAX_CALCULATE_INCREMENTAL){ $job['total_tax_invoicable'] += round(($tasks[$task_id]['sum_amount'] * ($job['total_tax_rate'] / 100)),module_config::c('currency_decimal_places',2)); }else{ $job['total_sub_amount_invoicable_taxable'] += $tasks[$task_id]['sum_amount']; } } //(min($task['hours'],$task['completed']) * $job['hourly_rate']); }*/ } if ($task['taxable'] && $task['billable']) { $job['total_sub_amount_taxable'] += $tasks[$task_id]['sum_amount']; if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL) { //$job['total_tax'] += round(($tasks[$task_id]['sum_amount'] * ($job['total_tax_rate'] / 100)),module_config::c('currency_decimal_places',2)); // todo - incremental multi-tax calculation foreach ($job['taxes'] as $job_tax_id => $job_tax) { if (!isset($job['taxes'][$job_tax_id]['total'])) { $job['taxes'][$job_tax_id]['total'] = 0; } $job['taxes'][$job_tax_id]['total'] += $tasks[$task_id]['sum_amount']; $job['taxes'][$job_tax_id]['amount'] += round($tasks[$task_id]['sum_amount'] * ($job_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); } } } if ($task['billable']) { $job['total_sub_amount'] += $tasks[$task_id]['sum_amount']; } else { $job['total_sub_amount_unbillable'] += $tasks[$task_id]['sum_amount']; } $job_percentage_complete_averages[] = self::get_percentage($tasks[$task_id]); // new staff expenses calculations if (self::job_task_has_split_hours($job_id, $job, $task_id, $task)) { $tasks[$task_id]['staff_sum_amount'] = 0; switch ($task['manual_task_type']) { case _TASK_TYPE_QTY_AMOUNT: $tasks[$task_id]['staff_sum_amount'] = $task['staff_amount'] * $task['staff_hours']; break; case _TASK_TYPE_AMOUNT_ONLY: $tasks[$task_id]['staff_sum_amount'] = $task['staff_amount']; break; case _TASK_TYPE_HOURS_AMOUNT: $tasks[$task_id]['staff_sum_amount'] = $task['staff_amount'] == 0 ? $task['staff_hours'] * $job['staff_hourly_rate'] : $task['staff_amount'] * $task['staff_hours']; break; } if ($task['billable']) { $job['staff_total_sub_amount'] += $tasks[$task_id]['staff_sum_amount']; if (!isset($job['staff_total_grouped'][$task['user_id']])) { $job['staff_total_grouped'][$task['user_id']] = 0; } $job['staff_total_grouped'][$task['user_id']] += $tasks[$task_id]['staff_sum_amount']; } else { $job['staff_total_sub_amount_unbillable'] += $tasks[$task_id]['staff_sum_amount']; } } } // end task loop $job['total_hours_remain'] = $job['total_hours'] - $job['total_hours_completed']; // add any discounts. if ($job['discount_amount'] != 0) { if ($job['discount_type'] == _DISCOUNT_TYPE_AFTER_TAX) { // after tax discount :::::::::: // handled below. //$job['final_modification'] = -$job['discount_amount']; } else { if ($job['discount_type'] == _DISCOUNT_TYPE_BEFORE_TAX) { // before tax discount::::: //$job['final_modification'] = -$job['discount_amount']; // problem : this 'discount_amount_on_tax' calculation may not match the correct final discount calculation as per below if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL) { // tax calculated along the way. // we have discounted the 'total amount taxable' so that means we need to reduce the tax amount by that much as well. foreach ($job['taxes'] as $job_tax_id => $job_tax) { $this_tax_discount = round($job['discount_amount'] * ($job['taxes'][$job_tax_id]['percent'] / 100), module_config::c('currency_decimal_places', 2)); $job['discount_amount_on_tax'] += $this_tax_discount; if (!isset($job['taxes'][$job_tax_id]['total'])) { $job['taxes'][$job_tax_id]['total'] = 0; } $job['taxes'][$job_tax_id]['total'] -= $job['discount_amount']; $job['taxes'][$job_tax_id]['amount'] -= $this_tax_discount; $job['taxes'][$job_tax_id]['discount'] = $this_tax_discount; } } else { // we work out what the tax would have been if there was no applied discount // this is used in job.php $job['taxes_backup'] = $job['taxes']; $job['total_sub_amount_taxable_backup'] = $job['total_sub_amount_taxable']; $total_tax_before_discount = 0; foreach ($job['taxes'] as $job_tax_id => $job_tax) { $job['taxes'][$job_tax_id]['total'] = $job['total_sub_amount_taxable']; $job['taxes'][$job_tax_id]['amount'] = round($job['total_sub_amount_taxable'] * ($job_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); // here we adjust the 'total_sub_amount_taxable' to include the value from the previous calculation. // this is for multiple taxes that addup as they go (eg: Canada) if (isset($job_tax['increment']) && $job_tax['increment']) { $job['total_sub_amount_taxable'] += $job['taxes'][$job_tax_id]['amount']; } $total_tax_before_discount += $job['taxes'][$job_tax_id]['amount']; } $job['taxes'] = $job['taxes_backup']; $job['total_sub_amount_taxable'] = $job['total_sub_amount_taxable_backup']; } // remove the discount amount from the 'sub total' and the 'taxable total' but don't go negative on it. // remove the discount from any non-taxable portion first. $non_taxable_amount = $job['total_sub_amount'] - $job['total_sub_amount_taxable']; $non_taxable_discount = min($job['discount_amount'], $non_taxable_amount); $taxable_discount = $job['discount_amount'] - $non_taxable_discount; //echo "non tax $non_taxable_amount \n nontax discount: $non_taxable_discount \n tax discount: $taxable_discount \n";print_r($job);exit; $job['total_sub_amount'] -= $job['discount_amount']; $job['total_sub_amount_taxable'] -= $taxable_discount; } } } if (count($job_percentage_complete_averages) > 0) { if (!isset($job['total_percent_complete_manual']) || !$job['total_percent_complete_manual']) { $job['total_percent_complete'] = round(array_sum($job_percentage_complete_averages) / count($job_percentage_complete_averages), 2); } else { $job['total_percent_complete_calculated'] = round(array_sum($job_percentage_complete_averages) / count($job_percentage_complete_averages), 2); } } /*if($job['total_hours'] > 0){ // total hours completed. work out job task based on hours completed. $job['total_percent_complete'] = round($job['total_hours_completed'] / $job['total_hours'],2); }else if($non_hourly_job_count>0){ // work out job completed rate based on $non_hourly_job_completed and $non_hourly_job_count $job['total_percent_complete'] = round($non_hourly_job_completed/$non_hourly_job_count,2); }*/ // find any invoices $invoices = module_invoice::get_invoices(array('job_id' => $job_id)); foreach ($invoices as $invoice) { $invoice = module_invoice::get_invoice($invoice['invoice_id']); if (!$invoice) { continue; } //print_r($invoice); // we only ad up the invoiced tasks that are from this job // an invoice could have added manually more items to it, so this would throw the price out. $this_invoice = 0; $this_invoice_taxable = 0; $invoice_items = module_invoice::get_invoice_items($invoice['invoice_id']); // first loop will find out of this is a merged invoice or not. $merged_invoice = false; foreach ($invoice_items as $invoice_item) { if ($invoice_item['task_id'] && !isset($tasks[$invoice_item['task_id']])) { $merged_invoice = true; } } // if it's a merged invoice we don't add non-task-id items to the total. // if its a normal non-merged invoice then we can add the non-task linked items to the total. if (!$merged_invoice) { $this_invoice = $invoice['total_amount']; } else { foreach ($invoice_items as $invoice_item) { if ($invoice_item['task_id'] && isset($tasks[$invoice_item['task_id']]) && $tasks[$invoice_item['task_id']]['billable']) { $this_invoice += $tasks[$invoice_item['task_id']]['sum_amount']; if ($invoice_item['taxable']) { $this_invoice_taxable += $tasks[$invoice_item['task_id']]['sum_amount']; if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL) { foreach ($invoice_item['taxes'] as $invoice_item_tax) { $this_invoice += round($tasks[$invoice_item['task_id']]['sum_amount'] * ($invoice_item_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); } } } } } } // any discounts ? $job['invoice_discount_amount'] += $invoice['discount_amount']; $job['invoice_discount_amount_on_tax'] += $invoice['discount_amount_on_tax']; // todo - move all this tax calculation back to if ($merged_invoice && module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_AT_END && $this_invoice_taxable > 0) { $this_invoice_tax = 0; foreach ($invoice['taxes'] as $invoice_tax) { // todo - incremental or what not in here. $this_invoice_tax = $this_invoice_tax + $this_invoice_taxable * ($invoice_tax['percent'] / 100); } $this_invoice += $this_invoice_tax; //$this_invoice = ($this_invoice + ($this_invoice_taxable * ($invoice['total_tax_rate'] / 100))); } //print_r($invoice); if ($invoice['deposit_job_id'] == $job_id) { $job['total_amount_invoiced_deposit'] += $this_invoice; } else { } $job['total_amount_invoiced'] += $this_invoice; $job['total_amount_paid'] += min($invoice['total_amount_paid'], $this_invoice); $job['total_amount_outstanding'] += min($invoice['total_amount_due'], $this_invoice); } // todo: save these two values in the database so that future changes do not affect them. if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_AT_END) { $job['total_tax'] = 0; $job['total_tax_invoicable'] = 0; $previous_tax_id = false; foreach ($job['taxes'] as $job_tax_id => $job_tax) { if (!isset($job['taxes'][$job_tax_id]['total'])) { $job['taxes'][$job_tax_id]['total'] = 0; } if (!isset($job['taxes'][$job_tax_id]['total_invoicable'])) { $job['taxes'][$job_tax_id]['total_invoicable'] = 0; } if (!isset($job['taxes'][$job_tax_id]['amount_invoicable'])) { $job['taxes'][$job_tax_id]['amount_invoicable'] = 0; } $job['taxes'][$job_tax_id]['total'] += $job['total_sub_amount_taxable']; $job['taxes'][$job_tax_id]['total_invoicable'] += $job['total_sub_amount_invoicable_taxable']; if (isset($job_tax['increment']) && $job_tax['increment'] && $previous_tax_id) { $job['taxes'][$job_tax_id]['total'] += $job['taxes'][$previous_tax_id]['amount']; $job['taxes'][$job_tax_id]['total_invoicable'] += $job['taxes'][$previous_tax_id]['amount_invoicable']; } $t = round($job['taxes'][$job_tax_id]['total'] * ($job_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); $job['taxes'][$job_tax_id]['amount'] += $t; $job['total_tax'] += $t; $t = round($job['taxes'][$job_tax_id]['total_invoicable'] * ($job_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); $job['taxes'][$job_tax_id]['amount_invoicable'] += $t; $job['total_tax_invoicable'] += $t; $previous_tax_id = $job_tax_id; } //$job['total_tax'] = ( ($job['total_sub_amount_taxable']) * ($job['total_tax_rate'] / 100)); //$job['total_tax_invoicable'] =$job['total_sub_amount_invoicable_taxable'] > 0 ? ($job['total_sub_amount_invoicable_taxable'] * ($job['total_tax_rate'] / 100)) : 0; } $job['total_amount'] = round($job['total_sub_amount'] + $job['total_tax'], module_config::c('currency_decimal_places', 2)); if ($job['discount_type'] == _DISCOUNT_TYPE_AFTER_TAX) { $job['total_amount'] -= $job['discount_amount']; $job['total_sub_amount_invoicable'] -= $job['discount_amount']; } $job['total_amount_invoicable'] = $job['total_sub_amount_invoicable'] + $job['total_tax_invoicable']; // + ($job['total_sub_amount_invoicable'] * ($job['total_tax_rate'] / 100)); $job['total_amount_due'] = $job['total_amount'] - $job['total_amount_paid']; //todo: chekc if this is wrong with non-invoicable tasks. //$job['total_amount_outstanding'] = $job['total_amount_invoiced'] - $job['total_amount_paid']; $job['total_amount_discounted'] = $job['total_amount'] - $job['invoice_discount_amount'] - $job['invoice_discount_amount_on_tax']; //$job['total_amount_invoicable'] = $job['total_amount_invoicable'] - $job['invoice_discounts']-$job['invoice_discounts_tax']; $job['total_amount_todo'] = $job['total_amount_discounted'] - $job['total_amount_invoiced'] - $job['total_amount_invoicable']; //$job['total_amount_paid'] - // staff calculations if ($job['staff_total_sub_amount'] > 0) { // tax for staff?? $job['staff_total_amount'] = $job['staff_total_sub_amount']; } $job['total_net_amount'] = $job['total_amount'] - $job['staff_total_amount']; } } if (isset($cache_key)) { module_cache::put('job', $cache_key, $job, $cache_timeout); } self::save_job_cache($job_id, $job); return $job; }
public static function get_customer_restrictions() { if (!self::is_logged_in()) { return array(-1 => -1); } /* new feature: we use this function instead of our hardcoded: switch($customer_access){ case _CUSTOMER_ACCESS_ALL: case _CUSTOMER_ACCESS_CONTACTS: case _CUSTOMER_ACCESS_TASKS: case _CUSTOMER_ACCESS_STAFF: } through out the system. */ /*$res = (isset($_SESSION['_restrict_customer_id'])) ? $_SESSION['_restrict_customer_id'] : false; if(!is_array($res) && $res > 0){ $res = array($res); }*/ if (isset(self::$customer_restrictions[module_security::get_loggedin_id()])) { return self::$customer_restrictions[module_security::get_loggedin_id()]; } self::$customer_restrictions[module_security::get_loggedin_id()] = array(); $customers = module_customer::get_customers(array(), array('columns' => 'c.customer_id', 'as_resource' => false)); if (count($customers) > 0) { foreach ($customers as $customer) { self::$customer_restrictions[module_security::get_loggedin_id()][$customer['customer_id']] = $customer['customer_id']; } } else { self::$customer_restrictions[module_security::get_loggedin_id()] = array(-1 => -1); } return self::$customer_restrictions[module_security::get_loggedin_id()]; }
public static function get_quote($quote_id, $full = true, $skip_permissions = false) { $quote_id = (int) $quote_id; if ($quote_id <= 0) { $quote = array(); } else { $cache_key = self::_quote_cache_key($quote_id, array($quote_id, $full, $skip_permissions)); if ($cached_item = module_cache::get('quote', $cache_key)) { if (function_exists('hook_filter_var')) { $cached_item = hook_filter_var('get_quote', $cached_item, $quote_id); } return $cached_item; } $cache_key_full = self::_quote_cache_key($quote_id, array($quote_id, true, $skip_permissions)); if ($cache_key_full != $cache_key && ($cached_item = module_cache::get('quote', $cache_key_full))) { if (function_exists('hook_filter_var')) { $cached_item = hook_filter_var('get_quote', $cached_item, $quote_id); } return $cached_item; } $cache_timeout = module_config::c('cache_objects', 60); $quote = get_single("quote", "quote_id", $quote_id); } // check permissions if ($quote && isset($quote['quote_id']) && $quote['quote_id'] == $quote_id) { switch (self::get_quote_access_permissions()) { case _QUOTE_ACCESS_ALL: break; case _QUOTE_ACCESS_ASSIGNED: // only assigned quotes! $has_quote_access = false; if ($quote['user_id'] == module_security::get_loggedin_id()) { $has_quote_access = true; break; } $tasks = module_quote::get_tasks($quote['quote_id']); foreach ($tasks as $task) { if ($task['user_id'] == module_security::get_loggedin_id()) { $has_quote_access = true; break; } } unset($tasks); if (!$has_quote_access) { if ($skip_permissions) { $quote['_no_access'] = true; // set a flag for custom processing. we check for this when calling get_customer with the skip permissions argument. (eg: in the ticket file listing link) } else { $quote = false; } } break; case _QUOTE_ACCESS_CUSTOMER: // tie in with customer permissions to only get quotes from customers we can access. $customers = module_customer::get_customers(); $has_quote_access = false; if (isset($customers[$quote['customer_id']])) { $has_quote_access = true; } /*foreach($customers as $customer){ // todo, if($quote['customer_id'] == 0) // ignore this permission if($customer['customer_id']==$quote['customer_id']){ $has_quote_access = true; break; } }*/ unset($customers); if (!$has_quote_access) { if ($skip_permissions) { $quote['_no_access'] = true; // set a flag for custom processing. we check for this when calling get_customer with the skip permissions argument. (eg: in the ticket file listing link) } else { $quote = false; } } break; } if (!$quote) { $quote = array(); if (function_exists('hook_filter_var')) { $quote = hook_filter_var('get_quote', $quote, $quote_id); } return $quote; } $quote['taxes'] = get_multiple('quote_tax', array('quote_id' => $quote_id), 'quote_tax_id', 'exact', 'order'); } if (!$full) { if (isset($cache_key)) { module_cache::put('quote', $cache_key, $quote, $cache_timeout); } if (function_exists('hook_filter_var')) { $quote = hook_filter_var('get_quote', $quote, $quote_id); } return $quote; } if (!$quote) { $customer_id = 0; if (isset($_REQUEST['customer_id']) && $_REQUEST['customer_id']) { // $customer_id = (int) $_REQUEST['customer_id']; // find default website id to use. if (isset($_REQUEST['website_id'])) { $website_id = (int) $_REQUEST['website_id']; } else { } } $default_quote_name = module_config::c('quote_default_new_name', ''); if (module_config::c('quote_name_incrementing', 0)) { $quote_number = module_config::c('quote_name_incrementing_next', 1); // see if there is an quote number matching this one. $this_quote_number = $quote_number; do { $quotes = get_multiple('quote', array('name' => $this_quote_number)); //'customer_id'=>$customer_id, if (!count($quotes)) { $quote_number = $this_quote_number; } else { $this_quote_number++; } } while (count($quotes)); module_config::save_config('quote_name_incrementing_next', $quote_number); $default_quote_name = $quote_number . $default_quote_name; } $quote = array('quote_id' => 'new', 'customer_id' => $customer_id, 'website_id' => isset($_REQUEST['website_id']) ? $_REQUEST['website_id'] : 0, 'hourly_rate' => module_config::c('hourly_rate', 60), 'name' => $default_quote_name, 'date_create' => date('Y-m-d'), 'date_approved' => '0000-00-00', 'approved_by' => '', 'user_id' => module_security::get_loggedin_id(), 'contact_user_id' => -1, 'status' => module_config::s('quote_status_default', 'New'), 'tax_type' => module_config::c('invoice_tax_type', 0), 'type' => module_config::s('quote_type_default', 'Website Design'), 'currency_id' => module_config::c('default_currency_id', 1), 'auto_task_numbers' => '0', 'default_task_type' => module_config::c('default_task_type', _TASK_TYPE_HOURS_AMOUNT), 'description' => '', 'discount_description' => _l('Discount:'), 'discount_amount' => 0, 'discount_type' => module_config::c('invoice_discount_type', _DISCOUNT_TYPE_BEFORE_TAX)); // some defaults from the db. $quote['total_tax_rate'] = module_config::c('tax_percent', 10); $quote['total_tax_name'] = module_config::c('tax_name', 'TAX'); if ($customer_id > 0) { $customer_data = module_customer::get_customer($customer_id, false, true); if ($customer_data && isset($customer_data['default_tax']) && $customer_data['default_tax'] >= 0) { $quote['total_tax_rate'] = $customer_data['default_tax']; $quote['total_tax_name'] = $customer_data['default_tax_name']; } } } // new support for multiple taxes if (!isset($quote['taxes']) || !count($quote['taxes']) && $quote['total_tax_rate'] > 0) { $quote['taxes'] = array(); $tax_rates = explode(',', $quote['total_tax_rate']); $tax_names = explode(',', $quote['total_tax_name']); foreach ($tax_rates as $tax_rate_id => $tax_rate_amount) { if ($tax_rate_amount > 0) { $quote['taxes'][] = array('order' => 0, 'percent' => $tax_rate_amount, 'name' => isset($tax_names[$tax_rate_id]) ? $tax_names[$tax_rate_id] : $quote['total_tax_name'], 'total' => 0, 'amount' => 0, 'discount' => 0, 'increment' => module_config::c('tax_multiple_increment', 0)); } } } if ($quote) { // work out total hours etc.. $quote['total_hours'] = 0; $quote['total_hours_completed'] = 0; $quote['total_hours_overworked'] = 0; $quote['total_sub_amount'] = 0; $quote['total_sub_amount_taxable'] = 0; $quote['total_sub_amount_unbillable'] = 0; $quote['total_sub_amount_invoicable'] = 0; $quote['total_sub_amount_invoicable_taxable'] = 0; $quote['total_amount_invoicable'] = 0; $quote['total_tasks_remain'] = 0; $quote['total_amount'] = 0; $quote['total_amount_paid'] = 0; $quote['total_amount_invoiced'] = 0; $quote['total_amount_invoiced_deposit'] = 0; $quote['total_amount_todo'] = 0; $quote['total_amount_outstanding'] = 0; $quote['total_amount_due'] = 0; $quote['total_hours_remain'] = 0; $quote['total_percent_complete'] = 0; $quote['total_tax'] = 0; $quote['total_tax_invoicable'] = 0; // $quote['invoice_discount_amount'] = 0; // $quote['invoice_discount_amount_on_tax'] = 0; // $quote['total_amount_discounted'] = 0; // new feature to invoice incompleted tasks $quote['uninvoiced_quote_task_ids'] = array(); $quote_items = self::get_quote_items((int) $quote['quote_id'], $quote); foreach ($quote_items as $quote_item) { if ($quote_item['quote_item_amount'] != 0) { // we have a custom amount for this quote_item if ($quote_item['billable']) { $quote['total_sub_amount'] += $quote_item['quote_item_amount']; if ($quote_item['taxable']) { $quote['total_sub_amount_taxable'] += $quote_item['quote_item_amount']; if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL) { // tax calculated along the way (this isn't the recommended way, but was included as a feature request) // we add tax to each of the tax array items //$quote['total_tax'] += round(($quote_item['quote_item_amount'] * ($quote['total_tax_rate'] / 100)),module_config::c('currency_decimal_places',2)); foreach ($quote['taxes'] as $quote_tax_id => $quote_tax) { if (!isset($quote['taxes'][$quote_tax_id]['total'])) { $quote['taxes'][$quote_tax_id]['total'] = 0; } $quote['taxes'][$quote_tax_id]['total'] += $quote_item['quote_item_amount']; $quote['taxes'][$quote_tax_id]['amount'] += round($quote_item['quote_item_amount'] * ($quote_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); } } } } else { $quote['total_sub_amount_unbillable'] += $quote_item['quote_item_amount']; } } } // add any discounts. if ($quote['discount_amount'] != 0) { if ($quote['discount_type'] == _DISCOUNT_TYPE_AFTER_TAX) { // after tax discount :::::::::: // handled below. //$quote['final_modification'] = -$quote['discount_amount']; } else { if ($quote['discount_type'] == _DISCOUNT_TYPE_BEFORE_TAX) { // before tax discount::::: //$quote['final_modification'] = -$quote['discount_amount']; // problem : this 'discount_amount_on_tax' calculation may not match the correct final discount calculation as per below if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_INCREMENTAL) { // tax calculated along the way. // we have discounted the 'total amount taxable' so that means we need to reduce the tax amount by that much as well. foreach ($quote['taxes'] as $quote_tax_id => $quote_tax) { $this_tax_discount = round($quote['discount_amount'] * ($quote['taxes'][$quote_tax_id]['percent'] / 100), module_config::c('currency_decimal_places', 2)); $quote['discount_amount_on_tax'] += $this_tax_discount; if (!isset($quote['taxes'][$quote_tax_id]['total'])) { $quote['taxes'][$quote_tax_id]['total'] = 0; } $quote['taxes'][$quote_tax_id]['total'] -= $quote['discount_amount']; $quote['taxes'][$quote_tax_id]['amount'] -= $this_tax_discount; $quote['taxes'][$quote_tax_id]['discount'] = $this_tax_discount; } } else { // we work out what the tax would have been if there was no applied discount // this is used in job.php $quote['taxes_backup'] = $quote['taxes']; $quote['total_sub_amount_taxable_backup'] = $quote['total_sub_amount_taxable']; $total_tax_before_discount = 0; foreach ($quote['taxes'] as $quote_tax_id => $quote_tax) { $quote['taxes'][$quote_tax_id]['total'] = $quote['total_sub_amount_taxable']; $quote['taxes'][$quote_tax_id]['amount'] = round($quote['total_sub_amount_taxable'] * ($quote_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); // here we adjust the 'total_sub_amount_taxable' to include the value from the previous calculation. // this is for multiple taxes that addup as they go (eg: Canada) if (isset($quote_tax['increment']) && $quote_tax['increment']) { $quote['total_sub_amount_taxable'] += $quote['taxes'][$quote_tax_id]['amount']; } $total_tax_before_discount += $quote['taxes'][$quote_tax_id]['amount']; } $quote['taxes'] = $quote['taxes_backup']; $quote['total_sub_amount_taxable'] = $quote['total_sub_amount_taxable_backup']; } $quote['total_sub_amount'] -= $quote['discount_amount']; $quote['total_sub_amount_taxable'] -= $quote['discount_amount']; } } } if (module_config::c('tax_calculate_mode', _TAX_CALCULATE_AT_END) == _TAX_CALCULATE_AT_END) { // tax needs to be calculated based on the total_sub_amount_taxable $previous_quote_tax_id = false; foreach ($quote['taxes'] as $quote_tax_id => $quote_tax) { $quote['taxes'][$quote_tax_id]['total'] = $quote['total_sub_amount_taxable']; if (isset($quote_tax['increment']) && $quote_tax['increment'] && $previous_quote_tax_id) { $quote['taxes'][$quote_tax_id]['total'] += $quote['taxes'][$previous_quote_tax_id]['amount']; } $quote['taxes'][$quote_tax_id]['amount'] = round($quote['taxes'][$quote_tax_id]['total'] * ($quote_tax['percent'] / 100), module_config::c('currency_decimal_places', 2)); // here we adjust the 'total_sub_amount_taxable' to include the value from the previous calculation. // this is for multiple taxes that addup as they go (eg: Canada) $previous_quote_tax_id = $quote_tax_id; } //$quote['total_tax'] = round(($quote['total_sub_amount_taxable'] * ($quote['total_tax_rate'] / 100)),module_config::c('currency_decimal_places',2)); } else { //$quote['total_tax'] = 0; } if (isset($quote['tax_type']) && $quote['tax_type'] == 1) { // hack! not completely correct, oh well. // todo - make this work with more than 1 tax rate. // $amount / 1.05 ( this is 1 + tax %) // this will only work if a single tax has been included. if (is_array($quote['taxes']) && count($quote['taxes']) > 1) { set_error('Included tax calculation only works with 1 tax rate'); } else { if (is_array($quote['taxes']) && count($quote['taxes'])) { reset($quote['taxes']); $quote_tax_id = key($quote['taxes']); if (isset($quote['taxes'][$quote_tax_id])) { $taxable_amount = $quote['total_sub_amount_taxable'] / (1 + $quote['taxes'][$quote_tax_id]['percent'] / 100); $quote['taxes'][$quote_tax_id]['amount'] = $quote['total_sub_amount_taxable'] - $taxable_amount; $quote['total_sub_amount'] = $quote['total_sub_amount'] - $quote['taxes'][$quote_tax_id]['amount']; } } } } $quote['total_tax'] = 0; foreach ($quote['taxes'] as $quote_tax_id => $quote_tax) { $quote['total_tax'] += $quote_tax['amount']; } $quote['total_amount'] = $quote['total_sub_amount'] + $quote['total_tax']; if ($quote['discount_type'] == _DISCOUNT_TYPE_AFTER_TAX) { $quote['total_amount'] -= $quote['discount_amount']; } $quote['total_amount'] = round($quote['total_amount'], module_config::c('currency_decimal_places', 2)); } if (isset($cache_key)) { module_cache::put('quote', $cache_key, $quote, $cache_timeout); } if (function_exists('hook_filter_var')) { $quote = hook_filter_var('get_quote', $quote, $quote_id); } return $quote; }
* Licence: Please check CodeCanyon.net for licence details. * More licence clarification available here: http://codecanyon.net/wiki/support/legal-terms/licensing-terms/ * Deploy: 9809 f200f46c2a19bb98d112f2d32a8de0c4 * Envato: 4ffca17e-861e-4921-86c3-8931978c40ca, 0a3014a3-2b8f-460b-8850-d6025aa845f8 * Package Date: 2015-11-25 03:08:08 * IP Address: 67.79.165.254 */ if (class_exists('module_customer', false) && module_security::can_user(module_security::get_loggedin_id(), 'Show Dashboard Widgets')) { $customer_types = module_customer::get_customer_types(); foreach ($customer_types as $customer_type) { // if(!empty($customer_type['type_name_plural']) && $customer_type['customer_type_id']) { // dtbaker if (!empty($customer_type['type_name_plural'])) { // dtbaker if (module_customer::can_i('view', $customer_type['type_name_plural'])) { // find out how many open customers are left.. $customers = module_customer::get_customers(array('customer_type_id' => $customer_type['customer_type_id']), true); ob_start(); // icons from http://ionicons.com/ ?> <div class="small-box bg-yellow"> <div class="inner"> <h3> <?php echo mysql_num_rows($customers); ?> </h3> <p> <?php _e('Current %s', htmlspecialchars($customer_type['type_name_plural'])); ?>
public static function hook_filter_var_customer_list($call, $customer_attributes) { if (!is_array($customer_attributes)) { $customer_attributes = array(); } foreach (module_customer::get_customers(array(), array('columns' => 'c.customer_id, c.customer_name')) as $customer) { $customer_attributes[$customer['customer_id']] = $customer['customer_name']; } return $customer_attributes; }
<?php /** * Copyright: dtbaker 2012 * Licence: Please check CodeCanyon.net for licence details. * More licence clarification available here: http://codecanyon.net/wiki/support/legal-terms/licensing-terms/ * Deploy: 9809 f200f46c2a19bb98d112f2d32a8de0c4 * Envato: 4ffca17e-861e-4921-86c3-8931978c40ca * Package Date: 2015-11-25 02:55:20 * IP Address: 67.79.165.254 */ $search = isset($_REQUEST['search']) ? $_REQUEST['search'] : array(); $customers = module_customer::get_customers($search); $pagination = process_pagination($customers); ?> <h2> <?php if (module_customer::can_i('create', 'Customers')) { ?> <span class="button"> <?php echo create_link("Create New Customer", "add", module_customer::link_open('new')); ?> </span> <?php } ?>
<input type="checkbox" name="finance_category_new_checked" value="new"> <input type="text" name="finance_category_new" value=""> <?php })); if (class_exists('module_company', false) && module_company::can_i('view', 'Company') && module_company::is_enabled()) { $companys = module_company::get_companys(); $companys_rel = array(); foreach ($companys as $company) { $companys_rel[$company['company_id']] = $company['name']; } $fieldset_data['elements'][] = array('title' => 'Company', 'field' => array('type' => 'select', 'name' => 'company_id', 'value' => isset($finance['company_id']) ? $finance['company_id'] : '', 'options' => $companys_rel, 'blank' => _l(' - Default - '), 'help' => 'Link this finance item with an individual company. It is better to select a Customer below and assign the Customer to a Company.')); } if (module_config::c('finance_link_to_jobs', 1) && module_job::can_i('view', 'Jobs')) { $fieldset_data['elements'][] = array('title' => 'Linked Customer', 'fields' => array(function () use(&$finance, $locked) { echo print_select_box(module_customer::get_customers(), 'customer_id', $finance['customer_id'], '', _l(' - None - '), 'customer_name'); ?> <script type="text/javascript"> $(function(){ $('#customer_id').change(function(){ // change our customer id. var new_customer_id = $(this).val(); $.ajax({ type: 'POST', url: '<?php echo module_job::link_open(false); ?> ', data: { '_process': 'ajax_job_list',