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,64 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
Query,
} from '@nestjs/common';
import { VendorCreditsApplicationService } from './VendorCreditsApplication.service';
import { IVendorCreditsQueryDTO } from './types/VendorCredit.types';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import {
CreateVendorCreditDto,
EditVendorCreditDto,
} from './dtos/VendorCredit.dto';
@Controller('vendor-credits')
@ApiTags('vendor-credits')
export class VendorCreditsController {
constructor(
private readonly vendorCreditsApplication: VendorCreditsApplicationService,
) {}
@Post()
@ApiOperation({ summary: 'Create a new vendor credit.' })
async createVendorCredit(@Body() dto: CreateVendorCreditDto) {
return this.vendorCreditsApplication.createVendorCredit(dto);
}
@Put(':id/open')
@ApiOperation({ summary: 'Open the given vendor credit.' })
async openVendorCredit(@Param('id') vendorCreditId: number) {
return this.vendorCreditsApplication.openVendorCredit(vendorCreditId);
}
@Get()
@ApiOperation({ summary: 'Retrieves the vendor credits.' })
async getVendorCredits(@Query() filterDTO: IVendorCreditsQueryDTO) {
return this.vendorCreditsApplication.getVendorCredits(filterDTO);
}
@Put(':id')
@ApiOperation({ summary: 'Edit the given vendor credit.' })
async editVendorCredit(
@Param('id') vendorCreditId: number,
@Body() dto: EditVendorCreditDto,
) {
return this.vendorCreditsApplication.editVendorCredit(vendorCreditId, dto);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete the given vendor credit.' })
async deleteVendorCredit(@Param('id') vendorCreditId: number) {
return this.vendorCreditsApplication.deleteVendorCredit(vendorCreditId);
}
@Get(':id')
@ApiOperation({ summary: 'Retrieves the vendor credit details.' })
async getVendorCredit(@Param('id') vendorCreditId: number) {
return this.vendorCreditsApplication.getVendorCredit(vendorCreditId);
}
}

View File

@@ -0,0 +1,72 @@
import { Module } from '@nestjs/common';
import { CreateVendorCreditService } from './commands/CreateVendorCredit.service';
import { DeleteVendorCreditService } from './commands/DeleteVendorCredit.service';
import { EditVendorCreditService } from './commands/EditVendorCredit.service';
import { VendorCreditDTOTransformService } from './commands/VendorCreditDTOTransform.service';
import { VendorCreditAutoIncrementService } from './commands/VendorCreditAutoIncrement.service';
import { GetRefundVendorCreditService } from '../VendorCreditsRefund/queries/GetRefundVendorCredit.service';
import { GetVendorCreditService } from './queries/GetVendorCredit.service';
import { VendorCreditsController } from './VendorCredits.controller';
import { ItemsModule } from '../Items/items.module';
import { TemplateInjectableModule } from '../TemplateInjectable/TemplateInjectable.module';
import { AutoIncrementOrdersModule } from '../AutoIncrementOrders/AutoIncrementOrders.module';
import { ChromiumlyTenancyModule } from '../ChromiumlyTenancy/ChromiumlyTenancy.module';
import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module';
import { BranchesModule } from '../Branches/Branches.module';
import { WarehousesModule } from '../Warehouses/Warehouses.module';
import { VendorCreditsApplicationService } from './VendorCreditsApplication.service';
import { OpenVendorCreditService } from './commands/OpenVendorCredit.service';
import { VendorCreditGlEntriesSubscriber } from './subscribers/VendorCreditGLEntriesSubscriber';
import { VendorCreditGLEntries } from './commands/VendorCreditGLEntries';
import { LedgerModule } from '../Ledger/Ledger.module';
import { AccountsModule } from '../Accounts/Accounts.module';
import VendorCreditInventoryTransactionsSubscriber from './subscribers/VendorCreditInventoryTransactionsSusbcriber';
import { VendorCreditInventoryTransactions } from './commands/VendorCreditInventoryTransactions';
import { GetVendorCreditsService } from './queries/GetVendorCredits.service';
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { InventoryCostModule } from '../InventoryCost/InventoryCost.module';
@Module({
imports: [
ItemsModule,
PdfTemplatesModule,
ChromiumlyTenancyModule,
TemplateInjectableModule,
AutoIncrementOrdersModule,
BranchesModule,
WarehousesModule,
LedgerModule,
AccountsModule,
DynamicListModule,
InventoryCostModule
],
providers: [
CreateVendorCreditService,
DeleteVendorCreditService,
EditVendorCreditService,
VendorCreditDTOTransformService,
VendorCreditAutoIncrementService,
GetRefundVendorCreditService,
GetVendorCreditService,
GetVendorCreditsService,
VendorCreditsApplicationService,
OpenVendorCreditService,
VendorCreditGLEntries,
VendorCreditGlEntriesSubscriber,
VendorCreditInventoryTransactions,
VendorCreditInventoryTransactionsSubscriber
],
exports: [
CreateVendorCreditService,
DeleteVendorCreditService,
EditVendorCreditService,
VendorCreditDTOTransformService,
VendorCreditAutoIncrementService,
GetRefundVendorCreditService,
GetVendorCreditService,
VendorCreditsApplicationService,
OpenVendorCreditService
],
controllers: [VendorCreditsController],
})
export class VendorCreditsModule {}

View File

@@ -0,0 +1,93 @@
import { Knex } from 'knex';
import { CreateVendorCreditService } from './commands/CreateVendorCredit.service';
import { DeleteVendorCreditService } from './commands/DeleteVendorCredit.service';
import { EditVendorCreditService } from './commands/EditVendorCredit.service';
import { GetVendorCreditService } from './queries/GetVendorCredit.service';
import { IVendorCreditEditDTO, IVendorCreditsQueryDTO } from './types/VendorCredit.types';
import { IVendorCreditCreateDTO } from './types/VendorCredit.types';
import { Injectable } from '@nestjs/common';
import { OpenVendorCreditService } from './commands/OpenVendorCredit.service';
import { GetVendorCreditsService } from './queries/GetVendorCredits.service';
import { CreateVendorCreditDto, EditVendorCreditDto } from './dtos/VendorCredit.dto';
@Injectable()
export class VendorCreditsApplicationService {
/**
* @param {CreateVendorCreditService} createVendorCreditService - Create vendor credit service.
* @param {EditVendorCreditService} editVendorCreditService - Edit vendor credit service.
* @param {DeleteVendorCreditService} deleteVendorCreditService - Delete vendor credit service.
* @param {GetVendorCreditService} getVendorCreditService - Get vendor credit service.
*/
constructor(
private readonly createVendorCreditService: CreateVendorCreditService,
private readonly editVendorCreditService: EditVendorCreditService,
private readonly deleteVendorCreditService: DeleteVendorCreditService,
private readonly getVendorCreditService: GetVendorCreditService,
private readonly openVendorCreditService: OpenVendorCreditService,
private readonly getVendorCreditsService: GetVendorCreditsService,
) {}
/**
* Creates a new vendor credit.
* @param {CreateVendorCreditDto} dto - The vendor credit create DTO.
* @param {Knex.Transaction} trx - The transaction.
* @returns {Promise<VendorCredit>} The created vendor credit.
*/
createVendorCredit(dto: CreateVendorCreditDto, trx?: Knex.Transaction) {
return this.createVendorCreditService.newVendorCredit(dto, trx);
}
/**
* Opens the given vendor credit.
* @param {number} vendorCreditId - The vendor credit id.
* @returns {Promise<VendorCredit>} The opened vendor credit.
*/
openVendorCredit(vendorCreditId: number) {
return this.openVendorCreditService.openVendorCredit(vendorCreditId);
}
/**
* Edits the given vendor credit.
* @param {number} vendorCreditId - The vendor credit id.
* @param {EditVendorCreditDto} dto - The vendor credit edit DTO.
* @param {Knex.Transaction} trx - The transaction.
* @returns {Promise<VendorCredit>} The edited vendor credit.
*/
editVendorCredit(
vendorCreditId: number,
dto: EditVendorCreditDto,
trx?: Knex.Transaction,
) {
return this.editVendorCreditService.editVendorCredit(
vendorCreditId,
dto,
trx,
);
}
/**
* Deletes the given vendor credit.
* @param {number} vendorCreditId - The vendor credit id.
* @param {Knex.Transaction} trx - The transaction.
* @returns {Promise<VendorCredit>} The deleted vendor credit.
*/
deleteVendorCredit(vendorCreditId: number, trx?: Knex.Transaction) {
return this.deleteVendorCreditService.deleteVendorCredit(
vendorCreditId,
trx,
);
}
getVendorCredit(vendorCreditId: number, trx?: Knex.Transaction) {
return this.getVendorCreditService.getVendorCredit(vendorCreditId, trx);
}
/**
* Retrieves the paginated filterable vendor credits list.
* @param {IVendorCreditsQueryDTO} query
* @returns {}
*/
getVendorCredits(query: IVendorCreditsQueryDTO) {
return this.getVendorCreditsService.getVendorCredits(query);
}
}

View File

@@ -0,0 +1,93 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import {
IVendorCreditCreatedPayload,
IVendorCreditCreatingPayload,
} from '@/modules/VendorCredit/types/VendorCredit.types';
import { VendorCredit } from '../models/VendorCredit';
import { Vendor } from '@/modules/Vendors/models/Vendor';
import { events } from '@/common/events/events';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { VendorCreditDTOTransformService } from './VendorCreditDTOTransform.service';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { CreateVendorCreditDto } from '../dtos/VendorCredit.dto';
@Injectable()
export class CreateVendorCreditService {
/**
* @param {UnitOfWork} uow - The unit of work service.
* @param {ItemsEntriesService} itemsEntriesService - The items entries service.
* @param {EventEmitter2} eventPublisher - The event emitter service.
* @param {typeof VendorCredit} vendorCreditModel - The vendor credit model.
* @param {typeof Vendor} vendorModel - The vendor model.
*/
constructor(
private readonly uow: UnitOfWork,
private readonly itemsEntriesService: ItemsEntriesService,
private readonly eventPublisher: EventEmitter2,
private readonly vendorCreditDTOTransformService: VendorCreditDTOTransformService,
@Inject(VendorCredit.name)
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
@Inject(Vendor.name)
private readonly vendorModel: TenantModelProxy<typeof Vendor>,
) {}
/**
* Creates a new vendor credit.
* @param {IVendorCreditCreateDTO} vendorCreditCreateDTO -
* @param {Knex.Transaction} trx -
*/
public newVendorCredit = async (
vendorCreditCreateDTO: CreateVendorCreditDto,
trx?: Knex.Transaction,
) => {
// Triggers `onVendorCreditCreate` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onCreate, {
vendorCreditCreateDTO,
});
// Retrieve the given vendor or throw not found service error.
const vendor = await this.vendorModel()
.query()
.findById(vendorCreditCreateDTO.vendorId)
.throwIfNotFound();
// Validate items should be sellable items.
await this.itemsEntriesService.validateNonSellableEntriesItems(
vendorCreditCreateDTO.entries,
);
// Transforms the credit DTO to storage layer.
const vendorCreditModel =
await this.vendorCreditDTOTransformService.transformCreateEditDTOToModel(
vendorCreditCreateDTO,
vendor.currencyCode,
);
// Saves the vendor credit transactions under UOW environment.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onVendorCreditCreating` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onCreating, {
vendorCreditCreateDTO,
trx,
} as IVendorCreditCreatingPayload);
// Saves the vendor credit graph.
const vendorCredit = await this.vendorCreditModel()
.query(trx)
.upsertGraphAndFetch({
...vendorCreditModel,
});
// Triggers `onVendorCreditCreated` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onCreated, {
vendorCredit,
vendorCreditCreateDTO,
trx,
} as IVendorCreditCreatedPayload);
return vendorCredit;
}, trx);
};
}

