Files
hub/app/Console/Commands/CreateTestInvoiceForApproval.php
kelly 3905f86d6a feat: V1 Release - Complete platform with all premium modules
Major Features:
- CRM Lite: Pipeline, tasks, accounts, calendar, inbox
- AI Copilot: Multi-provider support, brand voice, content rules
- Marketing: Campaigns, templates, channels, broadcasts
- Intelligence: Buyer analytics, market intelligence dashboard
- Orchestrator: Sales & marketing automation with AI
- Compliance: License tracking (minimal shell)
- Conversations: Buyer-seller messaging with email/SMS routing

Infrastructure:
- Suites & Plans system for feature gating
- 60+ new migrations
- Module middleware for access control
- Database seeders for production sync
- Enhanced product management (varieties, inventory modes)

Documentation:
- V1 scope, launch checklist, QA scripts
- Module current state audit
- Feature matrix (standard vs premium)
2025-12-01 09:48:40 -07:00

217 lines
6.8 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Business;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\User;
use App\Services\InvoiceService;
use Illuminate\Console\Command;
class CreateTestInvoiceForApproval extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'test:invoice-approval {--buyer-email=}';
/**
* The console command description.
*/
protected $description = 'Create a test order with invoice ready for buyer approval testing';
/**
* Execute the console command.
*/
public function handle()
{
$this->info('🧪 Creating test invoice for approval workflow...');
$this->newLine();
// Find or get buyer
$buyer = $this->getBuyer();
if (! $buyer) {
$this->error('No buyer user found. Please create a buyer account first.');
return 1;
}
$this->info("✓ Using buyer: {$buyer->name} ({$buyer->email})");
// Get any business
$business = Business::first();
if (! $business) {
$this->error('No business found. Please seed database first.');
return 1;
}
$this->info("✓ Business: {$business->name}");
// Get some products that have inventory
$products = Product::whereHas('inventoryItems', function ($q) {
$q->where('quantity_on_hand', '>', 10);
})->where('is_active', true)->take(5)->get();
if ($products->isEmpty()) {
$this->error('No products found. Please seed products first.');
return 1;
}
$this->info("✓ Found {$products->count()} products for order");
// Create order
$order = $this->createOrder($buyer, $business);
$this->info("✓ Created order: {$order->order_number}");
// Add items to order
$this->addItemsToOrder($order, $products);
$this->info("✓ Added {$order->items->count()} items to order");
// Simulate order workflow to ready_for_invoice status
$this->progressOrderToInvoice($order);
$this->info("✓ Progressed order through workflow to 'awaiting_invoice_approval' status");
// Generate invoice
$invoiceService = app(InvoiceService::class);
$invoice = $invoiceService->generateFromOrder($order);
$this->info("✓ Generated invoice: {$invoice->invoice_number}");
// Display test URLs
$this->newLine();
$this->info('📋 Test URLs:');
$this->line(" Buyer Invoice View: /b/invoices/{$invoice->id}");
$this->line(" Buyer Orders View: /b/orders/{$order->id}");
$this->newLine();
$this->info('🎯 Testing Workflow:');
$this->line(' 1. Login as buyer: '.$buyer->email);
$this->line(' 2. Navigate to invoice view');
$this->line(' 3. Test actions:');
$this->line(' - Click "Modify Invoice" to edit quantities');
$this->line(' - Reduce a quantity (e.g., from 10 to 8)');
$this->line(' - Click "Remove" on an item');
$this->line(' - Click "Save Changes"');
$this->line(' - Or click "Approve Invoice" without changes');
$this->line(' - Or click "Reject Invoice" with a reason');
$this->newLine();
$this->info('✅ Test data created successfully!');
$this->newLine();
// Display database check commands
$this->info('🔍 Verify in database:');
$this->line(" Order: SELECT * FROM orders WHERE id = {$order->id};");
$this->line(" Invoice: SELECT * FROM invoices WHERE id = {$invoice->id};");
$this->line(" Changes: SELECT * FROM order_changes WHERE order_id = {$order->id};");
return 0;
}
/**
* Get buyer user from option or first available.
*/
protected function getBuyer(): ?User
{
if ($email = $this->option('buyer-email')) {
return User::where('email', $email)->first();
}
// Find first buyer user
return User::where('user_type', 'buyer')->first() ?? User::first();
}
/**
* Create a test order.
*/
protected function createOrder(User $buyer, Business $business): Order
{
return Order::create([
'order_number' => 'ORD-TEST-'.strtoupper(substr(md5(time()), 0, 10)),
'business_id' => $business->id,
'user_id' => $buyer->id,
'subtotal' => 0, // Will be calculated
'tax' => 0,
'total' => 0,
'status' => 'new',
'created_by' => 'buyer',
'payment_terms' => 'net_30',
'due_date' => now()->addDays(30),
'notes' => 'Test order for invoice approval workflow testing',
]);
}
/**
* Add items to order.
*/
protected function addItemsToOrder(Order $order, $products): void
{
$subtotal = 0;
foreach ($products as $product) {
$quantity = rand(5, 15);
$lineTotal = bcmul((string) $product->price, (string) $quantity, 2);
OrderItem::create([
'order_id' => $order->id,
'product_id' => $product->id,
'product_name' => $product->name,
'product_sku' => $product->sku,
'brand_name' => $product->brand?->name ?? 'N/A',
'quantity' => $quantity,
'picked_qty' => $quantity, // Simulate picked
'unit_price' => $product->price,
'line_total' => $lineTotal,
]);
$subtotal = bcadd((string) $subtotal, (string) $lineTotal, 2);
}
// Update order totals
$tax = bcmul((string) $subtotal, '0.00', 2); // No tax for testing
$total = bcadd((string) $subtotal, (string) $tax, 2);
$order->update([
'subtotal' => $subtotal,
'tax' => $tax,
'total' => $total,
]);
}
/**
* Progress order through workflow to invoiced status.
*/
protected function progressOrderToInvoice(Order $order): void
{
// Accept order
$order->update([
'status' => 'accepted',
'accepted_at' => now()->subDays(2),
'picking_ticket_number' => rand(1000, 9999),
]);
// Mark as in progress (picking started)
$order->update([
'status' => 'in_progress',
'in_progress_at' => now()->subDays(2),
'workorder_status' => 50,
]);
// Complete picking
$order->update([
'status' => 'ready_for_invoice',
'ready_for_invoice_at' => now()->subDay(),
'workorder_status' => 100,
]);
// Invoice generated
$order->update([
'status' => 'awaiting_invoice_approval',
'invoiced_at' => now(),
]);
}
}