- Import Cannabrands data from MySQL to PostgreSQL (strains, categories, companies, locations, contacts, products, images, invoices) - Make migrations idempotent for parallel test execution - Add ParallelTesting setup for separate test databases per process - Update product type constraint for imported data - Keep MysqlImport seeders for reference (data already in PG)
180 lines
5.4 KiB
PHP
180 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Seller\Crm;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Activity;
|
|
use App\Models\Business;
|
|
use App\Models\Crm\CrmEvent;
|
|
use App\Models\Crm\CrmTask;
|
|
use App\Models\SalesOpportunity;
|
|
use App\Models\SendMenuLog;
|
|
use Illuminate\Http\Request;
|
|
|
|
class AccountController extends Controller
|
|
{
|
|
/**
|
|
* Display accounts listing
|
|
*/
|
|
public function index(Request $request, Business $business)
|
|
{
|
|
$accounts = Business::where('type', 'buyer')
|
|
->where('status', 'approved')
|
|
->with(['contacts'])
|
|
->orderBy('name')
|
|
->paginate(25);
|
|
|
|
return view('seller.crm.accounts.index', compact('business', 'accounts'));
|
|
}
|
|
|
|
/**
|
|
* Show account details
|
|
*/
|
|
public function show(Request $request, Business $business, Business $account)
|
|
{
|
|
$account->load(['contacts']);
|
|
|
|
// Get orders for this account from this seller
|
|
$orders = $account->orders()
|
|
->whereHas('items.product.brand', function ($q) use ($business) {
|
|
$q->where('business_id', $business->id);
|
|
})
|
|
->latest()
|
|
->limit(10)
|
|
->get();
|
|
|
|
// Get opportunities for this account from this seller
|
|
// SalesOpportunity uses business_id for the buyer
|
|
$opportunities = SalesOpportunity::where('seller_business_id', $business->id)
|
|
->where('business_id', $account->id)
|
|
->with(['stage', 'brand'])
|
|
->latest()
|
|
->get();
|
|
|
|
// Get tasks related to this account
|
|
// CrmTask uses business_id for the buyer
|
|
$tasks = CrmTask::where('seller_business_id', $business->id)
|
|
->where('business_id', $account->id)
|
|
->whereNull('completed_at')
|
|
->with('assignee')
|
|
->orderBy('due_at')
|
|
->limit(5)
|
|
->get();
|
|
|
|
// Get conversation events for this account
|
|
$conversationEvents = CrmEvent::where('seller_business_id', $business->id)
|
|
->where('buyer_business_id', $account->id)
|
|
->latest('occurred_at')
|
|
->limit(20)
|
|
->get();
|
|
|
|
// Get menu send history for this account
|
|
$sendHistory = SendMenuLog::where('business_id', $business->id)
|
|
->where('customer_id', $account->id)
|
|
->with(['menu', 'brand'])
|
|
->latest('sent_at')
|
|
->limit(10)
|
|
->get();
|
|
|
|
// Get activity log for this account
|
|
$activities = Activity::where('seller_business_id', $business->id)
|
|
->where('business_id', $account->id)
|
|
->with(['causer'])
|
|
->latest()
|
|
->limit(20)
|
|
->get();
|
|
|
|
// Compute stats for this account (orders from this seller)
|
|
$ordersQuery = $account->orders()
|
|
->whereHas('items.product.brand', function ($q) use ($business) {
|
|
$q->where('business_id', $business->id);
|
|
});
|
|
|
|
$pipelineValue = $opportunities->where('status', 'open')->sum('value');
|
|
|
|
$stats = [
|
|
'total_orders' => $ordersQuery->count(),
|
|
'total_revenue' => $ordersQuery->sum('total') ?? 0,
|
|
'open_opportunities' => $opportunities->where('status', 'open')->count(),
|
|
'pipeline_value' => $pipelineValue ?? 0,
|
|
];
|
|
|
|
return view('seller.crm.accounts.show', compact(
|
|
'business',
|
|
'account',
|
|
'stats',
|
|
'orders',
|
|
'opportunities',
|
|
'tasks',
|
|
'conversationEvents',
|
|
'sendHistory',
|
|
'activities'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Show account contacts
|
|
*/
|
|
public function contacts(Request $request, Business $business, Business $account)
|
|
{
|
|
$contacts = $account->contacts()->paginate(25);
|
|
|
|
return view('seller.crm.accounts.contacts', compact('business', 'account', 'contacts'));
|
|
}
|
|
|
|
/**
|
|
* Show account opportunities
|
|
*/
|
|
public function opportunities(Request $request, Business $business, Business $account)
|
|
{
|
|
return view('seller.crm.accounts.opportunities', compact('business', 'account'));
|
|
}
|
|
|
|
/**
|
|
* Show account orders
|
|
*/
|
|
public function orders(Request $request, Business $business, Business $account)
|
|
{
|
|
return view('seller.crm.accounts.orders', compact('business', 'account'));
|
|
}
|
|
|
|
/**
|
|
* Show account activity
|
|
*/
|
|
public function activity(Request $request, Business $business, Business $account)
|
|
{
|
|
return view('seller.crm.accounts.activity', compact('business', 'account'));
|
|
}
|
|
|
|
/**
|
|
* Show account tasks
|
|
*/
|
|
public function tasks(Request $request, Business $business, Business $account)
|
|
{
|
|
return view('seller.crm.accounts.tasks', compact('business', 'account'));
|
|
}
|
|
|
|
/**
|
|
* Store a note for an account
|
|
*/
|
|
public function storeNote(Request $request, Business $business, Business $account)
|
|
{
|
|
$request->validate([
|
|
'note' => 'required|string|max:5000',
|
|
]);
|
|
|
|
CrmEvent::log(
|
|
sellerBusinessId: $business->id,
|
|
eventType: 'note_added',
|
|
summary: $request->input('note'),
|
|
buyerBusinessId: $account->id,
|
|
userId: auth()->id(),
|
|
channel: 'system'
|
|
);
|
|
|
|
return redirect()
|
|
->route('seller.business.crm.accounts.show', [$business->slug, $account->slug])
|
|
->with('success', 'Note added successfully.');
|
|
}
|
|
}
|