View File

@@ -0,0 +1,128 @@
import { Inject, Injectable } from '@nestjs/common';
import { Knex } from 'knex';
import { EventEmitter2 } from '@nestjs/event-emitter';
import {
IVendorCreditDeletedPayload,
IVendorCreditDeletingPayload,
} from '@/modules/VendorCredit/types/VendorCredit.types';
import { ERRORS } from '../constants';
import { VendorCredit } from '../models/VendorCredit';
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
import { VendorCreditAppliedBill } from '../../VendorCreditsApplyBills/models/VendorCreditAppliedBill';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { ServiceError } from '@/modules/Items/ServiceError';
import { RefundVendorCredit } from '../../VendorCreditsRefund/models/RefundVendorCredit';
import { events } from '@/common/events/events';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class DeleteVendorCreditService {
/**
* @param {typeof VendorCredit} vendorCreditModel - The vendor credit model.
* @param {typeof ItemEntry} itemEntryModel - The item entry model.
* @param {EventEmitter2} eventPublisher - The event emitter service.
* @param {UnitOfWork} uow - The unit of work service.
* @param {typeof RefundVendorCredit} refundVendorCreditModel - The refund vendor credit model.
* @param {typeof VendorCreditAppliedBill} vendorCreditAppliedBillModel - The vendor credit applied bill model.
*/
constructor(
private eventPublisher: EventEmitter2,
private uow: UnitOfWork,
@Inject(ItemEntry.name)
private itemEntryModel: TenantModelProxy<typeof ItemEntry>,
@Inject(VendorCredit.name)
private vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
@Inject(RefundVendorCredit.name)
private refundVendorCreditModel: TenantModelProxy<
typeof RefundVendorCredit
>,
@Inject(VendorCreditAppliedBill.name)
private vendorCreditAppliedBillModel: TenantModelProxy<
typeof VendorCreditAppliedBill
>,
) {}
/**
* Deletes the given vendor credit.
* @param {number} vendorCreditId - Vendor credit id.
*/
public deleteVendorCredit = async (
vendorCreditId: number,
trx?: Knex.Transaction,
) => {
// Retrieve the old vendor credit.
const oldVendorCredit = await this.vendorCreditModel()
.query()
.findById(vendorCreditId)
.throwIfNotFound();
// Validates vendor credit has no associate refund transactions.
await this.validateVendorCreditHasNoRefundTransactions(vendorCreditId);
// Validates vendor credit has no associated applied to bills transactions.
await this.validateVendorCreditHasNoApplyBillsTransactions(vendorCreditId);
// Deletes the vendor credit transactions under UOW environment.
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onVendorCreditEditing` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onDeleting, {
oldVendorCredit,
trx,
} as IVendorCreditDeletingPayload);
// Deletes the associated credit note entries.
await this.itemEntryModel()
.query(trx)
.where('reference_id', vendorCreditId)
.where('reference_type', 'VendorCredit')
.delete();
// Deletes the credit note transaction.
await this.vendorCreditModel()
.query(trx)
.findById(vendorCreditId)
.delete();
// Triggers `onVendorCreditDeleted` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onDeleted, {
vendorCreditId,
oldVendorCredit,
trx,
} as IVendorCreditDeletedPayload);
});
};
/**
* Validates vendor credit has no refund transactions.
* @param {number} vendorCreditId
*/
private validateVendorCreditHasNoRefundTransactions = async (
vendorCreditId: number,
): Promise<void> => {
const refundCredits = await this.refundVendorCreditModel()
.query()
.where('vendorCreditId', vendorCreditId);
if (refundCredits.length > 0) {
throw new ServiceError(ERRORS.VENDOR_CREDIT_HAS_REFUND_TRANSACTIONS);
}
};
/**
* Validate vendor credit has no applied transactions to bills.
* @param {number} vendorCreditId
*/
private validateVendorCreditHasNoApplyBillsTransactions = async (
vendorCreditId: number,
): Promise<void> => {
const appliedTransactions = await this.vendorCreditAppliedBillModel()
.query()
.where('vendorCreditId', vendorCreditId);
if (appliedTransactions.length > 0) {
throw new ServiceError(ERRORS.VENDOR_CREDIT_HAS_APPLIED_BILLS);
}
};
}

View File

@@ -0,0 +1,110 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import {
IVendorCreditEditedPayload,
IVendorCreditEditingPayload,
} from '../types/VendorCredit.types';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
import { VendorCredit } from '../models/VendorCredit';
import { Contact } from '@/modules/Contacts/models/Contact';
import { events } from '@/common/events/events';
import { VendorCreditDTOTransformService } from './VendorCreditDTOTransform.service';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { EditVendorCreditDto } from '../dtos/VendorCredit.dto';
@Injectable()
export class EditVendorCreditService {
/**
* @param {EventEmitter2} eventPublisher - The event emitter service.
* @param {UnitOfWork} uow - The unit of work service.
* @param {ItemsEntriesService} itemsEntriesService - The items entries service.
* @param {typeof VendorCredit} vendorCreditModel - The vendor credit model.
* @param {typeof Contact} contactModel - The contact model.
*/
constructor(
private readonly eventPublisher: EventEmitter2,
private readonly uow: UnitOfWork,
private readonly itemsEntriesService: ItemsEntriesService,
private readonly vendorCreditDTOTransform: VendorCreditDTOTransformService,
@Inject(VendorCredit.name)
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
@Inject(Contact.name)
private readonly contactModel: TenantModelProxy<typeof Contact>,
) {}
/**
* Deletes the given vendor credit.
* @param {number} vendorCreditId - Vendor credit id.
* @param {EditVendorCreditDto} vendorCreditDto -
*/
public editVendorCredit = async (
vendorCreditId: number,
vendorCreditDTO: EditVendorCreditDto,
trx?: Knex.Transaction,
) => {
// Retrieve the vendor credit or throw not found service error.
const oldVendorCredit = await this.vendorCreditModel()
.query()
.findById(vendorCreditId)
.throwIfNotFound();
// Validate customer existance.
const vendor = await this.contactModel()
.query()
.modify('vendor')
.findById(vendorCreditDTO.vendorId)
.throwIfNotFound();
// Validate items ids existance.
await this.itemsEntriesService.validateItemsIdsExistance(
vendorCreditDTO.entries,
);
// Validate non-sellable entries items.
await this.itemsEntriesService.validateNonSellableEntriesItems(
vendorCreditDTO.entries,
);
// Validate the items entries existance.
await this.itemsEntriesService.validateEntriesIdsExistance(
vendorCreditId,
'VendorCredit',
vendorCreditDTO.entries,
);
// Transformes edit DTO to model storage layer.
const vendorCreditModel =
await this.vendorCreditDTOTransform.transformCreateEditDTOToModel(
vendorCreditDTO,
vendor.currencyCode,
oldVendorCredit,
);
// Edits the vendor credit graph under unit-of-work envirement.
return this.uow.withTransaction(async (trx) => {
// Triggers `onVendorCreditEditing` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onEditing, {
oldVendorCredit,
vendorCreditDTO,
trx,
} as IVendorCreditEditingPayload);
// Saves the vendor credit graph to the storage.
const vendorCredit = await this.vendorCreditModel()
.query(trx)
.upsertGraphAndFetch({
id: vendorCreditId,
...vendorCreditModel,
});
// Triggers `onVendorCreditEdited event.
await this.eventPublisher.emitAsync(events.vendorCredit.onEdited, {
oldVendorCredit,
vendorCredit,
vendorCreditDTO,
trx,
} as IVendorCreditEditedPayload);
return vendorCredit;
}, trx);
};
}

