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)
217 lines
6.8 KiB
PHP
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(),
|
|
]);
|
|
}
|
|
}
|