feat(nestjs): migrate to NestJS

This commit is contained in:
Ahmed Bouhuolia
2025-04-07 11:51:24 +02:00
parent f068218a16
commit 55fcc908ef
3779 changed files with 631 additions and 195332 deletions

View 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);
// };
// }

View File

@@ -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);
// };
// }

View File

@@ -0,0 +1,66 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
Query,
} from '@nestjs/common';
import { VendorsApplication } from './VendorsApplication.service';
import {
IVendorOpeningBalanceEditDTO,
IVendorsFilter,
} from './types/Vendors.types';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { CreateVendorDto } from './dtos/CreateVendor.dto';
import { EditVendorDto } from './dtos/EditVendor.dto';
@Controller('vendors')
@ApiTags('vendors')
export class VendorsController {
constructor(private vendorsApplication: VendorsApplication) {}
@Get()
@ApiOperation({ summary: 'Retrieves the vendors.' })
getVendors(@Query() filterDTO: IVendorsFilter) {
return this.vendorsApplication.getVendors(filterDTO);
}
@Get(':id')
@ApiOperation({ summary: 'Retrieves the vendor details.' })
getVendor(@Param('id') vendorId: number) {
return this.vendorsApplication.getVendor(vendorId);
}
@Post()
@ApiOperation({ summary: 'Create a new vendor.' })
createVendor(@Body() vendorDTO: CreateVendorDto) {
return this.vendorsApplication.createVendor(vendorDTO);
}
@Put(':id')
@ApiOperation({ summary: 'Edit the given vendor.' })
editVendor(@Param('id') vendorId: number, @Body() vendorDTO: EditVendorDto) {
return this.vendorsApplication.editVendor(vendorId, vendorDTO);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete the given vendor.' })
deleteVendor(@Param('id') vendorId: number) {
return this.vendorsApplication.deleteVendor(vendorId);
}
@Put(':id/opening-balance')
@ApiOperation({ summary: 'Edit the given vendor opening balance.' })
editOpeningBalance(
@Param('id') vendorId: number,
@Body() openingBalanceDTO: IVendorOpeningBalanceEditDTO,
) {
return this.vendorsApplication.editOpeningBalance(
vendorId,
openingBalanceDTO,
);
}
}

View File

@@ -0,0 +1,36 @@
import { Module } from '@nestjs/common';
import { TenancyDatabaseModule } from '../Tenancy/TenancyDB/TenancyDB.module';
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
import { ActivateVendorService } from './commands/ActivateVendor.service';
import { CreateEditVendorDTOService } from './commands/CreateEditVendorDTO';
import { CreateVendorService } from './commands/CreateVendor.service';
import { DeleteVendorService } from './commands/DeleteVendor.service';
import { EditOpeningBalanceVendorService } from './commands/EditOpeningBalanceVendor.service';
import { EditVendorService } from './commands/EditVendor.service';
import { GetVendorService } from './queries/GetVendor';
import { VendorValidators } from './commands/VendorValidators';
import { VendorsApplication } from './VendorsApplication.service';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { VendorsController } from './Vendors.controller';
import { GetVendorsService } from './queries/GetVendors.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
@Module({
imports: [TenancyDatabaseModule, DynamicListModule],
controllers: [VendorsController],
providers: [
ActivateVendorService,
CreateEditVendorDTOService,
CreateVendorService,
EditVendorService,
EditOpeningBalanceVendorService,
GetVendorService,
GetVendorsService,
VendorValidators,
DeleteVendorService,
VendorsApplication,
TransformerInjectable,
TenancyContext,
],
})
export class VendorsModule {}

View File

@@ -0,0 +1,88 @@
import { Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { CreateVendorService } from './commands/CreateVendor.service';
import { EditVendorService } from './commands/EditVendor.service';
import { DeleteVendorService } from './commands/DeleteVendor.service';
import { EditOpeningBalanceVendorService } from './commands/EditOpeningBalanceVendor.service';
import { GetVendorService } from './queries/GetVendor';
import {
IVendorOpeningBalanceEditDTO,
IVendorsFilter,
} from './types/Vendors.types';
import { GetVendorsService } from './queries/GetVendors.service';
import { CreateVendorDto } from './dtos/CreateVendor.dto';
import { EditVendorDto } from './dtos/EditVendor.dto';
@Injectable()
export class VendorsApplication {
constructor(
private createVendorService: CreateVendorService,
private editVendorService: EditVendorService,
private deleteVendorService: DeleteVendorService,
private editOpeningBalanceService: EditOpeningBalanceVendorService,
private getVendorService: GetVendorService,
private getVendorsService: GetVendorsService,
) {}
/**
* Creates a new vendor.
* @param {IVendorNewDTO} vendorDTO
* @return {Promise<void>}
*/
public createVendor(vendorDTO: CreateVendorDto, trx?: Knex.Transaction) {
return this.createVendorService.createVendor(vendorDTO, trx);
}
/**
* Edits details of the given vendor.
* @param {number} vendorId -
* @param {IVendorEditDTO} vendorDTO -
* @returns {Promise<IVendor>}
*/
public editVendor(vendorId: number, vendorDTO: EditVendorDto) {
return this.editVendorService.editVendor(vendorId, vendorDTO);
}
/**
* Deletes the given vendor.
* @param {number} vendorId
* @return {Promise<void>}
*/
public deleteVendor(vendorId: number) {
return this.deleteVendorService.deleteVendor(vendorId);
}
/**
* Changes the opening balance of the given customer.
* @param {number} vendorId
* @param {IVendorOpeningBalanceEditDTO} openingBalanceEditDTO
* @returns {Promise<IVendor>}
*/
public editOpeningBalance(
vendorId: number,
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO,
) {
return this.editOpeningBalanceService.editOpeningBalance(
vendorId,
openingBalanceEditDTO,
);
}
/**
* Retrieves the vendor details.
* @param {number} vendorId - Vendor ID.
* @returns
*/
public getVendor(vendorId: number) {
return this.getVendorService.getVendor(vendorId);
}
/**
* Retrieves the vendors paginated list.
* @param {IVendorsFilter} filterDTO
* @returns {Promise<{ vendors: Vendor[], pagination: IPaginationMeta, filterMeta: IFilterMeta }>>}
*/
public getVendors(filterDTO: IVendorsFilter) {
return this.getVendorsService.getVendorsList(filterDTO);
}
}

View File

@@ -0,0 +1,30 @@
// import { Inject, Service } from 'typedi';
// import { IItemsFilter } from '@/interfaces';
// import { Exportable } from '@/services/Export/Exportable';
// import { VendorsApplication } from './VendorsApplication';
// import { EXPORT_SIZE_LIMIT } from '@/services/Export/constants';
// @Service()
// export class VendorsExportable extends Exportable {
// @Inject()
// private vendorsApplication: VendorsApplication;
// /**
// * Retrieves the accounts data to exportable sheet.
// * @param {number} tenantId
// * @returns
// */
// public exportable(tenantId: number, query: IItemsFilter) {
// const parsedQuery = {
// sortOrder: 'DESC',
// columnSortBy: 'created_at',
// ...query,
// page: 1,
// pageSize: EXPORT_SIZE_LIMIT,
// } as IItemsFilter;
// return this.vendorsApplication
// .getVendors(tenantId, parsedQuery)
// .then((output) => output.vendors);
// }
// }

View File

@@ -0,0 +1,32 @@
// import { Importable } from '@/services/Import/Importable';
// import { CreateVendor } from './CRUD/CreateVendor.service';
// import { Knex } from 'knex';
// import { Inject, Service } from 'typedi';
// import { VendorsSampleData } from './_SampleData';
// @Service()
// export class VendorsImportable extends Importable {
// @Inject()
// private createVendorService: CreateVendor;
// /**
// * Maps the imported data to create a new vendor service.
// * @param {number} tenantId
// * @param {} createDTO
// * @param {Knex.Transaction} trx
// */
// public async importable(
// tenantId: number,
// createDTO: any,
// trx?: Knex.Transaction<any, any[]>
// ): Promise<void> {
// await this.createVendorService.createVendor(tenantId, createDTO, trx);
// }
// /**
// * Retrieves the sample data of vendors sample sheet.
// */
// public sampleData(): any[] {
// return VendorsSampleData;
// }
// }

View File

@@ -0,0 +1,122 @@
export const VendorsSampleData = [
{
"First Name": "Nicolette",
"Last Name": "Schamberger",
"Company Name": "Homenick - Hane",
"Display Name": "Rowland Rowe",
"Email": "cicero86@yahoo.com",
"Personal Phone Number": "811-603-2235",
"Work Phone Number": "906-993-5190",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "T",
"Note": "Doloribus autem optio temporibus dolores mollitia sit.",
"Billing Address 1": "862 Jessika Well",
"Billing Address 2": "1091 Dorthy Mount",
"Billing Address City": "Deckowfort",
"Billing Address Country": "Ghana",
"Billing Address Phone": "825-011-5207",
"Billing Address Postcode": "38228",
"Billing Address State": "Oregon",
"Shipping Address 1": "37626 Thiel Villages",
"Shipping Address 2": "132 Batz Avenue",
"Shipping Address City": "Pagacburgh",
"Shipping Address Country": "Albania",
"Shipping Address Phone": "171-546-3701",
"Shipping Address Postcode": "13709",
"Shipping Address State": "Georgia"
},
{
"First Name": "Hermann",
"Last Name": "Crooks",
"Company Name": "Veum - Schaefer",
"Display Name": "Harley Veum",
"Email": "immanuel56@hotmail.com",
"Personal Phone Number": "449-780-9999",
"Work Phone Number": "970-473-5785",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "T",
"Note": "Doloribus dolore dolor dicta vitae in fugit nisi quibusdam.",
"Billing Address 1": "532 Simonis Spring",
"Billing Address 2": "3122 Nicolas Inlet",
"Billing Address City": "East Matteofort",
"Billing Address Country": "Holy See (Vatican City State)",
"Billing Address Phone": "366-084-8629",
"Billing Address Postcode": "41607",
"Billing Address State": "Montana",
"Shipping Address 1": "2889 Tremblay Plaza",
"Shipping Address 2": "71355 Kutch Isle",
"Shipping Address City": "D'Amorehaven",
"Shipping Address Country": "Monaco",
"Shipping Address Phone": "614-189-3328",
"Shipping Address Postcode": "09634-0435",
"Shipping Address State": "Nevada"
},
{
"First Name": "Nellie",
"Last Name": "Gulgowski",
"Company Name": "Boyle, Heller and Jones",
"Display Name": "Randall Kohler",
"Email": "anibal_frami@yahoo.com",
"Personal Phone Number": "498-578-0740",
"Work Phone Number": "394-550-6827",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "T",
"Note": "Vero quibusdam rem fugit aperiam est modi.",
"Billing Address 1": "214 Sauer Villages",
"Billing Address 2": "30687 Kacey Square",
"Billing Address City": "Jayceborough",
"Billing Address Country": "Benin",
"Billing Address Phone": "332-820-1127",
"Billing Address Postcode": "16425-3887",
"Billing Address State": "Mississippi",
"Shipping Address 1": "562 Diamond Loaf",
"Shipping Address 2": "9595 Satterfield Trafficway",
"Shipping Address City": "Alexandrinefort",
"Shipping Address Country": "Puerto Rico",
"Shipping Address Phone": "776-500-8456",
"Shipping Address Postcode": "30258",
"Shipping Address State": "South Dakota"
},
{
"First Name": "Stone",
"Last Name": "Jerde",
"Company Name": "Cassin, Casper and Maggio",
"Display Name": "Clint McLaughlin",
"Email": "nathanael22@yahoo.com",
"Personal Phone Number": "562-790-6059",
"Work Phone Number": "686-838-0027",
"Website": "http://google.com",
"Opening Balance": 54302.23,
"Opening Balance At": "2022-02-02",
"Opening Balance Ex. Rate": 2,
"Currency": "LYD",
"Active": "T",
"Note": "Quis cumque molestias rerum.",
"Billing Address 1": "22590 Cathy Harbor",
"Billing Address 2": "24493 Brycen Brooks",
"Billing Address City": "Elnorashire",
"Billing Address Country": "Andorra",
"Billing Address Phone": "701-852-8005",
"Billing Address Postcode": "5680",
"Billing Address State": "Nevada",
"Shipping Address 1": "5355 Erdman Bridge",
"Shipping Address 2": "421 Jeanette Camp",
"Shipping Address City": "East Philip",
"Shipping Address Country": "Venezuela",
"Shipping Address Phone": "426-119-0858",
"Shipping Address Postcode": "34929-0501",
"Shipping Address State": "Tennessee"
}
]

View File

@@ -0,0 +1,59 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { VendorValidators } from './VendorValidators';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Vendor } from '../models/Vendor';
import { events } from '@/common/events/events';
import { IVendorActivatedPayload } from '../types/Vendors.types';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class ActivateVendorService {
constructor(
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
private readonly validators: VendorValidators,
@Inject(Vendor.name)
private readonly vendorModel: TenantModelProxy<typeof Vendor>,
) {}
/**
* Inactive the given contact.
* @param {number} vendorId - Vendor id.
* @returns {Promise<void>}
*/
public async activateVendor(vendorId: number): Promise<void> {
// Retrieves the old vendor or throw not found error.
const oldVendor = await this.vendorModel()
.query()
.findById(vendorId)
.throwIfNotFound();
// Validate whether the vendor is already published.
this.validators.validateNotAlreadyPublished(oldVendor);
// Edits the vendor with associated transactions on unit-of-work environment.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onVendorActivating` event.
await this.eventPublisher.emitAsync(events.vendors.onActivating, {
trx,
oldVendor,
} as IVendorActivatedPayload);
// Updates the vendor on the storage.
const vendor = await this.vendorModel()
.query(trx)
.updateAndFetchById(vendorId, {
active: true,
});
// Triggers `onVendorActivated` event.
await this.eventPublisher.emitAsync(events.vendors.onActivated, {
trx,
oldVendor,
vendor,
} as IVendorActivatedPayload);
});
}
}

