$dbConfig */ $dbConfig = require $basePath . '/config/database.php'; $args = $argv; array_shift($args); $useRemote = in_array('--use-remote', $args, true); $append = in_array('--append', $args, true); $count = 30; $profile = 'default'; foreach ($args as $arg) { if (str_starts_with($arg, '--count=')) { $count = max(1, min(500, (int) substr($arg, 8))); } if (str_starts_with($arg, '--profile=')) { $parsedProfile = strtolower(trim((string) substr($arg, 10))); if (in_array($parsedProfile, ['default', 'realistic'], true)) { $profile = $parsedProfile; } } } if ($useRemote) { $remoteHost = (string) Env::get('DB_HOST_REMOTE', ''); if ($remoteHost !== '') { $dbConfig['host'] = $remoteHost; } } $pdo = ConnectionFactory::make($dbConfig); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo 'Deploy + seed orders schema' . PHP_EOL; echo '[db-host] ' . (string) ($dbConfig['host'] ?? '') . PHP_EOL; echo '[count] ' . $count . PHP_EOL; echo '[profile] ' . $profile . PHP_EOL; echo '[mode] ' . ($append ? 'append' : 'reset+seed') . PHP_EOL; try { deploySchema($pdo, $basePath . '/database/drafts/20260302_orders_schema_v1.sql'); seedData($pdo, $count, $append, $profile); printSummary($pdo); echo 'Done.' . PHP_EOL; } catch (Throwable $exception) { fwrite(STDERR, '[error] ' . $exception->getMessage() . PHP_EOL); exit(1); } function deploySchema(PDO $pdo, string $sqlFile): void { if (!is_file($sqlFile)) { throw new RuntimeException('SQL draft file not found: ' . $sqlFile); } $rawSql = (string) file_get_contents($sqlFile); $withoutLineComments = preg_replace('/^\s*--.*$/m', '', $rawSql) ?? $rawSql; $statements = preg_split('/;\s*[\r\n]+/', $withoutLineComments) ?: []; foreach ($statements as $statement) { $sql = trim($statement); if ($sql === '') { continue; } $pdo->exec($sql); } echo '[schema] applied' . PHP_EOL; } function seedData(PDO $pdo, int $count, bool $append, string $profile): void { if (!$append) { $pdo->exec('SET FOREIGN_KEY_CHECKS = 0'); foreach ([ 'order_tag_links', 'order_status_history', 'order_notes', 'order_documents', 'order_shipments', 'order_payments', 'order_items', 'order_addresses', 'orders', 'order_tags_dict', 'integration_order_sync_state', ] as $table) { $pdo->exec('TRUNCATE TABLE ' . $table); } $pdo->exec('SET FOREIGN_KEY_CHECKS = 1'); } $now = new DateTimeImmutable('now'); $statuses = loadStatusesForSeed($pdo); $paymentTypes = ['transfer', 'card', 'cod', 'blik', 'cash']; $carriers = ['dhl', 'inpost', 'dpd', 'ups', 'gls']; $itemTypes = ['product', 'shipment', 'service']; $currencies = ['PLN', 'EUR', 'USD']; $sources = ['shop', 'marketplace', 'api']; $noteTypes = ['customer_message', 'internal_note']; $tags = ['priority', 'fraud-check', 'gift', 'invoice', 'vip', 'return-risk', 'express']; $insertTag = $pdo->prepare( 'INSERT INTO order_tags_dict (integration_id, source_tag_id, tag_name, is_active, updated_at_external, payload_json, created_at, updated_at) VALUES (:integration_id, :source_tag_id, :tag_name, :is_active, :updated_at_external, :payload_json, :created_at, :updated_at)' ); foreach ($tags as $idx => $tagName) { $insertTag->execute([ 'integration_id' => random_int(1, 3), 'source_tag_id' => 'tag_' . ($idx + 1), 'tag_name' => $tagName, 'is_active' => 1, 'updated_at_external' => $now->format('Y-m-d H:i:s'), 'payload_json' => json_encode(['name' => $tagName], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } $tagRows = $pdo->query('SELECT id FROM order_tags_dict')->fetchAll(PDO::FETCH_COLUMN); $tagIds = array_map(static fn ($id) => (int) $id, is_array($tagRows) ? $tagRows : []); $insertOrder = $pdo->prepare( 'INSERT INTO orders ( integration_id, source, source_order_id, external_order_id, external_platform_id, external_platform_account_id, external_status_id, external_payment_type_id, payment_status, external_carrier_id, external_carrier_account_id, customer_login, is_invoice, is_encrypted, is_canceled_by_buyer, currency, total_without_tax, total_with_tax, total_paid, send_date_min, send_date_max, ordered_at, source_created_at, source_updated_at, preferences_json, payload_json, fetched_at, created_at, updated_at ) VALUES ( :integration_id, :source, :source_order_id, :external_order_id, :external_platform_id, :external_platform_account_id, :external_status_id, :external_payment_type_id, :payment_status, :external_carrier_id, :external_carrier_account_id, :customer_login, :is_invoice, :is_encrypted, :is_canceled_by_buyer, :currency, :total_without_tax, :total_with_tax, :total_paid, :send_date_min, :send_date_max, :ordered_at, :source_created_at, :source_updated_at, :preferences_json, :payload_json, :fetched_at, :created_at, :updated_at )' ); $insertAddress = $pdo->prepare( 'INSERT INTO order_addresses ( order_id, address_type, name, phone, email, street_name, street_number, city, zip_code, country, department, parcel_external_id, parcel_name, address_class, company_tax_number, company_name, payload_json, created_at, updated_at ) VALUES ( :order_id, :address_type, :name, :phone, :email, :street_name, :street_number, :city, :zip_code, :country, :department, :parcel_external_id, :parcel_name, :address_class, :company_tax_number, :company_name, :payload_json, :created_at, :updated_at )' ); $insertItem = $pdo->prepare( 'INSERT INTO order_items ( order_id, source_item_id, external_item_id, ean, sku, original_name, original_code, original_price_with_tax, original_price_without_tax, media_url, quantity, tax_rate, item_status, unit, item_type, source_product_id, source_product_set_id, sort_order, payload_json, created_at, updated_at ) VALUES ( :order_id, :source_item_id, :external_item_id, :ean, :sku, :original_name, :original_code, :original_price_with_tax, :original_price_without_tax, :media_url, :quantity, :tax_rate, :item_status, :unit, :item_type, :source_product_id, :source_product_set_id, :sort_order, :payload_json, :created_at, :updated_at )' ); $insertPayment = $pdo->prepare( 'INSERT INTO order_payments ( order_id, source_payment_id, external_payment_id, payment_type_id, payment_date, amount, currency, comment, payload_json, created_at, updated_at ) VALUES ( :order_id, :source_payment_id, :external_payment_id, :payment_type_id, :payment_date, :amount, :currency, :comment, :payload_json, :created_at, :updated_at )' ); $insertShipment = $pdo->prepare( 'INSERT INTO order_shipments ( order_id, source_shipment_id, external_shipment_id, tracking_number, carrier_provider_id, posted_at, media_uuid, payload_json, created_at, updated_at ) VALUES ( :order_id, :source_shipment_id, :external_shipment_id, :tracking_number, :carrier_provider_id, :posted_at, :media_uuid, :payload_json, :created_at, :updated_at )' ); $insertDocument = $pdo->prepare( 'INSERT INTO order_documents ( order_id, source_document_id, external_document_id, document_number, price_with_tax, price_without_tax, currency, currency_value, document_type_id, media_uuid, source_created_at, payload_json, created_at, updated_at ) VALUES ( :order_id, :source_document_id, :external_document_id, :document_number, :price_with_tax, :price_without_tax, :currency, :currency_value, :document_type_id, :media_uuid, :source_created_at, :payload_json, :created_at, :updated_at )' ); $insertNote = $pdo->prepare( 'INSERT INTO order_notes ( order_id, source_note_id, note_type, created_at_external, comment, payload_json, created_at, updated_at ) VALUES ( :order_id, :source_note_id, :note_type, :created_at_external, :comment, :payload_json, :created_at, :updated_at )' ); $insertStatusHistory = $pdo->prepare( 'INSERT INTO order_status_history ( order_id, from_status_id, to_status_id, changed_at, change_source, comment, payload_json, created_at ) VALUES ( :order_id, :from_status_id, :to_status_id, :changed_at, :change_source, :comment, :payload_json, :created_at )' ); $insertTagLink = $pdo->prepare( 'INSERT INTO order_tag_links (order_id, tag_dict_id, assigned_at, payload_json, created_at) VALUES (:order_id, :tag_dict_id, :assigned_at, :payload_json, :created_at)' ); $firstNames = ['Jan', 'Anna', 'Piotr', 'Katarzyna', 'Marek', 'Ewa', 'Tomasz', 'Magda', 'Pawel', 'Karolina']; $lastNames = ['Kowalski', 'Nowak', 'Wisniewski', 'Wojcik', 'Kaczmarek', 'Mazur', 'Krawczyk', 'Zielinski']; $cities = ['Warszawa', 'Krakow', 'Gdansk', 'Poznan', 'Wroclaw', 'Lodz', 'Lublin', 'Katowice']; $pdo->beginTransaction(); try { for ($i = 1; $i <= $count; $i++) { $integrationId = random_int(1, 3); $source = $sources[array_rand($sources)]; $currency = $currencies[array_rand($currencies)]; $status = $statuses[array_rand($statuses)]; $paymentType = $paymentTypes[array_rand($paymentTypes)]; $carrier = $carriers[array_rand($carriers)]; $isInvoice = random_int(0, 1) === 1; $orderedAt = $now->sub(new DateInterval('P' . random_int(0, 45) . 'DT' . random_int(0, 23) . 'H')); $updatedAt = $orderedAt->add(new DateInterval('PT' . random_int(1, 120) . 'H')); $sendMin = $orderedAt->add(new DateInterval('P' . random_int(0, 2) . 'D')); $sendMax = $sendMin->add(new DateInterval('P' . random_int(0, 4) . 'D')); $totalNet = (float) random_int(5000, 35000) / 100.0; $totalGross = round($totalNet * 1.23, 2); $paidRatio = [0.0, 0.5, 1.0, 1.1][array_rand([0, 1, 2, 3])]; $totalPaid = round($totalGross * $paidRatio, 2); $paymentStatus = random_int(0, 3); if ($profile === 'realistic') { $status = weightedChoice([ 'new' => 10, 'confirmed' => 16, 'paid' => 18, 'processing' => 16, 'packed' => 12, 'shipped' => 12, 'delivered' => 10, 'cancelled' => 4, 'returned' => 2, ]); $paymentType = weightedChoice([ 'transfer' => 30, 'card' => 30, 'blik' => 22, 'cod' => 14, 'cash' => 4, ]); $carrier = weightedChoice([ 'inpost' => 35, 'dhl' => 25, 'dpd' => 20, 'ups' => 12, 'gls' => 8, ]); $isInvoice = random_int(1, 100) <= 35; $orderedAt = $now->sub(new DateInterval('P' . random_int(0, 60) . 'DT' . random_int(0, 23) . 'H')); $updatedAt = $orderedAt->add(new DateInterval('PT' . random_int(6, 168) . 'H')); $sendMin = $orderedAt->add(new DateInterval('P' . random_int(0, 1) . 'D')); $sendMax = $sendMin->add(new DateInterval('P' . random_int(1, 3) . 'D')); $totalNet = (float) random_int(6000, 42000) / 100.0; $totalGross = round($totalNet * 1.23, 2); $statusFinancialRules = [ 'new' => ['ratio' => [0.0, 0.0, 0.5], 'payment_status' => [0, 0, 1]], 'confirmed' => ['ratio' => [0.0, 0.5, 1.0], 'payment_status' => [0, 1, 2]], 'paid' => ['ratio' => [1.0, 1.0, 1.0, 0.5], 'payment_status' => [2, 2, 2, 1]], 'processing' => ['ratio' => [1.0, 1.0, 1.0], 'payment_status' => [2, 2, 2]], 'packed' => ['ratio' => [1.0, 1.0, 1.0], 'payment_status' => [2, 2, 2]], 'shipped' => ['ratio' => [1.0, 1.0, 1.0], 'payment_status' => [2, 2, 2]], 'delivered' => ['ratio' => [1.0, 1.0, 1.0], 'payment_status' => [2, 2, 2]], 'cancelled' => ['ratio' => [0.0, 0.0, 0.5], 'payment_status' => [0, 0, 1]], 'returned' => ['ratio' => [1.0, 1.0, 0.0], 'payment_status' => [3, 3, 0]], ]; if (isset($statusFinancialRules[$status])) { $rule = $statusFinancialRules[$status]; $paidRatio = (float) $rule['ratio'][array_rand($rule['ratio'])]; $paymentStatus = (int) $rule['payment_status'][array_rand($rule['payment_status'])]; } else { $paidRatio = [0.0, 0.5, 1.0][array_rand([0, 1, 2])]; $paymentStatus = [0, 1, 2][array_rand([0, 1, 2])]; } $totalPaid = round($totalGross * $paidRatio, 2); if ($status === 'returned' && $paymentStatus === 3) { $totalPaid = 0.00; } } $sourceOrderId = 'ORD-' . $integrationId . '-' . $orderedAt->format('Ymd') . '-' . str_pad((string) $i, 4, '0', STR_PAD_LEFT); $externalOrderId = 'EXT-' . random_int(100000, 999999); $platformId = 'platform_' . random_int(1, 5); $platformAccountId = 'account_' . random_int(1, 8); $customerName = $firstNames[array_rand($firstNames)] . ' ' . $lastNames[array_rand($lastNames)]; $customerEmail = strtolower(str_replace(' ', '.', $customerName)) . random_int(1, 99) . '@example.com'; $city = $cities[array_rand($cities)]; $insertOrder->execute([ 'integration_id' => $integrationId, 'source' => $source, 'source_order_id' => $sourceOrderId, 'external_order_id' => $externalOrderId, 'external_platform_id' => $platformId, 'external_platform_account_id' => $platformAccountId, 'external_status_id' => $status, 'external_payment_type_id' => $paymentType, 'payment_status' => $paymentStatus, 'external_carrier_id' => $carrier, 'external_carrier_account_id' => 'carrier_acc_' . random_int(1, 6), 'customer_login' => strtolower(str_replace(' ', '.', $customerName)), 'is_invoice' => $isInvoice ? 1 : 0, 'is_encrypted' => random_int(0, 1), 'is_canceled_by_buyer' => $status === 'cancelled' ? 1 : 0, 'currency' => $currency, 'total_without_tax' => number_format($totalNet, 2, '.', ''), 'total_with_tax' => number_format($totalGross, 2, '.', ''), 'total_paid' => number_format($totalPaid, 2, '.', ''), 'send_date_min' => $sendMin->format('Y-m-d H:i:s'), 'send_date_max' => $sendMax->format('Y-m-d H:i:s'), 'ordered_at' => $orderedAt->format('Y-m-d H:i:s'), 'source_created_at' => $orderedAt->format('Y-m-d H:i:s'), 'source_updated_at' => $updatedAt->format('Y-m-d H:i:s'), 'preferences_json' => json_encode(['gift_wrap' => (bool) random_int(0, 1)], JSON_UNESCAPED_UNICODE), 'payload_json' => json_encode(['mock' => true, 'v' => 1], JSON_UNESCAPED_UNICODE), 'fetched_at' => $now->format('Y-m-d H:i:s'), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); $orderId = (int) $pdo->lastInsertId(); foreach (['customer', 'delivery'] as $addressType) { $insertAddress->execute([ 'order_id' => $orderId, 'address_type' => $addressType, 'name' => $customerName, 'phone' => '+485' . random_int(10000000, 99999999), 'email' => $customerEmail, 'street_name' => 'Testowa', 'street_number' => (string) random_int(1, 120), 'city' => $city, 'zip_code' => sprintf('%02d-%03d', random_int(10, 99), random_int(100, 999)), 'country' => 'PL', 'department' => null, 'parcel_external_id' => random_int(0, 3) === 0 ? 'PACZ-' . random_int(1000, 9999) : null, 'parcel_name' => null, 'address_class' => $isInvoice ? 'company' : 'house', 'company_tax_number' => $isInvoice ? 'PL' . random_int(1000000000, 9999999999) : null, 'company_name' => $isInvoice ? 'Firma ' . $customerName : null, 'payload_json' => json_encode(['type' => $addressType], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } if ($isInvoice) { $insertAddress->execute([ 'order_id' => $orderId, 'address_type' => 'invoice', 'name' => $customerName, 'phone' => '+485' . random_int(10000000, 99999999), 'email' => $customerEmail, 'street_name' => 'Firmowa', 'street_number' => (string) random_int(1, 120), 'city' => $city, 'zip_code' => sprintf('%02d-%03d', random_int(10, 99), random_int(100, 999)), 'country' => 'PL', 'department' => null, 'parcel_external_id' => null, 'parcel_name' => null, 'address_class' => 'company', 'company_tax_number' => 'PL' . random_int(1000000000, 9999999999), 'company_name' => 'Firma ' . $customerName, 'payload_json' => json_encode(['type' => 'invoice'], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } $itemsCount = random_int(1, 5); for ($j = 1; $j <= $itemsCount; $j++) { $itemType = $itemTypes[array_rand($itemTypes)]; $qty = $itemType === 'product' ? random_int(1, 3) : 1; $priceGross = (float) random_int(1500, 12000) / 100.0; $priceNet = round($priceGross / 1.23, 2); $insertItem->execute([ 'order_id' => $orderId, 'source_item_id' => 'ITEM-' . $orderId . '-' . $j, 'external_item_id' => 'EXTI-' . random_int(10000, 99999), 'ean' => (string) random_int(1000000000000, 9999999999999), 'sku' => 'SKU-' . random_int(10000, 99999), 'original_name' => ucfirst($itemType) . ' ' . random_int(1, 999), 'original_code' => strtoupper(substr($itemType, 0, 3)) . '-' . random_int(100, 999), 'original_price_with_tax' => number_format($priceGross, 2, '.', ''), 'original_price_without_tax' => number_format($priceNet, 2, '.', ''), 'media_url' => random_int(1, 100) <= 85 ? ('https://picsum.photos/seed/order-item-' . $orderId . '-' . $j . '/120/120') : null, 'quantity' => number_format((float) $qty, 3, '.', ''), 'tax_rate' => 23.00, 'item_status' => 'active', 'unit' => 'szt.', 'item_type' => $itemType, 'source_product_id' => 'P-' . random_int(1000, 9999), 'source_product_set_id' => null, 'sort_order' => $j - 1, 'payload_json' => json_encode(['idx' => $j], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } $paymentsCount = random_int(1, 2); if ($profile === 'realistic') { if ($totalPaid <= 0.00) { $paymentsCount = random_int(0, 1) === 0 ? 0 : 1; } elseif ($totalPaid < $totalGross) { $paymentsCount = 1; } else { $paymentsCount = random_int(1, 100) <= 75 ? 1 : 2; } } if ($paymentsCount > 0) { for ($j = 1; $j <= $paymentsCount; $j++) { $amount = $paymentsCount === 1 || $j < $paymentsCount ? round($totalPaid / $paymentsCount, 2) : round($totalPaid - round(($totalPaid / $paymentsCount) * ($paymentsCount - 1), 2), 2); $insertPayment->execute([ 'order_id' => $orderId, 'source_payment_id' => 'PAY-' . $orderId . '-' . $j, 'external_payment_id' => 'EXTP-' . random_int(10000, 99999), 'payment_type_id' => $paymentType, 'payment_date' => $orderedAt->add(new DateInterval('PT' . random_int(0, 72) . 'H'))->format('Y-m-d H:i:s'), 'amount' => number_format(max(0.0, $amount), 2, '.', ''), 'currency' => $currency, 'comment' => 'Payment #' . $j, 'payload_json' => json_encode(['part' => $j], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } } $shipmentsCount = random_int(0, 2); if ($profile === 'realistic') { if (in_array($status, ['shipped', 'delivered', 'returned'], true)) { $shipmentsCount = random_int(1, 2); } elseif ($status === 'packed') { $shipmentsCount = random_int(0, 100) <= 35 ? 1 : 0; } else { $shipmentsCount = 0; } } for ($j = 1; $j <= $shipmentsCount; $j++) { $insertShipment->execute([ 'order_id' => $orderId, 'source_shipment_id' => 'SHIP-' . $orderId . '-' . $j, 'external_shipment_id' => 'EXTS-' . random_int(10000, 99999), 'tracking_number' => 'TRK' . random_int(100000000, 999999999), 'carrier_provider_id' => $carriers[array_rand($carriers)], 'posted_at' => $orderedAt->add(new DateInterval('P' . random_int(1, 6) . 'D'))->format('Y-m-d H:i:s'), 'media_uuid' => null, 'payload_json' => json_encode(['shipment_no' => $j], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } $documentsCount = random_int(0, 2); if ($profile === 'realistic') { if ($isInvoice || in_array($status, ['paid', 'processing', 'packed', 'shipped', 'delivered'], true)) { $documentsCount = random_int(1, 100) <= 75 ? 1 : 2; } else { $documentsCount = random_int(1, 100) <= 15 ? 1 : 0; } } for ($j = 1; $j <= $documentsCount; $j++) { $insertDocument->execute([ 'order_id' => $orderId, 'source_document_id' => 'DOC-' . $orderId . '-' . $j, 'external_document_id' => 'EXTD-' . random_int(10000, 99999), 'document_number' => 'FV/' . random_int(1, 9999) . '/' . $now->format('Y'), 'price_with_tax' => number_format($totalGross, 2, '.', ''), 'price_without_tax' => number_format($totalNet, 2, '.', ''), 'currency' => $currency, 'currency_value' => number_format(1.0, 4, '.', ''), 'document_type_id' => 'invoice', 'media_uuid' => null, 'source_created_at' => $orderedAt->add(new DateInterval('P' . random_int(0, 3) . 'D'))->format('Y-m-d H:i:s'), 'payload_json' => json_encode(['document_no' => $j], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } $notesCount = random_int(0, 3); for ($j = 1; $j <= $notesCount; $j++) { $insertNote->execute([ 'order_id' => $orderId, 'source_note_id' => 'NOTE-' . $orderId . '-' . $j, 'note_type' => $noteTypes[array_rand($noteTypes)], 'created_at_external' => $orderedAt->add(new DateInterval('PT' . random_int(1, 96) . 'H'))->format('Y-m-d H:i:s'), 'comment' => 'Losowa notatka #' . $j . ' dla zamowienia ' . $sourceOrderId, 'payload_json' => json_encode(['n' => $j], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } if ($profile === 'realistic') { $historyPath = buildStatusPathForFinal($status); $historyDate = $orderedAt; for ($j = 1; $j < count($historyPath); $j++) { $historyDate = $historyDate->add(new DateInterval('PT' . random_int(2, 30) . 'H')); $insertStatusHistory->execute([ 'order_id' => $orderId, 'from_status_id' => $historyPath[$j - 1], 'to_status_id' => $historyPath[$j], 'changed_at' => $historyDate->format('Y-m-d H:i:s'), 'change_source' => ['import', 'manual', 'api', 'sync'][array_rand([0, 1, 2, 3])], 'comment' => 'Auto-seed status change', 'payload_json' => json_encode(['step' => $j], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), ]); } } else { $historySteps = random_int(2, 6); $currentStatus = 'new'; $historyDate = $orderedAt; for ($j = 1; $j <= $historySteps; $j++) { $nextStatus = $statuses[array_rand($statuses)]; $historyDate = $historyDate->add(new DateInterval('PT' . random_int(2, 30) . 'H')); $insertStatusHistory->execute([ 'order_id' => $orderId, 'from_status_id' => $currentStatus, 'to_status_id' => $nextStatus, 'changed_at' => $historyDate->format('Y-m-d H:i:s'), 'change_source' => ['import', 'manual', 'api', 'sync'][array_rand([0, 1, 2, 3])], 'comment' => 'Auto-seed status change', 'payload_json' => json_encode(['step' => $j], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), ]); $currentStatus = $nextStatus; } } shuffle($tagIds); $assignCount = random_int(0, min(3, count($tagIds))); for ($j = 0; $j < $assignCount; $j++) { $insertTagLink->execute([ 'order_id' => $orderId, 'tag_dict_id' => $tagIds[$j], 'assigned_at' => $orderedAt->add(new DateInterval('PT' . random_int(1, 20) . 'H'))->format('Y-m-d H:i:s'), 'payload_json' => json_encode(['auto' => true], JSON_UNESCAPED_UNICODE), 'created_at' => $now->format('Y-m-d H:i:s'), ]); } } $syncInsert = $pdo->prepare( 'INSERT INTO integration_order_sync_state ( integration_id, last_synced_order_updated_at, last_synced_source_order_id, last_synced_external_order_id, last_run_at, last_success_at, last_error, created_at, updated_at ) VALUES ( :integration_id, :last_synced_order_updated_at, :last_synced_source_order_id, :last_synced_external_order_id, :last_run_at, :last_success_at, :last_error, :created_at, :updated_at )' ); for ($i = 1; $i <= 3; $i++) { $syncInsert->execute([ 'integration_id' => $i, 'last_synced_order_updated_at' => $now->format('Y-m-d H:i:s'), 'last_synced_source_order_id' => 'ORD-' . $i . '-' . $now->format('Ymd') . '-9999', 'last_synced_external_order_id' => 'EXT-' . random_int(100000, 999999), 'last_run_at' => $now->format('Y-m-d H:i:s'), 'last_success_at' => $now->format('Y-m-d H:i:s'), 'last_error' => null, 'created_at' => $now->format('Y-m-d H:i:s'), 'updated_at' => $now->format('Y-m-d H:i:s'), ]); } $pdo->commit(); } catch (Throwable $exception) { if ($pdo->inTransaction()) { $pdo->rollBack(); } throw $exception; } echo '[seed] inserted orders and child data' . PHP_EOL; } /** * @param array $weights */ function weightedChoice(array $weights): string { $sum = array_sum($weights); if ($sum <= 0) { throw new RuntimeException('Invalid weights.'); } $pick = random_int(1, $sum); $running = 0; foreach ($weights as $value => $weight) { $running += $weight; if ($pick <= $running) { return (string) $value; } } return (string) array_key_first($weights); } /** * @return list */ function buildStatusPathForFinal(string $finalStatus): array { $happyPath = ['new', 'confirmed', 'paid', 'processing', 'packed', 'shipped', 'delivered']; if ($finalStatus === 'new') { return ['new']; } if (in_array($finalStatus, $happyPath, true)) { $index = array_search($finalStatus, $happyPath, true); return array_slice($happyPath, 0, ((int) $index) + 1); } if ($finalStatus === 'cancelled') { $variants = [ ['new', 'cancelled'], ['new', 'confirmed', 'cancelled'], ['new', 'confirmed', 'paid', 'cancelled'], ['new', 'confirmed', 'paid', 'processing', 'cancelled'], ]; return $variants[array_rand($variants)]; } if ($finalStatus === 'returned') { return ['new', 'confirmed', 'paid', 'processing', 'packed', 'shipped', 'delivered', 'returned']; } return ['new', $finalStatus]; } /** * @return list */ function loadStatusesForSeed(PDO $pdo): array { $fallback = ['new', 'confirmed', 'paid', 'processing', 'packed', 'shipped', 'delivered', 'cancelled', 'returned']; try { $rows = $pdo->query('SELECT code FROM order_statuses WHERE is_active = 1 ORDER BY sort_order ASC, id ASC')->fetchAll(PDO::FETCH_COLUMN); } catch (Throwable) { return $fallback; } if (!is_array($rows)) { return $fallback; } $result = []; foreach ($rows as $row) { $code = strtolower(trim((string) $row)); if ($code === '' || in_array($code, $result, true)) { continue; } $result[] = $code; } return $result === [] ? $fallback : $result; } function printSummary(PDO $pdo): void { $tables = [ 'orders', 'order_addresses', 'order_items', 'order_payments', 'order_shipments', 'order_documents', 'order_notes', 'order_status_history', 'order_tags_dict', 'order_tag_links', 'integration_order_sync_state', ]; echo 'Summary counts:' . PHP_EOL; foreach ($tables as $table) { $count = (int) $pdo->query('SELECT COUNT(*) FROM ' . $table)->fetchColumn(); echo ' ' . $table . ': ' . $count . PHP_EOL; } }