ultimatepos/Modules/AssetExchange/Console/AssetExchangeSeedDemo.php

306 lines
13 KiB
PHP

<?php
namespace Modules\AssetExchange\Console;
use App\Business;
use App\Contact;
use App\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Modules\AssetExchange\Models\AppraisalLine;
use Modules\AssetExchange\Models\Listing;
use Modules\AssetExchange\Models\PriceInquiry;
use Modules\AssetExchange\Models\PriceInquiryRecipient;
use Modules\AssetExchange\Models\ResearchCase;
use Modules\AssetExchange\Models\ResearchTask;
use Modules\AssetExchange\Services\AppraisalService;
use Modules\AssetExchange\Services\DealService;
use Modules\AssetExchange\Services\ListingService;
use Modules\AssetExchange\Services\PriceInquiryService;
use Modules\AssetExchange\Services\ResearchCaseService;
class AssetExchangeSeedDemo extends Command
{
protected $signature = 'pos:assetexchangeSeedDemo {business_id?} {--fresh : Remove previous demo records first}';
protected $description = 'Seed demo Listing, Appraisal and Inquiry data for AssetExchange E2E checks';
protected const DEMO_TITLE = '[DEMO-AEX] خط تولید بسته‌بندی دست‌دوم';
public function __construct(
protected ListingService $listingService,
protected AppraisalService $appraisalService,
protected PriceInquiryService $inquiryService,
protected ResearchCaseService $researchCaseService,
protected DealService $dealService
) {
parent::__construct();
}
public function handle(): int
{
$businessId = (int) ($this->argument('business_id') ?: Business::query()->value('id') ?: 0);
if ($businessId <= 0) {
$this->error('No business found.');
return self::FAILURE;
}
$business = Business::find($businessId);
if (! $business) {
$this->error("Business {$businessId} not found.");
return self::FAILURE;
}
$user = User::where('business_id', $businessId)->orderBy('id')->first();
if (! $user) {
$this->error("No user found for business {$businessId}.");
return self::FAILURE;
}
if ($this->option('fresh')) {
$this->purgeDemo($businessId);
} elseif (Listing::where('business_id', $businessId)->where('title', self::DEMO_TITLE)->exists()) {
$this->warn('Demo data already exists. Use --fresh to recreate.');
$this->printSummary($businessId);
return self::SUCCESS;
}
$supplierId = Contact::where('business_id', $businessId)
->whereIn('type', ['supplier', 'both'])
->orderBy('id')
->value('id');
if (! $supplierId) {
$supplierId = Contact::create([
'business_id' => $businessId,
'type' => 'supplier',
'supplier_business_name' => 'تأمین‌کننده دمو AEX',
'name' => 'تأمین‌کننده دمو AEX',
'mobile' => '09000000000',
'created_by' => $user->id,
'contact_status' => 'active',
])->id;
$this->line('Created demo supplier contact.');
}
$this->info("Seeding AssetExchange demo for business #{$businessId}...");
try {
DB::transaction(function () use ($business, $user, $supplierId) {
$listing = $this->listingService->create($business, [
'title' => self::DEMO_TITLE,
'description' => 'داده تستی خودکار برای smoke/E2E ماژول AssetExchange.',
'listing_type' => 'machine_used',
'scope_type' => 'whole',
'manufacturer' => 'Krones',
'model' => 'PET-Line 24K',
'serial_number' => 'DEMO-SN-'.now()->format('Ymd'),
'year_manufactured' => 2018,
'working_hours' => 12500,
'asking_price' => 8500000000,
'location_text' => 'تهران — انبار نمایشی',
'condition_grade' => 'B',
'intake_source' => 'internal_demo',
], $user);
$listing = $this->listingService->submit($business, $listing, $user);
$listing = $this->listingService->assignBroker($business, $listing, $user->id, $user);
$appraisal = $this->appraisalService->startAppraisal($business, $listing, $user, [
'methodology' => 'hybrid',
'field_visit_date' => now()->toDateString(),
'appraisal_fee' => 15000000,
'auto_import' => false,
]);
AppraisalLine::create([
'business_id' => $business->id,
'appraisal_id' => $appraisal->id,
'line_kind' => 'machine',
'name' => 'خط اصلی بسته‌بندی',
'manufacturer' => 'Krones',
'model' => 'PET-Line 24K',
'health_percentage' => 72,
'age_years' => 7,
'replacement_cost' => 12000000000,
'renovation_cost' => 1800000000,
'line_as_is_value' => 7200000000,
'line_post_renovation_value' => 9000000000,
'renovation_need' => 'moderate',
'keep_in_line' => true,
'sort_order' => 0,
]);
AppraisalLine::create([
'business_id' => $business->id,
'appraisal_id' => $appraisal->id,
'line_kind' => 'part',
'name' => 'درایور موتور اصلی',
'health_percentage' => 55,
'age_years' => 7,
'replacement_cost' => 450000000,
'renovation_cost' => 120000000,
'line_as_is_value' => 250000000,
'line_post_renovation_value' => 370000000,
'renovation_need' => 'high',
'keep_in_line' => true,
'sort_order' => 1,
]);
$appraisal = $this->appraisalService->recalculate($appraisal);
$appraisal = $this->appraisalService->approve(
$business,
$appraisal,
$user,
'گزارش دمو: تجهیز قابل خرید با نوسازی محدود درایور و بازرسی هیدرولیک.'
);
$inquiryData = [
'title' => 'استعلام دمو — قطعات و سرویس خط',
'inquiry_type' => 'supplier_rfq',
'appraisal_id' => $appraisal->id,
'from_appraisal' => true,
'due_date' => now()->addDays(3)->format('Y-m-d'),
'internal_user_ids' => [$user->id],
];
if ($supplierId) {
$inquiryData['supplier_contact_ids'] = [$supplierId];
}
$inquiry = $this->inquiryService->create($business, $listing, $inquiryData, $user);
$inquiry = $this->inquiryService->send($inquiry, $user);
$slaInquiry = $this->inquiryService->create($business, $listing, [
'title' => 'استعلام دمو SLA — معوق',
'inquiry_type' => 'supplier_rfq',
'due_date' => now()->subDay()->format('Y-m-d'),
'lines' => [[
'part_name' => 'سنسور فشار',
'description' => 'نمونه SLA معوق',
'target_condition' => 'new',
'reference_price' => 35000000,
]],
'supplier_contact_ids' => [$supplierId],
], $user);
$slaInquiry = $this->inquiryService->send($slaInquiry, $user);
PriceInquiryRecipient::where('price_inquiry_id', $slaInquiry->id)
->where('recipient_type', 'supplier')
->where('status', 'pending')
->update(['sent_at' => Carbon::now()->subHours(60)]);
$this->researchCaseService->openCase($business, $listing->fresh(), [
'title' => 'پرونده تحقیق دمو — '.$listing->title,
'spawn_templates' => true,
'lead_researcher_id' => $user->id,
], $user);
$listing = $listing->fresh();
$deal = $this->dealService->createDeal($business, $listing, 'brokerage', [
'appraisal_id' => $appraisal->id,
'agreed_price' => $appraisal->recommended_buy_price,
'commission_percent' => 3,
'notes' => 'معامله دمو — کارگزاری',
], $user);
try {
$this->dealService->completeDeal($business, $deal, $user);
} catch (\Throwable) {
// ERP/CMMS bridges may be unavailable in demo environments.
}
});
} catch (\Throwable $e) {
$this->error('Seed failed: '.$e->getMessage());
return self::FAILURE;
}
$this->info('Demo data seeded successfully.');
$this->printSummary($businessId);
return self::SUCCESS;
}
protected function purgeDemo(int $businessId): void
{
$listingIds = Listing::withTrashed()
->where('business_id', $businessId)
->where('title', self::DEMO_TITLE)
->pluck('id');
if ($listingIds->isEmpty()) {
return;
}
$caseIds = ResearchCase::where('business_id', $businessId)
->whereIn('listing_id', $listingIds)
->pluck('id');
if ($caseIds->isNotEmpty()) {
ResearchTask::whereIn('research_case_id', $caseIds)->delete();
ResearchCase::whereIn('id', $caseIds)->delete();
}
if (\Illuminate\Support\Facades\Schema::hasTable('aex_commissions')) {
$dealIds = \Modules\AssetExchange\Models\Deal::withTrashed()
->whereIn('listing_id', $listingIds)
->pluck('id');
if ($dealIds->isNotEmpty()) {
\Modules\AssetExchange\Models\Commission::whereIn('deal_id', $dealIds)->delete();
\Modules\AssetExchange\Models\Deal::withTrashed()->whereIn('id', $dealIds)->forceDelete();
}
}
$inquiryIds = PriceInquiry::where('business_id', $businessId)
->whereIn('listing_id', $listingIds)
->pluck('id');
if ($inquiryIds->isNotEmpty()) {
PriceInquiryRecipient::whereIn('price_inquiry_id', $inquiryIds)->delete();
PriceInquiry::whereIn('id', $inquiryIds)->delete();
}
foreach ($listingIds as $listingId) {
$appraisalIds = \Modules\AssetExchange\Models\Appraisal::withTrashed()
->where('listing_id', $listingId)
->pluck('id');
if ($appraisalIds->isNotEmpty()) {
AppraisalLine::whereIn('appraisal_id', $appraisalIds)->delete();
\Modules\AssetExchange\Models\Appraisal::withTrashed()->whereIn('id', $appraisalIds)->forceDelete();
}
}
Listing::withTrashed()->whereIn('id', $listingIds)->forceDelete();
$this->line('Previous demo records removed.');
}
protected function printSummary(int $businessId): void
{
$listing = Listing::where('business_id', $businessId)->where('title', self::DEMO_TITLE)->first();
if (! $listing) {
return;
}
$appraisal = $listing->appraisals()->latest('id')->first();
$inquiries = $listing->priceInquiries()->orderBy('id')->get();
$rfq = $inquiries->first(fn ($i) => str_contains($i->title, 'قطعات'));
$sla = $inquiries->first(fn ($i) => str_contains($i->title, 'SLA'));
$research = $listing->researchCases()->latest('id')->first();
$deal = $listing->deals()->latest('id')->first();
$this->table(['Entity', 'Code', 'Status'], [
['Listing', $listing->listing_code, $listing->status],
['Appraisal', $appraisal?->appraisal_number ?? '—', $appraisal?->status ?? '—'],
['Research', $research?->case_code ?? '—', $research?->status ?? '—'],
['Deal', $deal?->deal_code ?? '—', $deal?->status ?? '—'],
['Inquiry (RFQ)', $rfq?->inquiry_code ?? '—', $rfq?->status ?? '—'],
['Inquiry (SLA)', $sla?->inquiry_code ?? '—', $sla?->status ?? '—'],
['Appraisal PDF', $appraisal?->report_pdf_path ? 'stored' : '—', ''],
]);
}
}