Files
autocrm/app/Services/PayrollCalculator.php
Vasyka 06696727dd Faza 6: Activity log + Kanban + Payroll + cleanup
══════ Activity log (Spatie) ══════
- spatie/laravel-activitylog v5 instalat
- Migration cu company_id pentru tenant scoping
- Trait Auditable (App\Models\Concerns\Auditable):
  - LogOptions cu logFillable + logOnlyDirty + dontSubmitEmptyLogs
  - tapActivity auto-fill company_id + causer
  - Descrieri RO (creat/modificat/șters/restaurat)
- Aplicat pe: Client, Vehicle, Lead, Deal, WorkOrder, Payment, Expense
- ActivityResource (group Admin → Jurnal activitate)
  - Listă read-only, scope pe tenant, filtre by description/today

══════ Kanban Work Orders ══════
- Custom Filament page la /app/kanban (group Service)
- 6 coloane (new → diagnosis → agreement → in_work → awaiting_parts → ready)
- Drag-drop nativ HTML5 cu wire:click moveCard()
- Cards arată: număr fișă, client, auto, plate, master, total
- Link 'Deschide' direct la editare WO

══════ Payroll (Salarii) ══════
Schema:
- employee_profiles: user_id, position, base_salary, works_pct, parts_pct
- payroll_runs: period (YYYY-MM), base, works_revenue/pct, parts_margin/pct,
  bonus, fines, advance, total auto-calculat
- payroll_adjustments: bonus/fine/advance per period

PayrollCalculator service:
- compute($userId, $period) — calculează auto:
  - Manopere finalizate de mecanic în luna respectivă (sum total)
  - Marja pieselor montate de el (sell-buy * qty)
  - Bonus + fines + advance from adjustments
  - Total = base + works% + parts% + bonus - fines - advance

Resources Filament (group Finanțe):
- EmployeeProfileResource: profil cu % comisioane
- PayrollRunResource: salarii cu action 'Calculează luna curentă' (toți userii)
  + per-row 'Recalculează'; Sum summary pe total
- PayrollAdjustmentResource: gestionare bonus/penalizări/avansuri

══════ Cleanup ══════
- Șterse toate /__debug, /__seed, /__try-login, /__force-login, /__whoami,
  /__coolify-check (security)
- Routes/web.php conține doar / redirect, /manifest.json, /sw.js

Total Filament tenant routes: 92.
2026-05-07 09:52:01 +00:00

80 lines
2.9 KiB
PHP

<?php
namespace App\Services;
use App\Models\Tenant\EmployeeProfile;
use App\Models\Tenant\PayrollAdjustment;
use App\Models\Tenant\PayrollRun;
use App\Models\Tenant\WorkOrderPart;
use App\Models\Tenant\WorkOrderWork;
use Carbon\Carbon;
class PayrollCalculator
{
/**
* Compute (or upsert) the payroll run for a given user + month.
* Period format: YYYY-MM.
*/
public function compute(int $userId, string $period, bool $persist = true): PayrollRun
{
$start = Carbon::createFromFormat('Y-m', $period)->startOfMonth();
$end = (clone $start)->endOfMonth();
$profile = EmployeeProfile::where('user_id', $userId)->first();
$base = (float) ($profile?->base_salary ?? 0);
$worksPct = (float) ($profile?->works_pct ?? 0);
$partsPct = (float) ($profile?->parts_pct ?? 0);
// Manopere finalizate de utilizator în perioadă.
$worksRevenue = (float) WorkOrderWork::where('master_id', $userId)
->where('status', 'done')
->whereBetween('updated_at', [$start, $end])
->sum('total');
$worksPctAmount = round($worksRevenue * $worksPct / 100, 2);
// Marja pe piesele montate de utilizator (nu există FK direct, aprox via work_order.master_id)
$partsMargin = (float) WorkOrderPart::where('status', 'installed')
->whereBetween('updated_at', [$start, $end])
->whereHas('workOrder', fn ($q) => $q->where('master_id', $userId))
->get()
->sum(fn ($p) => ((float) $p->sell_price - (float) $p->buy_price) * (float) $p->qty);
$partsPctAmount = round($partsMargin * $partsPct / 100, 2);
// Bonus / penalizări / avans pe perioadă.
$adjustments = PayrollAdjustment::where('user_id', $userId)
->where('period', $period)
->get();
$bonus = (float) $adjustments->where('type', 'bonus')->sum('amount');
$fines = (float) $adjustments->where('type', 'fine')->sum('amount');
$advance = (float) $adjustments->where('type', 'advance')->sum('amount');
$total = round(
$base + $worksPctAmount + $partsPctAmount + $bonus - $fines - $advance,
2
);
$attrs = [
'base' => $base,
'works_revenue' => $worksRevenue,
'works_pct_amount' => $worksPctAmount,
'parts_margin' => $partsMargin,
'parts_pct_amount' => $partsPctAmount,
'bonus' => $bonus,
'fines' => $fines,
'advance' => $advance,
'total' => max(0, $total),
];
if (! $persist) {
return new PayrollRun(['user_id' => $userId, 'period' => $period] + $attrs);
}
$run = PayrollRun::updateOrCreate(
['user_id' => $userId, 'period' => $period, 'company_id' => app(\App\Tenancy\TenantManager::class)->currentId()],
$attrs
);
return $run;
}
}