mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import { CustomerValidators } from './CustomerValidators';
|
||||
import {
|
||||
ICustomerActivatingPayload,
|
||||
ICustomerActivatedPayload,
|
||||
} from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class ActivateCustomer {
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private validators: CustomerValidators;
|
||||
|
||||
/**
|
||||
* Inactive the given contact.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} contactId - Contact id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async activateCustomer(
|
||||
tenantId: number,
|
||||
customerId: number
|
||||
): Promise<void> {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the customer or throw not found error.
|
||||
const oldCustomer = await Contact.query()
|
||||
.findById(customerId)
|
||||
.modify('customer')
|
||||
.throwIfNotFound();
|
||||
|
||||
this.validators.validateNotAlreadyPublished(oldCustomer);
|
||||
|
||||
// Edits the given customer with associated transactions on unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onCustomerActivating` event.
|
||||
await this.eventPublisher.emitAsync(events.customers.onActivating, {
|
||||
tenantId,
|
||||
trx,
|
||||
oldCustomer,
|
||||
} as ICustomerActivatingPayload);
|
||||
|
||||
// Update the given customer details.
|
||||
const customer = await Contact.query(trx)
|
||||
.findById(customerId)
|
||||
.update({ active: true });
|
||||
|
||||
// Triggers `onCustomerActivated` event.
|
||||
await this.eventPublisher.emitAsync(events.customers.onActivated, {
|
||||
tenantId,
|
||||
trx,
|
||||
oldCustomer,
|
||||
customer,
|
||||
} as ICustomerActivatedPayload);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
ICustomer,
|
||||
ICustomerEventCreatedPayload,
|
||||
ICustomerEventCreatingPayload,
|
||||
ICustomerNewDTO,
|
||||
ISystemUser,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import { CreateEditCustomerDTO } from './CreateEditCustomerDTO';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class CreateCustomer {
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private customerDTO: CreateEditCustomerDTO;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Creates a new customer.
|
||||
* @param {number} tenantId
|
||||
* @param {ICustomerNewDTO} customerDTO
|
||||
* @return {Promise<ICustomer>}
|
||||
*/
|
||||
public async createCustomer(
|
||||
tenantId: number,
|
||||
customerDTO: ICustomerNewDTO,
|
||||
authorizedUser: ISystemUser
|
||||
): Promise<ICustomer> {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Transformes the customer DTO to customer object.
|
||||
const customerObj = await this.customerDTO.transformCreateDTO(
|
||||
tenantId,
|
||||
customerDTO
|
||||
);
|
||||
// Creates a new customer under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onCustomerCreating` event.
|
||||
await this.eventPublisher.emitAsync(events.customers.onCreating, {
|
||||
tenantId,
|
||||
customerDTO,
|
||||
trx,
|
||||
} as ICustomerEventCreatingPayload);
|
||||
|
||||
// Creates a new contact as customer.
|
||||
const customer = await Contact.query().insertAndFetch({
|
||||
...customerObj,
|
||||
});
|
||||
// Triggers `onCustomerCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.customers.onCreated, {
|
||||
customer,
|
||||
tenantId,
|
||||
customerId: customer.id,
|
||||
authorizedUser,
|
||||
trx,
|
||||
} as ICustomerEventCreatedPayload);
|
||||
|
||||
return customer;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import moment from 'moment';
|
||||
import { defaultTo, omit, isEmpty } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
ContactService,
|
||||
ICustomer,
|
||||
ICustomerEditDTO,
|
||||
ICustomerNewDTO,
|
||||
} from '@/interfaces';
|
||||
import { TenantMetadata } from '@/system/models';
|
||||
|
||||
@Service()
|
||||
export class CreateEditCustomerDTO {
|
||||
/**
|
||||
* Transformes the create/edit DTO.
|
||||
* @param {ICustomerNewDTO | ICustomerEditDTO} customerDTO
|
||||
* @returns
|
||||
*/
|
||||
private transformCommonDTO = (
|
||||
customerDTO: ICustomerNewDTO | ICustomerEditDTO
|
||||
): Partial<ICustomer> => {
|
||||
return {
|
||||
...omit(customerDTO, ['customerType']),
|
||||
contactType: customerDTO.customerType,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes the create DTO.
|
||||
* @param {ICustomerNewDTO} customerDTO
|
||||
* @returns {}
|
||||
*/
|
||||
public transformCreateDTO = async (
|
||||
tenantId: number,
|
||||
customerDTO: ICustomerNewDTO
|
||||
) => {
|
||||
const commonDTO = this.transformCommonDTO(customerDTO);
|
||||
|
||||
// Retrieves the tenant metadata.
|
||||
const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
|
||||
|
||||
return {
|
||||
...commonDTO,
|
||||
currencyCode: commonDTO.currencyCode || tenantMeta?.baseCurrency,
|
||||
active: defaultTo(customerDTO.active, true),
|
||||
contactService: ContactService.Customer,
|
||||
...(!isEmpty(customerDTO.openingBalanceAt)
|
||||
? {
|
||||
openingBalanceAt: moment(
|
||||
customerDTO?.openingBalanceAt
|
||||
).toMySqlDateTime(),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes the edit DTO.
|
||||
* @param {ICustomerEditDTO} customerDTO
|
||||
* @returns
|
||||
*/
|
||||
public transformEditDTO = (customerDTO: ICustomerEditDTO) => {
|
||||
const commonDTO = this.transformCommonDTO(customerDTO);
|
||||
|
||||
return {
|
||||
...commonDTO,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { ERRORS } from '../constants';
|
||||
|
||||
@Service()
|
||||
export class CustomerValidators {
|
||||
/**
|
||||
* Validates the given customer is not already published.
|
||||
* @param {ICustomer} customer
|
||||
*/
|
||||
public validateNotAlreadyPublished = (customer) => {
|
||||
if (customer.active) {
|
||||
throw new ServiceError(ERRORS.CUSTOMER_ALREADY_ACTIVE);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
ICustomerDeletingPayload,
|
||||
ICustomerEventDeletedPayload,
|
||||
ISystemUser,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ERRORS } from '../constants';
|
||||
|
||||
@Service()
|
||||
export class DeleteCustomer {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Deletes the given customer from the storage.
|
||||
* @param {number} tenantId
|
||||
* @param {number} customerId
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async deleteCustomer(
|
||||
tenantId: number,
|
||||
customerId: number,
|
||||
authorizedUser: ISystemUser
|
||||
): Promise<void> {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the customer of throw not found service error.
|
||||
const oldCustomer = await Contact.query()
|
||||
.findById(customerId)
|
||||
.modify('customer')
|
||||
.throwIfNotFound()
|
||||
.queryAndThrowIfHasRelations({
|
||||
type: ERRORS.CUSTOMER_HAS_TRANSACTIONS,
|
||||
});
|
||||
|
||||
// Triggers `onCustomerDeleting` event.
|
||||
await this.eventPublisher.emitAsync(events.customers.onDeleting, {
|
||||
tenantId,
|
||||
customerId,
|
||||
oldCustomer,
|
||||
} as ICustomerDeletingPayload);
|
||||
|
||||
// Deletes the customer and associated entities under UOW transaction.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Delete the customer from the storage.
|
||||
await Contact.query(trx).findById(customerId).delete();
|
||||
|
||||
// Throws `onCustomerDeleted` event.
|
||||
await this.eventPublisher.emitAsync(events.customers.onDeleted, {
|
||||
tenantId,
|
||||
customerId,
|
||||
oldCustomer,
|
||||
authorizedUser,
|
||||
trx,
|
||||
} as ICustomerEventDeletedPayload);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
ICustomer,
|
||||
ICustomerEditDTO,
|
||||
ICustomerEventEditedPayload,
|
||||
ICustomerEventEditingPayload,
|
||||
ISystemUser,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { CreateEditCustomerDTO } from './CreateEditCustomerDTO';
|
||||
|
||||
@Service()
|
||||
export class EditCustomer {
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private customerDTO: CreateEditCustomerDTO;
|
||||
|
||||
/**
|
||||
* Edits details of the given customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} customerId
|
||||
* @param {ICustomerEditDTO} customerDTO
|
||||
* @return {Promise<ICustomer>}
|
||||
*/
|
||||
public async editCustomer(
|
||||
tenantId: number,
|
||||
customerId: number,
|
||||
customerDTO: ICustomerEditDTO
|
||||
): Promise<ICustomer> {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the vendor or throw not found error.
|
||||
const oldCustomer = await Contact.query()
|
||||
.findById(customerId)
|
||||
.modify('customer')
|
||||
.throwIfNotFound();
|
||||
|
||||
// Transformes the given customer DTO to object.
|
||||
const customerObj = this.customerDTO.transformEditDTO(customerDTO);
|
||||
|
||||
// Edits the given customer under unit-of-work evnirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onCustomerEditing` event.
|
||||
await this.eventPublisher.emitAsync(events.customers.onEditing, {
|
||||
tenantId,
|
||||
customerDTO,
|
||||
customerId,
|
||||
trx,
|
||||
} as ICustomerEventEditingPayload);
|
||||
|
||||
// Edits the customer details on the storage.
|
||||
const customer = await Contact.query().updateAndFetchById(customerId, {
|
||||
...customerObj,
|
||||
});
|
||||
// Triggers `onCustomerEdited` event.
|
||||
await this.eventPublisher.emitAsync(events.customers.onEdited, {
|
||||
customerId,
|
||||
customer,
|
||||
trx,
|
||||
} as ICustomerEventEditedPayload);
|
||||
|
||||
return customer;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
ICustomer,
|
||||
ICustomerOpeningBalanceEditDTO,
|
||||
ICustomerOpeningBalanceEditedPayload,
|
||||
ICustomerOpeningBalanceEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@Service()
|
||||
export class EditOpeningBalanceCustomer {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
/**
|
||||
* Changes the opening balance of the given customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} customerId
|
||||
* @param {number} openingBalance
|
||||
* @param {string|Date} openingBalanceAt
|
||||
*/
|
||||
public async changeOpeningBalance(
|
||||
tenantId: number,
|
||||
customerId: number,
|
||||
openingBalanceEditDTO: ICustomerOpeningBalanceEditDTO
|
||||
): Promise<ICustomer> {
|
||||
const { Customer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old customer or throw not found error.
|
||||
const oldCustomer = await Customer.query()
|
||||
.findById(customerId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Mutates the customer opening balance under unit-of-work.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onCustomerOpeningBalanceChanging` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.customers.onOpeningBalanceChanging,
|
||||
{
|
||||
tenantId,
|
||||
oldCustomer,
|
||||
openingBalanceEditDTO,
|
||||
trx,
|
||||
} as ICustomerOpeningBalanceEditingPayload
|
||||
);
|
||||
// Mutates the customer on the storage.
|
||||
const customer = await Customer.query().patchAndFetchById(customerId, {
|
||||
...openingBalanceEditDTO,
|
||||
});
|
||||
// Triggers `onCustomerOpeingBalanceChanged` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.customers.onOpeningBalanceChanged,
|
||||
{
|
||||
tenantId,
|
||||
customer,
|
||||
oldCustomer,
|
||||
openingBalanceEditDTO,
|
||||
trx,
|
||||
} as ICustomerOpeningBalanceEditedPayload
|
||||
);
|
||||
return customer;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import I18nService from '@/services/I18n/I18nService';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import CustomerTransfromer from '../CustomerTransformer';
|
||||
|
||||
@Service()
|
||||
export class GetCustomer {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieve the given customer details.
|
||||
* @param {number} tenantId
|
||||
* @param {number} customerId
|
||||
*/
|
||||
public async getCustomer(tenantId: number, customerId: number) {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the customer model or throw not found error.
|
||||
const customer = await Contact.query()
|
||||
.modify('customer')
|
||||
.findById(customerId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Retrieves the transformered customers.
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
customer,
|
||||
new CustomerTransfromer()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
ICustomer,
|
||||
ICustomersFilter,
|
||||
IFilterMeta,
|
||||
IPaginationMeta,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import CustomerTransfromer from '../CustomerTransformer';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GetCustomers {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Parses customers list filter DTO.
|
||||
* @param filterDTO -
|
||||
*/
|
||||
private parseCustomersListFilterDTO(filterDTO) {
|
||||
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve customers paginated list.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {ICustomersFilter} filter - Cusotmers filter.
|
||||
*/
|
||||
public async getCustomersList(
|
||||
tenantId: number,
|
||||
filterDTO: ICustomersFilter
|
||||
): Promise<{
|
||||
customers: ICustomer[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { Customer } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses customers list filter DTO.
|
||||
const filter = this.parseCustomersListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Customer,
|
||||
filter
|
||||
);
|
||||
// Customers.
|
||||
const { results, pagination } = await Customer.query()
|
||||
.onBuild((builder) => {
|
||||
dynamicList.buildQuery()(builder);
|
||||
builder.modify('inactiveMode', filter.inactiveMode);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
// Retrieves the transformed customers.
|
||||
const customers = await this.transformer.transform(
|
||||
tenantId,
|
||||
results,
|
||||
new CustomerTransfromer()
|
||||
);
|
||||
return {
|
||||
customers,
|
||||
pagination,
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user