settings = $settings; $this->resolver = $resolver; } /** * Headline totals. * * @return array{value:float,units:int} */ public function totals(): array { $value = 0.0; $units = 0; foreach ( $this->lines() as $line ) { $value += $line['value']; ++$units; } return array( 'value' => $value, 'units' => $units, ); } /** * Per-line rows for the inventory-value report. * * @return array> */ public function rows(): array { $rows = array_map( fn( array $l ): array => array( 'product' => $this->resolver->unit_label( (int) $l['product_id'] ), 'sku' => $this->resolver->sku( (int) $l['product_id'] ), 'stock' => $l['stock'], 'unit_cost' => $l['unit_cost'], 'value' => $l['value'], ), $this->lines() ); usort( $rows, static fn( $a, $b ): int => $b['value'] <=> $a['value'] ); return $rows; } /** * Build the valued lines from live stock. * * Each returned line: { product_id, stock, unit_cost, value } — where * product_id is the entity being valued (a simple/variation unit, or a * parent variable product for parent-managed stock). * * @return array> */ private function lines(): array { // Cost per enabled unit (variation or simple), keyed by unit id. $costs = array(); foreach ( $this->settings->enabled( 100000, 0 ) as $row ) { $costs[ (int) $row['product_id'] ] = Money::to_float( $row['current_cost'] ); } $lines = array(); $parent_variation = array(); // parent_id => [ unit_id => cost ] for variable products. foreach ( $costs as $unit_id => $cost ) { $product = $this->resolver->product( $unit_id ); if ( ! $product ) { continue; } if ( $product->is_type( 'variation' ) ) { $parent_variation[ (int) $product->get_parent_id() ][ $unit_id ] = $cost; continue; } // Simple product: value its own live stock. $stock = $this->resolver->stock_quantity( $unit_id ); if ( null !== $stock && $cost > 0 ) { $lines[] = array( 'product_id' => $unit_id, 'stock' => $stock, 'unit_cost' => $cost, 'value' => $stock * $cost, ); } } // Variable products: decide parent-managed vs per-variation. foreach ( $parent_variation as $parent_id => $variation_costs ) { if ( $this->resolver->manages_stock_at_parent( $parent_id ) ) { // One pooled quantity; value it once at the average variation cost. $stock = $this->resolver->total_stock( $parent_id ); if ( null === $stock ) { continue; } $avg_cost = count( $variation_costs ) > 0 ? array_sum( $variation_costs ) / count( $variation_costs ) : 0.0; if ( $avg_cost > 0 ) { $lines[] = array( 'product_id' => $parent_id, 'stock' => $stock, 'unit_cost' => $avg_cost, 'value' => $stock * $avg_cost, ); } continue; } // Per-variation stock: value each variation's own live stock. foreach ( $variation_costs as $unit_id => $cost ) { $stock = $this->resolver->stock_quantity( $unit_id ); if ( null !== $stock && $cost > 0 ) { $lines[] = array( 'product_id' => $unit_id, 'stock' => $stock, 'unit_cost' => $cost, 'value' => $stock * $cost, ); } } } return $lines; } }