View File

@@ -0,0 +1,94 @@
import { Inject, Injectable } from '@nestjs/common';
import {
IVendorCreditOpenedPayload,
IVendorCreditOpeningPayload,
IVendorCreditOpenPayload,
} from '../types/VendorCredit.types';
import { ERRORS } from '../constants';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { VendorCredit } from '../models/VendorCredit';
import { events } from '@/common/events/events';
import { ServiceError } from '@/modules/Items/ServiceError';
import { Knex } from 'knex';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class OpenVendorCreditService {
/**
* @param {EventEmitter2} eventPublisher - The event emitter service.
* @param {UnitOfWork} uow - The unit of work service.
* @param {typeof VendorCredit} vendorCreditModel - The vendor credit model.
*/
constructor(
private eventPublisher: EventEmitter2,
private uow: UnitOfWork,
@Inject(VendorCredit.name)
private vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
) {}
/**
* Opens the given credit note.
* @param {number} vendorCreditId -
* @returns {Promise<IVendorCredit>}
*/
public openVendorCredit = async (
vendorCreditId: number,
trx?: Knex.Transaction,
): Promise<VendorCredit> => {
// Retrieve the vendor credit or throw not found service error.
const oldVendorCredit = await this.vendorCreditModel()
.query()
.findById(vendorCreditId)
.throwIfNotFound();
// Throw service error if the credit note is already open.
this.throwErrorIfAlreadyOpen(oldVendorCredit);
// Triggers `onVendorCreditOpen` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onOpen, {
vendorCreditId,
oldVendorCredit,
} as IVendorCreditOpenPayload);
// Sales the credit note transactions with associated entries.
return this.uow.withTransaction(async (trx) => {
const eventPayload = {
vendorCreditId,
oldVendorCredit,
trx,
} as IVendorCreditOpeningPayload;
// Triggers `onCreditNoteOpening` event.
await this.eventPublisher.emitAsync(
events.creditNote.onOpening,
eventPayload as IVendorCreditOpeningPayload,
);
// Saves the vendor credit graph to the storage.
const vendorCredit = await this.vendorCreditModel()
.query(trx)
.findById(vendorCreditId)
.updateAndFetchById(vendorCreditId, {
openedAt: new Date(),
});
// Triggers `onVendorCreditOpened` event.
await this.eventPublisher.emitAsync(events.vendorCredit.onOpened, {
...eventPayload,
vendorCredit,
} as IVendorCreditOpenedPayload);
return vendorCredit;
}, trx);
};
/**
* Throw error if the vendor credit is already open.
* @param {IVendorCredit} vendorCredit
*/
public throwErrorIfAlreadyOpen = (vendorCredit: VendorCredit) => {
if (vendorCredit.openedAt) {
throw new ServiceError(ERRORS.VENDOR_CREDIT_ALREADY_OPENED);
}
};
}

View File

@@ -0,0 +1,31 @@
import { AutoIncrementOrdersService } from '@/modules/AutoIncrementOrders/AutoIncrementOrders.service';
import { Injectable } from '@nestjs/common';
@Injectable()
export class VendorCreditAutoIncrementService {
/**
* @param {AutoIncrementOrdersService} autoIncrementOrdersService - Auto increment orders service.
*/
constructor(private autoIncrementOrdersService: AutoIncrementOrdersService) {}
/**
* Retrieve the next unique credit number.
* @param {number} tenantId - Tenant id.
* @return {string}
*/
public getNextCreditNumber = (): Promise<string> => {
return this.autoIncrementOrdersService.getNextTransactionNumber(
'vendor_credit',
);
};
/**
* Increment the vendor credit serial next number.
*/
public incrementSerialNumber = () => {
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
'vendor_credit',
);
};
}

View File

@@ -0,0 +1,103 @@
import * as moment from 'moment';
import { omit } from 'lodash';
import * as R from 'ramda';
import * as composeAsync from 'async/compose';
import { ERRORS } from '../constants';
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
import { WarehouseTransactionDTOTransform } from '@/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform';
import { VendorCredit } from '../models/VendorCredit';
import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index';
import { VendorCreditAutoIncrementService } from './VendorCreditAutoIncrement.service';
import { ServiceError } from '@/modules/Items/ServiceError';
import { Injectable } from '@nestjs/common';
import {
CreateVendorCreditDto,
EditVendorCreditDto,
VendorCreditEntryDto,
} from '../dtos/VendorCredit.dto';
@Injectable()
export class VendorCreditDTOTransformService {
/**
* @param {ItemsEntriesService} itemsEntriesService - The items entries service.
* @param {BranchTransactionDTOTransformer} branchDTOTransform - The branch transaction DTO transformer.
* @param {WarehouseTransactionDTOTransform} warehouseDTOTransform - The warehouse transaction DTO transformer.
* @param {VendorCreditAutoIncrementService} vendorCreditAutoIncrement - The vendor credit auto increment service.
*/
constructor(
private itemsEntriesService: ItemsEntriesService,
private branchDTOTransform: BranchTransactionDTOTransformer,
private warehouseDTOTransform: WarehouseTransactionDTOTransform,
private vendorCreditAutoIncrement: VendorCreditAutoIncrementService,
) {}
/**
* Transforms the credit/edit vendor credit DTO to model.
* @param {IVendorCreditCreateDTO | IVendorCreditEditDTO} vendorCreditDTO
* @param {string} vendorCurrencyCode -
* @param {IVendorCredit} oldVendorCredit -
* @returns {VendorCredit}
*/
public transformCreateEditDTOToModel = async (
vendorCreditDTO: CreateVendorCreditDto | EditVendorCreditDto,
vendorCurrencyCode: string,
oldVendorCredit?: VendorCredit,
): Promise<VendorCredit> => {
// Calculates the total amount of items entries.
const amount = this.itemsEntriesService.getTotalItemsEntries(
vendorCreditDTO.entries,
);
const entries = R.compose(
// Associate the default index to each item entry.
assocItemEntriesDefaultIndex,
// Associate the reference type to item entries.
R.map((entry: VendorCreditEntryDto) => ({
referenceType: 'VendorCredit',
...entry,
})),
)(vendorCreditDTO.entries);
// Retreive the next vendor credit number.
const autoNextNumber =
await this.vendorCreditAutoIncrement.getNextCreditNumber();
// Detarmines the credit note number.
const vendorCreditNumber =
vendorCreditDTO.vendorCreditNumber ||
oldVendorCredit?.vendorCreditNumber ||
autoNextNumber;
const initialDTO = {
...omit(vendorCreditDTO, ['open', 'attachments']),
amount,
currencyCode: vendorCurrencyCode,
exchangeRate: vendorCreditDTO.exchangeRate || 1,
vendorCreditNumber,
entries,
...(vendorCreditDTO.open &&
!oldVendorCredit?.openedAt && {
openedAt: moment().toMySqlDateTime(),
}),
};
return composeAsync(
this.branchDTOTransform.transformDTO<VendorCredit>,
this.warehouseDTOTransform.transformDTO<VendorCredit>,
)(initialDTO) as VendorCredit;
};
/**
* Validate the credit note remaining amount.
* @param {ICreditNote} creditNote
* @param {number} amount
*/
public validateCreditRemainingAmount = (
vendorCredit: VendorCredit,
amount: number,
) => {
if (vendorCredit.creditsRemaining < amount) {
throw new ServiceError(ERRORS.VENDOR_CREDIT_HAS_NO_REMAINING_AMOUNT);
}
};
}

View File