View File

@@ -0,0 +1,73 @@
import * as moment from 'moment';
import { defaultTo, isEmpty } from 'lodash';
import { Injectable } from '@nestjs/common';
import { IVendorEditDTO, IVendorNewDTO } from '../types/Vendors.types';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
import { ContactService } from '@/modules/Contacts/types/Contacts.types';
import { Vendor } from '../models/Vendor';
import { CreateVendorDto } from '../dtos/CreateVendor.dto';
@Injectable()
export class CreateEditVendorDTOService {
/**
* @param {TenancyContext} tenancyContext - Tenancy context service.
*/
constructor(private readonly tenancyContext: TenancyContext) {}
/**
* Transforms the common vendor DTO.
* @param {IVendorNewDTO | IVendorEditDTO} vendorDTO
* @returns {IVendorNewDTO | IVendorEditDTO}
*/
private transformCommonDTO = (vendorDTO: IVendorNewDTO | IVendorEditDTO) => {
return {
...vendorDTO,
};
};
/**
* Transformes the create vendor DTO.
* @param {IVendorNewDTO} vendorDTO -
* @returns {IVendorNewDTO}
*/
public transformCreateDTO = async (
vendorDTO: CreateVendorDto,
): Promise<Partial<Vendor>> => {
const commonDTO = this.transformCommonDTO(vendorDTO);
// Retrieves the tenant metadata.
const tenant = await this.tenancyContext.getTenant(true);
return {
...commonDTO,
currencyCode: vendorDTO.currencyCode || tenant.metadata.baseCurrency,
active: defaultTo(vendorDTO.active, true),
contactService: ContactService.Vendor,
...(!isEmpty(vendorDTO.openingBalanceAt)
? {
openingBalanceAt: moment(
vendorDTO?.openingBalanceAt,
).toMySqlDateTime(),
}
: {}),
openingBalanceExchangeRate: defaultTo(
vendorDTO.openingBalanceExchangeRate,
1,
),
};
};
/**
* Transformes the edit vendor DTO.
* @param {IVendorEditDTO} vendorDTO
* @returns {IVendorEditDTO}
*/
public transformEditDTO = (vendorDTO: IVendorEditDTO) => {
const commonDTO = this.transformCommonDTO(vendorDTO);
return {
...commonDTO,
};
};
}

