/** * Calculate costs from posted values * @param array $posted * @return string cost */ public function calculate_booking_cost($posted) { if (!empty($this->booking_cost)) { return $this->booking_cost; } // Get costs $costs = $this->product->get_costs(); // Get posted data $data = $this->get_posted_data($posted); $validate = $this->is_bookable($data); if (is_wp_error($validate)) { return $validate; } $base_cost = max(0, $this->product->wc_booking_cost); $base_block_cost = max(0, $this->product->wc_booking_base_cost); $total_block_cost = 0; $person_block_costs = 0; // See if we have an auto_assigned_resource_id if (isset($this->auto_assigned_resource_id)) { $data['_resource_id'] = $this->auto_assigned_resource_id; } // Get resource cost if (isset($data['_resource_id'])) { $resource = $this->product->get_resource($data['_resource_id']); $base_block_cost += $resource->get_block_cost(); $base_cost += $resource->get_base_cost(); } // Potentially increase costs if dealing with persons if (!empty($data['_persons'])) { if ($this->product->has_person_types()) { foreach ($data['_persons'] as $person_id => $person_count) { $person_cost = get_post_meta($person_id, 'cost', true); $person_block_cost = get_post_meta($person_id, 'block_cost', true); // Only a single cost - multiplication comes later if wc_booking_person_cost_multiplier is enabled if ($person_count > 0) { if ($person_cost > 0) { $base_cost += $person_cost * $person_count; } if ($person_block_cost > 0) { $person_block_costs += $person_block_cost * $person_count; } } } } } $this->applied_cost_rules = array(); $block_duration = $this->product->get_duration(); $block_unit = $this->product->get_duration_unit(); $blocks_booked = isset($data['_duration']) ? absint($data['_duration']) : $block_duration; $block_timestamp = $data['_start_date']; if ($this->product->is_duration_type('fixed')) { $blocks_booked = ceil($blocks_booked / $block_duration); } // Evaluate costs for each booked block for ($block = 0; $block < $blocks_booked; $block++) { $block_cost = $base_block_cost + $person_block_costs; $block_start_time_offset = $block * $block_duration; $block_end_time_offset = ($block + 1) * $block_duration; $block_start_time = $this->get_formatted_times(strtotime("+{$block_start_time_offset} {$block_unit}", $block_timestamp)); $block_end_time = $this->get_formatted_times(strtotime("+{$block_end_time_offset} {$block_unit}", $block_timestamp)); foreach ($costs as $rule_key => $rule) { $type = $rule[0]; $rules = $rule[1]; if (strrpos($type, 'time') === 0) { if (!in_array($this->product->get_duration_unit(), array('minute', 'hour'))) { continue; } if (!empty($rules['day'])) { if ($rules['day'] != $block_start_time['day_of_week']) { continue; } } $rule_start_time_hi = date("YmdHi", strtotime(str_replace(':', '', $rules['from']), $block_start_time['timestamp'])); $rule_end_time_hi = date("YmdHi", strtotime(str_replace(':', '', $rules['to']), $block_start_time['timestamp'])); $matched = false; // Reverse time rule - The end time is tomorrow e.g. 16:00 today - 12:00 tomorrow if ($rule_end_time_hi <= $rule_start_time_hi) { if ($block_end_time['time'] > $rule_start_time_hi) { $matched = true; } if ($block_start_time['time'] >= $rule_start_time_hi && $block_end_time['time'] >= $rule_end_time_hi) { $matched = true; } if ($block_start_time['time'] <= $rule_start_time_hi && $block_end_time['time'] <= $rule_end_time_hi) { $matched = true; } // Normal rule } else { if ($block_start_time['time'] >= $rule_start_time_hi && $block_end_time['time'] <= $rule_end_time_hi) { $matched = true; } } if ($matched) { $block_cost = $this->apply_cost($block_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]); $base_cost = $this->apply_base_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1], $rule_key); } } else { switch ($type) { case 'months': case 'weeks': case 'days': $check_date = $block_start_time['timestamp']; while ($check_date < $block_end_time['timestamp']) { $checking_date = $this->get_formatted_times($check_date); $date_key = $type == 'days' ? 'day_of_week' : substr($type, 0, -1); if (isset($rules[$checking_date[$date_key]])) { $rule = $rules[$checking_date[$date_key]]; $block_cost = $this->apply_cost($block_cost, $rule['block'][0], $rule['block'][1]); $base_cost = $this->apply_base_cost($base_cost, $rule['base'][0], $rule['base'][1], $rule_key); } $check_date = strtotime("+1 {$type}", $check_date); } break; case 'custom': $check_date = $block_start_time['timestamp']; while ($check_date < $block_end_time['timestamp']) { $checking_date = $this->get_formatted_times($check_date); if (isset($rules[$checking_date['year']][$checking_date['month']][$checking_date['day']])) { $rule = $rules[$checking_date['year']][$checking_date['month']][$checking_date['day']]; $block_cost = $this->apply_cost($block_cost, $rule['block'][0], $rule['block'][1]); $base_cost = $this->apply_base_cost($base_cost, $rule['base'][0], $rule['base'][1], $rule_key); } $check_date = strtotime("+1 day", $check_date); } break; case 'persons': if (!empty($data['_persons'])) { if ($rules['from'] <= array_sum($data['_persons']) && $rules['to'] >= array_sum($data['_persons'])) { $block_cost = $this->apply_cost($block_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]); $base_cost = $this->apply_base_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1], $rule_key); } } break; case 'blocks': if (!empty($data['_duration'])) { if ($rules['from'] <= $data['_duration'] && $rules['to'] >= $data['_duration']) { $block_cost = $this->apply_cost($block_cost, $rules['rule']['block'][0], $rules['rule']['block'][1]); $base_cost = $this->apply_base_cost($base_cost, $rules['rule']['base'][0], $rules['rule']['base'][1], $rule_key); } } break; } } } $total_block_cost += $block_cost; } // Person multiplier mutliplies all costs $this->booking_cost = max(0, $total_block_cost + $base_cost); if (!empty($data['_persons'])) { if ('yes' === $this->product->wc_booking_person_cost_multiplier) { $this->booking_cost = $this->booking_cost * array_sum($data['_persons']); } } return apply_filters('booking_form_calculated_booking_cost', $this->booking_cost, $this, $posted); }
/** * Get posted form data into a neat array * @param array $posted * @return array */ public function get_posted_data($posted = array()) { if (empty($posted)) { $posted = $_POST; } $data = array('_year' => '', '_month' => '', '_day' => '', '_persons' => array()); // Get date fields (y, m, d) if (!empty($posted['wc_bookings_field_start_date_year']) && !empty($posted['wc_bookings_field_start_date_month']) && !empty($posted['wc_bookings_field_start_date_day'])) { $data['_year'] = max(date('Y'), absint($posted['wc_bookings_field_start_date_year'])); $data['_month'] = absint($posted['wc_bookings_field_start_date_month']); $data['_day'] = absint($posted['wc_bookings_field_start_date_day']); $data['_date'] = $data['_year'] . '-' . $data['_month'] . '-' . $data['_day']; $data['date'] = date_i18n(get_option('date_format'), strtotime($data['_date'])); } // Get year month field if (!empty($posted['wc_bookings_field_start_date_yearmonth'])) { $yearmonth = strtotime($posted['wc_bookings_field_start_date_yearmonth'] . '-01'); $data['_year'] = absint(date('Y', $yearmonth)); $data['_month'] = absint(date('m', $yearmonth)); $data['_day'] = 1; $data['_date'] = $data['_year'] . '-' . $data['_month'] . '-' . $data['_day']; $data['date'] = date_i18n('F Y', $yearmonth); } // Get time field if (!empty($posted['wc_bookings_field_start_date_time'])) { $data['_time'] = wc_clean($posted['wc_bookings_field_start_date_time']); $data['time'] = date_i18n(get_option('time_format'), strtotime("{$data['_year']}-{$data['_month']}-{$data['_day']} {$data['_time']}")); } else { $data['_time'] = ''; } // Quantity being booked $data['_qty'] = 1; // Work out persons if ($this->product->has_persons()) { if ($this->product->has_person_types()) { $person_types = $this->product->get_person_types(); foreach ($person_types as $person_type) { $data[$person_type->post_title] = absint($posted['wc_bookings_field_persons_' . $person_type->ID]); $data['_persons'][$person_type->ID] = $data[$person_type->post_title]; } } else { $data[__('Persons', 'woocommerce-bookings')] = absint($posted['wc_bookings_field_persons']); $data['_persons'][0] = absint($posted['wc_bookings_field_persons']); } if ('yes' == $this->product->wc_booking_person_qty_multiplier) { $data['_qty'] = array_sum($data['_persons']); } } // Duration if ('customer' == $this->product->wc_booking_duration_type) { $booking_duration = isset($posted['wc_bookings_field_duration']) ? max(0, absint($posted['wc_bookings_field_duration'])) : 0; $booking_duration_unit = $this->product->get_duration_unit(); $data['_duration_unit'] = $booking_duration_unit; $data['_duration'] = $booking_duration; // Get the duration * block duration $total_duration = $booking_duration * $this->product->wc_booking_duration; // Nice formatted version switch ($booking_duration_unit) { case 'month': $data['duration'] = $total_duration . ' ' . _n('month', 'months', $total_duration, 'woocommerce-bookings'); break; case 'day': if ($total_duration % 7) { $data['duration'] = $total_duration . ' ' . _n('day', 'days', $total_duration, 'woocommerce-bookings'); } else { $data['duration'] = $total_duration / 7 . ' ' . _n('week', 'weeks', $total_duration, 'woocommerce-bookings'); } break; case 'hour': $data['duration'] = $total_duration . ' ' . _n('hour', 'hours', $total_duration, 'woocommerce-bookings'); break; case 'minute': $data['duration'] = $total_duration . ' ' . _n('minute', 'minutes', $total_duration, 'woocommerce-bookings'); break; default: $data['duration'] = $total_duration; break; } } else { // Fixed duration $booking_duration = $this->product->get_duration(); $booking_duration_unit = $this->product->get_duration_unit(); $total_duration = $booking_duration; } // Work out start and end dates/times if (!empty($data['_time'])) { $data['_start_date'] = strtotime("{$data['_year']}-{$data['_month']}-{$data['_day']} {$data['_time']}"); $data['_end_date'] = strtotime("+{$total_duration} {$booking_duration_unit}", $data['_start_date']); $data['_all_day'] = 0; } else { $data['_start_date'] = strtotime("{$data['_year']}-{$data['_month']}-{$data['_day']}"); $data['_end_date'] = strtotime("+{$total_duration} {$booking_duration_unit}", $data['_start_date']); $data['_all_day'] = 1; } // Get posted resource or assign one for the date range if ($this->product->has_resources()) { if ($this->product->is_resource_assignment_type('customer')) { if (!empty($posted['wc_bookings_field_resource']) && ($resource = $this->product->get_resource(absint($posted['wc_bookings_field_resource'])))) { $data['_resource_id'] = $resource->ID; $data['type'] = $resource->post_title; } else { $data['_resource_id'] = 0; } } else { // Assign an available resource automatically $available_bookings = $this->product->get_available_bookings($data['_start_date'], $data['_end_date'], 0, $data['_qty']); if (is_array($available_bookings)) { $data['_resource_id'] = current(array_keys($available_bookings)); } } } return $data; }