@@ -0,0 +1,167 @@
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
import { VendorCredit } from '../models/VendorCredit';
import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
import { AccountNormal } from '@/interfaces/Account';
import { Ledger } from '@/modules/Ledger/Ledger';
export class VendorCreditGL {
private APAccountId: number;
private purchaseDiscountAccountId: number;
private otherExpensesAccountId: number;
constructor(private vendorCredit: VendorCredit) {}
/**
* Sets the payable account (A/P) ID.
* @param {number} APAccountId
* @returns {VendorCreditGL}
*/
public setAPAccountId(APAccountId: number) {
this.APAccountId = APAccountId;
return this;
}
/**
* Sets the purchase discount account ID.
* @param {number} purchaseDiscountAccountId
* @returns {VendorCreditGL}
*/
public setPurchaseDiscountAccountId(purchaseDiscountAccountId: number) {
this.purchaseDiscountAccountId = purchaseDiscountAccountId;
return this;
}
/**
* Sets the other expenses account ID.
* @param {number} otherExpensesAccountId
* @returns {VendorCreditGL}
*/
public setOtherExpensesAccountId(otherExpensesAccountId: number) {
this.otherExpensesAccountId = otherExpensesAccountId;
return this;
}
/**
* Retrieve the vendor credit GL common entry.
* @param {IVendorCredit} vendorCredit
*/
public get vendorCreditGLCommonEntry() {
return {
date: this.vendorCredit.vendorCreditDate,
currencyCode: this.vendorCredit.currencyCode,
exchangeRate: this.vendorCredit.exchangeRate,
transactionId: this.vendorCredit.id,
transactionType: 'VendorCredit',
transactionNumber: this.vendorCredit.vendorCreditNumber,
referenceNumber: this.vendorCredit.referenceNo,
credit: 0,
debit: 0,
branchId: this.vendorCredit.branchId,
};
}
/**
* Retrieves the vendor credit payable GL entry.
* @returns {ILedgerEntry}
*/
public get vendorCreditPayableGLEntry(): ILedgerEntry {
const commonEntity = this.vendorCreditGLCommonEntry;
return {
...commonEntity,
debit: this.vendorCredit.totalLocal,
accountId: this.APAccountId,
contactId: this.vendorCredit.vendorId,
accountNormal: AccountNormal.CREDIT,
index: 1,
};
}
/**
* Retrieves the vendor credit item GL entry.
* @returns {ILedgerEntry}
*/
public getVendorCreditGLItemEntry(
entry: ItemEntry,
index: number,
): ILedgerEntry {
const commonEntity = this.vendorCreditGLCommonEntry;
const totalLocal = entry.totalExcludingTax * this.vendorCredit.exchangeRate;
return {
...commonEntity,
credit: totalLocal,
index: index + 2,
itemId: entry.itemId,
// itemQuantity: entry.quantity,
accountId:
'inventory' === entry.item.type
? entry.item.inventoryAccountId
: entry.costAccountId || entry.item.costAccountId,
accountNormal: AccountNormal.DEBIT,
};
}
/**
* Retrieves the vendor credit discount GL entry.
* @returns {ILedgerEntry}
*/
public get discountEntry(): ILedgerEntry {
const commonEntry = this.vendorCreditGLCommonEntry;
return {
...commonEntry,
debit: this.vendorCredit.discountAmountLocal,
accountId: this.purchaseDiscountAccountId,
accountNormal: AccountNormal.DEBIT,
index: 1,
indexGroup: 40,
};
}
/**
* Retrieves the vendor credit adjustment GL entry.
* @returns {ILedgerEntry}
*/
public get adjustmentEntry(): ILedgerEntry {
const commonEntry = this.vendorCreditGLCommonEntry;
const adjustmentAmount = Math.abs(this.vendorCredit.adjustmentLocal);
return {
...commonEntry,
credit: this.vendorCredit.adjustmentLocal > 0 ? adjustmentAmount : 0,
debit: this.vendorCredit.adjustmentLocal < 0 ? adjustmentAmount : 0,
accountId: this.otherExpensesAccountId,
accountNormal: AccountNormal.DEBIT,
index: 1,
indexGroup: 40,
};
}
/**
* Retrieve the vendor credit GL entries.
* @return {ILedgerEntry[]}
*/
public getVendorCreditGLEntries(): ILedgerEntry[] {
const payableEntry = this.vendorCreditPayableGLEntry;
const itemsEntries = this.vendorCredit.entries.map((entry, index) =>
this.getVendorCreditGLItemEntry(entry, index),
);
const discountEntry = this.discountEntry;
const adjustmentEntry = this.adjustmentEntry;
return [payableEntry, discountEntry, adjustmentEntry, ...itemsEntries];
}
/**
* Retrieves the vendor credit ledger.
* @returns {Ledger}
*/
public getVendorCreditLedger(): Ledger {
const entries = this.getVendorCreditGLEntries();
return new Ledger(entries);
}
}

View File

@@ -0,0 +1,89 @@
import { Knex } from 'knex';
import { VendorCreditGL } from './VendorCreditGL';
import { LedgerStorageService } from '@/modules/Ledger/LedgerStorage.service';
import { Inject, Injectable } from '@nestjs/common';
import { AccountRepository } from '@/modules/Accounts/repositories/Account.repository';
import { VendorCredit } from '../models/VendorCredit';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class VendorCreditGLEntries {
constructor(
private readonly ledgerStorage: LedgerStorageService,
private readonly accountRepository: AccountRepository,
@Inject(VendorCredit.name)
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
) {}
/**
* Creates vendor credit associated GL entries.
* @param {number} vendorCreditId
* @param {Knex.Transaction} trx
*/
public writeVendorCreditGLEntries = async (
vendorCreditId: number,
trx?: Knex.Transaction,
) => {
// Vendor credit with entries items.
const vendorCredit = await this.vendorCreditModel()
.query(trx)
.findById(vendorCreditId)
.withGraphFetched('entries.item');
// Retrieve the payable account (A/P) account.
const APAccount = await this.accountRepository.findOrCreateAccountsPayable(
vendorCredit.currencyCode,
{},
trx,
);
// Retrieve the purchase discount account.
const purchaseDiscountAccount =
await this.accountRepository.findOrCreatePurchaseDiscountAccount({}, trx);
// Retrieve the other expenses account.
const otherExpensesAccount =
await this.accountRepository.findOrCreateOtherExpensesAccount({}, trx);
const vendorCreditLedger = new VendorCreditGL(vendorCredit)
.setAPAccountId(APAccount.id)
.setPurchaseDiscountAccountId(purchaseDiscountAccount.id)
.setOtherExpensesAccountId(otherExpensesAccount.id)
.getVendorCreditLedger();
// Commits the ledger entries to the storage.
await this.ledgerStorage.commit(vendorCreditLedger, trx);
};
/**
* Edits vendor credit associated GL entries.
* @param {number} vendorCreditId
* @param {Knex.Transaction} trx
*/
public rewriteVendorCreditGLEntries = async (
vendorCreditId: number,
trx?: Knex.Transaction,
) => {
// Reverts the GL entries.
await this.revertVendorCreditGLEntries(vendorCreditId, trx);
// Re-write the GL entries.
await this.writeVendorCreditGLEntries(vendorCreditId, trx);
};
/**
* Reverts the vendor credit associated GL entries.
* @param {number} vendorCreditId - Vendor credit identifier.
* @param {Knex.Transaction} trx
*/
public async revertVendorCreditGLEntries(
vendorCreditId: number,
trx?: Knex.Transaction,
): Promise<void> {
await this.ledgerStorage.deleteByReference(
vendorCreditId,
'VendorCredit',
trx,
);
}
}

View File

@@ -0,0 +1,83 @@
// @ts-nocheck
import { Knex } from 'knex';
import { Injectable } from '@nestjs/common';
import { VendorCredit } from '../models/VendorCredit';
import { InventoryTransactionsService } from '@/modules/InventoryCost/commands/InventoryTransactions.service';
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
@Injectable()
export class VendorCreditInventoryTransactions {
constructor(
private readonly inventoryService: InventoryTransactionsService,
private readonly itemsEntriesService: ItemsEntriesService
) {}
/**
* Creates vendor credit associated inventory transactions.
* @param {IVnedorCredit} vendorCredit
* @param {Knex.Transaction} trx
*/
public createInventoryTransactions = async (
vendorCredit: VendorCredit,
trx: Knex.Transaction
): Promise<void> => {
// Loads the inventory items entries of the given sale invoice.
const inventoryEntries =
await this.itemsEntriesService.filterInventoryEntries(
vendorCredit.entries
);
const transaction = {
transactionId: vendorCredit.id,
transactionType: 'VendorCredit',
transactionNumber: vendorCredit.vendorCreditNumber,
exchangeRate: vendorCredit.exchangeRate,
date: vendorCredit.vendorCreditDate,
direction: 'OUT',
entries: inventoryEntries,
warehouseId: vendorCredit.warehouseId,
createdAt: vendorCredit.createdAt,
};
// Writes inventory tranactions.
await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
transaction,
false,
trx
);
};
/**
* Edits vendor credit associated inventory transactions.
* @param {number} vendorCreditId - Vendor credit id.
* @param {IVendorCredit} vendorCredit - Vendor credit.
* @param {Knex.Transactions} trx - Knex transaction.
*/
public async editInventoryTransactions(
vendorCreditId: number,
vendorCredit: VendorCredit,
trx?: Knex.Transaction
): Promise<void> {
// Deletes inventory transactions.
await this.deleteInventoryTransactions(vendorCreditId, trx);
// Re-write inventory transactions.
await this.createInventoryTransactions(vendorCredit, trx);
};
/**
* Deletes credit note associated inventory transactions.
* @param {number} vendorCreditId - Vendor credit id.
* @param {Knex.Transaction} trx - Knex transaction.
*/
public async deleteInventoryTransactions(
vendorCreditId: number,
trx?: Knex.Transaction
): Promise<void> {
// Deletes the inventory transactions by the given reference id and type.
await this.inventoryService.deleteInventoryTransactions(
vendorCreditId,
'VendorCredit',
trx
);
};
}

