function handle_hook($hook, $calling_module = false, $owner_table = false, $key_name = false, $key_value = false, $rel_data = false) { switch ($hook) { case "home_alerts": $alerts = array(); if (module_config::c('allow_note_reminders', 1)) { // find any jobs that are past the due date and dont have a finished date. $key = _l('Note Reminder'); if (class_exists('module_dashboard', false)) { module_dashboard::register_group($key, array('columns' => array('name' => _l('Reminder'), 'type' => _l('Type'), 'full_link' => _l('Link'), 'date' => _l('Date'), 'days' => _l('Date')))); } $sql = "SELECT * FROM `" . _DB_PREFIX . "note` n "; $sql .= " WHERE n.`reminder` = 1 AND n.note_time < " . (int) strtotime('+' . module_config::c('alert_days_in_future', 5) . ' days') . ""; $sql .= " AND ( n.`user_id` = 0 OR n.`user_id` = " . module_security::get_loggedin_id() . ")"; $sql .= " ORDER BY n.note_time ASC"; $tasks = qa($sql); foreach ($tasks as $task) { $alert_res = process_alert(date('Y-m-d', $task['note_time']), $key); if ($alert_res) { $alert_res['link'] = $task['rel_data']; // fix for linking when changing folder. $alert_res['type'] = _l(ucwords($task['owner_table'])); switch ($task['owner_table']) { case 'user': $user = module_user::get_user($task['owner_id']); if ($user['customer_id'] || $user['vendor_id']) { $alert_res['link'] = module_user::link_open_contact($task['owner_id'], false, $user); $alert_res['full_link'] = module_user::link_open_contact($task['owner_id'], true, $user); $alert_res['type'] = _l('Contact'); } else { $alert_res['link'] = module_user::link_open($task['owner_id'], false, $user); $alert_res['full_link'] = module_user::link_open($task['owner_id'], true, $user); } break; case 'invoice': $invoice_data = module_invoice::get_invoice($task['owner_id'], true); if (!$invoice_data || !isset($invoice_data['invoice_id']) || $invoice_data['invoice_id'] != $task['owner_id']) { continue 2; } $alert_res['link'] = module_invoice::link_open($task['owner_id'], false, $invoice_data); $alert_res['full_link'] = module_invoice::link_open($task['owner_id'], true, $invoice_data); break; case 'quote': $quote_data = module_quote::get_quote($task['owner_id'], true); if (!$quote_data || !isset($quote_data['quote_id']) || $quote_data['quote_id'] != $task['owner_id']) { continue 2; } $alert_res['link'] = module_quote::link_open($task['owner_id'], false, $quote_data); $alert_res['full_link'] = module_quote::link_open($task['owner_id'], true, $quote_data); break; case 'website': $website_data = module_website::get_website($task['owner_id']); if (!$website_data || !isset($website_data['website_id']) || $website_data['website_id'] != $task['owner_id']) { continue 2; } $alert_res['link'] = module_website::link_open($task['owner_id'], false); $alert_res['full_link'] = module_website::link_open($task['owner_id'], true); break; case 'customer': $customer_data = module_customer::get_customer($task['owner_id']); if (!$customer_data || !isset($customer_data['customer_id']) || $customer_data['customer_id'] != $task['owner_id']) { continue 2; } $alert_res['link'] = module_customer::link_open($task['owner_id'], false, $customer_data); $alert_res['full_link'] = module_customer::link_open($task['owner_id'], true, $customer_data); break; case 'vendor': $vendor_data = module_vendor::get_vendor($task['owner_id']); if (!$vendor_data || !isset($vendor_data['vendor_id']) || $vendor_data['vendor_id'] != $task['owner_id']) { continue 2; } $alert_res['link'] = module_vendor::link_open($task['owner_id'], false, $vendor_data); $alert_res['full_link'] = module_vendor::link_open($task['owner_id'], true, $vendor_data); break; case 'job': $job_data = module_job::get_job($task['owner_id']); if (!$job_data || !isset($job_data['job_id']) || $job_data['job_id'] != $task['owner_id']) { continue 2; } $alert_res['link'] = module_job::link_open($task['owner_id'], false, $job_data); $alert_res['full_link'] = module_job::link_open($task['owner_id'], true, $job_data); break; // todo - add others. } $alert_res['name'] = $task['note']; $alert_res['date'] = print_date($alert_res['date']); $alert_res['time'] = $task['note_time']; $alerts[] = $alert_res; } } } return $alerts; break; /*case "note_list": if($owner_id && $owner_id != 'new'){ $note_items = $this->get_notes(array("owner_table"=>$owner_table,"owner_id"=>$owner_id)); foreach($note_items as &$note_item){ // do it in loop here because of $this issues in static method below. // instead of include file below. $note_item['html'] = $this->print_note($note_item['note_id']); } include("pages/note_list.php"); }else{ echo 'Please save first before creating notes.'; } break;*/ /*case "note_list": if($owner_id && $owner_id != 'new'){ $note_items = $this->get_notes(array("owner_table"=>$owner_table,"owner_id"=>$owner_id)); foreach($note_items as &$note_item){ // do it in loop here because of $this issues in static method below. // instead of include file below. $note_item['html'] = $this->print_note($note_item['note_id']); } include("pages/note_list.php"); }else{ echo 'Please save first before creating notes.'; } break;*/ case "note_delete": // find the key we are saving this address against. $owner_id = (int) $key_value; if (!$owner_id || $owner_id == 'new') { // find one in the post data. if (isset($_REQUEST[$key_name])) { $owner_id = $_REQUEST[$key_name]; } } $note_hash = md5($owner_id . '|' . $owner_table); // just for posting unique arrays. if ($owner_table && $owner_id) { $this->note_delete($owner_table, $owner_id); } break; } }
} }); } } if (class_exists('module_group', false)) { $columns['group'] = array('title' => 'Group', 'callback' => function ($quote) { $groups = module_group::get_groups_search(array('owner_table' => 'quote', 'owner_id' => $quote['quote_id'])); $g = array(); foreach ($groups as $group) { $g[] = $group['name']; } echo implode(', ', $g); }); } $table_manager->set_columns($columns); $table_manager->row_callback = function ($row_data) { // load the full vendor data before displaying each row so we have access to more details return module_quote::get_quote($row_data['quote_id']); }; $table_manager->set_rows($quotes); if (class_exists('module_extra', false)) { $table_manager->display_extra('quote', function ($quote) { module_extra::print_table_data('quote', $quote['quote_id']); }); } $table_manager->pagination = true; $table_manager->print_table(); ?> </form>
* 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 */ if (!$quote_safe) { die('failed'); } if (!module_quote::can_i('edit', 'Quotes')) { die('no perms'); } $quote_id = (int) $_REQUEST['quote_id']; $quote = module_quote::get_quote($quote_id); // template for sending emails. // are we sending the paid one? or the dueone. //$template_name = 'quote_email'; $template_name = isset($_REQUEST['template_name']) ? $_REQUEST['template_name'] : 'quote_email'; $template = module_template::get_template_by_key($template_name); $quote['total_amount_print'] = dollar($quote['total_amount'], true, $quote['currency_id']); $quote['total_amount_due_print'] = dollar($quote['total_amount_due'], true, $quote['currency_id']); $quote['quote_name'] = $quote['name']; $quote['from_name'] = module_security::get_loggedin_name(); $quote['quote_url'] = module_quote::link_public($quote_id); ob_start(); include module_theme::include_ucm('includes/plugin_quote/template/quote_task_list.php'); $public_html = ob_get_clean(); $quote['task_list'] = $public_html; /*ob_start();
<th> <?php _e('Amount'); ?> </th> <?php } ?> </tr> </thead> <tbody> <?php foreach ($quotes as $quote) { $quote = module_quote::get_quote($quote['quote_id']); ?> <tr class="<?php echo $c++ % 2 ? "odd" : "even"; ?> "> <td class="row_action"> <?php echo module_quote::link_open($quote['quote_id'], true); ?> </td> <td> <?php echo print_date($quote['date_create']);
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 quote_approved($quote_id) { module_cache::clear('quote'); $quote_data = module_quote::get_quote($quote_id); hook_handle_callback('quote_approved', $quote_id); self::add_history($quote_id, 'Quote approved by ' . $quote_data['approved_by']); if (module_config::c('quote_approval_auto_email', 1) && $quote_data['user_id']) { // send an email to the assigned staff member letting them know the quote was approved. $template = module_template::get_template_by_key('quote_approved_email'); $replace = module_quote::get_replace_fields($quote_id, $quote_data); if (defined('_BLOCK_EMAILS') && _BLOCK_EMAILS) { $pdf = false; } else { $pdf = module_quote::generate_pdf($quote_id); } $template->assign_values($replace); $html = $template->render('html'); // send an email to this user. $email = module_email::new_email(); $email->replace_values = $replace; $email->set_to('user', $quote_data['user_id']); $email->set_bcc_manual(module_config::c('admin_email_address', ''), ''); //$email->set_from('user',); // nfi $email->set_subject($template->description); // do we send images inline? $email->set_html($html); if ($pdf) { $email->add_attachment($pdf); } $email->quote_id = $quote_id; $email->customer_id = $quote_data['customer_id']; $email->prevent_duplicates = true; if ($email->send()) { // it worked successfully!! // record a log on the quote when it's done. self::add_history($quote_id, _l('Quote approval emailed to staff member')); } else { /// log err? } } module_cache::clear('quote'); }
$fieldset_data['elements'][] = array('title' => 'Job', 'fields' => array(array('type' => 'select', 'name' => 'job_id', 'value' => $file['job_id'], 'options' => $c), function () use(&$file) { if ($file['job_id']) { echo ' '; echo '<a href="' . module_job::link_open($file['job_id'], false) . '">' . _l('Open Job »') . '</a>'; } })); } if (class_exists('module_quote', false) && module_quote::is_plugin_enabled()) { $c = array(); $res = module_quote::get_quotes(array('customer_id' => $file['customer_id'])); foreach ($res as $row) { $c[$row['quote_id']] = $row['name']; } if ($file['quote_id'] && !isset($c[$file['quote_id']])) { // this file is related to another quote. from another customer. $related_quote = module_quote::get_quote($file['quote_id'], false, true); if ($related_quote && $related_quote['quote_id'] == $file['quote_id']) { $related_customer = module_customer::get_customer($related_quote['customer_id'], true); if ($related_customer && $related_customer['customer_id'] == $related_quote['customer_id']) { $c[$file['quote_id']] = _l('%s (from %s)', $related_quote['name'], $related_customer['customer_name']); } else { $file['quote_id'] = false; } } else { $file['quote_id'] = false; } } $fieldset_data['elements'][] = array('title' => 'Quote', 'fields' => array(array('type' => 'select', 'name' => 'quote_id', 'value' => $file['quote_id'], 'options' => $c), function () use(&$file) { if ($file['quote_id']) { echo ' '; echo '<a href="' . module_quote::link_open($file['quote_id'], false) . '">' . _l('Open Quote »') . '</a>';
public static function hook_filter_generate_fieldset_options($callback_name, $fieldset_options) { if (is_array($fieldset_options) && isset($fieldset_options['id']) && $fieldset_options['id'] == 'quote_advanced') { $ticket_quote_rel_data = array(); $customer_id = 0; // which customer to bring related quotes in from. if (isset($_REQUEST['ticket_id']) && (int) $_REQUEST['ticket_id'] > 0) { $ticket_data = module_ticket::get_ticket($_REQUEST['ticket_id'], false); if ($ticket_data && $ticket_data['ticket_id'] == $_REQUEST['ticket_id']) { // we're creating a new quote linked to this particular ticket $ticket_quote_rel_data[] = $ticket_data['ticket_id']; $customer_id = $ticket_data['customer_id']; } } if (isset($_REQUEST['quote_id']) && (int) $_REQUEST['quote_id'] > 0) { // we're opening an existing quote, find any matching linked tickets. $quote_data = module_quote::get_quote($_REQUEST['quote_id']); if ($quote_data && $quote_data['quote_id'] == $_REQUEST['quote_id']) { if ($quote_data['customer_id']) { $customer_id = $quote_data['customer_id']; } // any existing ones from within the database? $existing = get_multiple('ticket_quote_rel', array('quote_id' => $quote_data['quote_id'])); foreach ($existing as $e) { if ($e['ticket_id']) { $ticket_quote_rel_data[] = $e['ticket_id']; } } } } $select_values = array(); if ($customer_id > 0) { $tickets = module_ticket::get_tickets(array('customer_id' => $customer_id)); while ($row = mysql_fetch_assoc($tickets)) { $select_values[$row['ticket_id']] = module_ticket::ticket_number($row['ticket_id']) . ' ' . substr($row['subject'], 0, 20) . '...'; } } if (!count($ticket_quote_rel_data)) { $ticket_quote_rel_data = array(false); } $ticket_links = array(); foreach ($ticket_quote_rel_data as $ticket_id) { if ($ticket_id > 0) { $ticket_links[] = module_ticket::link_open($ticket_id, true); } } $fieldset_options['elements']['ticket_rel_ids'] = array('title' => 'Linked Tickets', 'fields' => array('<div id="ticket_rel_ids_holder">', array('type' => 'select', 'name' => 'ticket_rel_ids[]', 'options' => $select_values, 'multiple' => 'ticket_rel_ids_holder', 'values' => $ticket_quote_rel_data), '</div>', '<div>' . implode(' ', $ticket_links) . '</div>')); } return $fieldset_options; }