dstpabuaran.com/tests/Feature/Admin/NotificationTest.php
Yoga Pangestu 0023309a8f Refactor services to improve role checks and streamline data retrieval
- Updated CustomerService to simplify getAll method.
- Refactored ProductService to utilize HasRoleChecks trait and improved role verification logic.
- Enhanced ProductVariantService with new methods for fetching data for restocking and transactions.
- Cleaned up RawMaterialService by removing unused methods and improving data retrieval.
- Adjusted SupplierService to streamline getAll method.
- Refactored RoleService to use Spatie's Role model and improved role filtering logic.
- Updated NotificationService to handle role labels more effectively.
- Improved StockMutationService by removing redundant paginated method.
- Cleaned up various frontend components to directly accept necessary props instead of nested data objects.
- Updated tests to reflect changes in service method names and ensure proper notification handling.
2026-08-09 11:33:25 +07:00

889 lines
28 KiB
PHP

<?php
use App\Models\Attendance;
use App\Models\CashAccount;
use App\Models\Employee;
use App\Models\Expense;
use App\Models\Payroll;
use App\Models\PayrollPeriod;
use App\Models\Product;
use App\Models\User;
use App\Notifications\WebPushNotification;
use App\Services\Admin\Finance\Cash\CashTransactionService;
use App\Services\Admin\Finance\EmployeeAdvanceService;
use App\Services\Admin\Finance\ExpenseService;
use App\Services\Admin\Finance\Payroll\PayrollPeriodService;
use App\Services\Admin\HR\AttendanceService;
use App\Services\Admin\HR\LeaveRequestService;
use App\Services\Admin\Master\Product\ProductService;
use App\Services\NotificationService;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Spatie\Permission\PermissionRegistrar;
uses(RefreshDatabase::class);
beforeEach(function () {
app()[PermissionRegistrar::class]->forgetCachedPermissions();
$this->seed(RolePermissionSeeder::class);
});
/*
|--------------------------------------------------------------------------
| HELPERS
|--------------------------------------------------------------------------
*/
function notifUserWithRole(string $role, bool $active = true): User
{
$user = User::factory()->create(['is_active' => $active]);
$user->assignRole($role);
return $user;
}
function notifEmployeeWithRole(string $role = 'Kasir'): array
{
$user = notifUserWithRole($role);
$employee = Employee::factory()->create(['user_id' => $user->id]);
return ['user' => $user, 'employee' => $employee];
}
/*
|--------------------------------------------------------------------------
| NOTIFICATION SERVICE — UNIT TESTS
|--------------------------------------------------------------------------
*/
test('NotificationService creates in-app notification for users with matching role', function () {
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
notifUserWithRole('Kasir'); // should NOT receive
NotificationService::notify(
roles: ['Owner', 'Developer'],
title: 'Test Title',
body: 'Test Body.',
url: '/test-url'
);
expect($developer->notifications()->count())->toBe(1);
expect($owner->notifications()->count())->toBe(1);
$notification = $developer->notifications()->first();
expect($notification->title)->toBe('Test Title')
->and($notification->body)->toBe('Test Body.')
->and($notification->url)->toBe('/test-url')
->and($notification->is_read)->toBeFalse();
});
test('NotificationService does not notify inactive users', function () {
notifUserWithRole('Developer', active: false);
$active = notifUserWithRole('Developer');
NotificationService::notify(
roles: ['Developer'],
title: 'Test',
body: 'Body.',
url: '/test'
);
expect($active->notifications()->count())->toBe(1);
expect(User::where('is_active', false)->first()->notifications()->count())->toBe(0);
});
test('NotificationService dispatches WebPushNotification to each user', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
NotificationService::notify(
roles: ['Developer', 'Owner'],
title: 'Push Title',
body: 'Push Body.',
url: '/push'
);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
});
test('NotificationService includes additionalUser when not already in role-based list', function () {
$kasir = notifUserWithRole('Kasir');
$developer = notifUserWithRole('Developer');
NotificationService::notify(
roles: ['Developer'],
title: 'Title',
body: 'Body.',
url: '/url',
additionalUser: $kasir
);
expect($kasir->notifications()->count())->toBe(1);
expect($developer->notifications()->count())->toBe(1);
});
test('NotificationService does not duplicate additionalUser already in role list', function () {
$developer = notifUserWithRole('Developer');
NotificationService::notify(
roles: ['Developer'],
title: 'Title',
body: 'Body.',
url: '/url',
additionalUser: $developer
);
expect($developer->notifications()->count())->toBe(1);
});
test('NotificationService handles null additionalUser', function () {
$developer = notifUserWithRole('Developer');
NotificationService::notify(
roles: ['Developer'],
title: 'Title',
body: 'Body.',
url: '/url',
additionalUser: null
);
expect($developer->notifications()->count())->toBe(1);
});
test('NotificationService notifies multiple users across multiple roles', function () {
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$direktur = notifUserWithRole('Direktur');
$adminToko = notifUserWithRole('Admin Toko');
notifUserWithRole('Kasir'); // should NOT receive
NotificationService::notify(
roles: ['Owner', 'Developer', 'Direktur', 'Admin Toko'],
title: 'Multi Role',
body: 'Body.',
url: '/multi'
);
expect($developer->notifications()->count())->toBe(1);
expect($owner->notifications()->count())->toBe(1);
expect($direktur->notifications()->count())->toBe(1);
expect($adminToko->notifications()->count())->toBe(1);
});
/*
|--------------------------------------------------------------------------
| PRODUCT NOTIFICATIONS
|--------------------------------------------------------------------------
*/
test('ProductService create sends notification to correct roles', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$kasir = notifUserWithRole('Kasir');
$this->actingAs($developer);
$service = new ProductService;
$product = $service->create([
'name' => 'Test Product',
'description' => 'A test product',
'status' => 'active',
'category_ids' => [],
'use_same_price' => false,
'variants' => [
[
'name' => 'Default',
'stock' => 10,
'reject_stock' => 0,
'retail_stock' => 5,
'prices' => [
['type' => 'retail', 'price' => 50000],
],
],
],
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertNothingSentTo($kasir);
expect($developer->notifications()->count())->toBe(1);
expect($developer->notifications()->first()->title)->toBe('Produk Baru');
});
test('ProductService update sends notification to correct roles', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$adminToko = notifUserWithRole('Admin Toko');
$this->actingAs($developer);
$service = new ProductService;
$product = $service->create([
'name' => 'Original Product',
'description' => 'Desc',
'status' => 'active',
'category_ids' => [],
'use_same_price' => false,
'variants' => [
[
'name' => 'Var 1',
'stock' => 10,
'reject_stock' => 0,
'retail_stock' => 5,
'prices' => [
['type' => 'retail', 'price' => 50000],
],
],
],
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::fake();
$product = $service->update($product, [
'name' => 'Updated Product',
'description' => 'Desc updated',
'status' => 'active',
'category_ids' => [],
'use_same_price' => false,
'variants' => [
[
'id' => $product->productVariants->first()->id,
'name' => 'Var 1',
'stock' => 20,
'reject_stock' => 0,
'retail_stock' => 10,
'prices' => [
['type' => 'retail', 'price' => 60000],
],
],
],
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertSentTo($adminToko, WebPushNotification::class);
});
/*
|--------------------------------------------------------------------------
| CASH ACCOUNT NOTIFICATIONS
|--------------------------------------------------------------------------
*/
test('CashTransactionService deposit sends notification to correct roles', function () {
Notification::fake();
$cashAccount = CashAccount::factory()->create(['balance' => 1000000]);
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$kasir = notifUserWithRole('Kasir');
$this->actingAs($developer);
$service = new CashTransactionService;
$service->deposit([
'amount' => 500000,
'description' => 'Setoran modal',
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertNothingSentTo($kasir);
expect($developer->notifications()->first()->title)->toBe('Setoran Kas Toko');
});
test('CashTransactionService withdrawal sends notification to correct roles', function () {
Notification::fake();
$cashAccount = CashAccount::factory()->create(['balance' => 1000000]);
$developer = notifUserWithRole('Developer');
$direktur = notifUserWithRole('Direktur');
$this->actingAs($developer);
$service = new CashTransactionService;
$service->withdrawal([
'amount' => 200000,
'description' => 'Penarikan kas',
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($direktur, WebPushNotification::class);
expect($developer->notifications()->first()->title)->toBe('Penarikan Kas Toko');
});
test('CashTransactionService update sends notification to correct roles', function () {
Notification::fake();
$cashAccount = CashAccount::factory()->create(['balance' => 1000000]);
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$this->actingAs($developer);
$service = new CashTransactionService;
$transaction = $service->deposit([
'amount' => 500000,
'description' => 'Initial deposit',
]);
Notification::fake();
$service->update($transaction, [
'amount' => 600000,
'description' => 'Updated deposit',
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
expect($developer->notifications()->where('title', 'Transaksi Kas Diperbarui')->count())->toBe(1);
});
test('CashTransactionService destroy sends notification to correct roles', function () {
Notification::fake();
$cashAccount = CashAccount::factory()->create(['balance' => 1000000]);
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$this->actingAs($developer);
$service = new CashTransactionService;
$transaction = $service->deposit([
'amount' => 500000,
'description' => 'To be deleted',
]);
Notification::fake();
$service->destroy($transaction);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
expect($developer->notifications()->where('title', 'Transaksi Kas Dihapus')->count())->toBe(1);
});
/*
|--------------------------------------------------------------------------
| EXPENSE NOTIFICATIONS
|--------------------------------------------------------------------------
*/
test('ExpenseService create sends notification to correct roles', function () {
Notification::fake();
CashAccount::factory()->create(['balance' => 5000000]);
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$kasir = notifUserWithRole('Kasir');
$this->actingAs($developer);
$service = new ExpenseService;
$expense = $service->create([
'amount' => 100000,
'description' => 'Office supplies',
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertNothingSentTo($kasir);
expect($developer->notifications()->first()->title)->toBe('Pengeluaran Baru');
});
test('ExpenseService update sends notification to correct roles', function () {
Notification::fake();
CashAccount::factory()->create(['balance' => 5000000]);
$developer = notifUserWithRole('Developer');
$adminToko = notifUserWithRole('Admin Toko');
$this->actingAs($developer);
$service = new ExpenseService;
$expense = $service->create([
'amount' => 100000,
'description' => 'Original',
]);
Notification::fake();
$service->update($expense, [
'amount' => 150000,
'description' => 'Updated expense',
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($adminToko, WebPushNotification::class);
expect($developer->notifications()->where('title', 'Pengeluaran Diperbarui')->count())->toBe(1);
});
test('ExpenseService delete sends notification to correct roles', function () {
Notification::fake();
CashAccount::factory()->create(['balance' => 5000000]);
$developer = notifUserWithRole('Developer');
$direktur = notifUserWithRole('Direktur');
$this->actingAs($developer);
$service = new ExpenseService;
$expense = $service->create([
'amount' => 100000,
'description' => 'To delete',
]);
Notification::fake();
$service->delete($expense);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($direktur, WebPushNotification::class);
expect($developer->notifications()->where('title', 'Pengeluaran Dihapus')->count())->toBe(1);
});
/*
|--------------------------------------------------------------------------
| EMPLOYEE ADVANCE (KASBON) NOTIFICATIONS
|--------------------------------------------------------------------------
*/
test('EmployeeAdvanceService create sends notification including the requesting employee', function () {
Notification::fake();
CashAccount::factory()->create(['balance' => 5000000]);
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$kasbonData = notifEmployeeWithRole('Kasir');
$employee = $kasbonData['employee'];
$kasbonUser = $kasbonData['user'];
$this->actingAs($kasbonUser);
$service = new EmployeeAdvanceService;
$advance = $service->create([
'amount' => 500000,
'description' => 'Emergency need',
'due_date' => now()->addDays(30)->toDateString(),
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertSentTo($kasbonUser, WebPushNotification::class);
expect($kasbonUser->notifications()->first()->title)->toBe('Kasbon Baru');
});
test('EmployeeAdvanceService approve sends notification including the employee', function () {
Notification::fake();
CashAccount::factory()->create(['balance' => 5000000]);
$developer = notifUserWithRole('Developer');
$kasbonData = notifEmployeeWithRole('Kasir');
$employee = $kasbonData['employee'];
$kasbonUser = $kasbonData['user'];
$this->actingAs($kasbonUser);
$service = new EmployeeAdvanceService;
$advance = $service->create([
'amount' => 300000,
'description' => 'Advance',
'due_date' => now()->addDays(14)->toDateString(),
]);
Notification::fake();
$admin = notifUserWithRole('Admin Toko');
$this->actingAs($admin);
$service->approve($advance);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($kasbonUser, WebPushNotification::class);
expect($kasbonUser->notifications()->where('title', 'Kasbon Disetujui')->count())->toBe(1);
});
test('EmployeeAdvanceService pay sends notification including the employee', function () {
Notification::fake();
CashAccount::factory()->create(['balance' => 5000000]);
$developer = notifUserWithRole('Developer');
$kasbonData = notifEmployeeWithRole('Kasir');
$employee = $kasbonData['employee'];
$kasbonUser = $kasbonData['user'];
$this->actingAs($kasbonUser);
$service = new EmployeeAdvanceService;
$advance = $service->create([
'amount' => 200000,
'description' => 'To pay',
'due_date' => now()->addDays(7)->toDateString(),
]);
$admin = notifUserWithRole('Admin Toko');
$this->actingAs($admin);
$service->approve($advance);
Notification::fake();
$service->pay($advance);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($kasbonUser, WebPushNotification::class);
expect($kasbonUser->notifications()->where('title', 'Kasbon Dibayar')->count())->toBe(1);
});
/*
|--------------------------------------------------------------------------
| PAYROLL NOTIFICATIONS
|--------------------------------------------------------------------------
*/
test('PayrollPeriodService pay sends notification including the employee', function () {
Notification::fake();
CashAccount::factory()->create(['balance' => 50000000]);
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$employeeData = notifEmployeeWithRole('Kasir');
$employee = $employeeData['employee'];
$employeeUser = $employeeData['user'];
$period = PayrollPeriod::factory()->create(['year' => 2026, 'month' => 7, 'status' => 'open']);
$payroll = Payroll::factory()->create([
'payroll_period_id' => $period->id,
'employee_id' => $employee->id,
'base_salary' => 3000000,
'bonus_amount' => 0,
'deduction_amount' => 0,
'total_amount' => 3000000,
'status' => 'unpaid',
]);
$this->actingAs($developer);
$service = new PayrollPeriodService;
$service->pay($payroll);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertSentTo($employeeUser, WebPushNotification::class);
expect($employeeUser->notifications()->where('title', 'Gaji Dibayar')->count())->toBe(1);
});
test('PayrollPeriodService cancel sends notification including the employee', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$direktur = notifUserWithRole('Direktur');
$employeeData = notifEmployeeWithRole('Kasir');
$employee = $employeeData['employee'];
$employeeUser = $employeeData['user'];
$period = PayrollPeriod::factory()->create(['year' => 2026, 'month' => 8, 'status' => 'open']);
$payroll = Payroll::factory()->create([
'payroll_period_id' => $period->id,
'employee_id' => $employee->id,
'base_salary' => 3000000,
'bonus_amount' => 0,
'deduction_amount' => 0,
'total_amount' => 3000000,
'status' => 'unpaid',
]);
$this->actingAs($developer);
$service = new PayrollPeriodService;
$service->cancel($payroll);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($direktur, WebPushNotification::class);
Notification::assertSentTo($employeeUser, WebPushNotification::class);
expect($employeeUser->notifications()->where('title', 'Gaji Dibatalkan')->count())->toBe(1);
});
/*
|--------------------------------------------------------------------------
| LEAVE REQUEST NOTIFICATIONS
|--------------------------------------------------------------------------
*/
test('LeaveRequestService create sends notification including the requesting employee', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$leaveData = notifEmployeeWithRole('Kasir');
$employee = $leaveData['employee'];
$leaveUser = $leaveData['user'];
$this->actingAs($leaveUser);
$service = new LeaveRequestService;
$leaveRequest = $service->create([
'start_date' => now()->addDays(5)->toDateString(),
'end_date' => now()->addDays(7)->toDateString(),
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertSentTo($leaveUser, WebPushNotification::class);
expect($leaveUser->notifications()->first()->title)->toBe('Pengajuan Cuti Baru');
});
test('LeaveRequestService approve sends notification including the employee', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$leaveData = notifEmployeeWithRole('Kasir');
$employee = $leaveData['employee'];
$leaveUser = $leaveData['user'];
$this->actingAs($leaveUser);
$service = new LeaveRequestService;
$leaveRequest = $service->create([
'start_date' => now()->addDays(10)->toDateString(),
'end_date' => now()->addDays(12)->toDateString(),
]);
Notification::fake();
$admin = notifUserWithRole('Admin Toko');
$this->actingAs($admin);
$service->approve($leaveRequest);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($leaveUser, WebPushNotification::class);
expect($leaveUser->notifications()->where('title', 'Cuti Disetujui')->count())->toBe(1);
});
test('LeaveRequestService reject sends notification including the employee', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$leaveData = notifEmployeeWithRole('Kasir');
$employee = $leaveData['employee'];
$leaveUser = $leaveData['user'];
$this->actingAs($leaveUser);
$service = new LeaveRequestService;
$leaveRequest = $service->create([
'start_date' => now()->addDays(15)->toDateString(),
'end_date' => now()->addDays(17)->toDateString(),
]);
Notification::fake();
$admin = notifUserWithRole('Admin Toko');
$this->actingAs($admin);
$service->reject($leaveRequest);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($leaveUser, WebPushNotification::class);
expect($leaveUser->notifications()->where('title', 'Cuti Ditolak')->count())->toBe(1);
});
/*
|--------------------------------------------------------------------------
| ATTENDANCE NOTIFICATIONS
|--------------------------------------------------------------------------
*/
test('AttendanceService checkIn sends notification to Developer, Owner, Direktur', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$direktur = notifUserWithRole('Direktur');
$adminToko = notifUserWithRole('Admin Toko');
$attData = notifEmployeeWithRole('Kasir');
$employee = $attData['employee'];
$attendant = $attData['user'];
$this->actingAs($attendant);
$service = new AttendanceService;
$attendance = $service->checkIn([
'latitude' => -6.200000,
'longitude' => 106.816666,
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertSentTo($direktur, WebPushNotification::class);
Notification::assertNothingSentTo($adminToko);
expect($developer->notifications()->first()->title)->toBe('Presensi Masuk');
});
test('AttendanceService checkOut sends notification to Developer, Owner, Direktur', function () {
Notification::fake();
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
$direktur = notifUserWithRole('Direktur');
$adminToko = notifUserWithRole('Admin Toko');
$attData = notifEmployeeWithRole('Kasir');
$employee = $attData['employee'];
$attendant = $attData['user'];
$this->actingAs($attendant);
$service = new AttendanceService;
$attendance = $service->checkIn([
'latitude' => -6.200000,
'longitude' => 106.816666,
]);
Notification::fake();
$attendance = $service->checkOut($attendance, [
'latitude' => -6.200100,
'longitude' => 106.816700,
]);
Notification::assertSentTo($developer, WebPushNotification::class);
Notification::assertSentTo($owner, WebPushNotification::class);
Notification::assertSentTo($direktur, WebPushNotification::class);
Notification::assertNothingSentTo($adminToko);
expect($developer->notifications()->where('title', 'Presensi Pulang')->count())->toBe(1);
});
/*
|--------------------------------------------------------------------------
| IN-APP NOTIFICATION RECORDS
|--------------------------------------------------------------------------
*/
test('services create in-app notification records in database', function () {
$developer = notifUserWithRole('Developer');
$owner = notifUserWithRole('Owner');
NotificationService::notify(
roles: ['Developer', 'Owner'],
title: 'DB Test',
body: 'Body here.',
url: '/db-test'
);
$this->assertDatabaseHas('notifications', [
'user_id' => $developer->id,
'title' => 'DB Test',
'body' => 'Body here.',
'url' => '/db-test',
'is_read' => false,
]);
$this->assertDatabaseHas('notifications', [
'user_id' => $owner->id,
'title' => 'DB Test',
]);
});
test('ProductService create creates in-app notification record', function () {
CashAccount::factory()->create(['balance' => 1000000]);
$developer = notifUserWithRole('Developer');
$this->actingAs($developer);
$service = new ProductService;
$product = $service->create([
'name' => 'DB Notification Product',
'description' => 'Test',
'status' => 'active',
'category_ids' => [],
'use_same_price' => false,
'variants' => [
[
'name' => 'Var',
'stock' => 10,
'reject_stock' => 0,
'retail_stock' => 5,
'prices' => [
['type' => 'retail', 'price' => 50000],
],
],
],
]);
$this->assertDatabaseHas('notifications', [
'user_id' => $developer->id,
'title' => 'Produk Baru',
]);
});
test('CashTransactionService deposit creates in-app notification record', function () {
CashAccount::factory()->create(['balance' => 5000000]);
$owner = notifUserWithRole('Owner');
$this->actingAs($owner);
$service = new CashTransactionService;
$service->deposit([
'amount' => 250000,
'description' => 'Test deposit',
]);
$this->assertDatabaseHas('notifications', [
'user_id' => $owner->id,
'title' => 'Setoran Kas Toko',
]);
});
test('AttendanceService checkIn creates in-app notification record', function () {
$developer = notifUserWithRole('Developer');
$attData = notifEmployeeWithRole('Kasir');
$employee = $attData['employee'];
$attendant = $attData['user'];
$this->actingAs($attendant);
$service = new AttendanceService;
$service->checkIn([
'latitude' => -6.2,
'longitude' => 106.8,
]);
$this->assertDatabaseHas('notifications', [
'user_id' => $developer->id,
'title' => 'Presensi Masuk',
]);
});