bootTenant(); $user = $this->makeMaster(); EmployeeProfile::create([ 'user_id' => $user->id, 'base_salary' => 5000, 'works_pct' => 30, // 30% from done work revenue 'parts_pct' => 10, // 10% from parts margin ]); $client = Client::create(['name' => 'C', 'phone' => '+37399100100', 'type' => 'individual', 'status' => 'active']); $vehicle = Vehicle::create(['client_id' => $client->id, 'make' => 'X', 'model' => 'Y', 'plate' => 'PR-1']); $wo = WorkOrder::create([ 'number' => WorkOrder::generateNumber($ctx['company']->id), 'client_id' => $client->id, 'vehicle_id' => $vehicle->id, 'opened_at' => today()->subDays(3), 'status' => 'in_work', 'master_id' => $user->id, ]); // A done labor: 2h × 400 = 800 → 30% = 240 WorkOrderWork::create([ 'work_order_id' => $wo->id, 'master_id' => $user->id, 'name' => 'X', 'hours' => 2, 'price_per_hour' => 400, 'status' => 'done', ]); // An installed part: sell 350 - buy 200 = 150 margin × 1 qty → 10% = 15 WorkOrderPart::create([ 'work_order_id' => $wo->id, 'name' => 'P', 'qty' => 1, 'sell_price' => 350, 'buy_price' => 200, 'status' => 'installed', ]); // Adjustments for the period PayrollAdjustment::create([ 'user_id' => $user->id, 'period' => '2026-06', 'type' => 'bonus', 'amount' => 500, 'date' => today(), ]); PayrollAdjustment::create([ 'user_id' => $user->id, 'period' => '2026-06', 'type' => 'fine', 'amount' => 100, 'date' => today(), ]); PayrollAdjustment::create([ 'user_id' => $user->id, 'period' => '2026-06', 'type' => 'advance', 'amount' => 200, 'date' => today(), ]); $run = app(PayrollCalculator::class)->compute($user->id, '2026-06'); // base 5000 + works 240 + parts 15 + bonus 500 - fine 100 - advance 200 = 5455 $this->assertEquals(5455.0, (float) $run->total); $this->assertEquals(5000.0, (float) $run->base); $this->assertEquals(240.0, (float) $run->works_pct_amount); $this->assertEquals(15.0, (float) $run->parts_pct_amount); Carbon::setTestNow(); } public function test_compute_ignores_other_periods_and_other_users(): void { Carbon::setTestNow('2026-06-15'); $ctx = $this->bootTenant(); $me = $this->makeMaster(); $other = $this->makeMaster(); EmployeeProfile::create(['user_id' => $me->id, 'base_salary' => 1000, 'works_pct' => 100, 'parts_pct' => 0]); $c = Client::create(['name' => 'X', 'phone' => '+37300000000', 'type' => 'individual', 'status' => 'active']); $v = Vehicle::create(['client_id' => $c->id, 'make' => 'X', 'model' => 'Y', 'plate' => 'NOISE']); $wo = WorkOrder::create([ 'number' => WorkOrder::generateNumber($ctx['company']->id), 'client_id' => $c->id, 'vehicle_id' => $v->id, 'opened_at' => today(), 'status' => 'in_work', ]); // Another master's work — must be excluded. WorkOrderWork::create([ 'work_order_id' => $wo->id, 'master_id' => $other->id, 'name' => 'X', 'hours' => 5, 'price_per_hour' => 400, 'status' => 'done', ]); // Adjustment for a different period — must be excluded. PayrollAdjustment::create([ 'user_id' => $me->id, 'period' => '2026-05', 'type' => 'bonus', 'amount' => 9999, 'date' => today()->subMonth(), ]); $run = app(PayrollCalculator::class)->compute($me->id, '2026-06'); $this->assertEquals(1000.0, (float) $run->total, 'only base — no own works, no current-period adjustments'); Carbon::setTestNow(); } private function bootTenant(): array { $plan = Plan::firstOrCreate(['slug' => 'test'], ['name' => 'T', 'price' => 0, 'features' => []]); $company = Company::create([ 'plan_id' => $plan->id, 'slug' => 'pr-' . uniqid(), 'name' => 'Payroll Co', 'status' => 'active', ]); app(TenantManager::class)->setCurrent($company); return compact('company'); } private function makeMaster(): User { return User::create([ 'name' => 'M', 'email' => 'm-' . uniqid() . '@example.com', 'password' => bcrypt('x'), 'role' => 'master', 'status' => 'active', ]); } }