View File

@@ -0,0 +1,66 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Vendor } from '../models/Vendor';
import { events } from '@/common/events/events';
import {
IVendorEventCreatedPayload,
IVendorEventCreatingPayload,
IVendorNewDTO,
} from '../types/Vendors.types';
import { CreateEditVendorDTOService } from './CreateEditVendorDTO';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { CreateVendorDto } from '../dtos/CreateVendor.dto';
@Injectable()
export class CreateVendorService {
/**
* @param {UnitOfWork} uow - Unit of work service.
* @param {EventEmitter2} eventPublisher - Event emitter service.
* @param {CreateEditVendorDTOService} transformDTO - Create edit vendor DTO service.
* @param {typeof Vendor} vendorModel - Vendor model.
*/
constructor(
private readonly uow: UnitOfWork,
private readonly eventPublisher: EventEmitter2,
private readonly transformDTO: CreateEditVendorDTOService,
@Inject(Vendor.name)
private readonly vendorModel: TenantModelProxy<typeof Vendor>,
) {}
/**
* Creates a new vendor.
* @param {IVendorNewDTO} vendorDTO
* @return {Promise<void>}
*/
public async createVendor(vendorDTO: CreateVendorDto, trx?: Knex.Transaction) {
// Transforms create DTO to customer object.
const vendorObject = await this.transformDTO.transformCreateDTO(vendorDTO);
// Creates vendor contact under unit-of-work environment.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onVendorCreating` event.
await this.eventPublisher.emitAsync(events.vendors.onCreating, {
vendorDTO,
trx,
} as IVendorEventCreatingPayload);
// Creates a new contact as vendor.
const vendor = await this.vendorModel()
.query(trx)
.insertAndFetch({
...vendorObject,
});
// Triggers `onVendorCreated` event.
await this.eventPublisher.emitAsync(events.vendors.onCreated, {
vendorId: vendor.id,
vendor,
trx,
} as IVendorEventCreatedPayload);
return vendor;
}, trx);
}
}