View File

@@ -0,0 +1,36 @@
// import { Inject, Service } from 'typedi';
// import { IVendorCreditsQueryDTO } from '@/interfaces';
// import ListVendorCredits from '../queries/ListVendorCredits';
// import { Exportable } from '@/services/Export/Exportable';
// import { QueryBuilder } from 'knex';
// @Service()
// export class VendorCreditsExportable extends Exportable {
// @Inject()
// private getVendorCredits: ListVendorCredits;
// /**
// * Retrieves the vendor credits data to exportable sheet.
// * @param {number} tenantId -
// * @param {IVendorCreditsQueryDTO} query -
// * @returns {}
// */
// public exportable(tenantId: number, query: IVendorCreditsQueryDTO) {
// const filterQuery = (query) => {
// query.withGraphFetched('branch');
// query.withGraphFetched('warehouse');
// };
// const parsedQuery = {
// sortOrder: 'desc',
// columnSortBy: 'created_at',
// ...query,
// page: 1,
// pageSize: 12000,
// filterQuery,
// } as IVendorCreditsQueryDTO;
// return this.getVendorCredits
// .getVendorCredits(tenantId, parsedQuery)
// .then((output) => output.vendorCredits);
// }
// }

View File

@@ -0,0 +1,45 @@
// import { Inject, Service } from 'typedi';
// import { Knex } from 'knex';
// import { Importable } from '@/services/Import/Importable';
// import CreateVendorCredit from './CreateVendorCredit.service';
// import { IVendorCreditCreateDTO } from '@/interfaces';
// import { VendorCreditsSampleData } from '../constants';
// @Service()
// export class VendorCreditsImportable extends Importable {
// @Inject()
// private createVendorCreditService: CreateVendorCredit;
// /**
// * Importing to account service.
// * @param {number} tenantId
// * @param {IAccountCreateDTO} createAccountDTO
// * @returns
// */
// public importable(
// tenantId: number,
// createPaymentDTO: IVendorCreditCreateDTO,
// trx?: Knex.Transaction
// ) {
// return this.createVendorCreditService.newVendorCredit(
// tenantId,
// createPaymentDTO,
// trx
// );
// }
// /**
// * Concurrrency controlling of the importing process.
// * @returns {number}
// */
// public get concurrency() {
// return 1;
// }
// /**
// * Retrieves the sample data that used to download accounts sample sheet.
// */
// public sampleData(): any[] {
// return VendorCreditsSampleData;
// }
// }

View File

@@ -0,0 +1,82 @@
export const ERRORS = {
VENDOR_CREDIT_NOT_FOUND: 'VENDOR_CREDIT_NOT_FOUND',
VENDOR_CREDIT_ALREADY_OPENED: 'VENDOR_CREDIT_ALREADY_OPENED',
VENDOR_CREDIT_HAS_NO_REMAINING_AMOUNT:
'VENDOR_CREDIT_HAS_NO_REMAINING_AMOUNT',
VENDOR_CREDIT_APPLY_TO_BILLS_NOT_FOUND:
'VENDOR_CREDIT_APPLY_TO_BILLS_NOT_FOUND',
BILLS_HAS_NO_REMAINING_AMOUNT: 'BILLS_HAS_NO_REMAINING_AMOUNT',
VENDOR_CREDIT_HAS_REFUND_TRANSACTIONS:
'VENDOR_CREDIT_HAS_REFUND_TRANSACTIONS',
VENDOR_CREDIT_HAS_APPLIED_BILLS: 'VENDOR_CREDIT_HAS_APPLIED_BILLS',
};
export const DEFAULT_VIEW_COLUMNS = [];
export const DEFAULT_VIEWS = [
{
name: 'vendor_credit.view.draft',
slug: 'draft',
rolesLogicExpression: '1',
roles: [
{ index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' },
],
columns: DEFAULT_VIEW_COLUMNS,
},
{
name: 'vendor_credit.view.published',
slug: 'published',
rolesLogicExpression: '1',
roles: [
{
index: 1,
fieldKey: 'status',
comparator: 'equals',
value: 'published',
},
],
columns: DEFAULT_VIEW_COLUMNS,
},
{
name: 'vendor_credit.view.open',
slug: 'open',
rolesLogicExpression: '1',
roles: [
{
index: 1,
fieldKey: 'status',
comparator: 'equals',
value: 'open',
},
],
columns: DEFAULT_VIEW_COLUMNS,
},
{
name: 'vendor_credit.view.closed',
slug: 'closed',
rolesLogicExpression: '1',
roles: [
{
index: 1,
fieldKey: 'status',
comparator: 'equals',
value: 'closed',
},
],
columns: DEFAULT_VIEW_COLUMNS,
},
];
export const VendorCreditsSampleData = [
{
Vendor: 'Randall Kohler VENDOR',
'Vendor Credit Date': '2024-01-01',
'Vendor Credit No.': 'VC-0001',
'Reference No.': 'REF-00001',
'Exchange Rate': '',
Note: 'Note',
Open: 'T',
'Item Name': 'Hettinger, Schumm and Bartoletti',
Quantity: 100,
Rate: 100,
},
];

View File

@@ -0,0 +1,157 @@
import { ItemEntryDto } from '@/modules/TransactionItemEntry/dto/ItemEntry.dto';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
IsArray,
IsEnum,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
ValidateNested,
} from 'class-validator';
enum DiscountType {
Percentage = 'percentage',
Amount = 'amount',
}
export class VendorCreditEntryDto extends ItemEntryDto {}
class AttachmentDto {
@IsString()
key: string;
}
export class CommandVendorCreditDto {
@IsNumber()
@IsNotEmpty()
@ApiProperty({
description: 'The id of the vendor',
example: 1,
})
vendorId: number;
@IsNumber()
@IsOptional()
@ApiProperty({
description: 'The exchange rate of the vendor credit',
example: 1,
})
exchangeRate?: number;
@IsString()
@IsOptional()
@ApiProperty({
description: 'The vendor credit number',
example: '123456',
})
vendorCreditNumber?: string;
@IsString()
@IsOptional()
@ApiProperty({
description: 'The reference number of the vendor credit',
example: '123456',
})
referenceNo?: string;
@IsString()
@IsNotEmpty()
@ApiProperty({
description: 'The date of the vendor credit',
example: '2021-01-01',
})
vendorCreditDate: string;
@IsString()
@IsOptional()
@ApiProperty({
description: 'The note of the vendor credit',
example: '123456',
})
note?: string;
@IsOptional()
@ApiProperty({
description: 'The open status of the vendor credit',
example: true,
})
open: boolean = false;
@IsNumber()
@IsOptional()
@ApiProperty({
description: 'The warehouse id of the vendor credit',
example: 1,
})
warehouseId?: number;
@IsNumber()
@IsOptional()
@ApiProperty({
description: 'The branch id of the vendor credit',
example: 1,
})
branchId?: number;
@IsArray()
@ValidateNested({ each: true })
@Type(() => VendorCreditEntryDto)
@ApiProperty({
description: 'The entries of the vendor credit',
example: [
{
itemId: 1,
quantity: 1,
unitPrice: 1,
discount: 1,
discountType: DiscountType.Percentage,
accountId: 1,
amount: 1,
},
],
})
entries: VendorCreditEntryDto[];
@IsArray()
@ValidateNested({ each: true })
@Type(() => AttachmentDto)
@IsOptional()
@ApiProperty({
description: 'The attachments of the vendor credit',
example: [
{
key: '123456',
},
],
})
attachments?: AttachmentDto[];
@IsNumber()
@IsOptional()
@ApiProperty({
description: 'The discount of the vendor credit',
example: 1,
})
discount?: number;
@IsEnum(DiscountType)
@IsOptional()
@ApiProperty({
description: 'The discount type of the vendor credit',
example: DiscountType.Percentage,
})
discountType?: DiscountType;
@IsNumber()
@IsOptional()
@ApiProperty({
description: 'The adjustment of the vendor credit',
example: 1,
})
adjustment?: number;
}
export class CreateVendorCreditDto extends CommandVendorCreditDto {}
export class EditVendorCreditDto extends CommandVendorCreditDto {}

View File

