$rows */ public function __construct( private array $rows ) {} /** * The report's display rows. * * @return list */ public function rows(): array { return $this->rows; } public function count(): int { return count( $this->rows ); } public function totalAmount(): float { return round( array_sum( array_column( $this->rows, 'amount' ) ), 2 ); } /** * Total HST collected across all rows — the figure the studio remits. */ public function totalTax(): float { return round( array_sum( array_column( $this->rows, 'tax_amount' ) ), 2 ); } public function grandTotal(): float { return round( array_sum( array_column( $this->rows, 'total' ) ), 2 ); } /** * Render the report as CSV, including a trailing totals row. */ public function toCsv(): string { $lines = []; $lines[] = $this->csvLine( [ 'Date', 'Student', 'Instructor', 'Method', 'Status', 'Subtotal', 'HST Rate', 'HST', 'Total' ] ); foreach ( $this->rows as $row ) { $lines[] = $this->csvLine( [ $row['date'], $row['student'], $row['instructor'], $row['method'], $row['status'], number_format( $row['amount'], 2, '.', '' ), number_format( $row['tax_rate'], 2, '.', '' ), number_format( $row['tax_amount'], 2, '.', '' ), number_format( $row['total'], 2, '.', '' ), ] ); } $lines[] = $this->csvLine( [ 'Totals', '', '', '', '', number_format( $this->totalAmount(), 2, '.', '' ), '', number_format( $this->totalTax(), 2, '.', '' ), number_format( $this->grandTotal(), 2, '.', '' ), ] ); return implode( "\n", $lines ) . "\n"; } /** * Format one CSV record, quoting fields and escaping embedded quotes. * * @param list $fields */ private function csvLine( array $fields ): string { $escaped = array_map( static fn( string $field ): string => '"' . str_replace( '"', '""', $field ) . '"', $fields ); return implode( ',', $escaped ); } }