View File

@@ -0,0 +1,60 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Vendor } from '../models/Vendor';
import { events } from '@/common/events/events';
import {
IVendorEventDeletedPayload,
IVendorEventDeletingPayload,
} from '../types/Vendors.types';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class DeleteVendorService {
/**
* @param {EventEmitter2} eventPublisher - Event emitter service.
* @param {UnitOfWork} uow - Unit of work service.
* @param {typeof Vendor} contactModel - Vendor model.
*/
constructor(
private eventPublisher: EventEmitter2,
private uow: UnitOfWork,
@Inject(Vendor.name) private vendorModel: TenantModelProxy<typeof Vendor>,
) {}
/**
* Deletes the given vendor.
* @param {number} vendorId
* @return {Promise<void>}
*/
public async deleteVendor(vendorId: number) {
// Retrieves the old vendor or throw not found service error.
const oldVendor = await this.vendorModel()
.query()
.findById(vendorId)
.throwIfNotFound();
// .queryAndThrowIfHasRelations({
// type: ERRORS.VENDOR_HAS_TRANSACTIONS,
// });
// Triggers `onVendorDeleting` event.
await this.eventPublisher.emitAsync(events.vendors.onDeleting, {
vendorId,
oldVendor,
} as IVendorEventDeletingPayload);
// Deletes vendor contact under unit-of-work.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Deletes the vendor contact from the storage.
await this.vendorModel().query(trx).findById(vendorId).delete();
// Triggers `onVendorDeleted` event.
await this.eventPublisher.emitAsync(events.vendors.onDeleted, {
vendorId,
oldVendor,
trx,
} as IVendorEventDeletedPayload);
});
}
}

