Skip to content

Commit f9ac16b

Browse files
committed
feat(phase-10): demo data seeders for all 35 ERP modules
- Add 31 new seeders covering every module (Accounting, Appointments, Approvals, CRM, Discuss, Documents, Ecommerce, Events, FieldService, Fleet, Frontdesk, Helpdesk, KnowledgeBase, LiveChat, Lunch, Maintenance, Manufacturing, Marketing, PM, POS, Planning, Purchase, QualityControl, Rental, Repairs, Sign, SocialMarketing, Subcontracting, Subscriptions, Survey, Website) - Wire all seeders into DatabaseSeeder.php in dependency order - Fix InventorySeeder: switch Category → ProductCategory to match the products.category_id FK that was migrated to product_categories - php artisan migrate:fresh --seed runs cleanly end-to-end Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 5b6fb89 commit f9ac16b

33 files changed

Lines changed: 2221 additions & 6 deletions
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace Database\Seeders;
4+
5+
use App\Modules\Accounting\Models\Account;
6+
use App\Modules\Accounting\Models\JournalEntry;
7+
use App\Modules\Accounting\Models\JournalEntryLine;
8+
use App\Modules\Core\Models\Tenant;
9+
use Illuminate\Database\Seeder;
10+
11+
class AccountingSeeder extends Seeder
12+
{
13+
public function run(): void
14+
{
15+
$tenant = Tenant::first();
16+
17+
if (! $tenant) {
18+
return;
19+
}
20+
21+
// Seed the default chart of accounts for this tenant if not already present.
22+
Account::seedDefaults($tenant->id);
23+
24+
$cash = Account::where('tenant_id', $tenant->id)->where('code', '1000')->first();
25+
$revenue = Account::where('tenant_id', $tenant->id)->where('code', '4000')->first();
26+
$expense = Account::where('tenant_id', $tenant->id)->where('code', '5000')->first();
27+
$ap = Account::where('tenant_id', $tenant->id)->where('code', '2000')->first();
28+
29+
// Journal Entry 1 — Sales revenue received in cash
30+
$entry1 = JournalEntry::create([
31+
'tenant_id' => $tenant->id,
32+
'entry_number' => 'JE-2026-00001',
33+
'reference' => 'INV-2026-001',
34+
'description' => 'Cash received for sales revenue',
35+
'entry_date' => '2026-01-15',
36+
'status' => 'posted',
37+
'posted_at' => now(),
38+
]);
39+
40+
JournalEntryLine::create([
41+
'journal_entry_id' => $entry1->id,
42+
'account_id' => $cash->id,
43+
'description' => 'Cash receipt',
44+
'debit' => 5000.00,
45+
'credit' => 0.00,
46+
]);
47+
48+
JournalEntryLine::create([
49+
'journal_entry_id' => $entry1->id,
50+
'account_id' => $revenue->id,
51+
'description' => 'Sales revenue',
52+
'debit' => 0.00,
53+
'credit' => 5000.00,
54+
]);
55+
56+
// Journal Entry 2 — Operating expense paid in cash
57+
$entry2 = JournalEntry::create([
58+
'tenant_id' => $tenant->id,
59+
'entry_number' => 'JE-2026-00002',
60+
'reference' => 'EXP-2026-001',
61+
'description' => 'Operating expenses paid',
62+
'entry_date' => '2026-01-20',
63+
'status' => 'posted',
64+
'posted_at' => now(),
65+
]);
66+
67+
JournalEntryLine::create([
68+
'journal_entry_id' => $entry2->id,
69+
'account_id' => $expense->id,
70+
'description' => 'Office supplies expense',
71+
'debit' => 1200.00,
72+
'credit' => 0.00,
73+
]);
74+
75+
JournalEntryLine::create([
76+
'journal_entry_id' => $entry2->id,
77+
'account_id' => $cash->id,
78+
'description' => 'Cash payment',
79+
'debit' => 0.00,
80+
'credit' => 1200.00,
81+
]);
82+
}
83+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
namespace Database\Seeders;
4+
5+
use App\Modules\Appointments\Models\Appointment;
6+
use App\Modules\Appointments\Models\AppointmentSlot;
7+
use App\Modules\Appointments\Models\AppointmentType;
8+
use App\Modules\Core\Models\Tenant;
9+
use Illuminate\Database\Seeder;
10+
11+
class AppointmentsSeeder extends Seeder
12+
{
13+
public function run(): void
14+
{
15+
$tenant = Tenant::first();
16+
17+
if (! $tenant) {
18+
return;
19+
}
20+
21+
// Appointment Types
22+
$consultation = AppointmentType::create([
23+
'tenant_id' => $tenant->id,
24+
'name' => 'Initial Consultation',
25+
'description' => 'First meeting with a new client to assess needs',
26+
'duration_minutes' => 60,
27+
'location' => 'Meeting Room A',
28+
'max_capacity' => 1,
29+
'is_active' => true,
30+
'color' => '#3B82F6',
31+
]);
32+
33+
$followUp = AppointmentType::create([
34+
'tenant_id' => $tenant->id,
35+
'name' => 'Follow-Up Session',
36+
'description' => 'Ongoing support session for existing clients',
37+
'duration_minutes' => 30,
38+
'location' => 'Meeting Room B',
39+
'max_capacity' => 1,
40+
'is_active' => true,
41+
'color' => '#10B981',
42+
]);
43+
44+
// Appointment Slots
45+
$slot1 = AppointmentSlot::create([
46+
'tenant_id' => $tenant->id,
47+
'appointment_type_id' => $consultation->id,
48+
'start_at' => '2026-07-01 09:00:00',
49+
'end_at' => '2026-07-01 10:00:00',
50+
'capacity' => 1,
51+
'booked_count' => 1,
52+
'is_available' => false,
53+
]);
54+
55+
$slot2 = AppointmentSlot::create([
56+
'tenant_id' => $tenant->id,
57+
'appointment_type_id' => $consultation->id,
58+
'start_at' => '2026-07-02 14:00:00',
59+
'end_at' => '2026-07-02 15:00:00',
60+
'capacity' => 1,
61+
'booked_count' => 0,
62+
'is_available' => true,
63+
]);
64+
65+
$slot3 = AppointmentSlot::create([
66+
'tenant_id' => $tenant->id,
67+
'appointment_type_id' => $followUp->id,
68+
'start_at' => '2026-07-03 11:00:00',
69+
'end_at' => '2026-07-03 11:30:00',
70+
'capacity' => 2,
71+
'booked_count' => 1,
72+
'is_available' => true,
73+
]);
74+
75+
// Appointments
76+
Appointment::create([
77+
'tenant_id' => $tenant->id,
78+
'appointment_slot_id' => $slot1->id,
79+
'appointment_type_id' => $consultation->id,
80+
'customer_name' => 'Sarah Johnson',
81+
'customer_email' => 'sarah.johnson@example.com',
82+
'customer_phone' => '+1-555-0101',
83+
'notes' => 'Referred by existing client. Interested in premium tier.',
84+
'status' => 'confirmed',
85+
'confirmed_at' => '2026-06-28 10:00:00',
86+
]);
87+
88+
Appointment::create([
89+
'tenant_id' => $tenant->id,
90+
'appointment_slot_id' => $slot3->id,
91+
'appointment_type_id' => $followUp->id,
92+
'customer_name' => 'Michael Torres',
93+
'customer_email' => 'michael.torres@example.com',
94+
'customer_phone' => '+1-555-0202',
95+
'notes' => 'Monthly check-in.',
96+
'status' => 'pending',
97+
]);
98+
}
99+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Database\Seeders;
4+
5+
use App\Modules\Approvals\Models\ApprovalRequest;
6+
use App\Modules\Core\Models\Tenant;
7+
use Illuminate\Database\Seeder;
8+
9+
class ApprovalsSeeder extends Seeder
10+
{
11+
public function run(): void
12+
{
13+
$tenant = Tenant::first();
14+
15+
if (! $tenant) {
16+
return;
17+
}
18+
19+
// Pending purchase order approval
20+
ApprovalRequest::create([
21+
'tenant_id' => $tenant->id,
22+
'entity_type' => 'purchase_order',
23+
'entity_id' => 1,
24+
'entity_title' => 'PO-2026-001 — Office Equipment ($3,500)',
25+
'status' => 'pending',
26+
'current_step' => 1,
27+
'total_steps' => 2,
28+
]);
29+
30+
// Approved expense claim
31+
ApprovalRequest::create([
32+
'tenant_id' => $tenant->id,
33+
'entity_type' => 'expense_claim',
34+
'entity_id' => 2,
35+
'entity_title' => 'EXP-2026-015 — Travel & Accommodation ($850)',
36+
'status' => 'approved',
37+
'current_step' => 1,
38+
'total_steps' => 1,
39+
'approved_at' => now()->subDays(3),
40+
]);
41+
42+
// Rejected leave request
43+
ApprovalRequest::create([
44+
'tenant_id' => $tenant->id,
45+
'entity_type' => 'leave_request',
46+
'entity_id' => 5,
47+
'entity_title' => 'Annual Leave — Jane Smith (5 days)',
48+
'status' => 'rejected',
49+
'current_step' => 1,
50+
'total_steps' => 1,
51+
'rejected_at' => now()->subDays(1),
52+
'rejection_reason' => 'Insufficient leave balance for the requested period.',
53+
]);
54+
}
55+
}

erp/database/seeders/CrmSeeder.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace Database\Seeders;
4+
5+
use App\Modules\Core\Models\Tenant;
6+
use App\Modules\CRM\Models\CrmLead;
7+
use Illuminate\Database\Seeder;
8+
9+
class CrmSeeder extends Seeder
10+
{
11+
public function run(): void
12+
{
13+
$tenant = Tenant::first();
14+
15+
if (! $tenant) {
16+
return;
17+
}
18+
19+
// Open lead
20+
CrmLead::create([
21+
'tenant_id' => $tenant->id,
22+
'title' => 'Enterprise Software Suite Inquiry',
23+
'type' => 'lead',
24+
'contact_name' => 'Alice Mercer',
25+
'company_name' => 'BlueSky Technologies',
26+
'email' => 'alice.mercer@bluesky.example',
27+
'phone' => '+1-555-0301',
28+
'source' => 'website',
29+
'expected_revenue' => 24000.00,
30+
'probability' => 25,
31+
'expected_close_date' => '2026-09-30',
32+
'priority' => 'high',
33+
'status' => 'open',
34+
'description' => 'Prospective client interested in the full ERP suite for a 50-person team.',
35+
]);
36+
37+
// Opportunity — won
38+
CrmLead::create([
39+
'tenant_id' => $tenant->id,
40+
'title' => 'Annual Support Contract Renewal',
41+
'type' => 'opportunity',
42+
'contact_name' => 'David Okafor',
43+
'company_name' => 'Pinnacle Logistics',
44+
'email' => 'david.okafor@pinnacle.example',
45+
'phone' => '+1-555-0402',
46+
'source' => 'referral',
47+
'expected_revenue' => 8500.00,
48+
'probability' => 100,
49+
'expected_close_date' => '2026-06-30',
50+
'priority' => 'normal',
51+
'status' => 'won',
52+
'won_at' => now()->subDays(5),
53+
]);
54+
55+
// Lead — lost
56+
CrmLead::create([
57+
'tenant_id' => $tenant->id,
58+
'title' => 'Payroll Module Standalone License',
59+
'type' => 'lead',
60+
'contact_name' => 'Rachel Kim',
61+
'company_name' => 'Sunrise Retail Group',
62+
'email' => 'rachel.kim@sunrise.example',
63+
'source' => 'trade_show',
64+
'expected_revenue' => 3200.00,
65+
'probability' => 0,
66+
'priority' => 'low',
67+
'status' => 'lost',
68+
'lost_reason' => 'Prospect chose a competitor with a lower price point.',
69+
'lost_at' => now()->subWeeks(2),
70+
]);
71+
}
72+
}

erp/database/seeders/DatabaseSeeder.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,36 @@ public function run(): void
3030
$this->call(InventorySeeder::class);
3131
$this->call(FinanceSeeder::class);
3232
$this->call(HRSeeder::class);
33+
$this->call(AccountingSeeder::class);
34+
$this->call(PurchaseSeeder::class);
35+
$this->call(CrmSeeder::class);
36+
$this->call(ManufacturingSeeder::class);
37+
$this->call(PmSeeder::class);
38+
$this->call(MaintenanceSeeder::class);
39+
$this->call(FleetSeeder::class);
40+
$this->call(RentalSeeder::class);
41+
$this->call(RepairsSeeder::class);
42+
$this->call(QualityControlSeeder::class);
43+
$this->call(SubcontractingSeeder::class);
44+
$this->call(MarketingSeeder::class);
45+
$this->call(EcommerceSeeder::class);
46+
$this->call(SubscriptionsSeeder::class);
47+
$this->call(AppointmentsSeeder::class);
48+
$this->call(ApprovalsSeeder::class);
49+
$this->call(DiscussSeeder::class);
50+
$this->call(DocumentsSeeder::class);
51+
$this->call(EventsSeeder::class);
52+
$this->call(FieldServiceSeeder::class);
53+
$this->call(FrontdeskSeeder::class);
54+
$this->call(HelpdeskSeeder::class);
55+
$this->call(KnowledgeBaseSeeder::class);
56+
$this->call(LiveChatSeeder::class);
57+
$this->call(LunchSeeder::class);
58+
$this->call(PlanningSeeder::class);
59+
$this->call(PosSeeder::class);
60+
$this->call(SignSeeder::class);
61+
$this->call(SocialMarketingSeeder::class);
62+
$this->call(SurveySeeder::class);
63+
$this->call(WebsiteSeeder::class);
3364
}
3465
}

0 commit comments

Comments
 (0)