payments->forBooking($b['id']) as $t){if('completed'!==$t['status'])continue; if($t['currency']!==$b['currency']||$t['amount']<0)throw new \ABeCe\Booking\Domain\CatalogException('abece_booking_invalid_payment_ledger',__('The payment ledger does not match this booking. Contact an administrator.','abece-booking'),409); if('payment'===$t['transaction_type'])$paid=Money::add($paid,$t['amount']);elseif('refund'===$t['transaction_type'])$refunded=Money::add($refunded,$t['amount']); } return $this->fromTotals($b,$paid,$refunded); } /** Same calculation for batched receivables, without per-booking ledger reads. */ public function fromTotals(array $b,int $paid,int $refunded,bool $invalid=false): array { if($invalid||$paid<0||$refunded<0||$paid>Money::MAX||$refunded>Money::MAX)throw new \ABeCe\Booking\Domain\CatalogException('abece_booking_invalid_payment_ledger',__('The payment ledger does not match this booking. Contact an administrator.','abece-booking'),409); $required=match($b['status']){'cancelled'=>$b['cancellation_fee'],'no_show'=>$b['no_show_fee'],'voided'=>0,default=>$b['final_amount']}; $net=$paid-$refunded;$outstanding=max($required-$net,0);$due=max($net-$required,0); $status=$due?'refund_pending':($outstanding?($net>0?'partially_paid':'unpaid'):($refunded?($refunded===$paid?'refunded':'partially_refunded'):($paid?'paid':'not_required'))); return ['required_amount'=>$required,'paid_amount'=>$paid,'refunded_amount'=>$refunded,'net_paid_amount'=>$net,'outstanding_amount'=>$outstanding,'refund_due'=>$due,'payment_status'=>$status]; } }