View File

@@ -0,0 +1,78 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import {
IVendorOpeningBalanceEditDTO,
IVendorOpeningBalanceEditedPayload,
IVendorOpeningBalanceEditingPayload,
} from '../types/Vendors.types';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Vendor } from '../models/Vendor';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class EditOpeningBalanceVendorService {
/**
* @param {EventEmitter2} eventPublisher - Event emitter service.
* @param {UnitOfWork} uow - Unit of work service.
* @param {typeof Vendor} vendorModel - Vendor model.
*/
constructor(
private readonly eventPublisher: EventEmitter2,
private readonly uow: UnitOfWork,
@Inject(Vendor.name)
private readonly vendorModel: TenantModelProxy<typeof Vendor>,
) {}
/**
* Changes the opening balance of the given customer.
* @param {number} vendorId
* @param {IVendorOpeningBalanceEditDTO} openingBalanceEditDTO
* @returns {Promise<IVendor>}
*/
public async editOpeningBalance(
vendorId: number,
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO,
) {
// Retrieves the old vendor or throw not found error.
const oldVendor = await this.vendorModel()
.query()
.findById(vendorId)
.throwIfNotFound();
// Mutates the customer opening balance under unit-of-work.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onVendorOpeingBalanceChanging` event.
await this.eventPublisher.emitAsync(
events.vendors.onOpeningBalanceChanging,
{
oldVendor,
openingBalanceEditDTO,
trx,
} as IVendorOpeningBalanceEditingPayload,
);
// Mutates the vendor on the storage.
const vendor = await this.vendorModel()
.query()
.patchAndFetchById(vendorId, {
...openingBalanceEditDTO,
});
// Triggers `onVendorOpeingBalanceChanged` event.
await this.eventPublisher.emitAsync(
events.vendors.onOpeningBalanceChanged,
{
vendor,
oldVendor,
openingBalanceEditDTO,
trx,
} as IVendorOpeningBalanceEditedPayload,
);
return vendor;
});
}
}

View File

@@ -0,0 +1,67 @@
import {
IVendorEventEditedPayload,
IVendorEventEditingPayload,
} from '../types/Vendors.types';
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { CreateEditVendorDTOService } from './CreateEditVendorDTO';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Vendor } from '../models/Vendor';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditVendorDto } from '../dtos/EditVendor.dto';
@Injectable()
export class EditVendorService {
constructor(
private readonly eventPublisher: EventEmitter2,
private readonly uow: UnitOfWork,
private readonly transformDTO: CreateEditVendorDTOService,
@Inject(Vendor.name)
private readonly vendorModel: TenantModelProxy<typeof Vendor>,
) {}
/**
* Edits details of the given vendor.
* @param {number} vendorId -
* @param {IVendorEditDTO} vendorDTO -
* @returns {Promise<IVendor>}
*/
public async editVendor(vendorId: number, vendorDTO: EditVendorDto) {
// Retrieve the vendor or throw not found error.
const oldVendor = await this.vendorModel()
.query()
.findById(vendorId)
.throwIfNotFound();
// Transforms vendor DTO to object.
const vendorObj = this.transformDTO.transformEditDTO(vendorDTO);
// Edits vendor contact under unit-of-work environment.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onVendorEditing` event.
await this.eventPublisher.emitAsync(events.vendors.onEditing, {
trx,
vendorDTO,
} as IVendorEventEditingPayload);
// Edits the vendor contact.
const vendor = await this.vendorModel()
.query()
.updateAndFetchById(vendorId, {
...vendorObj,
});
// Triggers `onVendorEdited` event.
await this.eventPublisher.emitAsync(events.vendors.onEdited, {
vendorId,
vendor,
trx,
} as IVendorEventEditedPayload);
return vendor;
});
}
}

View File

