Deploy Ladill Woo Manager / deploy (push) Successful in 2m24s
Seed stores, products, and orders within Free caps and paid-tier volume. Co-authored-by: Cursor <cursoragent@cursor.com>
260 lines
9.0 KiB
PHP
260 lines
9.0 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 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'];
|
|
|
|
return match ($plan) {
|
|
'enterprise' => [
|
|
[
|
|
'site_name' => 'Demo Accra Shop',
|
|
'site_url' => 'https://demo-accra.example.com',
|
|
'products' => 30,
|
|
'orders' => 12,
|
|
'categories' => $baseCategories,
|
|
],
|
|
[
|
|
'site_name' => 'Demo Kumasi Shop',
|
|
'site_url' => 'https://demo-kumasi.example.com',
|
|
'products' => 24,
|
|
'orders' => 8,
|
|
'categories' => ['Electronics', 'Gadgets'],
|
|
],
|
|
[
|
|
'site_name' => 'Demo Wholesale',
|
|
'site_url' => 'https://demo-wholesale.example.com',
|
|
'products' => 20,
|
|
'orders' => 6,
|
|
'categories' => ['Bulk', 'Packaging'],
|
|
],
|
|
],
|
|
'pro' => [
|
|
[
|
|
'site_name' => 'Demo Pro Shop',
|
|
'site_url' => 'https://demo-pro-shop.example.com',
|
|
'products' => 35,
|
|
'orders' => 10,
|
|
'categories' => $baseCategories,
|
|
],
|
|
[
|
|
'site_name' => 'Demo Outlet',
|
|
'site_url' => 'https://demo-outlet.example.com',
|
|
'products' => 15,
|
|
'orders' => 5,
|
|
'categories' => ['Clearance', 'Seasonal'],
|
|
],
|
|
],
|
|
default => [
|
|
[
|
|
'site_name' => 'Demo Free 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');
|
|
}
|
|
}
|
|
|
|
for ($i = 1; $i <= $spec['orders']; $i++) {
|
|
$product = $products[($i - 1) % count($products)];
|
|
$qty = 1 + ($i % 3);
|
|
$lineTotal = (int) $product->regular_price_minor * $qty;
|
|
|
|
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' => 'Demo Customer '.$i,
|
|
'customer_email' => 'customer'.$i.'@example.com',
|
|
'customer_phone' => '+233200000'.str_pad((string) $i, 3, '0', STR_PAD_LEFT),
|
|
'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],
|
|
]);
|
|
}
|
|
}
|
|
}
|