mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
feat(nestjs): migrate to NestJS
This commit is contained in:
115
packages/server/src/modules/Vendors/VendorGLEntries.ts
Normal file
115
packages/server/src/modules/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);
|
||||
// };
|
||||
// }
|
||||
66
packages/server/src/modules/Vendors/Vendors.controller.ts
Normal file
66
packages/server/src/modules/Vendors/Vendors.controller.ts
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
36
packages/server/src/modules/Vendors/Vendors.module.ts
Normal file
36
packages/server/src/modules/Vendors/Vendors.module.ts
Normal 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 {}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
30
packages/server/src/modules/Vendors/VendorsExportable.ts
Normal file
30
packages/server/src/modules/Vendors/VendorsExportable.ts
Normal 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);
|
||||
// }
|
||||
// }
|
||||
32
packages/server/src/modules/Vendors/VendorsImportable.ts
Normal file
32
packages/server/src/modules/Vendors/VendorsImportable.ts
Normal 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;
|
||||
// }
|
||||
// }
|
||||
122
packages/server/src/modules/Vendors/_SampleData.ts
Normal file
122
packages/server/src/modules/Vendors/_SampleData.ts
Normal 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"
|
||||
}
|
||||
]
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
27
packages/server/src/modules/Vendors/constants.ts
Normal file
27
packages/server/src/modules/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',
|
||||
};
|
||||
103
packages/server/src/modules/Vendors/dtos/CreateVendor.dto.ts
Normal file
103
packages/server/src/modules/Vendors/dtos/CreateVendor.dto.ts
Normal 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;
|
||||
}
|
||||
60
packages/server/src/modules/Vendors/dtos/EditVendor.dto.ts
Normal file
60
packages/server/src/modules/Vendors/dtos/EditVendor.dto.ts
Normal 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;
|
||||
}
|
||||
220
packages/server/src/modules/Vendors/models/Vendor.ts
Normal file
220
packages/server/src/modules/Vendors/models/Vendor.ts
Normal 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' },
|
||||
];
|
||||
}
|
||||
}
|
||||
28
packages/server/src/modules/Vendors/queries/GetVendor.ts
Normal file
28
packages/server/src/modules/Vendors/queries/GetVendor.ts
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
124
packages/server/src/modules/Vendors/types/Vendors.types.ts
Normal file
124
packages/server/src/modules/Vendors/types/Vendors.types.ts
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user