@@ -0,0 +1,16 @@
import { Injectable } from '@nestjs/common';
import { ServiceError } from '@/modules/Items/ServiceError';
import { ERRORS } from '../constants';
@Injectable()
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);
}
};
}

View 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',
};

View File

@@ -0,0 +1,103 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsBoolean, IsEmail, IsString } from 'class-validator';
import { ContactAddressDto } from '@/modules/Customers/dtos/ContactAddress.dto';
import { IsInt, IsNumber } from 'class-validator';
import { IsOptional, Min } from 'class-validator';
import { IsISO8601 } from 'class-validator';
export class CreateVendorDto extends ContactAddressDto {
@ApiProperty({ required: false, description: 'Vendor opening balance' })
@IsOptional()
@IsInt()
@Min(0)
openingBalance?: number;
@ApiProperty({
required: false,
description: 'Vendor opening balance exchange rate',
default: 1,
})
@IsOptional()
@IsNumber()
@Min(0.01)
openingBalanceExchangeRate?: number;
@ApiProperty({ required: false, description: 'Date of the opening balance' })
@IsOptional()
@IsISO8601()
openingBalanceAt?: Date;
@ApiProperty({
required: false,
description: 'Branch ID for the opening balance',
})
@IsOptional()
@IsInt()
openingBalanceBranchId?: number;
@ApiProperty({ description: 'Currency code for the vendor' })
@IsOptional()
@IsString()
currencyCode: string;
@ApiProperty({ required: false, description: 'Vendor salutation' })
@IsOptional()
@IsString()
salutation?: string;
@ApiProperty({ required: false, description: 'Vendor first name' })
@IsOptional()
@IsString()
firstName?: string;
@ApiProperty({ required: false, description: 'Vendor last name' })
@IsOptional()
@IsString()
lastName?: string;
@ApiProperty({ required: false, description: 'Vendor company name' })
@IsOptional()
@IsString()
companyName?: string;
@ApiProperty({ required: false, description: 'Vendor display name' })
@IsString()
displayName: string;
@ApiProperty({ required: false, description: 'Vendor website' })
@IsOptional()
@IsString()
website?: string;
@ApiProperty({ required: false, description: 'Vendor email address' })
@IsOptional()
@IsEmail()
email?: string;
@ApiProperty({ required: false, description: 'Vendor work phone number' })
@IsOptional()
@IsString()
workPhone?: string;
@ApiProperty({ required: false, description: 'Vendor personal phone number' })
@IsOptional()
@IsString()
personalPhone?: string;
@ApiProperty({
required: false,
description: 'Additional notes about the vendor',
})
@IsOptional()
@IsString()
note?: string;
@ApiProperty({
required: false,
description: 'Whether the vendor is active',
default: true,
})
@IsOptional()
@IsBoolean()
active?: boolean;
}

View File

@@ -0,0 +1,60 @@
import { ContactAddressDto } from '@/modules/Customers/dtos/ContactAddress.dto';
import { IsEmail, IsString, IsBoolean, IsOptional } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class EditVendorDto extends ContactAddressDto {
@ApiProperty({ required: false, description: 'Vendor salutation' })
@IsOptional()
@IsString()
salutation?: string;
@ApiProperty({ required: false, description: 'Vendor first name' })
@IsOptional()
@IsString()
firstName?: string;
@ApiProperty({ required: false, description: 'Vendor last name' })
@IsOptional()
@IsString()
lastName?: string;
@ApiProperty({ required: false, description: 'Vendor company name' })
@IsOptional()
@IsString()
companyName?: string;
@ApiProperty({ required: false, description: 'Vendor display name' })
@IsOptional()
@IsString()
displayName?: string;
@ApiProperty({ required: false, description: 'Vendor website' })
@IsOptional()
@IsString()
website?: string;
@ApiProperty({ required: false, description: 'Vendor email address' })
@IsOptional()
@IsEmail()
email?: string;
@ApiProperty({ required: false, description: 'Vendor work phone number' })
@IsOptional()
@IsString()
workPhone?: string;
@ApiProperty({ required: false, description: 'Vendor personal phone number' })
@IsOptional()
@IsString()
personalPhone?: string;
@ApiProperty({ required: false, description: 'Additional notes about the vendor' })
@IsOptional()
@IsString()
note?: string;
@ApiProperty({ required: false, description: 'Whether the vendor is active' })
@IsOptional()
@IsBoolean()
active?: boolean;
}

View File

