$resource['capacity'])CatalogValidator::invalid('participant_count',__('The number of participants exceeds the resource capacity.','abece-booking')); // Historical participant identities may remain on an admin-edited booking; no membership benefit is calculated. foreach($member_ids as $id)CatalogValidator::integer($id,'member_ids',1,PHP_INT_MAX); $start=Time::fromSql($starts_at);$end=Time::fromSql($ends_at); if($end<=$start||$start%60||$end%60||$end-$start>65535*60)CatalogValidator::invalid('ends_at',__('Select a positive booking interval aligned to whole minutes.','abece-booking')); $rows=array_values(array_filter($this->rules->configuration(),fn($r)=>BasicPriceConfiguration::basic($r)&&$r['is_active']&&$r['list_active']&&(!$r['resource_id']||$r['resource_id']===$resource['id'])&&(!$r['sport_id']||$r['sport_id']===$resource['sport_id']))); $scope=static fn($r)=>$r['resource_id']?2:($r['sport_id']?1:0);usort($rows,fn($a,$b)=>$scope($b)<=>$scope($a)); if(!$rows||$rows[0]['amount']===null)throw new CatalogException('pricing_not_configured',__('No price is configured for this time. Please contact the venue.','abece-booking'),400); if(isset($rows[1])&&$scope($rows[0])===$scope($rows[1]))BasicPriceConfiguration::conflict();$rate=$rows[0];$segments=[];$base=0; // Preserve existing midnight splitting and integer rounding in public all-day snapshots. for($t=$start;$t<$end;){$local=Time::timestamp($t);$next=min($end,$local->modify('tomorrow')->setTime(0,0)->getTimestamp());$amount=Money::roundRatio($rate['amount'],$next-$t,3600);$base=Money::add($base,$amount);$segments[]=['start'=>Time::iso($t),'end'=>Time::iso($next),'minutes'=>intdiv($next-$t,60),'hourly_rate'=>$rate['amount'],'base_amount'=>$amount,'discount_amount'=>0,'final_amount'=>$amount,'applied_rule_id'=>$rate['id'],'price_list_id'=>$rate['price_list_id'],'eligible_member_count'=>0,'member_benefits'=>[]];$t=$next;} return ['currency'=>$rate['list_currency'],'base_amount'=>$base,'discount_amount'=>0,'final_amount'=>$base,'customer_type'=>$customer_type,'participant_count'=>$participant_count,'eligible_member_count'=>0,'non_member_count'=>$participant_count,'segments'=>$segments,'member_benefits'=>[],'applied_price_lists'=>[$rate['price_list_id']],'applied_rules'=>[$rate['id']],'priced_at'=>current_time('mysql',true)]; } public function fields(array $price):array{return ['base_amount'=>$price['base_amount'],'discount_amount'=>$price['discount_amount'],'final_amount'=>$price['final_amount'],'currency'=>$price['currency'],'payment_status'=>$price['final_amount']?'unpaid':'not_required','pricing_snapshot'=>wp_json_encode($price)];} }