add server to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 11:57:50 +02:00
parent 28e309981b
commit 80b97b5fdc
1303 changed files with 137049 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
import { Knex } from 'knex';
import { Service, Inject } from 'typedi';
import {
IProjectTimeCreatedEventPayload,
IProjectTimeCreateDTO,
IProjectTimeCreateEventPayload,
IProjectTimeCreatePOJO,
IProjectTimeCreatingEventPayload,
} from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service()
export class CreateTimeService {
@Inject()
private uow: UnitOfWork;
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
/**
* Creates a new time.
* @param {number} taskId -
* @param {IProjectTimeCreateDTO} timeDTO -
* @returns {Promise<IProjectTimeCreatePOJO>}
*/
public createTime = async (
tenantId: number,
taskId: number,
timeDTO: IProjectTimeCreateDTO
): Promise<IProjectTimeCreatePOJO> => {
const { Time, Task } = this.tenancy.models(tenantId);
const task = await Task.query().findById(taskId).throwIfNotFound();
// Triggers `onProjectTimeCreate` event.
await this.eventPublisher.emitAsync(events.projectTime.onCreate, {
tenantId,
timeDTO,
} as IProjectTimeCreateEventPayload);
// Creates a new project under unit-of-work envirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onProjectTimeCreating` event.
await this.eventPublisher.emitAsync(events.projectTime.onCreating, {
tenantId,
timeDTO,
trx,
} as IProjectTimeCreatingEventPayload);
const time = await Time.query().insert({
...timeDTO,
taskId,
projectId: task.projectId,
});
// Triggers `onProjectTimeCreated` event.
await this.eventPublisher.emitAsync(events.projectTime.onCreated, {
tenantId,
time,
trx,
} as IProjectTimeCreatedEventPayload);
return time;
});
};
}

View File

@@ -0,0 +1,61 @@
import { Knex } from 'knex';
import { Service, Inject } from 'typedi';
import {
IProjectTimeDeletedEventPayload,
IProjectTimeDeleteEventPayload,
IProjectTimeDeletingEventPayload,
} from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service()
export class DeleteTimeService {
@Inject()
private uow: UnitOfWork;
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
/**
* Deletes the give task's time that associated to the given project.
* @param {number} projectId -
* @returns {Promise<void>}
*/
public deleteTime = async (tenantId: number, timeId: number) => {
const { Time } = this.tenancy.models(tenantId);
// Validate customer existance.
const oldTime = await Time.query().findById(timeId).throwIfNotFound();
// Triggers `onProjectDelete` event.
await this.eventPublisher.emitAsync(events.projectTime.onDelete, {
tenantId,
timeId,
} as IProjectTimeDeleteEventPayload);
// Deletes the given project under unit-of-work envirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onProjectDeleting` event.
await this.eventPublisher.emitAsync(events.projectTime.onDeleting, {
tenantId,
oldTime,
trx,
} as IProjectTimeDeletingEventPayload);
// Upsert the project object.
await Time.query(trx).findById(timeId).delete();
// Triggers `onProjectDeleted` event.
await this.eventPublisher.emitAsync(events.projectTime.onDeleted, {
tenantId,
oldTime,
trx,
} as IProjectTimeDeletedEventPayload);
});
};
}

View File

@@ -0,0 +1,76 @@
import { Knex } from 'knex';
import { Service, Inject } from 'typedi';
import {
IProjectTimeEditDTO,
IProjectTimeEditedEventPayload,
IProjectTimeEditEventPayload,
IProjectTimeEditingEventPayload,
IProjectTimeEditPOJO,
} from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import UnitOfWork from '@/services/UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service()
export class EditTimeService {
@Inject()
private uow: UnitOfWork;
@Inject()
private tenancy: HasTenancyService;
@Inject()
private eventPublisher: EventPublisher;
/**
* Edits the given project's time that associated to the given task.
* @param {number} tenantId - Tenant id.
* @param {number} taskId - Task id.
* @returns {Promise<IProjectTimeEditPOJO>}
*/
public editTime = async (
tenantId: number,
timeId: number,
timeDTO: IProjectTimeEditDTO
): Promise<IProjectTimeEditPOJO> => {
const { Time } = this.tenancy.models(tenantId);
// Validate customer existance.
const oldTime = await Time.query().findById(timeId).throwIfNotFound();
// Triggers `onProjectEdit` event.
await this.eventPublisher.emitAsync(events.projectTime.onEdit, {
tenantId,
oldTime,
timeDTO,
} as IProjectTimeEditEventPayload);
// Edits the given project under unit-of-work envirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onProjectEditing` event.
await this.eventPublisher.emitAsync(events.projectTime.onEditing, {
tenantId,
timeDTO,
oldTime,
trx,
} as IProjectTimeEditingEventPayload);
// Upsert the task's time object.
const time = await Time.query(trx).upsertGraphAndFetch({
id: timeId,
...timeDTO,
});
// Triggers `onProjectEdited` event.
await this.eventPublisher.emitAsync(events.projectTime.onEdited, {
tenantId,
oldTime,
timeDTO,
time,
trx,
} as IProjectTimeEditedEventPayload);
return time;
});
};
}