@@ -0,0 +1,220 @@
import { Model, mixin } from 'objection';
// import TenantModel from 'models/TenantModel';
// import PaginationQueryBuilder from './Pagination';
// import ModelSetting from './ModelSetting';
// import VendorSettings from './Vendor.Settings';
// import CustomViewBaseModel from './CustomViewBaseModel';
// import { DEFAULT_VIEWS } from '@/services/Contacts/Vendors/constants';
// import ModelSearchable from './ModelSearchable';
import { BaseModel } from '@/models/Model';
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
// class VendorQueryBuilder extends PaginationQueryBuilder {
// constructor(...args) {
// super(...args);
// this.onBuild((builder) => {
// if (builder.isFind() || builder.isDelete() || builder.isUpdate()) {
// builder.where('contact_service', 'vendor');
// }
// });
// }
// }
export class Vendor extends TenantBaseModel {
contactService: string;
contactType: string;
balance: number;
currencyCode: string;
openingBalance: number;
openingBalanceExchangeRate: number;
openingBalanceAt: Date | string;
salutation: string;
firstName: string;
lastName: string;
companyName: string;
displayName: string;
email: string;
workPhone: string;
personalPhone: string;
website: string;
billingAddress1: string;
billingAddress2: string;
billingAddressCity: string;
billingAddressCountry: string;
billingAddressEmail: string;
billingAddressPostcode: string;
billingAddressPhone: string;
billingAddressState: string;
shippingAddress1: string;
shippingAddress2: string;
shippingAddressCity: string;
shippingAddressCountry: string;
shippingAddressEmail: string;
shippingAddressPostcode: string;
shippingAddressPhone: string;
shippingAddressState: string;
note: string;
active: boolean;
/**
* Query builder.
*/
// static get QueryBuilder() {
// return VendorQueryBuilder;
// }
/**
* Table name
*/
static get tableName() {
return 'contacts';
}
/**
* Model timestamps.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Defined virtual attributes.
*/
static get virtualAttributes() {
return ['closingBalance', 'contactNormal', 'localOpeningBalance'];
}
/**
* Closing balance attribute.
*/
get closingBalance() {
return this.balance;
}
/**
* Retrieves the local opening balance.
* @returns {number}
*/
get localOpeningBalance() {
return this.openingBalance
? this.openingBalance * this.openingBalanceExchangeRate
: 0;
}
/**
* Retrieve the contact noraml;
*/
get contactNormal() {
return 'debit';
}
/**
* Model modifiers.
*/
static get modifiers() {
return {
/**
* Inactive/Active mode.
*/
inactiveMode(query, active = false) {
query.where('active', !active);
},
/**
* Filters the active customers.
*/
active(query) {
query.where('active', 1);
},
/**
* Filters the inactive customers.
*/
inactive(query) {
query.where('active', 0);
},
/**
* Filters the vendors that have overdue invoices.
*/
overdue(query) {
query.select(
'*',
Vendor.relatedQuery('overdueBills', query.knex())
.count()
.as('countOverdue'),
);
query.having('countOverdue', '>', 0);
},
/**
* Filters the unpaid customers.
*/
unpaid(query) {
query.whereRaw('`BALANCE` + `OPENING_BALANCE` <> 0');
},
};
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const { Bill } = require('../../Bills/models/Bill');
return {
bills: {
relation: Model.HasManyRelation,
modelClass: Bill,
join: {
from: 'contacts.id',
to: 'bills.vendorId',
},
},
overdueBills: {
relation: Model.HasManyRelation,
modelClass: Bill,
join: {
from: 'contacts.id',
to: 'bills.vendorId',
},
filter: (query) => {
query.modify('overdue');
},
},
};
}
// static get meta() {
// return VendorSettings;
// }
// /**
// * Retrieve the default custom views, roles and columns.
// */
// static get defaultViews() {
// return DEFAULT_VIEWS;
// }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'display_name', comparator: 'contains' },
{ condition: 'or', fieldKey: 'first_name', comparator: 'contains' },
{ condition: 'or', fieldKey: 'last_name', comparator: 'equals' },
{ condition: 'or', fieldKey: 'company_name', comparator: 'equals' },
{ condition: 'or', fieldKey: 'email', comparator: 'equals' },
{ condition: 'or', fieldKey: 'work_phone', comparator: 'equals' },
{ condition: 'or', fieldKey: 'personal_phone', comparator: 'equals' },
{ condition: 'or', fieldKey: 'website', comparator: 'equals' },
];
}
}

View File

@@ -0,0 +1,28 @@
import { Inject, Injectable } from '@nestjs/common';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { Vendor } from '../models/Vendor';
import { VendorTransfromer } from './VendorTransformer';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetVendorService {
constructor(
private readonly transformer: TransformerInjectable,
@Inject(Vendor.name)
private readonly vendorModel: TenantModelProxy<typeof Vendor>,
) {}
/**
* Retrieve the given vendor details.
* @param {number} vendorId
*/
public async getVendor(vendorId: number) {
const vendor = await this.vendorModel()
.query()
.findById(vendorId)
.throwIfNotFound();
// Transformes the vendor.
return this.transformer.transform(vendor, new VendorTransfromer());
}
}

View File

