with('versions.prices')->orderBy('tier')->get(); if ($families->isEmpty()) { $this->error('The catalogue is empty. Nothing can be sold.'); return self::FAILURE; } foreach ($families as $family) { $live = $family->versions->filter(fn (PlanVersion $v) => $v->isAvailableAt($now)); if ($live->count() > 1) { $problems[] = "{$family->key}: {$live->count()} versions on sale at once (". $live->pluck('version')->implode(', ').') — overlapping windows.'; } if ($family->sales_enabled && $live->isEmpty()) { $problems[] = "{$family->key}: on sale, but no version is available right now."; } foreach ($family->versions as $version) { if (! $version->isPublished()) { continue; } foreach ([Subscription::TERM_MONTHLY, Subscription::TERM_YEARLY] as $term) { $priced = $version->prices ->first(fn ($p) => $p->term === $term && $p->currency === $currency); if ($priced === null) { $problems[] = "{$family->key} v{$version->version}: no {$term} price in {$currency}."; } } } } // A contract that cannot say which version it was sold under has lost // its provenance, which is the one thing the version table is for. $orphaned = Subscription::query()->whereNull('plan_version_id')->count(); if ($orphaned > 0) { $problems[] = "{$orphaned} subscription(s) have no plan version recorded."; } foreach ($families as $family) { $state = $family->sales_enabled ? 'on sale' : 'withdrawn'; $version = $family->versions->first(fn (PlanVersion $v) => $v->isAvailableAt($now)); $this->line(sprintf( ' %-12s tier %d %-10s %s', $family->key, $family->tier, $state, $version !== null ? "v{$version->version}" : '—', )); } if ($problems === []) { $this->newLine(); $this->info('Catalogue is consistent.'); return self::SUCCESS; } $this->newLine(); foreach ($problems as $problem) { $this->error(' '.$problem); } return self::FAILURE; } }