@@ -0,0 +1,395 @@
import { Model, raw } from 'objection';
import { Vendor } from '@/modules/Vendors/models/Vendor';
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
import { Branch } from '@/modules/Branches/models/Branch.model';
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
import { DiscountType } from '@/common/types/Discount';
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
export class VendorCredit extends TenantBaseModel {
vendorId: number;
amount: number;
currencyCode: string;
vendorCreditDate: Date;
vendorCreditNumber: string;
referenceNo: string;
refundedAmount: number;
invoicedAmount: number;
adjustment: number;
exchangeRate: number;
note: string;
openedAt: Date;
userId: number;
discount: number;
discountType: DiscountType;
branchId: number;
warehouseId: number;
vendor?: Vendor;
warehouse?: Warehouse;
branch?: Branch;
entries?: ItemEntry[];
attachments?: Document[];
createdAt: Date;
updatedAt: Date;
/**
* Table name
*/
static get tableName() {
return 'vendor_credits';
}
/**
* Vendor credit amount in local currency.
* @returns {number}
*/
get localAmount() {
return this.amount * this.exchangeRate;
}
/**
* Vendor credit subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Vendor credit subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.subtotal * this.exchangeRate;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number | null}
*/
get adjustmentLocal() {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/**
* Vendor credit total.
* @returns {number}
*/
get total() {
return this.subtotal - this.discountAmount + this.adjustment;
}
/**
* Vendor credit total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/**
* Model modifiers.
*/
static get modifiers() {
return {
/**
* Filters the credit notes in draft status.
*/
draft(query) {
query.where('opened_at', null);
},
/**
* Filters the published vendor credits.
*/
published(query) {
query.whereNot('opened_at', null);
},
/**
* Filters the open vendor credits.
*/
open(query) {
query
.where(
raw(`COALESCE(REFUNDED_AMOUNT) + COALESCE(INVOICED_AMOUNT) <
COALESCE(AMOUNT)`),
)
.modify('published');
},
/**
* Filters the closed vendor credits.
*/
closed(query) {
query
.where(
raw(`COALESCE(REFUNDED_AMOUNT) + COALESCE(INVOICED_AMOUNT) =
COALESCE(AMOUNT)`),
)
.modify('published');
},
/**
* Status filter.
*/
filterByStatus(query, filterType) {
switch (filterType) {
case 'draft':
query.modify('draft');
break;
case 'published':
query.modify('published');
break;
case 'open':
default:
query.modify('open');
break;
case 'closed':
query.modify('closed');
break;
}
},
/**
*
*/
sortByStatus(query, order) {
query.orderByRaw(
`COALESCE(REFUNDED_AMOUNT) + COALESCE(INVOICED_AMOUNT) = COALESCE(AMOUNT) ${order}`,
);
},
};
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [
'isDraft',
'isPublished',
'isOpen',
'isClosed',
'creditsRemaining',
'localAmount',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'adjustmentLocal',
'total',
'totalLocal',
];
}
/**
* Detarmines whether the vendor credit is draft.
* @returns {boolean}
*/
get isDraft() {
return !this.openedAt;
}
/**
* Detarmines whether vendor credit is published.
* @returns {boolean}
*/
get isPublished() {
return !!this.openedAt;
}
/**
* Detarmines whether the credit note is open.
* @return {boolean}
*/
get isOpen() {
return !!this.openedAt && this.creditsRemaining > 0;
}
/**
* Detarmines whether the credit note is closed.
* @return {boolean}
*/
get isClosed() {
return this.openedAt && this.creditsRemaining === 0;
}
/**
* Retrieve the credits remaining.
* @returns {number}
*/
get creditsRemaining() {
return Math.max(this.amount - this.refundedAmount - this.invoicedAmount, 0);
}
/**
* Bill model settings.
*/
// static get meta() {
// return BillSettings;
// }
/**
* Relationship mapping.
*/
static get relationMappings() {
const { Vendor } = require('../../Vendors/models/Vendor');
const {
ItemEntry,
} = require('../../TransactionItemEntry/models/ItemEntry');
const { Branch } = require('../../Branches/models/Branch.model');
const { Document } = require('../../ChromiumlyTenancy/models/Document');
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
return {
/**
* Vendor credit may belongs to vendor.
*/
vendor: {
relation: Model.BelongsToOneRelation,
modelClass: Vendor,
join: {
from: 'vendor_credits.vendorId',
to: 'contacts.id',
},
filter(query) {
query.where('contact_service', 'vendor');
},
},
/**
* Vendor credit may has many item entries.
*/
entries: {
relation: Model.HasManyRelation,
modelClass: ItemEntry,
join: {
from: 'vendor_credits.id',
to: 'items_entries.referenceId',
},
filter(builder) {
builder.where('reference_type', 'VendorCredit');
builder.orderBy('index', 'ASC');
},
},
/**
* Vendor credit may belongs to branch.
*/
branch: {
relation: Model.BelongsToOneRelation,
modelClass: Branch,
join: {
from: 'vendor_credits.branchId',
to: 'branches.id',
},
},
/**
* Vendor credit may has associated warehouse.
*/
warehouse: {
relation: Model.BelongsToOneRelation,
modelClass: Warehouse,
join: {
from: 'vendor_credits.warehouseId',
to: 'warehouses.id',
},
},
/**
* Vendor credit may has many attached attachments.
*/
attachments: {
relation: Model.ManyToManyRelation,
modelClass: Document,
join: {
from: 'vendor_credits.id',
through: {
from: 'document_links.modelId',
to: 'document_links.documentId',
},
to: 'documents.id',
},
filter(query) {
query.where('model_ref', 'VendorCredit');
},
},
};
}
/**
*
*/
// static get meta() {
// return VendorCreditMeta;
// }
/**
* Retrieve the default custom views, roles and columns.
*/
// static get defaultViews() {
// return DEFAULT_VIEWS;
// }
/**
* Model search attributes.
*/
static get searchRoles() {
return [
{ fieldKey: 'credit_number', comparator: 'contains' },
{ condition: 'or', fieldKey: 'reference_no', comparator: 'contains' },
{ condition: 'or', fieldKey: 'amount', comparator: 'equals' },
];
}
/**
* Prevents mutate base currency since the model is not empty.
* @returns {boolean}
*/
static get preventMutateBaseCurrency() {
return true;
}
}

View File

@@ -0,0 +1,45 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { VendorCreditTransformer } from './VendorCreditTransformer';
import { ERRORS } from '../constants';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { VendorCredit } from '../models/VendorCredit';
import { ServiceError } from '@/modules/Items/ServiceError';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetVendorCreditService {
/**
* @param {typeof VendorCredit} vendorCreditModel - Vendor credit model.
* @param {TransformerInjectable} transformer - Transformer service.
*/
constructor(
@Inject(VendorCredit.name)
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
private readonly transformer: TransformerInjectable,
) {}
/**
* Retrieves the given vendor credit.
* @param {number} vendorCreditId - Vendor credit id.
* @param {Knex.Transaction} trx - Knex transaction.
*/
public async getVendorCredit(vendorCreditId: number, trx?: Knex.Transaction) {
// Retrieve the vendor credit model graph.
const vendorCredit = await this.vendorCreditModel()
.query(trx)
.findById(vendorCreditId)
.withGraphFetched('entries.item')
.withGraphFetched('vendor')
.withGraphFetched('branch')
.withGraphFetched('attachments');
if (!vendorCredit) {
throw new ServiceError(ERRORS.VENDOR_CREDIT_NOT_FOUND);
}
return this.transformer.transform(
vendorCredit,
new VendorCreditTransformer(),
);
}
}

View File

@@ -0,0 +1,68 @@
import * as R from 'ramda';
import { Inject, Injectable } from '@nestjs/common';
import { VendorCreditTransformer } from './VendorCreditTransformer';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { VendorCredit } from '../models/VendorCredit';
import { IVendorCreditsQueryDTO } from '../types/VendorCredit.types';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetVendorCreditsService {
constructor(
private readonly dynamicListService: DynamicListService,
private readonly transformer: TransformerInjectable,
@Inject(VendorCredit.name)
private readonly vendorCreditModel: TenantModelProxy<typeof VendorCredit>,
) {}
/**
* Parses the sale invoice list filter DTO.
* @param {IVendorCreditsQueryDTO} filterDTO
* @returns
*/
private parseListFilterDTO = (filterDTO: IVendorCreditsQueryDTO) => {
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
};
/**
* Retrieve the vendor credits list.
* @param {IVendorCreditsQueryDTO} vendorCreditQuery -
*/
public getVendorCredits = async (
vendorCreditQuery: IVendorCreditsQueryDTO,
) => {
// Parses stringified filter roles.
const filter = this.parseListFilterDTO(vendorCreditQuery);
// Dynamic list service.
const dynamicFilter = await this.dynamicListService.dynamicList(
VendorCredit,
filter,
);
const { results, pagination } = await this.vendorCreditModel()
.query()
.onBuild((builder) => {
builder.withGraphFetched('entries');
builder.withGraphFetched('vendor');
dynamicFilter.buildQuery()(builder);
// Gives ability to inject custom query to filter results.
vendorCreditQuery?.filterQuery &&
vendorCreditQuery?.filterQuery(builder);
})
.pagination(filter.page - 1, filter.pageSize);
// Transformes the vendor credits models to POJO.
const vendorCredits = await this.transformer.transform(
results,
new VendorCreditTransformer(),
);
return {
vendorCredits,
pagination,
filterMeta: dynamicFilter.getResponseMeta(),
};
};
}

View File

@@ -0,0 +1,180 @@
import { AttachmentTransformer } from "@/modules/Attachments/Attachment.transformer";
import { ItemEntryTransformer } from "@/modules/TransactionItemEntry/ItemEntry.transformer";
import { Transformer } from "@/modules/Transformer/Transformer";
import { VendorCredit } from "../models/VendorCredit";
export class VendorCreditTransformer extends Transformer {
/**
* Include these attributes to vendor credit object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
return [
'formattedAmount',
'formattedSubtotal',
'formattedVendorCreditDate',
'formattedCreatedAt',
'formattedCreditsRemaining',
'formattedInvoicedAmount',
'discountAmountFormatted',
'discountPercentageFormatted',
'discountAmountLocalFormatted',
'adjustmentFormatted',
'adjustmentLocalFormatted',
'totalFormatted',
'entries',
'attachments',
];
};
/**
* Retrieve formatted vendor credit date.
* @param {VendorCredit} credit
* @returns {String}
*/
protected formattedVendorCreditDate = (vendorCredit: VendorCredit): string => {
return this.formatDate(vendorCredit.vendorCreditDate);
};
/**
* Retireve formatted created at date.
* @param {VendorCredit} vendorCredit
* @returns {string}
*/
protected formattedCreatedAt = (vendorCredit: VendorCredit): string => {
return this.formatDate(vendorCredit.createdAt);
};
/**
* Retrieve formatted vendor credit amount.
* @param {VendorCredit} credit
* @returns {string}
*/
protected formattedAmount = (vendorCredit: VendorCredit): string => {
return this.formatNumber(vendorCredit.amount, {
currencyCode: vendorCredit.currencyCode,
});
};
/**
* Retrieves the vendor credit formatted subtotal.
* @param {VendorCredit} vendorCredit
* @returns {string}
*/
protected formattedSubtotal = (vendorCredit: VendorCredit): string => {
return this.formatNumber(vendorCredit.amount, { money: false });
};
/**
* Retrieve formatted credits remaining.
* @param {VendorCredit} credit
* @returns {string}
*/
protected formattedCreditsRemaining = (credit: VendorCredit): string => {
return this.formatNumber(credit.creditsRemaining, {
currencyCode: credit.currencyCode,
});
};
/**
* Retrieves the formatted discount amount.
* @param {IVendorCredit} credit
* @returns {string}
*/
protected discountAmountFormatted = (credit): string => {
return this.formatNumber(credit.discountAmount, {
currencyCode: credit.currencyCode,
excerptZero: true,
});
};
/**
* Retrieves the formatted discount amount in local currency.
* @param {VendorCredit} credit
* @returns {string}
*/
protected discountAmountLocalFormatted = (credit): string => {
return this.formatNumber(credit.discountAmountLocal, {
currencyCode: this.context.organization.baseCurrency,
excerptZero: true,
});
};
/**
* Retrieves the formatted discount percentage.
* @param {VendorCredit} credit
* @returns {string}
*/
protected discountPercentageFormatted = (credit): string => {
return credit.discountPercentage ? `${credit.discountPercentage}%` : '';
};
/**
* Retrieves the formatted adjustment amount.
* @param {VendorCredit} credit
* @returns {string}
*/
protected adjustmentFormatted = (credit): string => {
return this.formatNumber(credit.adjustment, {
currencyCode: credit.currencyCode,
excerptZero: true,
});
};
/**
* Retrieves the formatted adjustment amount in local currency.
* @param {VendorCredit} credit
* @returns {string}
*/
protected adjustmentLocalFormatted = (credit: VendorCredit): string => {
return this.formatNumber(credit.adjustmentLocal, {
currencyCode: this.context.organization.baseCurrency,
excerptZero: true,
});
};
/**
* Retrieves the formatted invoiced amount.
* @param credit
* @returns {string}
*/
protected formattedInvoicedAmount = (credit: VendorCredit): string => {
return this.formatNumber(credit.invoicedAmount, {
currencyCode: credit.currencyCode,
});
};
/**
* Retrieves the formatted total.
* @param {VendorCredit} credit
* @returns {string}
*/
protected totalFormatted = (credit: VendorCredit): string => {
return this.formatNumber(credit.total, {
currencyCode: credit.currencyCode,
});
};
/**
* Retrieves the entries of the bill.
* @param {VendorCredit} vendorCredit
* @returns {}
*/
protected entries = (vendorCredit: VendorCredit) => {
return this.item(vendorCredit.entries, new ItemEntryTransformer(), {
currencyCode: vendorCredit.currencyCode,
});
};
/**
* Retrieves the vendor credit attachments.
* @param {VendorCredit} invoice
* @returns
*/
protected attachments = (vendorCredit: VendorCredit) => {
return this.item(vendorCredit.attachments, new AttachmentTransformer());
};
}

View File

@@ -0,0 +1,48 @@
// import { Inject, Injectable } from '@nestjs/common';
// import { OnEvent } from '@nestjs/event-emitter';
// import { VendorCredit } from '../models/VendorCredit';
// import { IVendorEventDeletingPayload } from '@/modules/Vendors/types/Vendors.types';
// import { events } from '@/common/events/events';
// import { ServiceError } from '@/modules/Items/ServiceError';
// const ERRORS = {
// VENDOR_HAS_TRANSACTIONS: 'VENDOR_HAS_TRANSACTIONS',
// };
// @Injectable()
// export class DeleteVendorAssociatedVendorCredit {
// /**
// * @param {typeof VendorCredit} vendorCreditModel - Vendor credit model.
// */
// constructor(
// @Inject(VendorCredit.name)
// private readonly vendorCreditModel: typeof VendorCredit,
// ) {}
// /**
// * Validate vendor has no associated credit transaction once the vendor deleting.
// * @param {IVendorEventDeletingPayload} payload -
// */
// @OnEvent(events.vendors.onDeleting)
// public async validateVendorHasNoCreditsTransactionsOnceDeleting({
// vendorId,
// }: IVendorEventDeletingPayload) {
// await this.validateVendorHasNoCreditsTransactions(vendorId);
// }
// /**
// * Validate the given vendor has no associated vendor credit transactions.
// * @param {number} vendorId
// */
// public async validateVendorHasNoCreditsTransactions(
// vendorId: number,
// ): Promise<void> {
// const associatedVendors = await this.vendorCreditModel
// .query()
// .where('vendorId', vendorId);
// if (associatedVendors.length > 0) {
// throw new ServiceError(ERRORS.VENDOR_HAS_TRANSACTIONS);
// }
// }
// }

View File

@@ -0,0 +1,62 @@
// import { Service, Inject } from 'typedi';
// import {
// IRefundVendorCreditCreatedPayload,
// IRefundVendorCreditDeletedPayload,
// } from '@/interfaces';
// import events from '@/subscribers/events';
// import RefundSyncCreditRefundedAmount from './RefundSyncCreditRefundedAmount';
// @Service()
// export default class RefundSyncVendorCreditBalanceSubscriber {
// @Inject()
// refundSyncCreditRefunded: RefundSyncCreditRefundedAmount;
// /**
// * Attaches events with handlers.
// */
// public attach = (bus) => {
// bus.subscribe(
// events.vendorCredit.onRefundCreated,
// this.incrementRefundedAmountOnceRefundCreated
// );
// bus.subscribe(
// events.vendorCredit.onRefundDeleted,
// this.decrementRefundedAmountOnceRefundDeleted
// );
// };
// /**
// * Increment refunded vendor credit amount once refund transaction created.
// * @param {IRefundVendorCreditCreatedPayload} payload -
// */
// private incrementRefundedAmountOnceRefundCreated = async ({
// refundVendorCredit,
// vendorCredit,
// tenantId,
// trx,
// }: IRefundVendorCreditCreatedPayload) => {
// await this.refundSyncCreditRefunded.incrementCreditRefundedAmount(
// tenantId,
// refundVendorCredit.vendorCreditId,
// refundVendorCredit.amount,
// trx
// );
// };
// /**
// * Decrement refunded vendor credit amount once refund transaction deleted.
// * @param {IRefundVendorCreditDeletedPayload} payload -
// */
// private decrementRefundedAmountOnceRefundDeleted = async ({
// trx,
// oldRefundCredit,
// tenantId,
// }: IRefundVendorCreditDeletedPayload) => {
// await this.refundSyncCreditRefunded.decrementCreditNoteRefundAmount(
// tenantId,
// oldRefundCredit.vendorCreditId,
// oldRefundCredit.amount,
// trx
// );
// };
// }

View File

@@ -0,0 +1,59 @@
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import RefundVendorCreditGLEntries from '../RefundVendorCredits/commands/RefundVendorCreditGLEntries';
// import {
// IRefundCreditNoteDeletedPayload,
// IRefundVendorCreditCreatedPayload,
// } from '@/interfaces';
// @Service()
// export default class RefundVendorCreditGLEntriesSubscriber {
// @Inject()
// refundVendorGLEntries: RefundVendorCreditGLEntries;
// /**
// * Attaches events with handlers.
// */
// attach(bus) {
// bus.subscribe(
// events.vendorCredit.onRefundCreated,
// this.writeRefundVendorCreditGLEntriesOnceCreated
// );
// bus.subscribe(
// events.vendorCredit.onRefundDeleted,
// this.revertRefundVendorCreditOnceDeleted
// );
// }
// /**
// * Writes refund vendor credit GL entries once the transaction created.
// * @param {IRefundCreditNoteCreatedPayload} payload -
// */
// private writeRefundVendorCreditGLEntriesOnceCreated = async ({
// tenantId,
// trx,
// refundVendorCredit,
// }: IRefundVendorCreditCreatedPayload) => {
// await this.refundVendorGLEntries.saveRefundCreditGLEntries(
// tenantId,
// refundVendorCredit.id,
// trx
// );
// };
// /**
// * Reverts refund vendor credit GL entries once the transaction deleted.
// * @param {IRefundCreditNoteDeletedPayload} payload -
// */
// private revertRefundVendorCreditOnceDeleted = async ({
// tenantId,
// trx,
// refundCreditId,
// }: IRefundCreditNoteDeletedPayload) => {
// await this.refundVendorGLEntries.revertRefundCreditGLEntries(
// tenantId,
// refundCreditId,
// trx
// );
// };
// }

View File

@@ -0,0 +1,27 @@
// import { Service, Inject } from 'typedi';
// import events from '@/subscribers/events';
// import BaseVendorCredit from '../commands/VendorCreditDTOTransform.service';
// import { IVendorCreditCreatedPayload } from '@/interfaces';
// @Service()
// export default class VendorCreditAutoSerialSubscriber {
// @Inject()
// vendorCreditService: BaseVendorCredit;
// /**
// * Attaches events with handlers.
// */
// public attach(bus) {
// bus.subscribe(events.vendorCredit.onCreated, this.autoIncrementOnceCreated);
// }
// /**
// * Auto serial increment once the vendor credit created.
// * @param {IVendorCreditCreatedPayload} payload
// */
// private autoIncrementOnceCreated = ({
// tenantId,
// }: IVendorCreditCreatedPayload) => {
// this.vendorCreditService.incrementSerialNumber(tenantId);
// };
// }

View File

@@ -0,0 +1,83 @@
import { Injectable } from '@nestjs/common';
import {
IVendorCreditCreatedPayload,
IVendorCreditDeletedPayload,
IVendorCreditEditedPayload,
IVendorCreditOpenedPayload,
} from '../types/VendorCredit.types';
import { OnEvent } from '@nestjs/event-emitter';
import { VendorCreditGLEntries } from '../commands/VendorCreditGLEntries';
import { events } from '@/common/events/events';
@Injectable()
export class VendorCreditGlEntriesSubscriber {
constructor(public readonly vendorCreditGLEntries: VendorCreditGLEntries) {}
/**
* Writes GL entries of vendor credit once the transaction created.
* @param {IVendorCreditCreatedPayload} payload -
*/
@OnEvent(events.vendorCredit.onCreated)
public async writeGLEntriesOnceVendorCreditCreated({
vendorCredit,
trx,
}: IVendorCreditCreatedPayload): Promise<void> {
// Can't continue if the vendor credit is not open yet.
if (!vendorCredit.isPublished) return;
await this.vendorCreditGLEntries.writeVendorCreditGLEntries(
vendorCredit.id,
trx,
);
}
/**
* Writes Gl entries of vendor credit once the transaction opened.
* @param {IVendorCreditOpenedPayload} payload -
*/
@OnEvent(events.vendorCredit.onOpened)
public async writeGLEntgriesOnceVendorCreditOpened({
vendorCreditId,
trx,
}: IVendorCreditOpenedPayload): Promise<void> {
await this.vendorCreditGLEntries.writeVendorCreditGLEntries(
vendorCreditId,
trx,
);
}
/**
* Edits associated GL entries once vendor credit edited.
* @param {IVendorCreditEditedPayload} payload
*/
@OnEvent(events.vendorCredit.onEdited)
public async editGLEntriesOnceVendorCreditEdited({
vendorCredit,
trx,
}: IVendorCreditEditedPayload): Promise<void> {
// Can't continue if the vendor credit is not open yet.
if (!vendorCredit.isPublished) return;
await this.vendorCreditGLEntries.rewriteVendorCreditGLEntries(
vendorCredit.id,
trx,
);
}
/**
* Reverts the GL entries once vendor credit deleted.
* @param {IVendorCreditDeletedPayload} payload -
*/
@OnEvent(events.vendorCredit.onDeleted)
public async revertGLEntriesOnceDeleted({
vendorCreditId,
oldVendorCredit,
}: IVendorCreditDeletedPayload): Promise<void> {
// Can't continue of the vendor credit is not open yet.
if (!oldVendorCredit.isPublished) return;
await this.vendorCreditGLEntries.revertVendorCreditGLEntries(
vendorCreditId,
);
}
}

View File

@@ -0,0 +1,69 @@
import {
IVendorCreditCreatedPayload,
IVendorCreditDeletedPayload,
IVendorCreditEditedPayload,
} from '../types/VendorCredit.types';
import { VendorCreditInventoryTransactions } from '../commands/VendorCreditInventoryTransactions';
import { OnEvent } from '@nestjs/event-emitter';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
@Injectable()
export default class VendorCreditInventoryTransactionsSubscriber {
constructor(
private readonly inventoryTransactions: VendorCreditInventoryTransactions,
) {}
/**
* Writes inventory transactions once vendor created created.
* @param {IVendorCreditCreatedPayload} payload -
*/
@OnEvent(events.vendorCredit.onCreated)
@OnEvent(events.vendorCredit.onOpened)
public async writeInventoryTransactionsOnceCreated({
vendorCredit,
trx,
}: IVendorCreditCreatedPayload) {
// Can't continue if vendor credit is not opened.
if (!vendorCredit.openedAt) return null;
await this.inventoryTransactions.createInventoryTransactions(
vendorCredit,
trx,
);
}
/**
* Rewrites inventory transactions once vendor credit edited.
* @param {IVendorCreditEditedPayload} payload -
*/
@OnEvent(events.vendorCredit.onEdited)
public async rewriteInventroyTransactionsOnceEdited({
oldVendorCredit,
vendorCredit,
trx,
}: IVendorCreditEditedPayload) {
// Can't continue if vendor credit is not opened.
if (!vendorCredit.openedAt) return null;
await this.inventoryTransactions.editInventoryTransactions(
oldVendorCredit.id,
vendorCredit,
trx,
);
}
/**
* Reverts inventory transactions once vendor credit deleted.
* @param {IVendorCreditDeletedPayload} payload -
*/
@OnEvent(events.vendorCredit.onDeleted)
public async revertInventoryTransactionsOnceDeleted({
vendorCreditId,
trx,
}: IVendorCreditDeletedPayload) {
await this.inventoryTransactions.deleteInventoryTransactions(
vendorCreditId,
trx,
);
}
}

View File

@@ -0,0 +1,126 @@
import { Knex } from 'knex';
import { VendorCredit } from '../models/VendorCredit';
import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
import { IRefundVendorCreditDTO } from '@/modules/VendorCreditsRefund/types/VendorCreditRefund.types';
import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
import { DiscountType } from '@/common/types/Discount';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import {
CreateVendorCreditDto,
EditVendorCreditDto,
} from '../dtos/VendorCredit.dto';
export enum VendorCreditAction {
Create = 'Create',
Edit = 'Edit',
Delete = 'Delete',
View = 'View',
Refund = 'Refund',
}
export interface IVendorCreditEntryDTO extends IItemEntryDTO {}
export interface IVendorCreditsQueryDTO extends IDynamicListFilter {
page: number;
pageSize: number;
searchKeyword?: string;
filterQuery?: (q: any) => void;
}
export interface IVendorCreditDTO {
vendorId: number;
exchangeRate?: number;
vendorCreditNumber: string;
referenceNo: string;
vendorCreditDate: Date;
note: string;
open: boolean;
entries: IVendorCreditEntryDTO[];
branchId?: number;
warehouseId?: number;
attachments?: AttachmentLinkDTO[];
discount?: number;
discountType?: DiscountType;
adjustment?: number;
}
export interface IVendorCreditCreateDTO extends IVendorCreditDTO {}
export interface IVendorCreditEditDTO extends IVendorCreditDTO {}
export interface IVendorCreditCreatePayload {
refundVendorCreditDTO: IRefundVendorCreditDTO;
}
// Create Vendor Credit Events
// ------------------------
export interface IVendorCreditCreatingPayload {
vendorCredit: VendorCredit;
vendorCreditId: number;
vendorCreditCreateDTO: CreateVendorCreditDto;
trx: Knex.Transaction;
}
export interface IVendorCreditCreatedPayload {
vendorCredit: VendorCredit;
vendorCreditCreateDTO: CreateVendorCreditDto;
trx: Knex.Transaction;
}
export interface IVendorCreditCreatedPayload {}
// Delete Vendor Credit Events
// ------------------------
export interface IVendorCreditDeletedPayload {
trx: Knex.Transaction;
vendorCreditId: number;
oldVendorCredit: VendorCredit;
}
export interface IVendorCreditDeletingPayload {
trx: Knex.Transaction;
oldVendorCredit: VendorCredit;
}
// Edit Vendor Credit Events
// ------------------------
export interface IVendorCreditEditingPayload {
oldVendorCredit: VendorCredit;
vendorCreditDTO: EditVendorCreditDto;
trx: Knex.Transaction;
}
export interface IVendorCreditEditedPayload {
oldVendorCredit: VendorCredit;
vendorCredit: VendorCredit;
vendorCreditDTO: EditVendorCreditDto;
trx: Knex.Transaction;
}
export interface IApplyCreditToBillEntryDTO {
amount: number;
billId: number;
}
// Open Vendor Credit Events
// ------------------------
export interface IVendorCreditOpenedPayload {
// tenantId: number;
vendorCreditId: number;
vendorCredit: VendorCredit;
trx: Knex.Transaction;
}
export interface IVendorCreditOpenPayload {
// tenantId: number;
vendorCreditId: number;
oldVendorCredit: VendorCredit;
}
export interface IVendorCreditOpeningPayload {
// tenantId: number;
vendorCreditId: number;
oldVendorCredit: VendorCredit;
trx: Knex.Transaction;
}