@@ -0,0 +1,72 @@
import * as R from 'ramda';
import { Inject, Injectable } from '@nestjs/common';
import { Vendor } from '../models/Vendor';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { VendorTransfromer } from './VendorTransformer';
import { GetVendorsResponse, IVendorsFilter } from '../types/Vendors.types';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetVendorsService {
/**
* Constructor method.
* @param {DynamicListService} dynamicListService
* @param {TransformerInjectable} transformer
* @param {typeof Vendor} vendorModel
*/
constructor(
private dynamicListService: DynamicListService,
private transformer: TransformerInjectable,
@Inject(Vendor.name) private vendorModel: TenantModelProxy<typeof Vendor>,
) {}
/**
* Retrieve vendors datatable list.
* @param {IVendorsFilter} vendorsFilter - Vendors filter.
* @returns {Promise<GetVendorsResponse>}
*/
public async getVendorsList(
filterDTO: IVendorsFilter,
): Promise<GetVendorsResponse> {
// Parses vendors list filter DTO.
const filter = this.parseVendorsListFilterDTO(filterDTO);
// Dynamic list service.
const dynamicList = await this.dynamicListService.dynamicList(
this.vendorModel(),
filter,
);
// Vendors list.
const { results, pagination } = await this.vendorModel()
.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(
results,
new VendorTransfromer(),
);
return {
vendors: transformedVendors,
pagination,
filterMeta: dynamicList.getResponseMeta(),
};
}
/**
*
* @param filterDTO
* @returns
*/
private parseVendorsListFilterDTO(filterDTO) {
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
}
}

View File

@@ -0,0 +1,15 @@
import { ContactTransfromer } from "../../Contacts/Contact.transformer";
export class VendorTransfromer extends ContactTransfromer {
/**
* Include these attributes to expense object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
return [
'formattedBalance',
'formattedOpeningBalance',
'formattedOpeningBalanceAt'
];
};
}

View File

@@ -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
// );
// }
// };
// }

View File

@@ -0,0 +1,124 @@
// Vendor Interfaces.
import { Knex } from 'knex';
import { Vendor } from '../models/Vendor';
import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { CreateVendorDto } from '../dtos/CreateVendor.dto';
import { EditVendorDto } from '../dtos/EditVendor.dto';
// ----------------------------------
export interface IVendorNewDTO extends IContactAddressDTO {
currencyCode: string;
openingBalance?: number;
openingBalanceAt?: string;
openingBalanceExchangeRate?: number;
openingBalanceBranchId?: number;
salutation?: string;
firstName?: string;
lastName?: string;
companyName?: string;
displayName: string;
website?: string;
email?: string;
workPhone?: string;
personalPhone?: string;
note?: string;
active?: boolean;
}
export interface IVendorEditDTO extends IContactAddressDTO {
salutation?: string;
firstName?: string;
lastName?: string;
companyName?: string;
displayName?: string;
website?: string;
email?: string;
workPhone?: string;
personalPhone?: string;
note?: string;
active?: boolean;
}
export interface IVendorsFilter extends IDynamicListFilter {
stringifiedFilterRoles?: string;
page?: number;
pageSize?: number;
}
export interface GetVendorsResponse {
vendors: Vendor[];
pagination: IPaginationMeta;
filterMeta: IFilterMeta;
}
// Vendor Events.
// ----------------------------------
export interface IVendorEventCreatingPayload {
vendorDTO: CreateVendorDto;
trx: Knex.Transaction;
}
export interface IVendorEventCreatedPayload {
vendorId: number;
vendor: Vendor;
trx: Knex.Transaction;
}
export interface IVendorEventDeletingPayload {
vendorId: number;
oldVendor: Vendor;
}
export interface IVendorEventDeletedPayload {
vendorId: number;
oldVendor: Vendor;
trx?: Knex.Transaction;
}
export interface IVendorEventEditingPayload {
vendorDTO: EditVendorDto;
trx?: Knex.Transaction;
}
export interface IVendorEventEditedPayload {
vendorId: number;
vendor: Vendor;
trx?: Knex.Transaction;
}
export interface IVendorOpeningBalanceEditDTO {
openingBalance: number;
openingBalanceAt: Date | string;
openingBalanceExchangeRate: number;
openingBalanceBranchId?: number;
}
export interface IVendorOpeningBalanceEditingPayload {
oldVendor: Vendor;
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO;
trx?: Knex.Transaction;
}
export interface IVendorOpeningBalanceEditedPayload {
vendor: Vendor;
oldVendor: Vendor;
openingBalanceEditDTO: IVendorOpeningBalanceEditDTO;
trx: Knex.Transaction;
}
export interface IVendorActivatingPayload {
oldVendor: Vendor;
trx: Knex.Transaction;
}
export interface IVendorActivatedPayload {
vendor: Vendor;
oldVendor: Vendor;
trx?: Knex.Transaction;
}