View File

@@ -0,0 +1,37 @@
import { IProjectTimeGetPOJO } from '@/interfaces';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { Inject, Service } from 'typedi';
import { TimeTransformer } from './TimeTransformer';
@Service()
export class GetTimeService {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private transformer: TransformerInjectable;
/**
* Retrieve the tasks list.
* @param {number} tenantId - Tenant Id.
* @param {number} taskId - Task Id.
* @returns {Promise<IProjectTimeGetPOJO>}
*/
public getTime = async (
tenantId: number,
timeId: number
): Promise<IProjectTimeGetPOJO> => {
const { Time } = this.tenancy.models(tenantId);
// Retrieve the project.
const time = await Time.query()
.findById(timeId)
.withGraphFetched('project.contact')
.withGraphFetched('task')
.throwIfNotFound();
// Transformes and returns object.
return this.transformer.transform(tenantId, time, new TimeTransformer());
};
}

View File

@@ -0,0 +1,36 @@
import { Inject, Service } from 'typedi';
import { IProjectTimeGetPOJO } from '@/interfaces';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { TimeTransformer } from './TimeTransformer';
@Service()
export class GetTimelineService {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private transformer: TransformerInjectable;
/**
* Retrieve the tasks list.
* @param {number} tenantId - Tenant Id.
* @param {number} taskId - Task Id.
* @returns {Promise<IProjectTimeGetPOJO[]>}
*/
public getTimeline = async (
tenantId: number,
projectId: number
): Promise<IProjectTimeGetPOJO[]> => {
const { Time } = this.tenancy.models(tenantId);
// Retrieve the project.
const times = await Time.query()
.where('projectId', projectId)
.withGraphFetched('project.contact')
.withGraphFetched('task');
// Transformes and returns object.
return this.transformer.transform(tenantId, times, new TimeTransformer());
};
}

View File

@@ -0,0 +1,49 @@
import { Knex } from 'knex';
import { Inject, Service } from 'typedi';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export class SyncActualTimeTask {
@Inject()
private tenancy: HasTenancyService;
/**
* Increases the actual time of the given task.
* @param {number} tenantId
* @param {number} taskId
* @param {number} actualHours
* @param {Knex.Transaction} trx
*/
public increaseActualTimeTask = async (
tenantId: number,
taskId: number,
actualHours: number,
trx?: Knex.Transaction
) => {
const { Task } = this.tenancy.models(tenantId);
await Task.query(trx)
.findById(taskId)
.increment('actualHours', actualHours);
};
/**
* Decreases the actual time of the given task.
* @param {number} tenantId
* @param {number} taskId
* @param {number} actualHours
* @param {Knex.Transaction} trx
*/
public decreaseActualTimeTask = async (
tenantId: number,
taskId: number,
actualHours: number,
trx?: Knex.Transaction
) => {
const { Task } = this.tenancy.models(tenantId);
await Task.query(trx)
.findById(taskId)
.decrement('actualHours', actualHours);
};
}

View File

@@ -0,0 +1,91 @@
import { Inject, Service } from 'typedi';
import {
IProjectTimeCreatedEventPayload,
IProjectTimeDeletedEventPayload,
IProjectTimeEditedEventPayload,
} from '@/interfaces';
import events from '@/subscribers/events';
import { SyncActualTimeTask } from './SyncActualTimeTask';
@Service()
export class SyncActualTimeTaskSubscriber {
@Inject()
private syncActualTimeTask: SyncActualTimeTask;
/**
* Attaches events with handlers.
* @param bus
*/
attach(bus) {
bus.subscribe(
events.projectTime.onCreated,
this.handleIncreaseActualTimeOnTimeCreate
);
bus.subscribe(
events.projectTime.onDeleted,
this.handleDecreaseActaulTimeOnTimeDelete
);
bus.subscribe(
events.projectTime.onEdited,
this.handleAdjustActualTimeOnTimeEdited
);
}
/**
* Handles increasing the actual time of the task once time entry be created.
* @param {IProjectTimeCreatedEventPayload} payload -
*/
private handleIncreaseActualTimeOnTimeCreate = async ({
tenantId,
time,
trx,
}: IProjectTimeCreatedEventPayload) => {
await this.syncActualTimeTask.increaseActualTimeTask(
tenantId,
time.taskId,
time.duration,
trx
);
};
/**
* Handle decreasing the actual time of the tsak once time entry be deleted.
* @param {IProjectTimeDeletedEventPayload} payload
*/
private handleDecreaseActaulTimeOnTimeDelete = async ({
tenantId,
oldTime,
trx,
}: IProjectTimeDeletedEventPayload) => {
await this.syncActualTimeTask.decreaseActualTimeTask(
tenantId,
oldTime.taskId,
oldTime.duration,
trx
);
};
/**
* Handle adjusting the actual time of the task once time be edited.
* @param {IProjectTimeEditedEventPayload} payload -
*/
private handleAdjustActualTimeOnTimeEdited = async ({
tenantId,
time,
oldTime,
trx,
}: IProjectTimeEditedEventPayload) => {
await this.syncActualTimeTask.decreaseActualTimeTask(
tenantId,
oldTime.taskId,
oldTime.duration,
trx
);
await this.syncActualTimeTask.increaseActualTimeTask(
tenantId,
time.taskId,
time.duration,
trx
);
};
}

