Deploy Ladill Woo Manager / deploy (push) Successful in 47s
Store site names now reference the DemoWorld business identity for the active plan, and order customers use DemoWorld people (name, email, phone) instead of generic "Demo Customer N" placeholders. Staff logins skip the login reseed. Co-authored-by: Cursor <cursoragent@cursor.com>
265 lines
9.2 KiB
PHP
265 lines
9.2 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\User;
|
|
use App\Models\Woo\ProSubscription;
|
|
use App\Models\WooCategory;
|
|
use App\Models\WooOrder;
|
|
use App\Models\WooProduct;
|
|
use App\Models\WooStore;
|
|
use App\Services\Woo\SubscriptionService;
|
|
use App\Support\DemoWorld;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* Seed local demo WooCommerce manager data (no live WordPress).
|
|
*/
|
|
class DemoSeedCommand extends Command
|
|
{
|
|
protected $signature = 'demo:seed
|
|
{identity : User email or public_id}
|
|
{--plan=free : free|pro|enterprise}
|
|
{--reset : Wipe this user\'s stores/subscription before seeding}';
|
|
|
|
protected $description = 'Seed idempotent demo stores, categories, products, and orders for a user';
|
|
|
|
public function handle(SubscriptionService $subscriptions): int
|
|
{
|
|
$plan = strtolower((string) $this->option('plan'));
|
|
if (! in_array($plan, ['free', 'pro', 'enterprise'], true)) {
|
|
$this->error('Invalid --plan. Use free, pro, or enterprise.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$user = $this->resolveUser((string) $this->argument('identity'));
|
|
if (! $user) {
|
|
$this->error('User not found for identity: '.$this->argument('identity'));
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
if ($this->option('reset')) {
|
|
$this->resetTenant($user);
|
|
$this->info('Reset demo data for '.$user->email);
|
|
}
|
|
|
|
$this->assignPlan($subscriptions, $user, $plan);
|
|
|
|
$existing = WooStore::query()
|
|
->where('user_id', $user->id)
|
|
->where('metadata->demo', true)
|
|
->count();
|
|
|
|
if ($existing > 0 && ! $this->option('reset')) {
|
|
$this->info('Demo stores already present; plan ensured (idempotent).');
|
|
$this->line(sprintf('user=%s plan=%s stores=%d', $user->email, $plan, $existing));
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$storeSpecs = $this->storeSpecs($plan);
|
|
foreach ($storeSpecs as $spec) {
|
|
$this->seedStore($user, $spec);
|
|
}
|
|
|
|
$this->info(sprintf(
|
|
'Seeded %d demo store(s) for %s (%s).',
|
|
count($storeSpecs),
|
|
$user->email,
|
|
$plan,
|
|
));
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function resolveUser(string $identity): ?User
|
|
{
|
|
return User::query()
|
|
->where(function ($query) use ($identity) {
|
|
$query->where('email', $identity)->orWhere('public_id', $identity);
|
|
})
|
|
->first();
|
|
}
|
|
|
|
private function assignPlan(SubscriptionService $subscriptions, User $user, string $plan): void
|
|
{
|
|
if ($plan === 'free') {
|
|
ProSubscription::query()->where('user_id', $user->id)->delete();
|
|
|
|
return;
|
|
}
|
|
|
|
$subscriptions->activatePrepaid(
|
|
$user,
|
|
$plan === 'enterprise' ? ProSubscription::PLAN_ENTERPRISE : ProSubscription::PLAN_PRO,
|
|
12,
|
|
);
|
|
}
|
|
|
|
private function resetTenant(User $user): void
|
|
{
|
|
DB::transaction(function () use ($user) {
|
|
WooStore::query()->where('user_id', $user->id)->each(function (WooStore $store) {
|
|
$store->delete();
|
|
});
|
|
ProSubscription::query()->where('user_id', $user->id)->delete();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @return list<array{site_name: string, site_url: string, products: int, orders: int, categories: list<string>}>
|
|
*/
|
|
private function storeSpecs(string $plan): array
|
|
{
|
|
$baseCategories = ['Apparel', 'Accessories', 'Home'];
|
|
$businessName = DemoWorld::business($plan)['name'];
|
|
|
|
return match ($plan) {
|
|
'enterprise' => [
|
|
[
|
|
'site_name' => $businessName.' Shop',
|
|
'site_url' => 'https://demo-accra.example.com',
|
|
'products' => 30,
|
|
'orders' => 12,
|
|
'categories' => $baseCategories,
|
|
],
|
|
[
|
|
'site_name' => $businessName.' — Kumasi Branch Shop',
|
|
'site_url' => 'https://demo-kumasi.example.com',
|
|
'products' => 24,
|
|
'orders' => 8,
|
|
'categories' => ['Electronics', 'Gadgets'],
|
|
],
|
|
[
|
|
'site_name' => $businessName.' Wholesale',
|
|
'site_url' => 'https://demo-wholesale.example.com',
|
|
'products' => 20,
|
|
'orders' => 6,
|
|
'categories' => ['Bulk', 'Packaging'],
|
|
],
|
|
],
|
|
'pro' => [
|
|
[
|
|
'site_name' => $businessName.' Shop',
|
|
'site_url' => 'https://demo-pro-shop.example.com',
|
|
'products' => 35,
|
|
'orders' => 10,
|
|
'categories' => $baseCategories,
|
|
],
|
|
[
|
|
'site_name' => $businessName.' Outlet',
|
|
'site_url' => 'https://demo-outlet.example.com',
|
|
'products' => 15,
|
|
'orders' => 5,
|
|
'categories' => ['Clearance', 'Seasonal'],
|
|
],
|
|
],
|
|
default => [
|
|
[
|
|
'site_name' => $businessName.' Shop',
|
|
'site_url' => 'https://demo-free-shop.example.com',
|
|
'products' => 15,
|
|
'orders' => 4,
|
|
'categories' => $baseCategories,
|
|
],
|
|
],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param array{site_name: string, site_url: string, products: int, orders: int, categories: list<string>} $spec
|
|
*/
|
|
private function seedStore(User $user, array $spec): void
|
|
{
|
|
$store = WooStore::query()->create([
|
|
'user_id' => $user->id,
|
|
'site_url' => $spec['site_url'],
|
|
'site_name' => $spec['site_name'],
|
|
'status' => WooStore::STATUS_ACTIVE,
|
|
'connected_at' => now(),
|
|
'metadata' => [
|
|
'demo' => true,
|
|
'currency' => 'GHS',
|
|
],
|
|
]);
|
|
|
|
$categoryIds = [];
|
|
foreach ($spec['categories'] as $i => $name) {
|
|
$externalId = $i + 1;
|
|
WooCategory::query()->create([
|
|
'woo_store_id' => $store->id,
|
|
'external_id' => $externalId,
|
|
'name' => $name,
|
|
'slug' => Str::slug($name),
|
|
'product_count' => 0,
|
|
]);
|
|
$categoryIds[] = $externalId;
|
|
}
|
|
|
|
$products = [];
|
|
for ($i = 1; $i <= $spec['products']; $i++) {
|
|
$cat = $categoryIds[($i - 1) % max(count($categoryIds), 1)] ?? null;
|
|
$product = WooProduct::query()->create([
|
|
'woo_store_id' => $store->id,
|
|
'external_id' => $i,
|
|
'name' => sprintf('%s Product %02d', $spec['site_name'], $i),
|
|
'slug' => 'demo-product-'.$store->id.'-'.$i,
|
|
'sku' => 'DEMO-'.$store->id.'-'.$i,
|
|
'status' => WooProduct::STATUS_PUBLISH,
|
|
'type' => 'simple',
|
|
'regular_price_minor' => 2_500 + ($i * 100),
|
|
'stock_quantity' => 10 + $i,
|
|
'manage_stock' => true,
|
|
'category_external_ids' => $cat ? [$cat] : [],
|
|
'short_description' => 'Local demo product (no live WordPress).',
|
|
]);
|
|
$products[] = $product;
|
|
|
|
if ($cat) {
|
|
WooCategory::query()
|
|
->where('woo_store_id', $store->id)
|
|
->where('external_id', $cat)
|
|
->increment('product_count');
|
|
}
|
|
}
|
|
|
|
$people = DemoWorld::people();
|
|
|
|
for ($i = 1; $i <= $spec['orders']; $i++) {
|
|
$product = $products[($i - 1) % count($products)];
|
|
$qty = 1 + ($i % 3);
|
|
$lineTotal = (int) $product->regular_price_minor * $qty;
|
|
$customer = $people[($i - 1) % count($people)];
|
|
|
|
WooOrder::query()->create([
|
|
'woo_store_id' => $store->id,
|
|
'external_order_id' => 1000 + $i,
|
|
'order_number' => (string) (1000 + $i),
|
|
'wc_status' => $i % 4 === 0 ? 'completed' : 'processing',
|
|
'payment_status' => 'paid',
|
|
'customer_name' => DemoWorld::fullName($customer),
|
|
'customer_email' => $customer['email'],
|
|
'customer_phone' => $customer['phone'],
|
|
'line_items' => [[
|
|
'id' => $product->external_id,
|
|
'name' => $product->name,
|
|
'quantity' => $qty,
|
|
'total' => number_format($lineTotal / 100, 2, '.', ''),
|
|
'sku' => $product->sku,
|
|
]],
|
|
'currency' => 'GHS',
|
|
'total_minor' => $lineTotal,
|
|
'fulfillment_status' => $i % 3 === 0
|
|
? WooOrder::FULFILLMENT_DELIVERED
|
|
: WooOrder::FULFILLMENT_NEW,
|
|
'wc_updated_at' => now()->subDays($spec['orders'] - $i),
|
|
'metadata' => ['demo' => true],
|
|
]);
|
|
}
|
|
}
|
|
}
|