mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
add server to monorepo.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
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 { VendorValidators } from './VendorValidators';
|
||||
import { IVendorActivatedPayload } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class ActivateVendor {
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private validators: VendorValidators;
|
||||
|
||||
/**
|
||||
* Inactive the given contact.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} contactId - Contact id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async activateVendor(
|
||||
tenantId: number,
|
||||
vendorId: number
|
||||
): Promise<void> {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old vendor or throw not found error.
|
||||
const oldVendor = await Contact.query()
|
||||
.findById(vendorId)
|
||||
.modify('vendor')
|
||||
.throwIfNotFound();
|
||||
|
||||
// Validate whether the vendor is already published.
|
||||
this.validators.validateNotAlreadyPublished(oldVendor);
|
||||
|
||||
// Edits the vendor with associated transactions on unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onVendorActivating` event.
|
||||
await this.eventPublisher.emitAsync(events.vendors.onActivating, {
|
||||
tenantId,
|
||||
trx,
|
||||
oldVendor,
|
||||
} as IVendorActivatedPayload);
|
||||
|
||||
// Updates the vendor on the storage.
|
||||
const vendor = await Contact.query(trx).updateAndFetchById(vendorId, {
|
||||
active: true,
|
||||
});
|
||||
// Triggers `onVendorActivated` event.
|
||||
await this.eventPublisher.emitAsync(events.vendors.onActivated, {
|
||||
tenantId,
|
||||
trx,
|
||||
oldVendor,
|
||||
vendor,
|
||||
} as IVendorActivatedPayload);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import moment from 'moment';
|
||||
import { defaultTo, isEmpty } from 'lodash';
|
||||
import { Service } from 'typedi';
|
||||
import {
|
||||
ContactService,
|
||||
IVendor,
|
||||
IVendorEditDTO,
|
||||
IVendorNewDTO,
|
||||
} from '@/interfaces';
|
||||
import { TenantMetadata } from '@/system/models';
|
||||
|
||||
@Service()
|
||||
export class CreateEditVendorDTO {
|
||||
/**
|
||||
*
|
||||
* @param {IVendorNewDTO | IVendorEditDTO} vendorDTO
|
||||
* @returns
|
||||
*/
|
||||
private transformCommonDTO = (vendorDTO: IVendorNewDTO | IVendorEditDTO) => {
|
||||
return {
|
||||
...vendorDTO,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes the create vendor DTO.
|
||||
* @param {IVendorNewDTO} vendorDTO -
|
||||
* @returns {}
|
||||
*/
|
||||
public transformCreateDTO = async (
|
||||
tenantId: number,
|
||||
vendorDTO: IVendorNewDTO
|
||||
) => {
|
||||
const commonDTO = this.transformCommonDTO(vendorDTO);
|
||||
|
||||
// Retrieves the tenant metadata.
|
||||
const tenantMeta = await TenantMetadata.query().findOne({ tenantId });
|
||||
|
||||
return {
|
||||
...commonDTO,
|
||||
currencyCode: vendorDTO.currencyCode || tenantMeta.baseCurrency,
|
||||
active: defaultTo(vendorDTO.active, true),
|
||||
contactService: ContactService.Vendor,
|
||||
|
||||
...(!isEmpty(vendorDTO.openingBalanceAt)
|
||||
? {
|
||||
openingBalanceAt: moment(
|
||||
vendorDTO?.openingBalanceAt
|
||||
).toMySqlDateTime(),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes the edit vendor DTO.
|
||||
* @param {IVendorEditDTO} vendorDTO
|
||||
* @returns
|
||||
*/
|
||||
public transformEditDTO = (vendorDTO: IVendorEditDTO) => {
|
||||
const commonDTO = this.transformCommonDTO(vendorDTO);
|
||||
|
||||
return {
|
||||
...commonDTO,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
ISystemUser,
|
||||
IVendorEventCreatedPayload,
|
||||
IVendorEventCreatingPayload,
|
||||
IVendorNewDTO,
|
||||
} 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 { CreateEditVendorDTO } from './CreateEditVendorDTO';
|
||||
|
||||
@Service()
|
||||
export class CreateVendor {
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformDTO: CreateEditVendorDTO;
|
||||
|
||||
/**
|
||||
* Creates a new vendor.
|
||||
* @param {number} tenantId
|
||||
* @param {IVendorNewDTO} vendorDTO
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async createVendor(
|
||||
tenantId: number,
|
||||
vendorDTO: IVendorNewDTO,
|
||||
authorizedUser: ISystemUser
|
||||
) {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Transformes create DTO to customer object.
|
||||
const vendorObject = await this.transformDTO.transformCreateDTO(
|
||||
tenantId,
|
||||
vendorDTO
|
||||
);
|
||||
// Creates vendor contact under unit-of-work evnirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onVendorCreating` event.
|
||||
await this.eventPublisher.emitAsync(events.vendors.onCreating, {
|
||||
tenantId,
|
||||
vendorDTO,
|
||||
trx,
|
||||
} as IVendorEventCreatingPayload);
|
||||
|
||||
// Creates a new contact as vendor.
|
||||
const vendor = await Contact.query(trx).insertAndFetch({
|
||||
...vendorObject,
|
||||
});
|
||||
// Triggers `onVendorCreated` event.
|
||||
await this.eventPublisher.emitAsync(events.vendors.onCreated, {
|
||||
tenantId,
|
||||
vendorId: vendor.id,
|
||||
vendor,
|
||||
authorizedUser,
|
||||
trx,
|
||||
} as IVendorEventCreatedPayload);
|
||||
|
||||
return vendor;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
ISystemUser,
|
||||
IVendorEventDeletedPayload,
|
||||
IVendorEventDeletingPayload,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { ERRORS } from '../constants';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class DeleteVendor {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
/**
|
||||
* Deletes the given vendor.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorId
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async deleteVendor(
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
authorizedUser: ISystemUser
|
||||
) {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old vendor or throw not found service error.
|
||||
const oldVendor = await Contact.query()
|
||||
.modify('vendor')
|
||||
.findById(vendorId)
|
||||
.throwIfNotFound()
|
||||
.queryAndThrowIfHasRelations({
|
||||
type: ERRORS.VENDOR_HAS_TRANSACTIONS,
|
||||
});
|
||||
// Triggers `onVendorDeleting` event.
|
||||
await this.eventPublisher.emitAsync(events.vendors.onDeleting, {
|
||||
tenantId,
|
||||
vendorId,
|
||||
oldVendor,
|
||||
} as IVendorEventDeletingPayload);
|
||||
|
||||
// Deletes vendor contact under unit-of-work.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Deletes the vendor contact from the storage.
|
||||
await Contact.query(trx).findById(vendorId).delete();
|
||||
|
||||
// Triggers `onVendorDeleted` event.
|
||||
await this.eventPublisher.emitAsync(events.vendors.onDeleted, {
|
||||
tenantId,
|
||||
vendorId,
|
||||
authorizedUser,
|
||||
oldVendor,
|
||||
trx,
|
||||
} as IVendorEventDeletedPayload);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
IVendorOpeningBalanceEditDTO,
|
||||
IVendorOpeningBalanceEditedPayload,
|
||||
IVendorOpeningBalanceEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class EditOpeningBalanceVendor {
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Changes the opening balance of the given customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} customerId
|
||||
* @param {number} openingBalance
|
||||
* @param {string|Date} openingBalanceAt
|
||||
* @returns {Promise<IVendor>}
|
||||
*/
|
||||
public async editOpeningBalance(
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO
|
||||
) {
|
||||
const { Vendor } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieves the old vendor or throw not found error.
|
||||
const oldVendor = await Vendor.query().findById(vendorId).throwIfNotFound();
|
||||
|
||||
// Mutates the customer opening balance under unit-of-work.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onVendorOpeingBalanceChanging` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.vendors.onOpeningBalanceChanging,
|
||||
{
|
||||
tenantId,
|
||||
oldVendor,
|
||||
openingBalanceEditDTO,
|
||||
trx,
|
||||
} as IVendorOpeningBalanceEditingPayload
|
||||
);
|
||||
// Mutates the vendor on the storage.
|
||||
const vendor = await Vendor.query().patchAndFetchById(vendorId, {
|
||||
...openingBalanceEditDTO,
|
||||
});
|
||||
// Triggers `onVendorOpeingBalanceChanged` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.vendors.onOpeningBalanceChanged,
|
||||
{
|
||||
tenantId,
|
||||
vendor,
|
||||
oldVendor,
|
||||
openingBalanceEditDTO,
|
||||
trx,
|
||||
} as IVendorOpeningBalanceEditedPayload
|
||||
);
|
||||
return vendor;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
ISystemUser,
|
||||
IVendorEditDTO,
|
||||
IVendorEventEditedPayload,
|
||||
IVendorEventEditingPayload,
|
||||
} from '@/interfaces';
|
||||
import { Knex } from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import events from '@/subscribers/events';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { CreateEditVendorDTO } from './CreateEditVendorDTO';
|
||||
|
||||
@Service()
|
||||
export class EditVendor {
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private transformDTO: CreateEditVendorDTO;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Edits details of the given vendor.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} vendorId -
|
||||
* @param {IVendorEditDTO} vendorDTO -
|
||||
* @returns {Promise<IVendor>}
|
||||
*/
|
||||
public async editVendor(
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
vendorDTO: IVendorEditDTO,
|
||||
authorizedUser: ISystemUser
|
||||
) {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
// Retrieve the vendor or throw not found error.
|
||||
const oldVendor = await Contact.query()
|
||||
.findById(vendorId)
|
||||
.modify('vendor')
|
||||
.throwIfNotFound();
|
||||
|
||||
// Transformes vendor DTO to object.
|
||||
const vendorObj = this.transformDTO.transformEditDTO(vendorDTO);
|
||||
|
||||
// Edits vendor contact under unit-of-work envirement.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onVendorEditing` event.
|
||||
await this.eventPublisher.emitAsync(events.vendors.onEditing, {
|
||||
trx,
|
||||
tenantId,
|
||||
vendorDTO,
|
||||
} as IVendorEventEditingPayload);
|
||||
|
||||
// Edits the vendor contact.
|
||||
const vendor = await Contact.query().updateAndFetchById(vendorId, {
|
||||
...vendorObj,
|
||||
});
|
||||
// Triggers `onVendorEdited` event.
|
||||
await this.eventPublisher.emitAsync(events.vendors.onEdited, {
|
||||
tenantId,
|
||||
vendorId,
|
||||
vendor,
|
||||
authorizedUser,
|
||||
trx,
|
||||
} as IVendorEventEditedPayload);
|
||||
|
||||
return vendor;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import VendorTransfromer from '../VendorTransformer';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GetVendor {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieve the given vendor details.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorId
|
||||
*/
|
||||
public async getVendor(tenantId: number, vendorId: number) {
|
||||
const { Contact } = this.tenancy.models(tenantId);
|
||||
|
||||
const vendor = await Contact.query()
|
||||
.findById(vendorId)
|
||||
.modify('vendor')
|
||||
.throwIfNotFound();
|
||||
|
||||
// Transformes the vendor.
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
vendor,
|
||||
new VendorTransfromer()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import * as R from 'ramda';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import {
|
||||
IFilterMeta,
|
||||
IPaginationMeta,
|
||||
IVendor,
|
||||
IVendorsFilter,
|
||||
} from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||
import VendorTransfromer from '../VendorTransformer';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GetVendors {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private dynamicListService: DynamicListingService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieve vendors datatable list.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {IVendorsFilter} vendorsFilter - Vendors filter.
|
||||
*/
|
||||
public async getVendorsList(
|
||||
tenantId: number,
|
||||
filterDTO: IVendorsFilter
|
||||
): Promise<{
|
||||
vendors: IVendor[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
const { Vendor } = this.tenancy.models(tenantId);
|
||||
|
||||
// Parses vendors list filter DTO.
|
||||
const filter = this.parseVendorsListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicList = await this.dynamicListService.dynamicList(
|
||||
tenantId,
|
||||
Vendor,
|
||||
filter
|
||||
);
|
||||
// Vendors list.
|
||||
const { results, pagination } = await Vendor.query()
|
||||
.onBuild((builder) => {
|
||||
dynamicList.buildQuery()(builder);
|
||||
|
||||
// Switches between active/inactive modes.
|
||||
builder.modify('inactiveMode', filter.inactiveMode);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
// Transform the vendors.
|
||||
const transformedVendors = await this.transformer.transform(
|
||||
tenantId,
|
||||
results,
|
||||
new VendorTransfromer()
|
||||
);
|
||||
return {
|
||||
vendors: transformedVendors,
|
||||
pagination,
|
||||
filterMeta: dynamicList.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parseVendorsListFilterDTO(filterDTO) {
|
||||
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { ERRORS } from '../constants';
|
||||
|
||||
@Service()
|
||||
export class VendorValidators {
|
||||
/**
|
||||
* Validates the given vendor is not already activated.
|
||||
* @param {IVendor} vendor
|
||||
*/
|
||||
public validateNotAlreadyPublished = (vendor) => {
|
||||
if (vendor.active) {
|
||||
throw new ServiceError(ERRORS.VENDOR_ALREADY_ACTIVE);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import { VendorGLEntriesStorage } from '../VendorGLEntriesStorage';
|
||||
import {
|
||||
IVendorEventCreatedPayload,
|
||||
IVendorEventDeletedPayload,
|
||||
IVendorOpeningBalanceEditedPayload,
|
||||
} from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class VendorsWriteGLOpeningSubscriber {
|
||||
@Inject()
|
||||
private vendorGLEntriesStorage: VendorGLEntriesStorage;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.vendors.onCreated,
|
||||
this.handleWriteOpeningBalanceEntries
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendors.onDeleted,
|
||||
this.handleRevertOpeningBalanceEntries
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendors.onOpeningBalanceChanged,
|
||||
this.handleRewriteOpeningEntriesOnChanged
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the open balance journal entries once the vendor created.
|
||||
* @param {IVendorEventCreatedPayload} payload -
|
||||
*/
|
||||
private handleWriteOpeningBalanceEntries = async ({
|
||||
tenantId,
|
||||
vendor,
|
||||
trx,
|
||||
}: IVendorEventCreatedPayload) => {
|
||||
// Writes the vendor opening balance journal entries.
|
||||
if (vendor.openingBalance) {
|
||||
await this.vendorGLEntriesStorage.writeVendorOpeningBalance(
|
||||
tenantId,
|
||||
vendor.id,
|
||||
trx
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Revert the opening balance journal entries once the vendor deleted.
|
||||
* @param {IVendorEventDeletedPayload} payload -
|
||||
*/
|
||||
private handleRevertOpeningBalanceEntries = async ({
|
||||
tenantId,
|
||||
vendorId,
|
||||
trx,
|
||||
}: IVendorEventDeletedPayload) => {
|
||||
await this.vendorGLEntriesStorage.revertVendorOpeningBalance(
|
||||
tenantId,
|
||||
vendorId,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the rewrite opening balance entries once opening balnace changed.
|
||||
* @param {ICustomerOpeningBalanceEditedPayload} payload -
|
||||
*/
|
||||
private handleRewriteOpeningEntriesOnChanged = async ({
|
||||
tenantId,
|
||||
vendor,
|
||||
trx,
|
||||
}: IVendorOpeningBalanceEditedPayload) => {
|
||||
if (vendor.openingBalance) {
|
||||
await this.vendorGLEntriesStorage.rewriteVendorOpeningBalance(
|
||||
tenantId,
|
||||
vendor.id,
|
||||
trx
|
||||
);
|
||||
} else {
|
||||
await this.vendorGLEntriesStorage.revertVendorOpeningBalance(
|
||||
tenantId,
|
||||
vendor.id,
|
||||
trx
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
115
packages/server/src/services/Contacts/Vendors/VendorGLEntries.ts
Normal file
115
packages/server/src/services/Contacts/Vendors/VendorGLEntries.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { Service } from 'typedi';
|
||||
import { IVendor, AccountNormal, ILedgerEntry } from '@/interfaces';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
|
||||
@Service()
|
||||
export class VendorGLEntries {
|
||||
/**
|
||||
* Retrieves the opening balance GL common entry.
|
||||
* @param {IVendor} vendor -
|
||||
*/
|
||||
private getOpeningBalanceGLCommonEntry = (vendor: IVendor) => {
|
||||
return {
|
||||
exchangeRate: vendor.openingBalanceExchangeRate,
|
||||
currencyCode: vendor.currencyCode,
|
||||
|
||||
transactionType: 'VendorOpeningBalance',
|
||||
transactionId: vendor.id,
|
||||
|
||||
date: vendor.openingBalanceAt,
|
||||
userId: vendor.userId,
|
||||
contactId: vendor.id,
|
||||
|
||||
credit: 0,
|
||||
debit: 0,
|
||||
|
||||
branchId: vendor.openingBalanceBranchId,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the opening balance GL debit entry.
|
||||
* @param {number} costAccountId -
|
||||
* @param {IVendor} vendor
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getOpeningBalanceGLDebitEntry = (
|
||||
costAccountId: number,
|
||||
vendor: IVendor
|
||||
): ILedgerEntry => {
|
||||
const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor);
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
accountId: costAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
debit: vendor.localOpeningBalance,
|
||||
credit: 0,
|
||||
index: 2,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the opening balance GL credit entry.
|
||||
* @param {number} APAccountId
|
||||
* @param {IVendor} vendor
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getOpeningBalanceGLCreditEntry = (
|
||||
APAccountId: number,
|
||||
vendor: IVendor
|
||||
): ILedgerEntry => {
|
||||
const commonEntry = this.getOpeningBalanceGLCommonEntry(vendor);
|
||||
|
||||
return {
|
||||
...commonEntry,
|
||||
accountId: APAccountId,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
credit: vendor.localOpeningBalance,
|
||||
index: 1,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the opening balance GL entries.
|
||||
* @param {number} APAccountId
|
||||
* @param {number} costAccountId -
|
||||
* @param {IVendor} vendor
|
||||
* @returns {ILedgerEntry[]}
|
||||
*/
|
||||
public getOpeningBalanceGLEntries = (
|
||||
APAccountId: number,
|
||||
costAccountId: number,
|
||||
vendor: IVendor
|
||||
): ILedgerEntry[] => {
|
||||
const debitEntry = this.getOpeningBalanceGLDebitEntry(
|
||||
costAccountId,
|
||||
vendor
|
||||
);
|
||||
const creditEntry = this.getOpeningBalanceGLCreditEntry(
|
||||
APAccountId,
|
||||
vendor
|
||||
);
|
||||
return [debitEntry, creditEntry];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the opening balance ledger.
|
||||
* @param {number} APAccountId
|
||||
* @param {number} costAccountId -
|
||||
* @param {IVendor} vendor
|
||||
* @returns {Ledger}
|
||||
*/
|
||||
public getOpeningBalanceLedger = (
|
||||
APAccountId: number,
|
||||
costAccountId: number,
|
||||
vendor: IVendor
|
||||
) => {
|
||||
const entries = this.getOpeningBalanceGLEntries(
|
||||
APAccountId,
|
||||
costAccountId,
|
||||
vendor
|
||||
);
|
||||
return new Ledger(entries);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { VendorGLEntries } from './VendorGLEntries';
|
||||
|
||||
@Service()
|
||||
export class VendorGLEntriesStorage {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private ledegrRepository: LedgerStorageService;
|
||||
|
||||
@Inject()
|
||||
private vendorGLEntries: VendorGLEntries;
|
||||
|
||||
/**
|
||||
* Vendor opening balance journals.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public writeVendorOpeningBalance = async (
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { Vendor } = this.tenancy.models(tenantId);
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
const vendor = await Vendor.query(trx).findById(vendorId);
|
||||
|
||||
// Finds the expense account.
|
||||
const expenseAccount = await accountRepository.findOne({
|
||||
slug: 'other-expenses',
|
||||
});
|
||||
// Find or create the A/P account.
|
||||
const APAccount = await accountRepository.findOrCreateAccountsPayable(
|
||||
vendor.currencyCode,
|
||||
{},
|
||||
trx
|
||||
);
|
||||
// Retrieves the vendor opening balance ledger.
|
||||
const ledger = this.vendorGLEntries.getOpeningBalanceLedger(
|
||||
APAccount.id,
|
||||
expenseAccount.id,
|
||||
vendor
|
||||
);
|
||||
// Commits the ledger entries to the storage.
|
||||
await this.ledegrRepository.commit(tenantId, ledger, trx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts the vendor opening balance GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public revertVendorOpeningBalance = async (
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
await this.ledegrRepository.deleteByReference(
|
||||
tenantId,
|
||||
vendorId,
|
||||
'VendorOpeningBalance',
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes the vendor opening balance GL entries.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorId
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
public rewriteVendorOpeningBalance = async (
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
await this.writeVendorOpeningBalance(tenantId, vendorId, trx);
|
||||
|
||||
await this.revertVendorOpeningBalance(tenantId, vendorId, trx);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Service } from 'typedi';
|
||||
import ContactTransfromer from '../ContactTransformer';
|
||||
|
||||
export default class VendorTransfromer extends ContactTransfromer {
|
||||
/**
|
||||
* Include these attributes to expense object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'formattedBalance',
|
||||
'formattedOpeningBalance',
|
||||
'formattedOpeningBalanceAt'
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
ISystemUser,
|
||||
IVendorEditDTO,
|
||||
IVendorNewDTO,
|
||||
IVendorOpeningBalanceEditDTO,
|
||||
IVendorsFilter,
|
||||
} from '@/interfaces';
|
||||
import { CreateVendor } from './CRUD/CreateVendor';
|
||||
import { DeleteVendor } from './CRUD/DeleteVendor';
|
||||
import { EditOpeningBalanceVendor } from './CRUD/EditOpeningBalanceVendor';
|
||||
import { EditVendor } from './CRUD/EditVendor';
|
||||
import { GetVendor } from './CRUD/GetVendor';
|
||||
import { GetVendors } from './CRUD/GetVendors';
|
||||
|
||||
@Service()
|
||||
export class VendorsApplication {
|
||||
@Inject()
|
||||
private createVendorService: CreateVendor;
|
||||
|
||||
@Inject()
|
||||
private editVendorService: EditVendor;
|
||||
|
||||
@Inject()
|
||||
private deleteVendorService: DeleteVendor;
|
||||
|
||||
@Inject()
|
||||
private editOpeningBalanceService: EditOpeningBalanceVendor;
|
||||
|
||||
@Inject()
|
||||
private getVendorService: GetVendor;
|
||||
|
||||
@Inject()
|
||||
private getVendorsService: GetVendors;
|
||||
|
||||
/**
|
||||
* Creates a new vendor.
|
||||
* @param {number} tenantId
|
||||
* @param {IVendorNewDTO} vendorDTO
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public createVendor = (
|
||||
tenantId: number,
|
||||
vendorDTO: IVendorNewDTO,
|
||||
authorizedUser: ISystemUser
|
||||
) => {
|
||||
return this.createVendorService.createVendor(
|
||||
tenantId,
|
||||
vendorDTO,
|
||||
authorizedUser
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Edits details of the given vendor.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} vendorId -
|
||||
* @param {IVendorEditDTO} vendorDTO -
|
||||
* @returns {Promise<IVendor>}
|
||||
*/
|
||||
public editVendor = (
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
vendorDTO: IVendorEditDTO,
|
||||
authorizedUser: ISystemUser
|
||||
) => {
|
||||
return this.editVendorService.editVendor(
|
||||
tenantId,
|
||||
vendorId,
|
||||
vendorDTO,
|
||||
authorizedUser
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the given vendor.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorId
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public deleteVendor = (
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
authorizedUser: ISystemUser
|
||||
) => {
|
||||
return this.deleteVendorService.deleteVendor(
|
||||
tenantId,
|
||||
vendorId,
|
||||
authorizedUser
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the opening balance of the given customer.
|
||||
* @param {number} tenantId
|
||||
* @param {number} customerId
|
||||
* @param {number} openingBalance
|
||||
* @param {string|Date} openingBalanceAt
|
||||
* @returns {Promise<IVendor>}
|
||||
*/
|
||||
public editOpeningBalance = (
|
||||
tenantId: number,
|
||||
vendorId: number,
|
||||
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO
|
||||
) => {
|
||||
return this.editOpeningBalanceService.editOpeningBalance(
|
||||
tenantId,
|
||||
vendorId,
|
||||
openingBalanceEditDTO
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the vendor details.
|
||||
* @param {number} tenantId
|
||||
* @param {number} vendorId
|
||||
* @returns
|
||||
*/
|
||||
public getVendor = (tenantId: number, vendorId: number) => {
|
||||
return this.getVendorService.getVendor(tenantId, vendorId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the vendors paginated list.
|
||||
* @param {number} tenantId
|
||||
* @param {IVendorsFilter} filterDTO
|
||||
* @returns
|
||||
*/
|
||||
public getVendors = (tenantId: number, filterDTO: IVendorsFilter) => {
|
||||
return this.getVendorsService.getVendorsList(tenantId, filterDTO);
|
||||
};
|
||||
}
|
||||
27
packages/server/src/services/Contacts/Vendors/constants.ts
Normal file
27
packages/server/src/services/Contacts/Vendors/constants.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export const DEFAULT_VIEW_COLUMNS = [];
|
||||
|
||||
export const DEFAULT_VIEWS = [
|
||||
{
|
||||
name: 'Overdue',
|
||||
slug: 'overdue',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ index: 1, fieldKey: 'status', comparator: 'equals', value: 'overdue' },
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
{
|
||||
name: 'Unpaid',
|
||||
slug: 'unpaid',
|
||||
rolesLogicExpression: '1',
|
||||
roles: [
|
||||
{ index: 1, fieldKey: 'status', comparator: 'equals', value: 'unpaid' },
|
||||
],
|
||||
columns: DEFAULT_VIEW_COLUMNS,
|
||||
},
|
||||
];
|
||||
|
||||
export const ERRORS = {
|
||||
VENDOR_HAS_TRANSACTIONS: 'VENDOR_HAS_TRANSACTIONS',
|
||||
VENDOR_ALREADY_ACTIVE: 'VENDOR_ALREADY_ACTIVE',
|
||||
};
|
||||
Reference in New Issue
Block a user