View File

@@ -0,0 +1,57 @@
import { Transformer } from '@/lib/Transformer/Transformer';
import Time from 'models/Time';
import { formatMinutes } from 'utils/formatMinutes';
export class TimeTransformer extends Transformer {
/**
* Include these attributes to sale invoice object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
return ['projectName', 'taskName', 'customerName', 'durationFormatted'];
};
/**
* Exclude attributes.
* @returns {string[]}
*/
public excludeAttributes = (): string[] => {
return ['project', 'task'];
};
/**
* Retrieves the project name that associated to the time entry.
* @param {Time} time
* @returns {string}
*/
public projectName = (time: Time) => {
return time.project.name;
};
/**
* Retrieves the task name that associated to the time entry.
* @param {Time} time
* @returns {string}
*/
public taskName = (time: Time) => {
return time.task.name;
};
/**
* Retrieves the customer name that associated to the task of the time entry.
* @param {Time} time
* @returns {string}
*/
public customerName = (time: Time) => {
return time?.project?.contact?.displayName;
};
/**
* Retrieves the formatted duration.
* @param {Time} time
* @returns {string}
*/
public durationFormatted = (time: Time) => {
return formatMinutes(time.duration);
}
}

View File

@@ -0,0 +1,96 @@
import { Inject, Service } from 'typedi';
import { CreateTimeService } from './CreateTime';
import { EditTimeService } from './EditTime';
import { GetTimelineService } from './GetTimes';
import { GetTimeService } from './GetTime';
import { DeleteTimeService } from './DeleteTime';
import {
IProjectTimeCreateDTO,
IProjectTimeCreatePOJO,
IProjectTimeEditDTO,
IProjectTimeEditPOJO,
IProjectTimeGetPOJO,
} from '@/interfaces';
@Service()
export class TimesApplication {
@Inject()
private createTimeService: CreateTimeService;
@Inject()
private editTimeService: EditTimeService;
@Inject()
private deleteTimeService: DeleteTimeService;
@Inject()
private getTimeService: GetTimeService;
@Inject()
private getTimelineService: GetTimelineService;
/**
* Creates a new time for specific project's task.
* @param {number} tenantId - Tenant id.
* @param {IProjectTimeCreateDTO} timeDTO - Create project's time DTO.
* @return {Promise<IProjectTimeCreatePOJO>}
*/
public createTime = (
tenantId: number,
taskId: number,
timeDTO: IProjectTimeCreateDTO
): Promise<IProjectTimeCreatePOJO> => {
return this.createTimeService.createTime(tenantId, taskId, timeDTO);
};
/**
* Edits details of the given task.
* @param {number} tenantId - Tenant id.
* @param {number} vendorId - Vendor id.
* @param {IProjectCreateDTO} projectDTO - Create project DTO.
* @returns {Promise<IProjectTimeEditPOJO>}
*/
public editTime = (
tenantId: number,
timeId: number,
taskDTO: IProjectTimeEditDTO
): Promise<IProjectTimeEditPOJO> => {
return this.editTimeService.editTime(tenantId, timeId, taskDTO);
};
/**
* Deletes the given task.
* @param {number} tenantId
* @param {number} taskId
* @return {Promise<void>}
*/
public deleteTime = (tenantId: number, timeId: number): Promise<void> => {
return this.deleteTimeService.deleteTime(tenantId, timeId);
};
/**
* Retrieves the given task details.
* @param {number} tenantId
* @param {number} timeId
* @returns {Promise<IProjectTimeGetPOJO>}
*/
public getTime = (
tenantId: number,
timeId: number
): Promise<IProjectTimeGetPOJO> => {
return this.getTimeService.getTime(tenantId, timeId);
};
/**
* Retrieves the vendors paginated list.
* @param {number} tenantId
* @param {IVendorsFilter} filterDTO
* @returns {Promise<IProjectTimeGetPOJO[]>}
*/
public getTimeline = (
tenantId: number,
projectId: number
): Promise<IProjectTimeGetPOJO[]> => {
return this.getTimelineService.getTimeline(tenantId, projectId);
};
}