<td> <?php $c = array(); $quotes = module_quote::get_quotes(array('customer_id' => $job['customer_id'])); foreach ($quotes as $quote) { $c[$quote['quote_id']] = $quote['name']; } echo print_select_box($c, 'quote_id', $job['quote_id']); ?> <?php if ($job['quote_id'] && module_quote::can_i('view', 'Quotes')) { ?> <a href="<?php echo module_quote::link_open($job['quote_id'], false); ?> "><?php _e('Open'); ?> </a> <?php } ?> </td> </tr> <?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']); ?> </td> <td> <?php echo print_date($quote['date_approved']); ?> </td>
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; }
// are we sending the paid one? or the dueone. $template_name = isset($_REQUEST['template_name']) ? $_REQUEST['template_name'] : 'quote_staff_email'; $template = module_template::get_template_by_key($template_name); $quote['quote_name'] = $quote['name']; $quote['staff_name'] = $staff['name']; $quote['quote_url'] = module_quote::link_open($quote_id); $quote['quote_tasks'] = '<ul>'; $quote['task_count'] = 0; foreach ($quote_tasks as $quote_task) { if ($quote_task['user_id'] != $staff_id) { continue; } $quote['quote_tasks'] .= '<li><strong>' . $quote_task['description'] . '</strong>'; $quote['quote_tasks'] .= ' <br/>'; if ($quote_task['long_description']) { $quote['quote_tasks'] .= _l('Notes:') . ' <em>' . $quote_task['long_description'] . '</em><br/>'; } if ($quote_task['hours']) { $quote['quote_tasks'] .= _l('Hours:') . ' ' . $quote_task['hours'] . '<br/>'; } $quote['quote_tasks'] .= '</li>'; $quote['task_count']++; } $quote['quote_tasks'] .= '</ul>'; // find available "to" recipients. // customer contacts. $to = array(); $to[] = array('name' => $staff['name'], 'email' => $staff['email']); $template->assign_values($quote); module_email::print_compose(array('title' => _l('Email Quote: %s', $quote['name']), 'find_other_templates' => 'quote_staff_email', 'current_template' => $template_name, 'quote_id' => $quote['quote_id'], 'debug_message' => 'Sending quote to staff', 'to' => $to, 'bcc' => module_config::c('admin_email_address', ''), 'content' => $template->render('html'), 'subject' => $template->replace_description(), 'success_url' => module_quote::link_open($quote_id), 'cancel_url' => module_quote::link_open($quote_id)));
* IP Address: 67.79.165.254 */ if ($ticket_id > 0 && module_config::c('ticket_allow_billing', 1) && module_ticket::can_edit_tickets()) { $done_in_ticket_billing = true; $responsive_summary = array(); $quotes = array(); if (class_exists('module_quote', false) && module_quote::is_plugin_enabled()) { $quotes = module_quote::get_quotes(array('ticket_id' => $ticket_id)); foreach ($quotes as $quote) { $responsive_summary[] = module_quote::link_open($quote['quote_id'], true, $quote); } } $fieldset_data = array('heading' => array('type' => 'h3', 'title' => 'Ticket Billing', 'responsive' => array('summary' => implode(', ', $responsive_summary))), 'class' => 'tableclass tableclass_form tableclass_full', 'elements' => array()); $c = array(); $res = module_customer::get_customers(); while ($row = array_shift($res)) { $c[$row['customer_id']] = $row['customer_name']; } if ($ticket['customer_id'] < 0) { $ticket['customer_id'] = false; } $fieldset_data['elements'][] = array('title' => _l('Customer'), 'fields' => array(array('type' => 'select', 'name' => 'change_customer_id', 'value' => $ticket['customer_id'], 'options' => $c), array('type' => 'button', 'name' => 'new_customer', 'value' => _l('New'), 'onclick' => "window.location.href='" . module_customer::link_open('new', false) . "&move_user_id=" . $ticket['user_id'] . "';"))); if (class_exists('module_quote', false) && module_quote::is_plugin_enabled()) { $quote_list = ''; foreach ($quotes as $quote) { $quote_list .= module_quote::link_open($quote['quote_id'], true, $quote) . ' (<a href="#" onclick="ucm.ticket.add_to_message($(this).data(\'link\'));return false;" data-link="<a href=\'' . module_quote::link_public($quote['quote_id']) . '\'>View Quote</a>">insert link</a>) <br/>'; } $fieldset_data['elements'][] = array('title' => _l('Quotes'), 'fields' => array($quote_list, array('type' => 'button', 'name' => 'new_quote', 'value' => _l('New'), 'onclick' => "window.location.href='" . module_quote::link_open('new', false) . "&ticket_id=" . $ticket_id . "';"))); } echo module_form::generate_fieldset($fieldset_data); }
public static function quote_html($quote_id, $quote_data, $mode = 'html') { if ($quote_id && $quote_data) { // spit out the quote html into a file, then pass it to the pdf converter // to convert it into a PDF. $quote = $quote_data; if (class_exists('module_company', false) && isset($quote_data['company_id']) && (int) $quote_data['company_id'] > 0) { module_company::set_current_company_id($quote_data['company_id']); } $quote_template = isset($quote_data['quote_template_print']) && strlen($quote_data['quote_template_print']) ? $quote_data['quote_template_print'] : module_config::c('quote_template_print_default', 'quote_pdf'); $quote_template_suffix = ''; if ($quote_template != 'quote_pdf') { $quote_template_suffix = str_replace('quote_pdf', '', $quote_template); } ob_start(); include module_theme::include_ucm('includes/plugin_quote/template/quote_task_list.php'); $task_list_html = ob_get_clean(); $replace = self::get_replace_fields($quote_id, $quote_data); $replace['task_list'] = $task_list_html; $replace['quote_link'] = module_quote::link_public($quote_id); $replace['external_quote_template_html'] = ''; $external_quote_template = module_template::get_template_by_key('quote_pdf'); $external_quote_template->assign_values($replace); $replace['external_quote_template_html'] = $external_quote_template->replace_content(); ob_start(); $template = module_template::get_template_by_key($quote_template); $template->assign_values($replace); echo $template->render('html'); $quote_html = ob_get_clean(); return $quote_html; } return false; }
<?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 */ $quote_safe = true; // stop including files directly. if (!module_quote::can_i('view', 'Quotes')) { echo 'permission denied'; return; } if (isset($_REQUEST['quote_id'])) { if (isset($_REQUEST['email_staff'])) { include module_theme::include_ucm("includes/plugin_quote/pages/quote_admin_email_staff.php"); } else { if (isset($_REQUEST['email'])) { include module_theme::include_ucm("includes/plugin_quote/pages/quote_admin_email.php"); } else { if ((int) $_REQUEST['quote_id'] > 0) { include module_theme::include_ucm("includes/plugin_quote/pages/quote_admin_edit.php"); //include("quote_admin_edit.php"); } else { include module_theme::include_ucm("includes/plugin_quote/pages/quote_admin_create.php"); //include("quote_admin_create.php"); }
include('quote_public.php'); $quote['quote_tasks'] = ob_get_clean();*/ // generate the PDF ready for sending. $pdf = module_quote::generate_pdf($quote_id); // find available "to" recipients. // customer contacts. $to_select = false; if ($quote['customer_id']) { $customer = module_customer::get_customer($quote['customer_id']); $quote['customer_name'] = $customer['customer_name']; $to = module_user::get_contacts(array('customer_id' => $quote['customer_id'])); if ($quote['contact_user_id']) { $primary = module_user::get_user($quote['contact_user_id']); if ($primary) { $to_select = $primary['email']; } } else { if ($customer['primary_user_id']) { $primary = module_user::get_user($customer['primary_user_id']); if ($primary) { $to_select = $primary['email']; } } } } else { $to = array(); } $template->assign_values($quote); ob_start(); module_email::print_compose(array('title' => _l('Email Quote: %s', $quote['name']), 'find_other_templates' => 'quote_email', 'current_template' => $template_name, 'customer_id' => $quote['customer_id'], 'quote_id' => $quote['quote_id'], 'debug_message' => 'Sending quote as email', 'to' => $to, 'to_select' => $to_select, 'bcc' => module_config::c('admin_email_address', ''), 'content' => $template->render('html'), 'subject' => $template->replace_description(), 'success_url' => module_quote::link_open($quote_id), 'cancel_url' => module_quote::link_open($quote_id), 'attachments' => array(array('path' => $pdf, 'name' => basename($pdf), 'preview' => module_quote::link_public_print($quote_id)))));
if (function_exists('hook_handle_callback')) { hook_handle_callback('quote_task_after', $task_data['quote_id'], $task_data['quote_task_id'], $quote, $task_data); } ?> </td> <td colspan="<?php echo $colspan; ?> " valign="top"> <div> <?php _e('Task Type:'); ?> <?php $types = module_quote::get_task_types(); $types['-1'] = _l('Default (%s)', $types[$quote['default_task_type']]); module_form::generate_form_element(array('type' => 'select', 'name' => 'quote_task[' . $quote_task_id . '][manual_task_type]', 'id' => 'manual_task_type_' . $quote_task_id, 'options' => $types, 'blank' => false, 'value' => $task_data['manual_task_type_real'])); ?> </div> </td> <td class="edit_task_options"> <div> <?php if ($task_editable) { ?> <input type="hidden" name="quote_task[<?php echo $quote_task_id; ?> ][billable_t]" value="1">
public function delete_customer($customer_id, $remove_linked_data = true) { $customer_id = (int) $customer_id; if ($customer_id > 0) { if (_DEMO_MODE && $customer_id == 1) { set_error('Sorry this is a Demo Customer. It cannot be changed.'); redirect_browser(self::link_open($customer_id)); } $customer = self::get_customer($customer_id); if ($customer && $customer['customer_id'] == $customer_id) { // todo: Delete emails (wack these in this customer_deleted hook) hook_handle_callback('customer_deleted', $customer_id, $remove_linked_data); if (class_exists('module_group', false)) { // remove the customer from his groups module_group::delete_member($customer_id, 'customer'); } if (class_exists('module_extra', false)) { module_extra::delete_extras('customer', 'customer_id', $customer_id); } // remove the contacts from this customer foreach (module_user::get_contacts(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { module_user::delete_user($val['user_id']); } } // remove staff delete_from_db('customer_user_rel', 'customer_id', $customer_id); if (class_exists('module_note', false)) { module_note::note_delete("customer", 'customer_id', $customer_id); } handle_hook("address_delete", $this, 'all', "customer", 'customer_id', $customer_id); // todo, check the 'delete' permission on each one of these 'delete' method calls // do that better when we remove each of these and put them into the customer delete hook if ($remove_linked_data) { if (class_exists('module_website', false) && module_website::is_plugin_enabled()) { foreach (module_website::get_websites(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { module_website::delete_website($val['website_id']); } } } if (class_exists('module_job', false) && module_job::is_plugin_enabled()) { foreach (module_job::get_jobs(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { module_job::delete_job($val['job_id']); } } } if (class_exists('module_invoice', false) && module_invoice::is_plugin_enabled()) { foreach (module_invoice::get_invoices(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { module_invoice::delete_invoice($val['invoice_id']); } } } if (class_exists('module_quote', false) && module_quote::is_plugin_enabled()) { foreach (module_quote::get_quotes(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { module_quote::delete_quote($val['quote_id']); } } } //handle_hook("file_delete",$this,"customer",'customer_id',$customer_id); } else { // instead of deleting these records we just update them to customer_id = 0 if (class_exists('module_website', false) && module_website::is_plugin_enabled()) { foreach (module_website::get_websites(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { update_insert('website_id', $val['website_id'], 'website', array('customer_id' => 0)); } } } if (class_exists('module_job', false) && module_job::is_plugin_enabled()) { foreach (module_job::get_jobs(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { update_insert('job_id', $val['job_id'], 'job', array('customer_id' => 0)); } } } if (class_exists('module_invoice', false) && module_invoice::is_plugin_enabled()) { foreach (module_invoice::get_invoices(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { update_insert('invoice_id', $val['invoice_id'], 'invoice', array('customer_id' => 0)); } } } if (class_exists('module_quote', false) && module_quote::is_plugin_enabled()) { foreach (module_quote::get_quotes(array('customer_id' => $customer_id)) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { update_insert('quote_id', $val['quote_id'], 'quote', array('customer_id' => 0)); } } } if (class_exists('module_file', false) && module_file::is_plugin_enabled()) { foreach (module_file::get_files(array('owner_id' => $customer_id, 'owner_table' => 'customer')) as $val) { if ($val['customer_id'] && $val['customer_id'] == $customer_id) { update_insert('file_id', $val['file_id'], 'file', array('owner_id' => 0, 'owner_table' => '')); } } } } // finally delete the main customer record // (this is so the above code works with its sql joins) $sql = "DELETE FROM " . _DB_PREFIX . "customer WHERE customer_id = '" . $customer_id . "' LIMIT 1"; query($sql); } } }
?> <td><?php foreach ($module->get_data_link_keys() as $key) { if (isset($data[$key]) && (int) $data[$key] > 0) { switch ($key) { case 'customer_id': echo module_customer::link_open($data[$key], true); break; case 'job_id': echo module_job::link_open($data[$key], true); break; case 'invoice_id': echo module_invoice::link_open($data[$key], true); break; case 'quote_id': echo module_quote::link_open($data[$key], true); break; case 'file_id': echo module_file::link_open($data[$key], true); break; } } } ?> </td> <?php } /*<td><a href="<?php echo $module->link('',array("data_record_id"=>$data['data_record_id'],"customer_id"=>isset($_REQUEST['customer_id']) ? $_REQUEST['customer_id'] : false)); ?>"><?php echo $module->format_record_id($data['data_type_id'],$data['data_record_id']); ?></a></td> <td><?php echo $data['status']; ?></td> <td><?php echo print_date($data['date_updated']); ?></td> <?php */
if (module_quote::can_i('edit', 'Quote Tasks') && $quote['date_approved'] == '0000-00-00') { $task_editable = true; } else { $task_editable = false; } echo module_quote::generate_task_preview($quote_id, $quote, $quote_task_id, $task_data, $task_editable); } ?> </tbody> </table> <?php if ((int) $quote_id > 0) { ?> <div id="quote_summary"> <?php echo module_quote::generate_quote_summary($quote_id, $quote); ?> </div> <?php } ?> </div> <?php $fieldset_data = array('heading' => $header, 'elements_before' => ob_get_clean()); echo module_form::generate_fieldset($fieldset_data); } // end can i view quote tasks hook_handle_callback('layout_column_half', 'end'); ?>
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; }
} }); } } 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>
} $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']; } if (!isset($file['staff_ids']) || !is_array($file['staff_ids']) || !count($file['staff_ids'])) { $file['staff_ids'] = array(false); if (count($staff_member_rel) == 1) { $file['staff_ids'] = array(key($staff_member_rel)); } } $fieldset_data['elements'][] = array('title' => 'Staff', 'fields' => array(array('type' => 'hidden', 'name' => 'staff_ids_save', 'value' => 1), '<div id="staff_ids_holder" style="float:left;">', array('type' => 'select', 'name' => 'staff_ids[]', 'options' => $staff_member_rel, 'multiple' => 'staff_ids_holder', 'values' => $file['staff_ids']), '</div>', function () use(&$file) { if ($file['quote_id']) { echo ' '; echo '<a href="' . module_quote::link_open($file['quote_id'], false) . '">' . _l('Open Quote »') . '</a>'; } }, array('type' => 'html', 'value' => '', 'help' => 'Assign a staff member to this file. Staff members are users who have EDIT permissions on Job Tasks. Click the plus sign to add more staff members. You can apply the "Only Assigned Staff" permission in User Role settings to restrict staff members to these files.<br><br>If there are assigned staff members then those members will be the only ones to receive notifications when a change is made to the file. If no staff are assigned to this file then anyone with the "Receive Alerts" permission will receive file change/comment alerts.'))); echo module_form::generate_fieldset($fieldset_data); unset($fieldset_data); hook_handle_callback('layout_column_half', 2); $fieldset_data = array('heading' => array('title' => _l('Bucket Description'), 'type' => 'h3'), 'elements' => array(array('field' => array('type' => 'wysiwyg', 'name' => 'description', 'value' => $file['description'])))); echo module_form::generate_fieldset($fieldset_data); unset($fieldset_data); if ((int) $file_id > 0) { if (module_file::can_i('edit', 'Files')) { module_email::display_emails(array('title' => 'File Emails', 'search' => array('file_id' => $file_id))); } ob_start(); $search = array(); $search['bucket_parent_file_id'] = $file_id;
public function external_hook($hook) { switch ($hook) { case 'view': $file_id = isset($_REQUEST['i']) ? (int) $_REQUEST['i'] : false; $hash = isset($_REQUEST['hash']) ? trim($_REQUEST['hash']) : false; if ($file_id && $hash) { $correct_hash = $this->link_public($file_id, true); if ($correct_hash == $hash) { // all good to print a receipt for this payment. $file_data = $this->get_file($file_id, false); if ($file_data && $file_data['file_id'] == $file_id) { if (isset($_POST['save_file_comments'])) { if (isset($_POST['file_approve']) && isset($_POST['file_approve_go']) && isset($_POST['file_approve_name']) && strlen($_POST['file_approve_name']) > 0) { update_insert('file_id', $file_id, 'file', array('approved_time' => time(), 'approved_by' => $_POST['file_approve_name'])); // send email, same 'updated' email as before. $this->send_file_changed_notice($file_id, false, true); //redirect_browser($this->link_public($file_id)); $_REQUEST['new_comment_text'] = _l('File was approved at %s by %s', print_date(time(), true), htmlspecialchars($_POST['file_approve_name'])); } if (isset($_POST['pointers'])) { update_insert('file_id', $file_id, 'file', array('pointers' => $_POST['pointers'])); } $this->save_file_comments($file_id); redirect_browser($this->link_public($file_id)); } module_template::init_template('file_approval_view', '<h2>File Details</h2> File Name: <strong>{FILE_NAME}</strong> <br/> Download: <strong><a href="{FILE_DOWNLOAD_URL}">Click Here</a></strong> <br/> Status: <strong>{STATUS}</strong> <br/> Customer: <strong>{CUSTOMER_NAME}</strong> <br/> {if:JOB_NAME}Job: <strong>{JOB_NAME}</strong> <br/>{endif:JOB_NAME} {if:FILE_APPROVAL_PENDING} <h2>File Approval Pending</h2> <p>If you would like to approve this file please complete the form below:</p> <p>Your Name: <input type="text" name="file_approve_name"> </p> <p><input type="checkbox" name="file_approve_go" value="yes"> Yes, I approve this file. </p> <p><input type="submit" name="file_approve" value="Approve File" class="submit_button save_button"></p> {endif:FILE_APPROVAL_PENDING} {if:FILE_APPROVED} <h2>File Has Been Approved</h2> <p>Thank you, the file was approved by <strong>{APPROVED_BY}</strong> on <strong>{APPROVED_TIME}</strong>.</p> {endif:FILE_APPROVED} <h2>File Comments</h2> <p>Please feel free to add comments to this file using the form below.</p> {FILE_COMMENTS} {if:FILE_PREVIEW} <h2>File Preview</h2> <div style="overflow:scroll;">{FILE_PREVIEW}</div> {endif:FILE_PREVIEW} ', 'Used when displaying the file to a customer for approval.', 'code'); $template = module_template::get_template_by_key('file_approval_view'); // generate the html for the task output $job_data = $file_data['job_id'] ? module_job::get_replace_fields($file_data['job_id']) : array(); if (class_exists('module_quote', false)) { $quote_data = $file_data['quote_id'] ? module_quote::get_replace_fields($file_data['quote_id']) : array(); } $customer_data = $file_data['customer_id'] ? module_customer::get_replace_fields($file_data['customer_id']) : array(); $file_data['file_preview'] = module_file::generate_preview($file_id, $file_data['file_name'], $file_data); $file_data['FILE_DOWNLOAD_URL'] = module_file::link_public_view($file_id); if (isset($file_data['approved_time'])) { switch ($file_data['approved_time']) { case -1: $file_data['FILE_APPROVAL_PENDING'] = 1; break; case 0: break; default: $file_data['FILE_APPROVED'] = 1; $file_data['APPROVED_TIME'] = print_date($file_data['approved_time'], true); } } if (class_exists('module_extra', false) && module_extra::is_plugin_enabled()) { $all_extra_fields = module_extra::get_defaults('file'); foreach ($all_extra_fields as $e) { $file_data[$e['key']] = _l('N/A'); } // and find the ones with values: $extras = module_extra::get_extras(array('owner_table' => 'file', 'owner_id' => $file_id)); foreach ($extras as $e) { $file_data[$e['extra_key']] = $e['extra']; } } ob_start(); ?> <div id="file_notes"> <div style="border-top:1px dashed #CCCCCC; padding:3px; margin:3px 0;"> <textarea name="new_comment_text" style="width:100%;" class="no_permissions"></textarea> <div style="text-align: right;"> <input type="submit" name="butt_save_note" id="butt_save_note" value="<?php echo _l('Add Comment'); ?> " class="submit_button no_permissions"> </div> </div> <?php foreach (module_file::get_file_comments($file_id) as $item) { $note_text = forum_text($item['comment']); if (preg_match_all('/#(\\d+)/', $note_text, $matches)) { // foreach ($matches[1] as $digit) { $note_text = preg_replace('/#' . $digit . '([^\\d]*)/', '<span node_id=' . $digit . ' class="pointer-ids pointer-id-' . $digit . '">#' . $digit . '</span>$1', $note_text); } } ?> <div style="border-top:1px dashed #CCCCCC; padding:3px; margin:3px 0;"> <?php echo $note_text; ?> <div style="font-size:10px; text-align:right; color:#CCCCCC;">From <?php echo $item['create_user_id'] ? module_user::link_open($item['create_user_id'], true) : _l('Customer'); ?> on <?php echo print_date($item['date_created'], true); ?> </div> </div> <?php } ?> </div> <?php $file_data['file_comments'] = ob_get_clean(); $template->assign_values($file_data); $template->assign_values($customer_data); $template->assign_values($job_data); if (class_exists('module_quote', false)) { $quote_data['quote_approved_by'] = $quote_data['approved_by']; $quote_data['quote_date_approved'] = $quote_data['date_approved']; unset($quote_data['approved_by']); unset($quote_data['date_approved']); $template->assign_values($quote_data); } $template->page_title = $file_data['file_name']; $template->content = '<form action="" method="post"><input type="hidden" name="save_file_comments" value="1">' . $template->content . '</form>'; echo $template->render('pretty_html'); } } } break; case 'download_bucket': @ob_end_clean(); $file_id = isset($_REQUEST['i']) ? (int) $_REQUEST['i'] : false; $hash = isset($_REQUEST['hash']) ? trim($_REQUEST['hash']) : false; if ($file_id && $hash) { $correct_hash = $this->link_public_download_bucket($file_id, true); if ($correct_hash == $hash) { // all good to print a receipt for this payment. $file_data = $this->get_file($file_id, false); @ignore_user_abort(true); $search = array(); $search['bucket_parent_file_id'] = $file_id; $files = module_file::get_files($search); //Create ZIP $zip = new ZipArchive(); $zipName = "bucket-" . $file_id . "-" . md5($file_id . _UCM_SECRET) . ".zip"; if ($zip->open(_FILE_UPLOAD_PATH . $zipName, ZIPARCHIVE::CREATE) !== TRUE) { echo 'Failed to create bucket zip file'; exit; } foreach ($files as $file) { if (is_file($file['file_path'])) { $zip->addFromString($file['file_name'], file_get_contents($file['file_path'])); } } $zip->close(); //Set headers header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: public"); header("Content-Description: File Transfer"); header("Content-type: application/octet-stream"); //header("Content-Disposition: attachment; filename='" . $zipName . "'"); header("Content-Disposition: attachment; filename=\"" . preg_replace("#[^a-zA-Z0-9]+#", "-", $file_data['file_name']) . ".zip\";"); header("Content-Transfer-Encoding: binary"); header("Content-Length: " . filesize(_FILE_UPLOAD_PATH . $zipName)); @clearstatcache(); //Make sure the file size isn't cached $size = @readfile(_FILE_UPLOAD_PATH . $zipName); if (!$size) { echo file_get_contents(_FILE_UPLOAD_PATH . $zipName); } @unlink(_FILE_UPLOAD_PATH . $zipName); } } exit; break; case 'download': @ob_end_clean(); $file_id = isset($_REQUEST['i']) ? (int) $_REQUEST['i'] : false; $hash = isset($_REQUEST['hash']) ? trim($_REQUEST['hash']) : false; if ($file_id && $hash) { $correct_hash = $this->link_public_view($file_id, true); if ($correct_hash == $hash) { // all good to print a receipt for this payment. $file_data = $this->get_file($file_id, false); 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: " . dtbaker_mime_type($file_data['file_name'], $file_data['file_path'])); if (!isset($_REQUEST['embed'])) { 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; break; } }
</td> </tr> <?php } $replace['quote_summary'] = ob_get_clean(); /* START QUOTE LINE ITEMS */ $task_decimal_places = module_config::c('task_amount_decimal_places', -1); if ($task_decimal_places < 0) { $task_decimal_places = false; // use default currency dec places. } $task_decimal_places_trim = module_config::c('task_amount_decimal_places_trim', 0); $all_item_row_html = ''; $item_count = 0; // changed from 1 foreach (module_quote::get_quote_items($quote_id, $quote) as $quote_item_id => $quote_item_data) { $row_replace = array('item_odd_or_even' => $item_count++ % 2 ? 'odd' : 'even', 'item_number' => '', 'item_description' => '', 'item_tax' => 0, 'item_tax_rate' => ''); if (isset($quote_item_data['task_order']) && $quote_item_data['task_order'] > 0) { $row_replace['item_number'] = $quote_item_data['task_order']; } else { $row_replace['item_number'] = $item_count; } $row_replace['item_description'] .= htmlspecialchars($quote_item_data['description']); if (module_config::c('quote_show_long_desc', 1)) { $long_description = $quote_item_data['long_description']; if ($long_description != '') { $row_replace['item_description'] .= '<br/><em>' . forum_text($long_description) . '</em>'; } } /*if(isset($quote_item_data['date_done']) && $quote_item_data['date_done'] != '0000-00-00'){ $row_replace['item_date'] .= print_date($quote_item_data['date_done']);
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; } }
private function _handle_send_email() { $options = @unserialize(base64_decode($_REQUEST['options'])); if (!$options) { $options = array(); } $options = $this->get_email_compose_options($options); if (isset($_REQUEST['custom_to'])) { $custom_to = is_array($_REQUEST['custom_to']) ? $_REQUEST['custom_to'] : array($_REQUEST['custom_to']); $to = array(); foreach ($custom_to as $ct) { $ct = explode('||', $ct); $ct['email'] = $ct[0]; $ct['name'] = isset($ct[1]) ? $ct[1] : ''; $ct['user_id'] = isset($ct[2]) ? (int) $ct[2] : 0; $to[] = $ct; } } else { $to = isset($options['to']) && is_array($options['to']) ? $options['to'] : array(); } $email = $this->new_email(); $email->subject = $options['subject']; foreach ($to as $t) { if (isset($t['user_id']) && $t['user_id'] > 0) { $email->set_to('user', $t['user_id'], $t['email'], $t['name'] . (isset($t['last_name']) && module_config::c('email_to_full_name', 1) ? ' ' . $t['last_name'] : '')); } else { $email->set_to_manual($t['email'], $t['name'] . (isset($t['last_name']) && module_config::c('email_to_full_name', 1) ? ' ' . $t['last_name'] : '')); } } // set from is the default from address. if (isset($options['from_email'])) { $email->set_from_manual($options['from_email'], isset($options['from_name']) ? $options['from_name'] : ''); $email->set_bounce_address($options['from_email']); } if ($options['cc'] && is_array($options['cc'])) { foreach ($options['cc'] as $cc_details) { $bits = explode('||', $cc_details); if (count($bits) >= 2 && $bits[0]) { $email->set_cc_manual($bits[0], $bits[1]); } } } if ($options['bcc']) { $bcc = explode(',', $options['bcc']); foreach ($bcc as $b) { $b = trim($b); if (strlen($b)) { $email->set_bcc_manual($b, ''); } } } if (isset($options['company_id'])) { $email->company_id = $options['company_id']; } if (isset($options['customer_id'])) { // todo: verify this is a legit customer id we can send emails to. $email->customer_id = $options['customer_id']; if ($options['customer_id'] > 0) { foreach (module_customer::get_replace_fields($options['customer_id']) as $key => $val) { //echo "Replacing $key with $val <br>"; $email->replace($key, $val); } } } if (isset($options['newsletter_id'])) { $email->newsletter_id = $options['newsletter_id']; } if (isset($options['file_id'])) { $email->file_id = $options['file_id']; } if (isset($options['send_id'])) { $email->send_id = $options['send_id']; } if (isset($options['invoice_id'])) { $email->invoice_id = $options['invoice_id']; if ($options['invoice_id'] > 0) { foreach (module_invoice::get_replace_fields($options['invoice_id']) as $key => $val) { $email->replace($key, $val); } } } if (isset($options['job_id'])) { $email->job_id = $options['job_id']; if ($options['job_id'] > 0) { foreach (module_job::get_replace_fields($options['job_id']) as $key => $val) { $email->replace($key, $val); } } } if (isset($options['website_id'])) { $email->website_id = $options['website_id']; if ($options['website_id'] > 0) { foreach (module_website::get_replace_fields($options['website_id']) as $key => $val) { $email->replace($key, $val); } } } if (isset($options['quote_id'])) { $email->quote_id = $options['quote_id']; if ($options['quote_id'] > 0) { foreach (module_quote::get_replace_fields($options['quote_id']) as $key => $val) { $email->replace($key, $val); } } } // custom data integration if (class_exists('module_data', false) && module_config::c('custom_data_in_email', 1) && $options['customer_id'] > 0 && !empty($_REQUEST['custom_data_info']) && !empty($_REQUEST['custom_data_related'])) { global $plugins; // find all possible custom data entries $data_types = $plugins['data']->get_data_types(); foreach ($data_types as $data_type) { switch ($data_type['data_type_menu']) { case _CUSTOM_DATA_MENU_LOCATION_CUSTOMER: if ($plugins['data']->can_i('view', $data_type['data_type_name'])) { $search = array('customer_id' => $options['customer_id'], 'data_type_id' => $data_type['data_type_id']); // we have to limit the data types to only those created by current user if they are not administration $datas = $plugins['data']->get_datas($search); if ($datas) { // found some! does this exist in one of our inputs? if (!empty($_REQUEST['custom_data_info'][$data_type['data_type_id']]) && !empty($_REQUEST['custom_data_related'][$data_type['data_type_id']])) { $data_record_id = $_REQUEST['custom_data_related'][$data_type['data_type_id']]; $data_info = json_decode($_REQUEST['custom_data_info'][$data_type['data_type_id']], true); if (is_array($data_info) && isset($datas[$data_record_id])) { // we have a winner! $list_fields = array(); $data_field_groups = $plugins['data']->get_data_field_groups($data_type['data_type_id']); foreach ($data_field_groups as $data_field_group) { $data_fields = $plugins['data']->get_data_fields($data_field_group['data_field_group_id']); foreach ($data_fields as $data_field) { if ($data_field['show_list']) { $list_fields[$data_field['data_field_id']] = $data_field; } } } $list_data_items = $plugins['data']->get_data_items($data_record_id); foreach ($list_fields as $list_field) { $settings = @unserialize($list_data_items[$list_field['data_field_id']]['data_field_settings']); if (!isset($settings['field_type'])) { $settings['field_type'] = isset($list_field['field_type']) ? $list_field['field_type'] : false; } $value = false; if (isset($list_data_items[$list_field['data_field_id']])) { $value = $list_data_items[$list_field['data_field_id']]['data_text']; } if ($value) { $data_info['key'] = $value; break; } } $data_info['data_record_id'] = $data_record_id[$data_type['data_type_id']]; $email->custom_data[$data_type['data_type_id']] = $data_info; $email->set_custom_data($data_type['data_type_id'], $data_record_id); } } } } } } } // final override for first_name last_name if selected from the custom to drop down foreach ($to as $t) { if (isset($t['user_id']) && $t['user_id'] > 0) { $user = module_user::get_user($t['user_id']); if ($user) { if (strpos($options['content'], '{AUTO_LOGIN_LINK}') !== false && $t['user_id'] != 1) { $email->replace('AUTO_LOGIN_LINK', module_security::generate_auto_login_link($t['user_id'])); } $email->replace('first_name', $user['name']); $email->replace('last_name', $user['last_name']); } } } if (isset($options['note_id'])) { $email->note_id = $options['note_id']; } if (isset($options['debug_message'])) { $email->debug_message = $options['debug_message']; } $email->set_html($options['content']); foreach ($options['attachments'] as $attachment) { $email->AddAttachment($attachment['path'], $attachment['name']); } // new addition, manually added attachments. if (isset($_FILES['manual_attachment']) && isset($_FILES['manual_attachment']['tmp_name'])) { foreach ($_FILES['manual_attachment']['tmp_name'] as $key => $tmp_name) { if (is_uploaded_file($tmp_name) && isset($_FILES['manual_attachment']['name'][$key]) && strlen($_FILES['manual_attachment']['name'][$key])) { $email->AddAttachment($tmp_name, $_FILES['manual_attachment']['name'][$key]); } } } if ($email->send()) { if (isset($options['success_callback_args']) && count($options['success_callback_args']) && $options['success_callback'] && is_callable($options['success_callback'])) { // new callback method using call_user_func_array $args = $options['success_callback_args']; $args['email_id'] = $email->email_id; if (preg_match('#module_\\w#', $options['success_callback'])) { call_user_func($options['success_callback'], $args); } } /*else if($options['success_callback']){ eval($options['success_callback']); }*/ set_message('Email sent successfully'); redirect_browser($options['complete_url']); } else { set_error('Sending email failed: ' . $email->error_text); redirect_browser($options['cancel_url']); } }