mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-10-14 16:53:17 +01:00
add billable rate restriction for free organizations
This commit is contained in:
committed by
Constantin Graf
parent
deb0c26e32
commit
5a9c8a8bf7
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Models\Organization;
|
||||
use App\Service\BillableRateService;
|
||||
use App\Service\DashboardService;
|
||||
use App\Service\PermissionStore;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
@@ -154,12 +154,12 @@ class ChartController extends Controller
|
||||
*
|
||||
* @response array{value: int, currency: string}
|
||||
*/
|
||||
public function totalWeeklyBillableAmount(Organization $organization, DashboardService $dashboardService): JsonResponse
|
||||
public function totalWeeklyBillableAmount(Organization $organization, DashboardService $dashboardService, BillableRateService $billableRateService): JsonResponse
|
||||
{
|
||||
$this->checkPermission($organization, 'charts:view:own');
|
||||
$user = $this->user();
|
||||
|
||||
$showBillableRate = $this->member($organization)->role !== Role::Employee->value || $organization->employees_can_see_billable_rates;
|
||||
$showBillableRate = $billableRateService->canSeeBillableRates($organization, $this->member($organization));
|
||||
if (! $showBillableRate) {
|
||||
throw new AuthorizationException('You do not have permission to view billable rates.');
|
||||
}
|
||||
|
||||
@@ -81,10 +81,9 @@ class MemberController extends Controller
|
||||
{
|
||||
$this->checkPermission($organization, 'members:update', $member);
|
||||
|
||||
if ($request->has('billable_rate') && $member->billable_rate !== $request->getBillableRate()) {
|
||||
$oldBillableRate = $member->billable_rate;
|
||||
if ($request->has('billable_rate')) {
|
||||
$member->billable_rate = $request->getBillableRate();
|
||||
|
||||
$billableRateService->updateTimeEntriesBillableRateForMember($member);
|
||||
}
|
||||
if ($request->has('role') && $member->role !== $request->getRole()->value) {
|
||||
$newRole = $request->getRole();
|
||||
@@ -93,6 +92,10 @@ class MemberController extends Controller
|
||||
}
|
||||
$member->save();
|
||||
|
||||
if ($oldBillableRate !== $member->billable_rate) {
|
||||
$billableRateService->updateTimeEntriesBillableRateForMember($member);
|
||||
}
|
||||
|
||||
return new MemberResource($member);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Events\AfterCreateOrganization;
|
||||
use App\Http\Requests\V1\Organization\OrganizationDestroyRequest;
|
||||
use App\Http\Requests\V1\Organization\OrganizationStoreRequest;
|
||||
@@ -28,11 +27,11 @@ class OrganizationController extends Controller
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function show(Organization $organization): OrganizationResource
|
||||
public function show(Organization $organization, BillableRateService $billableRateService): OrganizationResource
|
||||
{
|
||||
$this->checkPermission($organization, 'organizations:view');
|
||||
|
||||
$showBillableRate = $this->member($organization)->role !== Role::Employee->value || $organization->employees_can_see_billable_rates;
|
||||
$showBillableRate = $billableRateService->canSeeBillableRates($organization, $this->member($organization));
|
||||
|
||||
return new OrganizationResource($organization, $showBillableRate);
|
||||
}
|
||||
@@ -81,14 +80,13 @@ class OrganizationController extends Controller
|
||||
if ($request->getBreaksEnabled() !== null) {
|
||||
$organization->breaks_enabled = $request->getBreaksEnabled();
|
||||
}
|
||||
$hasBillableRate = $request->has('billable_rate');
|
||||
if ($hasBillableRate) {
|
||||
$oldBillableRate = $organization->billable_rate;
|
||||
$oldBillableRate = $organization->billable_rate;
|
||||
if ($request->has('billable_rate')) {
|
||||
$organization->billable_rate = $request->getBillableRate();
|
||||
}
|
||||
$organization->save();
|
||||
|
||||
if ($hasBillableRate && $oldBillableRate !== $request->getBillableRate()) {
|
||||
if ($oldBillableRate !== $organization->billable_rate) {
|
||||
$billableRateService->updateTimeEntriesBillableRateForOrganization($organization);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Exceptions\Api\EntityStillInUseApiException;
|
||||
use App\Http\Requests\V1\Project\ProjectIndexRequest;
|
||||
use App\Http\Requests\V1\Project\ProjectStoreRequest;
|
||||
@@ -39,7 +38,7 @@ class ProjectController extends Controller
|
||||
*
|
||||
* @operationId getProjects
|
||||
*/
|
||||
public function index(Organization $organization, ProjectIndexRequest $request): ProjectCollection
|
||||
public function index(Organization $organization, ProjectIndexRequest $request, BillableRateService $billableRateService): ProjectCollection
|
||||
{
|
||||
$this->checkPermission($organization, 'projects:view');
|
||||
$canViewAllProjects = $this->hasPermission($organization, 'projects:view:all');
|
||||
@@ -63,7 +62,7 @@ class ProjectController extends Controller
|
||||
->orderBy('id')
|
||||
->paginate(config('app.pagination_per_page_default'));
|
||||
|
||||
$showBillableRate = $this->member($organization)->role !== Role::Employee->value || $organization->employees_can_see_billable_rates;
|
||||
$showBillableRate = $billableRateService->canSeeBillableRates($organization, $this->member($organization));
|
||||
|
||||
return new ProjectCollection($projects, $showBillableRate);
|
||||
}
|
||||
@@ -137,14 +136,16 @@ class ProjectController extends Controller
|
||||
}
|
||||
$oldBillableRate = $project->billable_rate;
|
||||
$clientIdChanged = false;
|
||||
$project->billable_rate = $request->getBillableRate();
|
||||
if ($request->has('billable_rate')) {
|
||||
$project->billable_rate = $request->getBillableRate();
|
||||
}
|
||||
if ($project->client_id !== $request->input('client_id')) {
|
||||
$project->client_id = $request->input('client_id');
|
||||
$clientIdChanged = true;
|
||||
}
|
||||
$project->save();
|
||||
|
||||
if ($oldBillableRate !== $request->getBillableRate()) {
|
||||
if ($oldBillableRate !== $project->billable_rate) {
|
||||
$billableRateService->updateTimeEntriesBillableRateForProject($project);
|
||||
}
|
||||
if ($clientIdChanged) {
|
||||
|
||||
@@ -79,7 +79,7 @@ class ProjectMemberController extends Controller
|
||||
$projectMember->project()->associate($project);
|
||||
$projectMember->save();
|
||||
|
||||
if ($request->getBillableRate() !== null) {
|
||||
if ($projectMember->billable_rate !== null) {
|
||||
$billableRateService->updateTimeEntriesBillableRateForProjectMember($projectMember);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class ProjectMemberController extends Controller
|
||||
$projectMember->billable_rate = $request->getBillableRate();
|
||||
$projectMember->save();
|
||||
|
||||
if ($oldBillableRate !== $request->getBillableRate()) {
|
||||
if ($oldBillableRate !== $projectMember->billable_rate) {
|
||||
$billableRateService->updateTimeEntriesBillableRateForProjectMember($projectMember);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Enums\ExportFormat;
|
||||
use App\Enums\Role;
|
||||
use App\Enums\TimeEntryType;
|
||||
use App\Exceptions\Api\FeatureIsNotAvailableInFreePlanApiException;
|
||||
use App\Exceptions\Api\OverlappingTimeEntryApiException;
|
||||
@@ -30,6 +29,7 @@ use App\Models\Organization;
|
||||
use App\Models\Project;
|
||||
use App\Models\Task;
|
||||
use App\Models\TimeEntry;
|
||||
use App\Service\BillableRateService;
|
||||
use App\Service\LocalizationService;
|
||||
use App\Service\ReportExport\TimeEntriesDetailedCsvExport;
|
||||
use App\Service\ReportExport\TimeEntriesDetailedExport;
|
||||
@@ -229,7 +229,7 @@ class TimeEntryController extends Controller
|
||||
*
|
||||
* @operationId exportTimeEntries
|
||||
*/
|
||||
public function indexExport(Organization $organization, TimeEntryIndexExportRequest $request, TimeEntryAggregationService $timeEntryAggregationService): JsonResponse
|
||||
public function indexExport(Organization $organization, TimeEntryIndexExportRequest $request, TimeEntryAggregationService $timeEntryAggregationService, BillableRateService $billableRateService): JsonResponse
|
||||
{
|
||||
$member = $this->member($organization);
|
||||
/** @var Member|null $memberFilter */
|
||||
@@ -247,7 +247,7 @@ class TimeEntryController extends Controller
|
||||
}
|
||||
$user = $this->user();
|
||||
$timezone = $user->timezone;
|
||||
$showBillableRate = $this->member($organization)->role !== Role::Employee->value || $organization->employees_can_see_billable_rates;
|
||||
$showBillableRate = $billableRateService->canSeeBillableRates($organization, $this->member($organization));
|
||||
$roundingType = $canAccessPremiumFeatures ? $request->getRoundingType() : null;
|
||||
$roundingMinutes = $canAccessPremiumFeatures ? $request->getRoundingMinutes() : null;
|
||||
|
||||
@@ -386,7 +386,7 @@ class TimeEntryController extends Controller
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function aggregate(Organization $organization, TimeEntryAggregateRequest $request, TimeEntryAggregationService $timeEntryAggregationService): array
|
||||
public function aggregate(Organization $organization, TimeEntryAggregateRequest $request, TimeEntryAggregationService $timeEntryAggregationService, BillableRateService $billableRateService): array
|
||||
{
|
||||
$member = $this->member($organization);
|
||||
/** @var Member|null $memberFilter */
|
||||
@@ -398,7 +398,7 @@ class TimeEntryController extends Controller
|
||||
}
|
||||
$canAccessPremiumFeatures = $this->canAccessPremiumFeatures($organization);
|
||||
$user = $this->user();
|
||||
$showBillableRate = $this->member($organization)->role !== Role::Employee->value || $organization->employees_can_see_billable_rates;
|
||||
$showBillableRate = $billableRateService->canSeeBillableRates($organization, $this->member($organization));
|
||||
|
||||
$group1Type = $request->getGroup();
|
||||
$group2Type = $request->getSubGroup();
|
||||
@@ -436,7 +436,7 @@ class TimeEntryController extends Controller
|
||||
* @throws NoOutputFileInResponse
|
||||
* @throws FeatureIsNotAvailableInFreePlanApiException
|
||||
*/
|
||||
public function aggregateExport(Organization $organization, TimeEntryAggregateExportRequest $request, TimeEntryAggregationService $timeEntryAggregationService): JsonResponse
|
||||
public function aggregateExport(Organization $organization, TimeEntryAggregateExportRequest $request, TimeEntryAggregationService $timeEntryAggregationService, BillableRateService $billableRateService): JsonResponse
|
||||
{
|
||||
$member = $this->member($organization);
|
||||
/** @var Member|null $memberFilter */
|
||||
@@ -453,7 +453,7 @@ class TimeEntryController extends Controller
|
||||
}
|
||||
$debug = $request->getDebug();
|
||||
$user = $this->user();
|
||||
$showBillableRate = $this->member($organization)->role !== Role::Employee->value || $organization->employees_can_see_billable_rates;
|
||||
$showBillableRate = $billableRateService->canSeeBillableRates($organization, $this->member($organization));
|
||||
|
||||
$group = $request->getGroup();
|
||||
$subGroup = $request->getSubGroup();
|
||||
|
||||
@@ -60,6 +60,7 @@ class HandleInertiaRequests extends Middleware
|
||||
'trial_until' => $billing->getTrialUntil($currentOrganization)?->toIso8601ZuluString(),
|
||||
'is_blocked' => $billing->isBlocked($currentOrganization),
|
||||
'goal_limit' => $billing->getGoalLimit($currentOrganization),
|
||||
'can_use_billable_rates' => $billing->canUseBillableRates($currentOrganization),
|
||||
] : null,
|
||||
'flash' => [
|
||||
'message' => fn () => $request->session()->get('message'),
|
||||
|
||||
@@ -68,7 +68,7 @@ class ProjectUpdateRequest extends BaseFormRequest
|
||||
return $builder->whereBelongsTo($this->organization, 'organization');
|
||||
})->uuid(),
|
||||
],
|
||||
// Billable rate in cents per hour (example: 8000 means 80.00 in the organization's currency)
|
||||
// Billable rate in cents per hour (example: 8000 means 80.00 in the organization's currency), the current rate is kept if the field is omitted
|
||||
'billable_rate' => array_merge([
|
||||
'nullable',
|
||||
],
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Enums\NumberFormat;
|
||||
use App\Enums\TimeFormat;
|
||||
use App\Http\Resources\V1\BaseResource;
|
||||
use App\Models\Organization;
|
||||
use App\Service\BillingContract;
|
||||
use App\Service\CurrencyService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -51,6 +52,8 @@ class OrganizationResource extends BaseResource
|
||||
'is_personal' => $this->resource->personal_team,
|
||||
/** Billable rate in cents per hour */
|
||||
'billable_rate' => $this->showBillableRate ? $this->resource->billable_rate : null,
|
||||
/** Whether the organization's plan allows billable flags and rates */
|
||||
'can_use_billable_rates' => app(BillingContract::class)->canUseBillableRates($this->resource),
|
||||
/** Can members of the organization with role "employee" see the billable rates */
|
||||
'employees_can_see_billable_rates' => $this->resource->employees_can_see_billable_rates,
|
||||
/** Can members of the organization with role "employee" manage tasks in public projects and projects they are assigned to */
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Models;
|
||||
|
||||
use App\Models\Concerns\CustomAuditable;
|
||||
use App\Models\Concerns\HasUuids;
|
||||
use App\Service\BillingContract;
|
||||
use Database\Factories\MemberFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -48,6 +49,16 @@ class Member extends Pivot implements AuditableContract
|
||||
*/
|
||||
protected $table = 'members';
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (Member $member): void {
|
||||
// Organizations that can not use billable rates can not change the billable rate
|
||||
if ($member->billable_rate !== $member->getOriginal('billable_rate') && ! app(BillingContract::class)->canUseBillableRates($member->loadMissing('organization')->organization)) {
|
||||
$member->billable_rate = $member->getOriginal('billable_rate');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Enums\NumberFormat;
|
||||
use App\Enums\TimeFormat;
|
||||
use App\Models\Concerns\CustomAuditable;
|
||||
use App\Models\Concerns\HasUuids;
|
||||
use App\Service\BillingContract;
|
||||
use Database\Factories\OrganizationFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -96,6 +97,25 @@ class Organization extends Model implements AuditableContract
|
||||
protected $attributes = [
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (Organization $organization): void {
|
||||
// Organizations that can not use billable rates can keep or remove the visibility for employees, but not
|
||||
// enable it, and can not change the billable rate
|
||||
$enablesVisibility = $organization->employees_can_see_billable_rates && ! $organization->getOriginal('employees_can_see_billable_rates');
|
||||
if (! $enablesVisibility && $organization->billable_rate === $organization->getOriginal('billable_rate')) {
|
||||
return;
|
||||
}
|
||||
if (app(BillingContract::class)->canUseBillableRates($organization)) {
|
||||
return;
|
||||
}
|
||||
if ($enablesVisibility) {
|
||||
$organization->employees_can_see_billable_rates = false;
|
||||
}
|
||||
$organization->billable_rate = $organization->getOriginal('billable_rate');
|
||||
});
|
||||
}
|
||||
|
||||
public function getAuditOwnerOrganizationId(): ?string
|
||||
{
|
||||
return $this->getKey();
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Models;
|
||||
|
||||
use App\Models\Concerns\CustomAuditable;
|
||||
use App\Models\Concerns\HasUuids;
|
||||
use App\Service\BillingContract;
|
||||
use Database\Factories\ProjectFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
@@ -136,6 +137,25 @@ class Project extends Model implements AuditableContract
|
||||
return $this->scopeComputedAttributesGenerate($builder, $attributes);
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (Project $project): void {
|
||||
// Organizations that can not use billable rates can keep or remove the billable flag, but not set it,
|
||||
// and can not change the billable rate
|
||||
$enablesBillable = $project->is_billable && ! $project->getOriginal('is_billable');
|
||||
if (! $enablesBillable && $project->billable_rate === $project->getOriginal('billable_rate')) {
|
||||
return;
|
||||
}
|
||||
if (app(BillingContract::class)->canUseBillableRates($project->loadMissing('organization')->organization)) {
|
||||
return;
|
||||
}
|
||||
if ($enablesBillable) {
|
||||
$project->is_billable = false;
|
||||
}
|
||||
$project->billable_rate = $project->getOriginal('billable_rate');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Organization, $this>
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Models;
|
||||
use App\Models\Concerns\AuditableThroughParent;
|
||||
use App\Models\Concerns\CustomAuditable;
|
||||
use App\Models\Concerns\HasUuids;
|
||||
use App\Service\BillingContract;
|
||||
use Database\Factories\ProjectMemberFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -48,6 +49,16 @@ class ProjectMember extends Model implements AuditableContract, AuditableThrough
|
||||
'billable_rate' => 'int',
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (ProjectMember $projectMember): void {
|
||||
// Organizations that can not use billable rates can not change the billable rate
|
||||
if ($projectMember->billable_rate !== $projectMember->getOriginal('billable_rate') && ! app(BillingContract::class)->canUseBillableRates($projectMember->loadMissing('project.organization')->project->organization)) {
|
||||
$projectMember->billable_rate = $projectMember->getOriginal('billable_rate');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Project, $this>
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Enums\TimeEntryType;
|
||||
use App\Models\Concerns\CustomAuditable;
|
||||
use App\Models\Concerns\HasUuids;
|
||||
use App\Service\BillableRateService;
|
||||
use App\Service\BillingContract;
|
||||
use Carbon\CarbonInterval;
|
||||
use Database\Factories\TimeEntryFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -140,6 +141,12 @@ class TimeEntry extends Model implements AuditableContract
|
||||
$timeEntry->client_id = null;
|
||||
$timeEntry->tags = [];
|
||||
}
|
||||
// Organizations that can not use billable rates can keep or remove the billable flag, but not set it
|
||||
if ($timeEntry->billable && ! $timeEntry->getOriginal('billable') && ! app(BillingContract::class)->canUseBillableRates($timeEntry->loadMissing('organization')->organization)) {
|
||||
$timeEntry->billable = false;
|
||||
// Note: The billable rate is computed before saving, non-billable time entries have no billable rate
|
||||
$timeEntry->billable_rate = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Models\Member;
|
||||
use App\Models\Organization;
|
||||
use App\Models\Project;
|
||||
@@ -13,6 +14,24 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class BillableRateService
|
||||
{
|
||||
public function __construct(
|
||||
private BillingContract $billing,
|
||||
) {}
|
||||
|
||||
public function canUseBillableRates(Organization $organization): bool
|
||||
{
|
||||
return $this->billing->canUseBillableRates($organization);
|
||||
}
|
||||
|
||||
public function canSeeBillableRates(Organization $organization, Member $member): bool
|
||||
{
|
||||
if (! $this->canUseBillableRates($organization)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $member->role !== Role::Employee->value || $organization->employees_can_see_billable_rates;
|
||||
}
|
||||
|
||||
public function updateTimeEntriesBillableRateForProjectMember(ProjectMember $projectMember): void
|
||||
{
|
||||
TimeEntry::query()
|
||||
|
||||
@@ -63,4 +63,13 @@ class BillingContract
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the organization can set and see billable rates
|
||||
* Without a billing system every organization can use billable rates
|
||||
*/
|
||||
public function canUseBillableRates(Organization $organization): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class ClockifyTimeEntriesImporter extends DefaultImporter
|
||||
$timeEntry->task_id = $taskId;
|
||||
$timeEntry->project_id = $projectId;
|
||||
$timeEntry->client_id = $clientId;
|
||||
$timeEntry->organization_id = $this->organization->id;
|
||||
$timeEntry->organization()->associate($this->organization);
|
||||
if (strlen($record['Description']) > 5000) {
|
||||
throw new ImportException('Time entry description is too long');
|
||||
}
|
||||
|
||||
@@ -126,6 +126,8 @@ abstract class DefaultImporter implements ImporterContract
|
||||
if ($project->billable_rate === 0) {
|
||||
$project->billable_rate = null;
|
||||
}
|
||||
// Note: Prevents a query per project for the billable rate restriction (see Project::booted)
|
||||
$project->setRelation('organization', $this->organization);
|
||||
});
|
||||
$this->projectMemberImportHelper = new ImportDatabaseHelper(ProjectMember::class, ['project_id', 'member_id'], true, function (Builder $builder): Builder {
|
||||
/** @var Builder<ProjectMember> $builder */
|
||||
@@ -140,6 +142,11 @@ abstract class DefaultImporter implements ImporterContract
|
||||
if ($projectMember->billable_rate === 0) {
|
||||
$projectMember->billable_rate = null;
|
||||
}
|
||||
// Note: Prevents queries per project member for the billable rate restriction (see ProjectMember::booted)
|
||||
$project = $this->projectImportHelper->getModelById($projectMember->project_id);
|
||||
if ($project !== null) {
|
||||
$projectMember->setRelation('project', $project->setRelation('organization', $this->organization));
|
||||
}
|
||||
});
|
||||
$this->tagImportHelper = new ImportDatabaseHelper(Tag::class, ['name', 'organization_id'], true, function (Builder $builder): Builder {
|
||||
/** @var Builder<Tag> $builder */
|
||||
|
||||
@@ -129,7 +129,7 @@ class GenericTimeEntriesImporter extends DefaultImporter
|
||||
$timeEntry->task_id = $taskId;
|
||||
$timeEntry->project_id = $projectId;
|
||||
$timeEntry->client_id = $clientId;
|
||||
$timeEntry->organization_id = $this->organization->id;
|
||||
$timeEntry->organization()->associate($this->organization);
|
||||
$timeEntry->description = $record['description'];
|
||||
if (! in_array($record['billable'], ['true', 'false'], true)) {
|
||||
throw new ImportException('Invalid billable value');
|
||||
|
||||
@@ -107,7 +107,7 @@ class HarvestTimeEntriesImporter extends DefaultImporter
|
||||
$timeEntry->task_id = $taskId;
|
||||
$timeEntry->project_id = $projectId;
|
||||
$timeEntry->client_id = $clientId;
|
||||
$timeEntry->organization_id = $this->organization->id;
|
||||
$timeEntry->organization()->associate($this->organization);
|
||||
if (strlen($record['Notes']) > 5000) {
|
||||
throw new ImportException('Time entry note is too long');
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ class SolidtimeImporter extends DefaultImporter
|
||||
$timeEntry->task_id = $taskId;
|
||||
$timeEntry->project_id = $projectId;
|
||||
$timeEntry->client_id = $clientId;
|
||||
$timeEntry->organization_id = $this->organization->id;
|
||||
$timeEntry->organization()->associate($this->organization);
|
||||
if (strlen($timeEntryRow['description']) > 5000) {
|
||||
throw new ImportException('Time entry description is too long');
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class TogglTimeEntriesImporter extends DefaultImporter
|
||||
$timeEntry->task_id = $taskId;
|
||||
$timeEntry->project_id = $projectId;
|
||||
$timeEntry->client_id = $clientId;
|
||||
$timeEntry->organization_id = $this->organization->id;
|
||||
$timeEntry->organization()->associate($this->organization);
|
||||
$timeEntry->description = $record['Description'];
|
||||
if (! in_array($record['Billable'], ['Yes', 'No'], true)) {
|
||||
throw new ImportException('Invalid billable value');
|
||||
|
||||
Reference in New Issue
Block a user