mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
refactor: migrate credit note and vendor credit services to nestjs
This commit is contained in:
@@ -46,6 +46,9 @@ import { BillsModule } from '../Bills/Bills.module';
|
|||||||
import { SaleInvoicesModule } from '../SaleInvoices/SaleInvoices.module';
|
import { SaleInvoicesModule } from '../SaleInvoices/SaleInvoices.module';
|
||||||
import { SaleReceiptsModule } from '../SaleReceipts/SaleReceipts.module';
|
import { SaleReceiptsModule } from '../SaleReceipts/SaleReceipts.module';
|
||||||
import { ManualJournalsModule } from '../ManualJournals/ManualJournals.module';
|
import { ManualJournalsModule } from '../ManualJournals/ManualJournals.module';
|
||||||
|
import { VendorCreditsModule } from '../VendorCredit/VendorCredits.module';
|
||||||
|
import { CreditNotesModule } from '../CreditNotes/CreditNotes.module';
|
||||||
|
import { VendorCreditApplyBillsModule } from '../VendorCreditsApplyBills/VendorCreditApplyBills.module';
|
||||||
// import { BillPaymentsModule } from '../BillPayments/BillPayments.module';
|
// import { BillPaymentsModule } from '../BillPayments/BillPayments.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -116,7 +119,10 @@ import { ManualJournalsModule } from '../ManualJournals/ManualJournals.module';
|
|||||||
SaleEstimatesModule,
|
SaleEstimatesModule,
|
||||||
SaleReceiptsModule,
|
SaleReceiptsModule,
|
||||||
BillsModule,
|
BillsModule,
|
||||||
ManualJournalsModule
|
ManualJournalsModule,
|
||||||
|
VendorCreditsModule,
|
||||||
|
VendorCreditApplyBillsModule,
|
||||||
|
CreditNotesModule,
|
||||||
// BillPaymentsModule,
|
// BillPaymentsModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
|
|||||||
import { Item } from '@/modules/Items/models/Item';
|
import { Item } from '@/modules/Items/models/Item';
|
||||||
import { BillPaymentEntry } from '@/modules/BillPayments/models/BillPaymentEntry';
|
import { BillPaymentEntry } from '@/modules/BillPayments/models/BillPaymentEntry';
|
||||||
import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost';
|
import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost';
|
||||||
import { VendorCreditAppliedBill } from '@/modules/VendorCredit/models/VendorCreditAppliedBill';
|
import { VendorCreditAppliedBill } from '@/modules/VendorCreditsApplyBills/models/VendorCreditAppliedBill';
|
||||||
import { transformToMap } from '@/utils/transform-to-key';
|
import { transformToMap } from '@/utils/transform-to-key';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { CreateCreditNoteService } from './commands/CreateCreditNote.service';
|
||||||
|
import { CreateRefundCreditNoteService } from './commands/CreateRefundCreditNote.service';
|
||||||
|
import { DeleteCreditNoteService } from './commands/DeleteCreditNote.service';
|
||||||
|
import { EditCreditNoteService } from './commands/EditCreditNote.service';
|
||||||
|
import { OpenCreditNoteService } from './commands/OpenCreditNote.service';
|
||||||
|
import { GetCreditNotePdf } from './queries/GetCreditNotePdf.serivce';
|
||||||
|
import {
|
||||||
|
ICreditNoteEditDTO,
|
||||||
|
ICreditNoteNewDTO,
|
||||||
|
ICreditNoteRefundDTO,
|
||||||
|
} from './types/CreditNotes.types';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreditNoteApplication {
|
||||||
|
constructor(
|
||||||
|
private createCreditNoteService: CreateCreditNoteService,
|
||||||
|
private createRefundCreditNoteService: CreateRefundCreditNoteService,
|
||||||
|
private editCreditNoteService: EditCreditNoteService,
|
||||||
|
private openCreditNoteService: OpenCreditNoteService,
|
||||||
|
private deleteCreditNoteService: DeleteCreditNoteService,
|
||||||
|
private getCreditNotePdfService: GetCreditNotePdf,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
createCreditNote(creditNoteDTO: ICreditNoteNewDTO) {
|
||||||
|
return this.createCreditNoteService.creditCreditNote(creditNoteDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
editCreditNote(creditNoteId: number, creditNoteDTO: ICreditNoteEditDTO) {
|
||||||
|
return this.editCreditNoteService.editCreditNote(
|
||||||
|
creditNoteId,
|
||||||
|
creditNoteDTO,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
openCreditNote(creditNoteId: number) {
|
||||||
|
return this.openCreditNoteService.openCreditNote(creditNoteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCreditNote(creditNoteId: number) {
|
||||||
|
return this.deleteCreditNoteService.deleteCreditNote(creditNoteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
createRefundCreditNote(
|
||||||
|
creditNoteId: number,
|
||||||
|
creditNoteDTO: ICreditNoteRefundDTO,
|
||||||
|
) {
|
||||||
|
return this.createRefundCreditNoteService.createCreditNoteRefund(
|
||||||
|
creditNoteId,
|
||||||
|
creditNoteDTO,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreditNotePdf(creditNoteId: number) {
|
||||||
|
return this.getCreditNotePdfService.getCreditNotePdf(creditNoteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Body, Controller, Delete, Param, Post, Put } from '@nestjs/common';
|
||||||
|
import { CreditNoteApplication } from './CreditNoteApplication.service';
|
||||||
|
import {
|
||||||
|
ICreditNoteEditDTO,
|
||||||
|
ICreditNoteNewDTO,
|
||||||
|
ICreditNoteRefundDTO,
|
||||||
|
} from './types/CreditNotes.types';
|
||||||
|
|
||||||
|
@Controller('credit-notes')
|
||||||
|
export class CreditNotesController {
|
||||||
|
/**
|
||||||
|
* @param {CreditNoteApplication} creditNoteApplication - The credit note application service.
|
||||||
|
*/
|
||||||
|
constructor(private creditNoteApplication: CreditNoteApplication) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
createCreditNote(@Body() creditNoteDTO: ICreditNoteNewDTO) {
|
||||||
|
return this.creditNoteApplication.createCreditNote(creditNoteDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put(':id')
|
||||||
|
editCreditNote(
|
||||||
|
@Param('id') creditNoteId: number,
|
||||||
|
@Body() creditNoteDTO: ICreditNoteEditDTO,
|
||||||
|
) {
|
||||||
|
return this.creditNoteApplication.editCreditNote(
|
||||||
|
creditNoteId,
|
||||||
|
creditNoteDTO,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(':id/open')
|
||||||
|
openCreditNote(@Param('id') creditNoteId: number) {
|
||||||
|
return this.creditNoteApplication.openCreditNote(creditNoteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
deleteCreditNote(@Param('id') creditNoteId: number) {
|
||||||
|
return this.creditNoteApplication.deleteCreditNote(creditNoteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(':id/refund')
|
||||||
|
createRefundCreditNote(
|
||||||
|
@Param('id') creditNoteId: number,
|
||||||
|
@Body() creditNoteDTO: ICreditNoteRefundDTO,
|
||||||
|
) {
|
||||||
|
return this.creditNoteApplication.createRefundCreditNote(
|
||||||
|
creditNoteId,
|
||||||
|
creditNoteDTO,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CreateCreditNoteService } from './commands/CreateCreditNote.service';
|
||||||
|
import { CommandCreditNoteDTOTransform } from './commands/CommandCreditNoteDTOTransform.service';
|
||||||
|
import { CreateRefundCreditNoteService } from './commands/CreateRefundCreditNote.service';
|
||||||
|
import { EditCreditNoteService } from './commands/EditCreditNote.service';
|
||||||
|
import { OpenCreditNoteService } from './commands/OpenCreditNote.service';
|
||||||
|
import { DeleteCreditNoteService } from './commands/DeleteCreditNote.service';
|
||||||
|
import { CreditNoteApplySyncCredit } from './commands/CreditNoteApplySyncCredit.service';
|
||||||
|
import { DeleteCustomerLinkedCreditNoteService } from './commands/DeleteCustomerLinkedCreditNote.service';
|
||||||
|
import { CreditNoteAutoIncrementService } from './commands/CreditNoteAutoIncrement.service';
|
||||||
|
import { CreditNoteApplication } from './CreditNoteApplication.service';
|
||||||
|
import { CreditNotesController } from './CreditNotes.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [
|
||||||
|
CreateCreditNoteService,
|
||||||
|
CommandCreditNoteDTOTransform,
|
||||||
|
CreateRefundCreditNoteService,
|
||||||
|
EditCreditNoteService,
|
||||||
|
OpenCreditNoteService,
|
||||||
|
DeleteCreditNoteService,
|
||||||
|
CreditNoteApplySyncCredit,
|
||||||
|
DeleteCustomerLinkedCreditNoteService,
|
||||||
|
CreditNoteAutoIncrementService,
|
||||||
|
CreditNoteApplication
|
||||||
|
],
|
||||||
|
controllers: [CreditNotesController],
|
||||||
|
})
|
||||||
|
export class CreditNotesModule {}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import BaseCreditNotes from '../CreditNotes';
|
||||||
|
import { ISaleInvoice } from '@/interfaces';
|
||||||
|
import { CreditNoteWithInvoicesToApplyTransformer } from './commands/CreditNoteWithInvoicesToApplyTransformer';
|
||||||
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class GetCreditNoteAssociatedInvoicesToApply extends BaseCreditNotes {
|
||||||
|
@Inject()
|
||||||
|
private transformer: TransformerInjectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve credit note associated invoices to apply.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @returns {Promise<ISaleInvoice[]>}
|
||||||
|
*/
|
||||||
|
public getCreditAssociatedInvoicesToApply = async (
|
||||||
|
tenantId: number,
|
||||||
|
creditNoteId: number
|
||||||
|
): Promise<ISaleInvoice[]> => {
|
||||||
|
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// Retireve credit note or throw not found service error.
|
||||||
|
const creditNote = await this.getCreditNoteOrThrowError(
|
||||||
|
tenantId,
|
||||||
|
creditNoteId
|
||||||
|
);
|
||||||
|
// Retrieves the published due invoices that associated to the given customer.
|
||||||
|
const saleInvoices = await SaleInvoice.query()
|
||||||
|
.where('customerId', creditNote.customerId)
|
||||||
|
.modify('dueInvoices')
|
||||||
|
.modify('published');
|
||||||
|
|
||||||
|
// Transformes the sale invoices models to POJO.
|
||||||
|
return this.transformer.transform(
|
||||||
|
tenantId,
|
||||||
|
saleInvoices,
|
||||||
|
new CreditNoteWithInvoicesToApplyTransformer()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { omit } from 'lodash';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import composeAsync from 'async/compose';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import {
|
||||||
|
ICreditNote,
|
||||||
|
ICreditNoteEditDTO,
|
||||||
|
ICreditNoteEntryNewDTO,
|
||||||
|
ICreditNoteNewDTO,
|
||||||
|
} from '../types/CreditNotes.types';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
|
||||||
|
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
|
||||||
|
import { WarehouseTransactionDTOTransform } from '@/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
||||||
|
import { BrandingTemplateDTOTransformer } from '../../PdfTemplate/BrandingTemplateDTOTransformer';
|
||||||
|
import { assocItemEntriesDefaultIndex } from '@/utils/associate-item-entries-index';
|
||||||
|
import { CreditNoteAutoIncrementService } from './CreditNoteAutoIncrement.service';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CommandCreditNoteDTOTransform {
|
||||||
|
/**
|
||||||
|
* @param {ItemsEntriesService} itemsEntriesService - The items entries service.
|
||||||
|
* @param {BranchTransactionDTOTransformer} branchDTOTransform - The branch transaction DTO transformer.
|
||||||
|
* @param {WarehouseTransactionDTOTransform} warehouseDTOTransform - The warehouse transaction DTO transformer.
|
||||||
|
* @param {BrandingTemplateDTOTransformer} brandingTemplatesTransformer - The branding template DTO transformer.
|
||||||
|
* @param {CreditNoteAutoIncrementService} creditNoteAutoIncrement - The credit note auto increment service.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly itemsEntriesService: ItemsEntriesService,
|
||||||
|
private readonly branchDTOTransform: BranchTransactionDTOTransformer,
|
||||||
|
private readonly warehouseDTOTransform: WarehouseTransactionDTOTransform,
|
||||||
|
private readonly brandingTemplatesTransformer: BrandingTemplateDTOTransformer,
|
||||||
|
private readonly creditNoteAutoIncrement: CreditNoteAutoIncrementService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes the credit/edit DTO to model.
|
||||||
|
* @param {ICreditNoteNewDTO | ICreditNoteEditDTO} creditNoteDTO
|
||||||
|
* @param {string} customerCurrencyCode -
|
||||||
|
*/
|
||||||
|
public transformCreateEditDTOToModel = async (
|
||||||
|
creditNoteDTO: ICreditNoteNewDTO | ICreditNoteEditDTO,
|
||||||
|
customerCurrencyCode: string,
|
||||||
|
oldCreditNote?: CreditNote,
|
||||||
|
): Promise<CreditNote> => {
|
||||||
|
// Retrieve the total amount of the given items entries.
|
||||||
|
const amount = this.itemsEntriesService.getTotalItemsEntries(
|
||||||
|
creditNoteDTO.entries,
|
||||||
|
);
|
||||||
|
const entries = R.compose(
|
||||||
|
// Associate the default index to each item entry.
|
||||||
|
assocItemEntriesDefaultIndex,
|
||||||
|
|
||||||
|
// Associate the reference type to credit note entries.
|
||||||
|
R.map((entry: ICreditNoteEntryNewDTO) => ({
|
||||||
|
...entry,
|
||||||
|
referenceType: 'CreditNote',
|
||||||
|
})),
|
||||||
|
)(creditNoteDTO.entries);
|
||||||
|
|
||||||
|
// Retreive the next credit note number.
|
||||||
|
const autoNextNumber = this.creditNoteAutoIncrement.getNextCreditNumber();
|
||||||
|
|
||||||
|
// Detarmines the credit note number.
|
||||||
|
const creditNoteNumber =
|
||||||
|
creditNoteDTO.creditNoteNumber ||
|
||||||
|
oldCreditNote?.creditNoteNumber ||
|
||||||
|
autoNextNumber;
|
||||||
|
|
||||||
|
const initialDTO = {
|
||||||
|
...omit(creditNoteDTO, ['open', 'attachments']),
|
||||||
|
creditNoteNumber,
|
||||||
|
amount,
|
||||||
|
currencyCode: customerCurrencyCode,
|
||||||
|
exchangeRate: creditNoteDTO.exchangeRate || 1,
|
||||||
|
entries,
|
||||||
|
...(creditNoteDTO.open &&
|
||||||
|
!oldCreditNote?.openedAt && {
|
||||||
|
openedAt: moment().toMySqlDateTime(),
|
||||||
|
}),
|
||||||
|
refundedAmount: 0,
|
||||||
|
invoicesAmount: 0,
|
||||||
|
};
|
||||||
|
const initialAsyncDTO = await composeAsync(
|
||||||
|
// Assigns the default branding template id to the invoice DTO.
|
||||||
|
this.brandingTemplatesTransformer.assocDefaultBrandingTemplate(
|
||||||
|
'CreditNote',
|
||||||
|
),
|
||||||
|
)(initialDTO);
|
||||||
|
|
||||||
|
return R.compose(
|
||||||
|
this.branchDTOTransform.transformDTO<ICreditNote>,
|
||||||
|
this.warehouseDTOTransform.transformDTO<ICreditNote>,
|
||||||
|
)(initialAsyncDTO);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the credit note remaining amount.
|
||||||
|
* @param {ICreditNote} creditNote
|
||||||
|
* @param {number} amount
|
||||||
|
*/
|
||||||
|
public validateCreditRemainingAmount = (
|
||||||
|
creditNote: CreditNote,
|
||||||
|
amount: number,
|
||||||
|
) => {
|
||||||
|
if (creditNote.creditsRemaining < amount) {
|
||||||
|
throw new ServiceError(ERRORS.CREDIT_NOTE_HAS_NO_REMAINING_AMOUNT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ICreditNoteCreatedPayload,
|
||||||
|
ICreditNoteCreatingPayload,
|
||||||
|
ICreditNoteNewDTO,
|
||||||
|
} from '../types/CreditNotes.types';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { Contact } from '../../Contacts/models/Contact';
|
||||||
|
import { CommandCreditNoteDTOTransform } from './CommandCreditNoteDTOTransform.service';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreateCreditNoteService {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly itemsEntriesService: ItemsEntriesService,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
private readonly creditNoteModel: typeof CreditNote,
|
||||||
|
private readonly contactModel: typeof Contact,
|
||||||
|
private readonly commandCreditNoteDTOTransform: CommandCreditNoteDTOTransform,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new credit note.
|
||||||
|
* @param creditNoteDTO
|
||||||
|
*/
|
||||||
|
public creditCreditNote = async (
|
||||||
|
creditNoteDTO: ICreditNoteNewDTO,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
) => {
|
||||||
|
// Triggers `onCreditNoteCreate` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onCreate, {
|
||||||
|
creditNoteDTO,
|
||||||
|
});
|
||||||
|
// Validate customer existance.
|
||||||
|
const customer = await this.contactModel
|
||||||
|
.query()
|
||||||
|
.modify('customer')
|
||||||
|
.findById(creditNoteDTO.customerId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Validate items ids existance.
|
||||||
|
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||||
|
creditNoteDTO.entries,
|
||||||
|
);
|
||||||
|
// Validate items should be sellable items.
|
||||||
|
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||||
|
creditNoteDTO.entries,
|
||||||
|
);
|
||||||
|
// Transformes the given DTO to storage layer data.
|
||||||
|
const creditNoteModel =
|
||||||
|
await this.commandCreditNoteDTOTransform.transformCreateEditDTOToModel(
|
||||||
|
creditNoteDTO,
|
||||||
|
customer.currencyCode,
|
||||||
|
);
|
||||||
|
// Creates a new credit card transactions under unit-of-work envirement.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onCreditNoteCreating` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onCreating, {
|
||||||
|
creditNoteDTO,
|
||||||
|
trx,
|
||||||
|
} as ICreditNoteCreatingPayload);
|
||||||
|
|
||||||
|
// Upsert the credit note graph.
|
||||||
|
const creditNote = await this.creditNoteModel.query(trx).upsertGraph({
|
||||||
|
...creditNoteModel,
|
||||||
|
});
|
||||||
|
// Triggers `onCreditNoteCreated` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onCreated, {
|
||||||
|
creditNoteDTO,
|
||||||
|
creditNote,
|
||||||
|
creditNoteId: creditNote.id,
|
||||||
|
trx,
|
||||||
|
} as ICreditNoteCreatedPayload);
|
||||||
|
|
||||||
|
return creditNote;
|
||||||
|
}, trx);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
ICreditNoteRefundDTO,
|
||||||
|
IRefundCreditNoteCreatedPayload,
|
||||||
|
IRefundCreditNoteCreatingPayload,
|
||||||
|
} from '../types/CreditNotes.types';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { RefundCreditNote } from '../models/RefundCreditNote';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { CommandCreditNoteDTOTransform } from './CommandCreditNoteDTOTransform.service';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreateRefundCreditNoteService {
|
||||||
|
/**
|
||||||
|
* @param {UnitOfWork} uow - The unit of work service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - The event emitter service.
|
||||||
|
* @param {CommandCreditNoteDTOTransform} commandCreditNoteDTOTransform - The command credit note DTO transform service.
|
||||||
|
* @param {typeof RefundCreditNote} refundCreditNoteModel - The refund credit note model.
|
||||||
|
* @param {typeof Account} accountModel - The account model.
|
||||||
|
* @param {typeof CreditNote} creditNoteModel - The credit note model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private uow: UnitOfWork,
|
||||||
|
private eventPublisher: EventEmitter2,
|
||||||
|
private commandCreditNoteDTOTransform: CommandCreditNoteDTOTransform,
|
||||||
|
|
||||||
|
@Inject(RefundCreditNote.name)
|
||||||
|
private refundCreditNoteModel: typeof RefundCreditNote,
|
||||||
|
|
||||||
|
@Inject(Account.name) private accountModel: typeof Account,
|
||||||
|
@Inject(CreditNote.name) private creditNoteModel: typeof CreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the credit note graph.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @param {ICreditNoteRefundDTO} newCreditNoteDTO
|
||||||
|
* @returns {Promise<IRefundCreditNote>}
|
||||||
|
*/
|
||||||
|
public async createCreditNoteRefund(
|
||||||
|
creditNoteId: number,
|
||||||
|
newCreditNoteDTO: ICreditNoteRefundDTO,
|
||||||
|
): Promise<RefundCreditNote> {
|
||||||
|
// Retrieve the credit note or throw not found service error.
|
||||||
|
const creditNote = await this.creditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Retrieve the withdrawal account or throw not found service error.
|
||||||
|
const fromAccount = await this.accountModel
|
||||||
|
.query()
|
||||||
|
.findById(newCreditNoteDTO.fromAccountId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Validate the credit note remaining amount.
|
||||||
|
this.commandCreditNoteDTOTransform?.validateCreditRemainingAmount(
|
||||||
|
creditNote,
|
||||||
|
newCreditNoteDTO.amount,
|
||||||
|
);
|
||||||
|
// Validate the refund withdrawal account type.
|
||||||
|
this.commandCreditNoteDTOTransform.validateRefundWithdrawwalAccountType(
|
||||||
|
fromAccount,
|
||||||
|
);
|
||||||
|
// Creates a refund credit note transaction.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onCreditNoteRefundCreating` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onRefundCreating, {
|
||||||
|
trx,
|
||||||
|
creditNote,
|
||||||
|
newCreditNoteDTO,
|
||||||
|
} as IRefundCreditNoteCreatingPayload);
|
||||||
|
|
||||||
|
// Stores the refund credit note graph to the storage layer.
|
||||||
|
const refundCreditNote = await this.refundCreditNoteModel
|
||||||
|
.query(trx)
|
||||||
|
.insertAndFetch({
|
||||||
|
...this.transformDTOToModel(creditNote, newCreditNoteDTO),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteRefundCreated` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onRefundCreated, {
|
||||||
|
trx,
|
||||||
|
refundCreditNote,
|
||||||
|
creditNote,
|
||||||
|
} as IRefundCreditNoteCreatedPayload);
|
||||||
|
|
||||||
|
return refundCreditNote;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes the refund credit note DTO to model.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @param {ICreditNoteRefundDTO} creditNoteDTO
|
||||||
|
* @returns {ICreditNote}
|
||||||
|
*/
|
||||||
|
private transformDTOToModel = (
|
||||||
|
creditNote: CreditNote,
|
||||||
|
creditNoteDTO: ICreditNoteRefundDTO,
|
||||||
|
): RefundCreditNote => {
|
||||||
|
return {
|
||||||
|
creditNoteId: creditNote.id,
|
||||||
|
currencyCode: creditNote.currencyCode,
|
||||||
|
...creditNoteDTO,
|
||||||
|
exchangeRate: creditNoteDTO.exchangeRate || 1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreditNoteApplySyncCredit {
|
||||||
|
constructor(
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private creditNoteModel: typeof CreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment credit note invoiced amount.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @param {number} invoicesAppliedAmount
|
||||||
|
* @param {Knex.Transaction} [trx]
|
||||||
|
*/
|
||||||
|
public async incrementCreditNoteInvoicedAmount(
|
||||||
|
creditNoteId: number,
|
||||||
|
invoicesAppliedAmount: number,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.creditNoteModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.increment('invoicesAmount', invoicesAppliedAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement credit note invoiced amount.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @param {number} invoicesAppliedAmount
|
||||||
|
* @param {Knex.Transaction} [trx]
|
||||||
|
*/
|
||||||
|
public async decrementCreditNoteInvoicedAmount(
|
||||||
|
creditNoteId: number,
|
||||||
|
invoicesAppliedAmount: number,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.creditNoteModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.decrement('invoicesAmount', invoicesAppliedAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
|
import Bluebird from 'bluebird';
|
||||||
|
import { ICreditNoteAppliedToInvoice } from '../types/CreditNotes.types';
|
||||||
|
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreditNoteApplySyncInvoicesCreditedAmount {
|
||||||
|
constructor(
|
||||||
|
@Inject(SaleInvoice.name)
|
||||||
|
private readonly saleInvoiceModel: typeof SaleInvoice,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment invoices credited amount.
|
||||||
|
* @param {ICreditNoteAppliedToInvoice[]} creditNoteAppliedInvoices -
|
||||||
|
* @param {Knex.Transaction} trx -
|
||||||
|
*/
|
||||||
|
public incrementInvoicesCreditedAmount = async (
|
||||||
|
creditNoteAppliedInvoices: ICreditNoteAppliedToInvoice[],
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
) => {
|
||||||
|
await Bluebird.each(
|
||||||
|
creditNoteAppliedInvoices,
|
||||||
|
(creditNoteAppliedInvoice: ICreditNoteAppliedToInvoice) => {
|
||||||
|
return this.saleInvoiceModel.query(trx)
|
||||||
|
.where('id', creditNoteAppliedInvoice.invoiceId)
|
||||||
|
.increment('creditedAmount', creditNoteAppliedInvoice.amount);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} invoicesIds
|
||||||
|
* @param {number} amount -
|
||||||
|
* @param {Knex.Transaction} knex -
|
||||||
|
*/
|
||||||
|
public decrementInvoiceCreditedAmount = async (
|
||||||
|
invoiceId: number,
|
||||||
|
amount: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
) => {
|
||||||
|
await this.saleInvoiceModel.query(trx)
|
||||||
|
.findById(invoiceId)
|
||||||
|
.decrement('creditedAmount', amount);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { sumBy } from 'lodash';
|
||||||
|
import {
|
||||||
|
ICreditNoteAppliedToInvoice,
|
||||||
|
ICreditNoteAppliedToInvoiceModel,
|
||||||
|
IApplyCreditToInvoicesDTO,
|
||||||
|
IApplyCreditToInvoicesCreatedPayload,
|
||||||
|
ICreditNote,
|
||||||
|
} from '../types/CreditNotes.types';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { PaymentReceivedValidators } from '@/modules/PaymentReceived/commands/PaymentReceivedValidators.service';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreditNoteApplyToInvoices {
|
||||||
|
/**
|
||||||
|
* @param {PaymentReceivedValidators} paymentReceiveValidators - The payment received validators service.
|
||||||
|
* @param {UnitOfWork} uow - The unit of work service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - The event emitter service.
|
||||||
|
* @param {typeof CreditNoteAppliedInvoice} creditNoteAppliedInvoiceModel - The credit note applied invoice model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly paymentReceiveValidators: PaymentReceivedValidators,
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(CreditNoteAppliedInvoice.name)
|
||||||
|
private readonly creditNoteAppliedInvoiceModel: typeof CreditNoteAppliedInvoice,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply credit note to the given invoices.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @param {IApplyCreditToInvoicesDTO} applyCreditToInvoicesDTO
|
||||||
|
*/
|
||||||
|
public async applyCreditNoteToInvoices(
|
||||||
|
creditNoteId: number,
|
||||||
|
applyCreditToInvoicesDTO: IApplyCreditToInvoicesDTO,
|
||||||
|
): Promise<ICreditNoteAppliedToInvoice[]> {
|
||||||
|
// Saves the credit note or throw not found service error.
|
||||||
|
const creditNote = await this.getCreditNoteOrThrowError(creditNoteId);
|
||||||
|
|
||||||
|
// Retrieve the applied invoices that associated to the credit note customer.
|
||||||
|
const appliedInvoicesEntries =
|
||||||
|
await this.paymentReceiveValidators.validateInvoicesIDsExistance(
|
||||||
|
creditNote.customerId,
|
||||||
|
applyCreditToInvoicesDTO.entries,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Transformes apply DTO to model.
|
||||||
|
const creditNoteAppliedModel = this.transformApplyDTOToModel(
|
||||||
|
applyCreditToInvoicesDTO,
|
||||||
|
creditNote,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validate invoices has remaining amount to apply.
|
||||||
|
this.validateInvoicesRemainingAmount(
|
||||||
|
appliedInvoicesEntries,
|
||||||
|
creditNoteAppliedModel.amount,
|
||||||
|
);
|
||||||
|
// Validate the credit note remaining amount.
|
||||||
|
this.validateCreditRemainingAmount(
|
||||||
|
creditNote,
|
||||||
|
creditNoteAppliedModel.amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Creates credit note apply to invoice transaction.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Saves the credit note apply to invoice graph to the storage layer.
|
||||||
|
const creditNoteAppliedInvoices = await this.creditNoteAppliedInvoiceModel
|
||||||
|
.query()
|
||||||
|
.insertGraph(creditNoteAppliedModel.entries);
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteApplyToInvoiceCreated` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.creditNote.onApplyToInvoicesCreated,
|
||||||
|
{
|
||||||
|
creditNote,
|
||||||
|
creditNoteAppliedInvoices,
|
||||||
|
trx,
|
||||||
|
} as IApplyCreditToInvoicesCreatedPayload,
|
||||||
|
);
|
||||||
|
|
||||||
|
return creditNoteAppliedInvoices;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes apply DTO to model.
|
||||||
|
* @param {IApplyCreditToInvoicesDTO} applyDTO
|
||||||
|
* @param {ICreditNote} creditNote
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
private transformApplyDTOToModel = (
|
||||||
|
applyDTO: IApplyCreditToInvoicesDTO,
|
||||||
|
creditNote: ICreditNote,
|
||||||
|
): ICreditNoteAppliedToInvoiceModel => {
|
||||||
|
const entries = applyDTO.entries.map((entry) => ({
|
||||||
|
invoiceId: entry.invoiceId,
|
||||||
|
amount: entry.amount,
|
||||||
|
creditNoteId: creditNote.id,
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
amount: sumBy(entries, 'amount'),
|
||||||
|
entries,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the invoice remaining amount.
|
||||||
|
* @param {ISaleInvoice[]} invoices
|
||||||
|
* @param {number} amount
|
||||||
|
*/
|
||||||
|
private validateInvoicesRemainingAmount = (
|
||||||
|
invoices: SaleInvoice[],
|
||||||
|
amount: number,
|
||||||
|
) => {
|
||||||
|
const invalidInvoices = invoices.filter(
|
||||||
|
(invoice) => invoice.dueAmount < amount,
|
||||||
|
);
|
||||||
|
if (invalidInvoices.length > 0) {
|
||||||
|
throw new ServiceError(ERRORS.INVOICES_HAS_NO_REMAINING_AMOUNT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { AutoIncrementOrdersService } from '@/modules/AutoIncrementOrders/AutoIncrementOrders.service';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreditNoteAutoIncrementService {
|
||||||
|
constructor(
|
||||||
|
private readonly autoIncrementOrdersService: AutoIncrementOrdersService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the next unique credit number.
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
public getNextCreditNumber(): string {
|
||||||
|
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||||
|
'credit_note',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the credit note serial next number.
|
||||||
|
*/
|
||||||
|
public incrementSerialNumber() {
|
||||||
|
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||||
|
'credit_note',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
// import { Inject, Service } from 'typedi';
|
||||||
|
// import { Knex } from 'knex';
|
||||||
|
// import * as R from 'ramda';
|
||||||
|
// import {
|
||||||
|
// AccountNormal,
|
||||||
|
// IItemEntry,
|
||||||
|
// ILedgerEntry,
|
||||||
|
// ICreditNote,
|
||||||
|
// ILedger,
|
||||||
|
// ICreditNoteGLCommonEntry,
|
||||||
|
// } from '@/interfaces';
|
||||||
|
// import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
// import Ledger from '@/services/Accounting/Ledger';
|
||||||
|
// import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||||
|
// import { SaleReceipt } from '@/models';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export default class CreditNoteGLEntries {
|
||||||
|
// @Inject()
|
||||||
|
// private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
// @Inject()
|
||||||
|
// private ledgerStorage: LedgerStorageService;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the credit note GL.
|
||||||
|
// * @param {ICreditNote} creditNote
|
||||||
|
// * @param {number} receivableAccount
|
||||||
|
// * @returns {Ledger}
|
||||||
|
// */
|
||||||
|
// private getCreditNoteGLedger = (
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// receivableAccount: number,
|
||||||
|
// discountAccount: number,
|
||||||
|
// adjustmentAccount: number
|
||||||
|
// ): Ledger => {
|
||||||
|
// const ledgerEntries = this.getCreditNoteGLEntries(
|
||||||
|
// creditNote,
|
||||||
|
// receivableAccount,
|
||||||
|
// discountAccount,
|
||||||
|
// adjustmentAccount
|
||||||
|
// );
|
||||||
|
// return new Ledger(ledgerEntries);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Saves credit note GL entries.
|
||||||
|
// * @param {number} tenantId -
|
||||||
|
// * @param {ICreditNote} creditNote - Credit note model.
|
||||||
|
// * @param {number} payableAccount - Payable account id.
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public saveCreditNoteGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// payableAccount: number,
|
||||||
|
// discountAccount: number,
|
||||||
|
// adjustmentAccount: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// const ledger = this.getCreditNoteGLedger(
|
||||||
|
// creditNote,
|
||||||
|
// payableAccount,
|
||||||
|
// discountAccount,
|
||||||
|
// adjustmentAccount
|
||||||
|
// );
|
||||||
|
|
||||||
|
// await this.ledgerStorage.commit(tenantId, ledger, trx);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Reverts the credit note associated GL entries.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {number} vendorCreditId
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public revertVendorCreditGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// creditNoteId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// await this.ledgerStorage.deleteByReference(
|
||||||
|
// tenantId,
|
||||||
|
// creditNoteId,
|
||||||
|
// 'CreditNote',
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Writes vendor credit associated GL entries.
|
||||||
|
// * @param {number} tenantId - Tenant id.
|
||||||
|
// * @param {number} creditNoteId - Credit note id.
|
||||||
|
// * @param {Knex.Transaction} trx - Knex transactions.
|
||||||
|
// */
|
||||||
|
// public createVendorCreditGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// creditNoteId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// const { CreditNote } = this.tenancy.models(tenantId);
|
||||||
|
// const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
|
// // Retrieve the credit note with associated entries and items.
|
||||||
|
// const creditNoteWithItems = await CreditNote.query(trx)
|
||||||
|
// .findById(creditNoteId)
|
||||||
|
// .withGraphFetched('entries.item');
|
||||||
|
|
||||||
|
// // Retreive the the `accounts receivable` account based on the given currency.
|
||||||
|
// const ARAccount = await accountRepository.findOrCreateAccountReceivable(
|
||||||
|
// creditNoteWithItems.currencyCode
|
||||||
|
// );
|
||||||
|
// const discountAccount = await accountRepository.findOrCreateDiscountAccount(
|
||||||
|
// {}
|
||||||
|
// );
|
||||||
|
// const adjustmentAccount =
|
||||||
|
// await accountRepository.findOrCreateOtherChargesAccount({});
|
||||||
|
// // Saves the credit note GL entries.
|
||||||
|
// await this.saveCreditNoteGLEntries(
|
||||||
|
// tenantId,
|
||||||
|
// creditNoteWithItems,
|
||||||
|
// ARAccount.id,
|
||||||
|
// discountAccount.id,
|
||||||
|
// adjustmentAccount.id,
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Edits vendor credit associated GL entries.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {number} creditNoteId
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public editVendorCreditGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// creditNoteId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// // Reverts vendor credit GL entries.
|
||||||
|
// await this.revertVendorCreditGLEntries(tenantId, creditNoteId, trx);
|
||||||
|
|
||||||
|
// // Creates vendor credit Gl entries.
|
||||||
|
// await this.createVendorCreditGLEntries(tenantId, creditNoteId, trx);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieve the credit note common entry.
|
||||||
|
// * @param {ICreditNote} creditNote -
|
||||||
|
// * @returns {ICreditNoteGLCommonEntry}
|
||||||
|
// */
|
||||||
|
// private getCreditNoteCommonEntry = (
|
||||||
|
// creditNote: ICreditNote
|
||||||
|
// ): ICreditNoteGLCommonEntry => {
|
||||||
|
// return {
|
||||||
|
// date: creditNote.creditNoteDate,
|
||||||
|
// userId: creditNote.userId,
|
||||||
|
// currencyCode: creditNote.currencyCode,
|
||||||
|
// exchangeRate: creditNote.exchangeRate,
|
||||||
|
|
||||||
|
// transactionType: 'CreditNote',
|
||||||
|
// transactionId: creditNote.id,
|
||||||
|
|
||||||
|
// transactionNumber: creditNote.creditNoteNumber,
|
||||||
|
// referenceNumber: creditNote.referenceNo,
|
||||||
|
|
||||||
|
// createdAt: creditNote.createdAt,
|
||||||
|
// indexGroup: 10,
|
||||||
|
|
||||||
|
// credit: 0,
|
||||||
|
// debit: 0,
|
||||||
|
|
||||||
|
// branchId: creditNote.branchId,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the creidt note A/R entry.
|
||||||
|
// * @param {ICreditNote} creditNote -
|
||||||
|
// * @param {number} ARAccountId -
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// private getCreditNoteAREntry = (
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// ARAccountId: number
|
||||||
|
// ): ILedgerEntry => {
|
||||||
|
// const commonEntry = this.getCreditNoteCommonEntry(creditNote);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntry,
|
||||||
|
// credit: creditNote.totalLocal,
|
||||||
|
// accountId: ARAccountId,
|
||||||
|
// contactId: creditNote.customerId,
|
||||||
|
// index: 1,
|
||||||
|
// accountNormal: AccountNormal.DEBIT,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieve the credit note item entry.
|
||||||
|
// * @param {ICreditNote} creditNote
|
||||||
|
// * @param {IItemEntry} entry
|
||||||
|
// * @param {number} index
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// private getCreditNoteItemEntry = R.curry(
|
||||||
|
// (
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// entry: IItemEntry,
|
||||||
|
// index: number
|
||||||
|
// ): ILedgerEntry => {
|
||||||
|
// const commonEntry = this.getCreditNoteCommonEntry(creditNote);
|
||||||
|
// const totalLocal = entry.totalExcludingTax * creditNote.exchangeRate;
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntry,
|
||||||
|
// debit: totalLocal,
|
||||||
|
// accountId: entry.sellAccountId || entry.item.sellAccountId,
|
||||||
|
// note: entry.description,
|
||||||
|
// index: index + 2,
|
||||||
|
// itemId: entry.itemId,
|
||||||
|
// itemQuantity: entry.quantity,
|
||||||
|
// accountNormal: AccountNormal.CREDIT,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the credit note discount entry.
|
||||||
|
// * @param {ICreditNote} creditNote
|
||||||
|
// * @param {number} discountAccountId
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// private getDiscountEntry = (
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// discountAccountId: number
|
||||||
|
// ): ILedgerEntry => {
|
||||||
|
// const commonEntry = this.getCreditNoteCommonEntry(creditNote);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntry,
|
||||||
|
// credit: creditNote.discountAmountLocal,
|
||||||
|
// accountId: discountAccountId,
|
||||||
|
// index: 1,
|
||||||
|
// accountNormal: AccountNormal.CREDIT,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the credit note adjustment entry.
|
||||||
|
// * @param {ICreditNote} creditNote
|
||||||
|
// * @param {number} adjustmentAccountId
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// private getAdjustmentEntry = (
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// adjustmentAccountId: number
|
||||||
|
// ): ILedgerEntry => {
|
||||||
|
// const commonEntry = this.getCreditNoteCommonEntry(creditNote);
|
||||||
|
// const adjustmentAmount = Math.abs(creditNote.adjustmentLocal);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntry,
|
||||||
|
// credit: creditNote.adjustmentLocal < 0 ? adjustmentAmount : 0,
|
||||||
|
// debit: creditNote.adjustmentLocal > 0 ? adjustmentAmount : 0,
|
||||||
|
// accountId: adjustmentAccountId,
|
||||||
|
// accountNormal: AccountNormal.CREDIT,
|
||||||
|
// index: 1,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieve the credit note GL entries.
|
||||||
|
// * @param {ICreditNote} creditNote - Credit note.
|
||||||
|
// * @param {IAccount} receivableAccount - Receviable account.
|
||||||
|
// * @returns {ILedgerEntry[]} - Ledger entries.
|
||||||
|
// */
|
||||||
|
// public getCreditNoteGLEntries = (
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// ARAccountId: number,
|
||||||
|
// discountAccountId: number,
|
||||||
|
// adjustmentAccountId: number
|
||||||
|
// ): ILedgerEntry[] => {
|
||||||
|
// const AREntry = this.getCreditNoteAREntry(creditNote, ARAccountId);
|
||||||
|
|
||||||
|
// const getItemEntry = this.getCreditNoteItemEntry(creditNote);
|
||||||
|
// const itemsEntries = creditNote.entries.map(getItemEntry);
|
||||||
|
|
||||||
|
// const discountEntry = this.getDiscountEntry(creditNote, discountAccountId);
|
||||||
|
// const adjustmentEntry = this.getAdjustmentEntry(
|
||||||
|
// creditNote,
|
||||||
|
// adjustmentAccountId
|
||||||
|
// );
|
||||||
|
|
||||||
|
// return [AREntry, discountEntry, adjustmentEntry, ...itemsEntries];
|
||||||
|
// };
|
||||||
|
// }
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||||
|
|
||||||
|
export class CreditNoteWithInvoicesToApplyTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formattedInvoiceDate',
|
||||||
|
'formattedDueDate',
|
||||||
|
'formattedAmount',
|
||||||
|
'formattedDueAmount',
|
||||||
|
'formattedPaymentAmount',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice date.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedInvoiceDate = (invoice): string => {
|
||||||
|
return this.formatDate(invoice.invoiceDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted due date.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDueDate = (invoice): string => {
|
||||||
|
return this.formatDate(invoice.dueDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice amount.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (invoice): string => {
|
||||||
|
return this.formatNumber(invoice.balance, {
|
||||||
|
currencyCode: invoice.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice due amount.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDueAmount = (invoice): string => {
|
||||||
|
return this.formatNumber(invoice.dueAmount, {
|
||||||
|
currencyCode: invoice.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted payment amount.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedPaymentAmount = (invoice): string => {
|
||||||
|
return this.formatNumber(invoice.paymentAmount, {
|
||||||
|
currencyCode: invoice.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// import { Inject, Service } from 'typedi';
|
||||||
|
// import { ICreditNotesQueryDTO } from '@/interfaces';
|
||||||
|
// import { Exportable } from '@/services/Export/Exportable';
|
||||||
|
// import ListCreditNotes from '../ListCreditNotes';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export class CreditNotesExportable extends Exportable {
|
||||||
|
// @Inject()
|
||||||
|
// private getCreditNotes: ListCreditNotes;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the accounts data to exportable sheet.
|
||||||
|
// * @param {number} tenantId -
|
||||||
|
// * @param {IVendorCreditsQueryDTO} query -
|
||||||
|
// * @returns {}
|
||||||
|
// */
|
||||||
|
// public exportable(tenantId: number, query: ICreditNotesQueryDTO) {
|
||||||
|
// const filterQuery = (query) => {
|
||||||
|
// query.withGraphFetched('branch');
|
||||||
|
// query.withGraphFetched('warehouse');
|
||||||
|
// };
|
||||||
|
// const parsedQuery = {
|
||||||
|
// sortOrder: 'desc',
|
||||||
|
// columnSortBy: 'created_at',
|
||||||
|
// ...query,
|
||||||
|
// page: 1,
|
||||||
|
// pageSize: 12000,
|
||||||
|
// filterQuery,
|
||||||
|
// } as ICreditNotesQueryDTO;
|
||||||
|
|
||||||
|
// return this.getCreditNotes
|
||||||
|
// .getCreditNotesList(tenantId, parsedQuery)
|
||||||
|
// .then((output) => output.creditNotes);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// import { Inject, Service } from 'typedi';
|
||||||
|
// import { Knex } from 'knex';
|
||||||
|
// import { ICreditNoteNewDTO } from '@/interfaces';
|
||||||
|
// import { Importable } from '../Import/Importable';
|
||||||
|
// import CreateCreditNote from './commands/CreateCreditNote.service';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export class CreditNotesImportable extends Importable {
|
||||||
|
// @Inject()
|
||||||
|
// private createCreditNoteImportable: CreateCreditNote;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Importing to account service.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {IAccountCreateDTO} createAccountDTO
|
||||||
|
// * @returns
|
||||||
|
// */
|
||||||
|
// public importable(
|
||||||
|
// tenantId: number,
|
||||||
|
// createAccountDTO: ICreditNoteNewDTO,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ) {
|
||||||
|
// return this.createCreditNoteImportable.newCreditNote(
|
||||||
|
// tenantId,
|
||||||
|
// createAccountDTO,
|
||||||
|
// 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 [];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// import { Inject, Service } from 'typedi';
|
||||||
|
// import { Knex } from 'knex';
|
||||||
|
// import { ICreditNote } from '@/interfaces';
|
||||||
|
// import InventoryService from '@/services/Inventory/Inventory';
|
||||||
|
// import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export default class CreditNoteInventoryTransactions {
|
||||||
|
// @Inject()
|
||||||
|
// inventoryService: InventoryService;
|
||||||
|
|
||||||
|
// @Inject()
|
||||||
|
// itemsEntriesService: ItemsEntriesService;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Creates credit note inventory transactions.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {ICreditNote} creditNote
|
||||||
|
// */
|
||||||
|
// public createInventoryTransactions = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// // Loads the inventory items entries of the given sale invoice.
|
||||||
|
// const inventoryEntries =
|
||||||
|
// await this.itemsEntriesService.filterInventoryEntries(
|
||||||
|
// tenantId,
|
||||||
|
// creditNote.entries
|
||||||
|
// );
|
||||||
|
// const transaction = {
|
||||||
|
// transactionId: creditNote.id,
|
||||||
|
// transactionType: 'CreditNote',
|
||||||
|
// transactionNumber: creditNote.creditNoteNumber,
|
||||||
|
// exchangeRate: creditNote.exchangeRate,
|
||||||
|
// date: creditNote.creditNoteDate,
|
||||||
|
// direction: 'IN',
|
||||||
|
// entries: inventoryEntries,
|
||||||
|
// createdAt: creditNote.createdAt,
|
||||||
|
// warehouseId: creditNote.warehouseId,
|
||||||
|
// };
|
||||||
|
// // Writes inventory tranactions.
|
||||||
|
// await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
|
||||||
|
// tenantId,
|
||||||
|
// transaction,
|
||||||
|
// false,
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Edits vendor credit associated inventory transactions.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {number} creditNoteId
|
||||||
|
// * @param {ICreditNote} creditNote
|
||||||
|
// * @param {Knex.Transactions} trx
|
||||||
|
// */
|
||||||
|
// public editInventoryTransactions = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// creditNoteId: number,
|
||||||
|
// creditNote: ICreditNote,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// // Deletes inventory transactions.
|
||||||
|
// await this.deleteInventoryTransactions(tenantId, creditNoteId, trx);
|
||||||
|
|
||||||
|
// // Re-write inventory transactions.
|
||||||
|
// await this.createInventoryTransactions(tenantId, creditNote, trx);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Deletes credit note associated inventory transactions.
|
||||||
|
// * @param {number} tenantId - Tenant id.
|
||||||
|
// * @param {number} creditNoteId - Credit note id.
|
||||||
|
// * @param {Knex.Transaction} trx -
|
||||||
|
// */
|
||||||
|
// public deleteInventoryTransactions = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// creditNoteId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// // Deletes the inventory transactions by the given reference id and type.
|
||||||
|
// await this.inventoryService.deleteInventoryTransactions(
|
||||||
|
// tenantId,
|
||||||
|
// creditNoteId,
|
||||||
|
// 'CreditNote',
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
// }
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
ICreditNoteDeletedPayload,
|
||||||
|
ICreditNoteDeletingPayload,
|
||||||
|
} from '../types/CreditNotes.types';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||||
|
import {
|
||||||
|
RefundCreditNote as RefundCreditNoteModel,
|
||||||
|
} from '../models/RefundCreditNote';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { ItemEntry } from '@/modules/Items/models/ItemEntry';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeleteCreditNoteService {
|
||||||
|
/**
|
||||||
|
* @param {UnitOfWork} uow - Unit of work.
|
||||||
|
* @param {EventEmitter2} eventPublisher - Event emitter.
|
||||||
|
* @param {typeof CreditNote} creditNoteModel - Credit note model.
|
||||||
|
* @param {typeof ItemEntry} itemEntryModel - Item entry model.
|
||||||
|
* @param {typeof CreditNoteAppliedInvoice} creditNoteAppliedInvoiceModel - Credit note applied invoice model.
|
||||||
|
* @param {typeof RefundCreditNote} refundCreditNoteModel - Refund credit note model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private readonly creditNoteModel: typeof CreditNote,
|
||||||
|
|
||||||
|
@Inject(ItemEntry.name)
|
||||||
|
private readonly itemEntryModel: typeof ItemEntry,
|
||||||
|
|
||||||
|
@Inject(CreditNoteAppliedInvoice.name)
|
||||||
|
private readonly creditNoteAppliedInvoiceModel: typeof CreditNoteAppliedInvoice,
|
||||||
|
|
||||||
|
@Inject(RefundCreditNoteModel.name)
|
||||||
|
private readonly refundCreditNoteModel: typeof RefundCreditNoteModel,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given credit note transactions.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async deleteCreditNote(creditNoteId: number): Promise<void> {
|
||||||
|
// Retrieve the credit note or throw not found service error.
|
||||||
|
const oldCreditNote = await this.creditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Validate credit note has no refund transactions.
|
||||||
|
await this.validateCreditNoteHasNoRefundTransactions(creditNoteId);
|
||||||
|
|
||||||
|
// Validate credit note has no applied invoices transactions.
|
||||||
|
await this.validateCreditNoteHasNoApplyInvoiceTransactions(creditNoteId);
|
||||||
|
|
||||||
|
// Deletes the credit note transactions under unit-of-work transaction.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onCreditNoteDeleting` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onDeleting, {
|
||||||
|
trx,
|
||||||
|
oldCreditNote,
|
||||||
|
} as ICreditNoteDeletingPayload);
|
||||||
|
|
||||||
|
// Deletes the associated credit note entries.
|
||||||
|
await this.itemEntryModel
|
||||||
|
.query(trx)
|
||||||
|
.where('reference_id', creditNoteId)
|
||||||
|
.where('reference_type', 'CreditNote')
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Deletes the credit note transaction.
|
||||||
|
await this.creditNoteModel.query(trx).findById(creditNoteId).delete();
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteDeleted` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onDeleted, {
|
||||||
|
oldCreditNote,
|
||||||
|
creditNoteId,
|
||||||
|
trx,
|
||||||
|
} as ICreditNoteDeletedPayload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates credit note has no associated refund transactions.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateCreditNoteHasNoRefundTransactions(
|
||||||
|
creditNoteId: number,
|
||||||
|
): Promise<void> {
|
||||||
|
const refundTransactions = await this.refundCreditNoteModel
|
||||||
|
.query()
|
||||||
|
.where('creditNoteId', creditNoteId);
|
||||||
|
|
||||||
|
if (refundTransactions.length > 0) {
|
||||||
|
throw new ServiceError(ERRORS.CREDIT_NOTE_HAS_REFUNDS_TRANSACTIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate credit note has no associated applied invoices transactions.
|
||||||
|
* @param {number} creditNoteId - Credit note id.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateCreditNoteHasNoApplyInvoiceTransactions(
|
||||||
|
creditNoteId: number,
|
||||||
|
): Promise<void> {
|
||||||
|
const appliedTransactions = await this.creditNoteAppliedInvoiceModel
|
||||||
|
.query()
|
||||||
|
.where('creditNoteId', creditNoteId);
|
||||||
|
|
||||||
|
if (appliedTransactions.length > 0) {
|
||||||
|
throw new ServiceError(ERRORS.CREDIT_NOTE_HAS_APPLIED_INVOICES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { IApplyCreditToInvoicesDeletedPayload } from '../types/CreditNotes.types';
|
||||||
|
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class DeleteCreditNoteApplyToInvoices {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
private readonly creditNoteAppliedInvoiceModel: typeof CreditNoteAppliedInvoice,
|
||||||
|
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private readonly creditNoteModel: typeof CreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply credit note to the given invoices.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @param {IApplyCreditToInvoicesDTO} applyCreditToInvoicesDTO
|
||||||
|
*/
|
||||||
|
public deleteApplyCreditNoteToInvoices = async (
|
||||||
|
applyCreditToInvoicesId: number,
|
||||||
|
): Promise<void> => {
|
||||||
|
const creditNoteAppliedToInvoice = await this.creditNoteAppliedInvoiceModel
|
||||||
|
.query()
|
||||||
|
.findById(applyCreditToInvoicesId);
|
||||||
|
|
||||||
|
if (!creditNoteAppliedToInvoice) {
|
||||||
|
throw new ServiceError(ERRORS.CREDIT_NOTE_APPLY_TO_INVOICES_NOT_FOUND);
|
||||||
|
}
|
||||||
|
// Retrieve the credit note or throw not found service error.
|
||||||
|
const creditNote = await this.creditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(creditNoteAppliedToInvoice.creditNoteId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Creates credit note apply to invoice transaction.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Delete credit note applied to invoices.
|
||||||
|
await this.creditNoteAppliedInvoiceModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(applyCreditToInvoicesId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteApplyToInvoiceDeleted` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.creditNote.onApplyToInvoicesDeleted,
|
||||||
|
{
|
||||||
|
creditNote,
|
||||||
|
creditNoteAppliedToInvoice,
|
||||||
|
trx,
|
||||||
|
} as IApplyCreditToInvoicesDeletedPayload,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeleteCustomerLinkedCreditNoteService {
|
||||||
|
constructor(
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private readonly creditNoteModel: typeof CreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the given customer has no linked credit note transactions.
|
||||||
|
* @param {number} customerId - The customer identifier.
|
||||||
|
*/
|
||||||
|
public async validateCustomerHasNoCreditTransaction(customerId: number) {
|
||||||
|
const associatedCredits = await this.creditNoteModel
|
||||||
|
.query()
|
||||||
|
.where('customerId', customerId);
|
||||||
|
|
||||||
|
if (associatedCredits.length > 0) {
|
||||||
|
throw new ServiceError(ERRORS.CUSTOMER_HAS_LINKED_CREDIT_NOTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
IRefundCreditNoteDeletedPayload,
|
||||||
|
IRefundCreditNoteDeletingPayload,
|
||||||
|
IRefundVendorCreditDeletedPayload,
|
||||||
|
} from '../types/CreditNotes.types';
|
||||||
|
import { RefundCreditNote } from '../models/RefundCreditNote';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class DeleteRefundCreditNote {
|
||||||
|
/**
|
||||||
|
* @param {UnitOfWork} uow
|
||||||
|
* @param {EventEmitter2} eventPublisher
|
||||||
|
* @param {typeof RefundCreditNoteModel} refundCreditNoteModel
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(RefundCreditNote.name)
|
||||||
|
private readonly refundCreditNoteModel: typeof RefundCreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the credit note graph.
|
||||||
|
* @param {number} refundCreditId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public deleteCreditNoteRefund = async (refundCreditId: number) => {
|
||||||
|
// Retrieve the old credit note or throw not found service error.
|
||||||
|
const oldRefundCredit = await this.refundCreditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(refundCreditId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteRefundDeleted` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onRefundDelete, {
|
||||||
|
refundCreditId,
|
||||||
|
oldRefundCredit,
|
||||||
|
} as IRefundCreditNoteDeletedPayload);
|
||||||
|
|
||||||
|
// Deletes refund credit note transactions with associated entries.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
const eventPayload = {
|
||||||
|
trx,
|
||||||
|
refundCreditId,
|
||||||
|
oldRefundCredit,
|
||||||
|
} as IRefundCreditNoteDeletedPayload | IRefundCreditNoteDeletingPayload;
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteRefundDeleting` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.creditNote.onRefundDeleting,
|
||||||
|
eventPayload,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deletes the refund credit note graph from the storage.
|
||||||
|
await this.refundCreditNoteModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(refundCreditId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteRefundDeleted` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.creditNote.onRefundDeleted,
|
||||||
|
eventPayload as IRefundVendorCreditDeletedPayload,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ICreditNoteEditDTO,
|
||||||
|
ICreditNoteEditedPayload,
|
||||||
|
ICreditNoteEditingPayload,
|
||||||
|
} from '../types/CreditNotes.types';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { Contact } from '../../Contacts/models/Contact';
|
||||||
|
import { ItemsEntriesService } from '../../Items/ItemsEntries.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { UnitOfWork } from '../../Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { CommandCreditNoteDTOTransform } from './CommandCreditNoteDTOTransform.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EditCreditNoteService {
|
||||||
|
/**
|
||||||
|
* @param {typeof CreditNote} creditNoteModel - The credit note model.
|
||||||
|
* @param {typeof Contact} contactModel - The contact model.
|
||||||
|
* @param {CommandCreditNoteDTOTransform} commandCreditNoteDTOTransform - The command credit note DTO transform service.
|
||||||
|
* @param {ItemsEntriesService} itemsEntriesService - The items entries service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - The event publisher.
|
||||||
|
* @param {UnitOfWork} uow - The unit of work.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
@Inject(CreditNote.name) private creditNoteModel: typeof CreditNote,
|
||||||
|
@Inject(Contact.name) private contactModel: typeof Contact,
|
||||||
|
private commandCreditNoteDTOTransform: CommandCreditNoteDTOTransform,
|
||||||
|
private itemsEntriesService: ItemsEntriesService,
|
||||||
|
private eventPublisher: EventEmitter2,
|
||||||
|
private uow: UnitOfWork,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits the given credit note.
|
||||||
|
* @param {ICreditNoteEditDTO} creditNoteEditDTO -
|
||||||
|
*/
|
||||||
|
public async editCreditNote(
|
||||||
|
creditNoteId: number,
|
||||||
|
creditNoteEditDTO: ICreditNoteEditDTO,
|
||||||
|
) {
|
||||||
|
// Retrieve the sale invoice or throw not found service error.
|
||||||
|
const oldCreditNote = await this.creditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Validate customer existance.
|
||||||
|
const customer = await this.contactModel
|
||||||
|
.query()
|
||||||
|
.findById(creditNoteEditDTO.customerId);
|
||||||
|
|
||||||
|
// Validate items ids existance.
|
||||||
|
await this.itemsEntriesService.validateItemsIdsExistance(
|
||||||
|
creditNoteEditDTO.entries,
|
||||||
|
);
|
||||||
|
// Validate non-sellable entries items.
|
||||||
|
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||||
|
creditNoteEditDTO.entries,
|
||||||
|
);
|
||||||
|
// Validate the items entries existance.
|
||||||
|
await this.itemsEntriesService.validateEntriesIdsExistance(
|
||||||
|
creditNoteId,
|
||||||
|
'CreditNote',
|
||||||
|
creditNoteEditDTO.entries,
|
||||||
|
);
|
||||||
|
// Transformes the given DTO to storage layer data.
|
||||||
|
const creditNoteModel =
|
||||||
|
await this.commandCreditNoteDTOTransform.transformCreateEditDTOToModel(
|
||||||
|
creditNoteEditDTO,
|
||||||
|
customer.currencyCode,
|
||||||
|
oldCreditNote,
|
||||||
|
);
|
||||||
|
// Sales the credit note transactions with associated entries.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onCreditNoteEditing` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onEditing, {
|
||||||
|
creditNoteEditDTO,
|
||||||
|
oldCreditNote,
|
||||||
|
trx,
|
||||||
|
} as ICreditNoteEditingPayload);
|
||||||
|
|
||||||
|
// Saves the credit note graph to the storage.
|
||||||
|
const creditNote = await this.creditNoteModel.query(trx).upsertGraph({
|
||||||
|
id: creditNoteId,
|
||||||
|
...creditNoteModel,
|
||||||
|
});
|
||||||
|
// Triggers `onCreditNoteEdited` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onEdited, {
|
||||||
|
trx,
|
||||||
|
oldCreditNote,
|
||||||
|
creditNoteId,
|
||||||
|
creditNote,
|
||||||
|
creditNoteEditDTO,
|
||||||
|
} as ICreditNoteEditedPayload);
|
||||||
|
|
||||||
|
return creditNote;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import {
|
||||||
|
ICreditNoteOpenedPayload,
|
||||||
|
ICreditNoteOpeningPayload,
|
||||||
|
} from '../types/CreditNotes.types';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class OpenCreditNoteService {
|
||||||
|
/**
|
||||||
|
* @param {EventEmitter2} eventPublisher - The event publisher.
|
||||||
|
* @param {UnitOfWork} uow - The unit of work.
|
||||||
|
* @param {typeof CreditNote} creditNoteModel - The credit note model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private readonly creditNoteModel: typeof CreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given credit note.
|
||||||
|
* @param {number} creditNoteId -
|
||||||
|
* @returns {Promise<CreditNote>}
|
||||||
|
*/
|
||||||
|
public openCreditNote = async (creditNoteId: number): Promise<CreditNote> => {
|
||||||
|
// Retrieve the sale invoice or throw not found service error.
|
||||||
|
const oldCreditNote = await this.creditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Throw service error if the credit note is already open.
|
||||||
|
this.throwErrorIfAlreadyOpen(oldCreditNote);
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteOpen` event.
|
||||||
|
this.eventPublisher.emitAsync(events.creditNote.onOpen, {
|
||||||
|
creditNoteId,
|
||||||
|
oldCreditNote,
|
||||||
|
});
|
||||||
|
// Sales the credit note transactions with associated entries.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
const eventPayload = {
|
||||||
|
creditNoteId,
|
||||||
|
oldCreditNote,
|
||||||
|
trx,
|
||||||
|
} as ICreditNoteOpeningPayload;
|
||||||
|
|
||||||
|
// Triggers `onCreditNoteOpening` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.creditNote.onOpening,
|
||||||
|
eventPayload,
|
||||||
|
);
|
||||||
|
// Saves the credit note graph to the storage.
|
||||||
|
const creditNote = await this.creditNoteModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.update({
|
||||||
|
openedAt: new Date(),
|
||||||
|
});
|
||||||
|
// Triggers `onCreditNoteOpened` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.creditNote.onOpened, {
|
||||||
|
...eventPayload,
|
||||||
|
creditNote,
|
||||||
|
} as ICreditNoteOpenedPayload);
|
||||||
|
|
||||||
|
return creditNote;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param creditNote
|
||||||
|
*/
|
||||||
|
public throwErrorIfAlreadyOpen = (creditNote) => {
|
||||||
|
if (creditNote.openedAt) {
|
||||||
|
throw new ServiceError(ERRORS.CREDIT_NOTE_ALREADY_OPENED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { RefundCreditNote } from '../models/RefundCreditNote';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RefundCreditNoteService {
|
||||||
|
/**
|
||||||
|
* @param {typeof RefundCreditNote} refundCreditNoteModel - The refund credit note model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
@Inject(RefundCreditNote.name)
|
||||||
|
private readonly refundCreditNoteModel: typeof RefundCreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the credit note graph.
|
||||||
|
* @param {number} refundCreditId
|
||||||
|
* @returns {Promise<RefundCreditNote>}
|
||||||
|
*/
|
||||||
|
public getCreditNoteRefundOrThrowError = async (
|
||||||
|
refundCreditId: number,
|
||||||
|
): Promise<RefundCreditNote> => {
|
||||||
|
const refundCreditNote = await this.refundCreditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(refundCreditId);
|
||||||
|
if (!refundCreditNote) {
|
||||||
|
throw new ServiceError(ERRORS.REFUND_CREDIT_NOTE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
return refundCreditNote;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the refund account type.
|
||||||
|
* @param {Account} account
|
||||||
|
*/
|
||||||
|
public validateRefundWithdrawwalAccountType = (account: Account): void => {
|
||||||
|
const supportedTypes = ['bank', 'cash', 'fixed-asset'];
|
||||||
|
|
||||||
|
if (supportedTypes.indexOf(account.accountType) === -1) {
|
||||||
|
throw new ServiceError(ERRORS.ACCOUNT_INVALID_TYPE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
// import { Inject, Service } from 'typedi';
|
||||||
|
// import { Knex } from 'knex';
|
||||||
|
// import { AccountNormal, ILedgerEntry, IRefundCreditNote } from '@/interfaces';
|
||||||
|
// import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
// import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||||
|
// import Ledger from '@/services/Accounting/Ledger';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export default class RefundCreditNoteGLEntries {
|
||||||
|
// @Inject()
|
||||||
|
// ledgerStorage: LedgerStorageService;
|
||||||
|
|
||||||
|
// @Inject()
|
||||||
|
// tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the refund credit common GL entry.
|
||||||
|
// * @param {IRefundCreditNote} refundCreditNote
|
||||||
|
// * @returns
|
||||||
|
// */
|
||||||
|
// private getRefundCreditCommonGLEntry = (
|
||||||
|
// refundCreditNote: IRefundCreditNote
|
||||||
|
// ) => {
|
||||||
|
// return {
|
||||||
|
// currencyCode: refundCreditNote.currencyCode,
|
||||||
|
// exchangeRate: refundCreditNote.exchangeRate,
|
||||||
|
|
||||||
|
// transactionType: 'RefundCreditNote',
|
||||||
|
// transactionId: refundCreditNote.id,
|
||||||
|
// date: refundCreditNote.date,
|
||||||
|
// userId: refundCreditNote.userId,
|
||||||
|
|
||||||
|
// referenceNumber: refundCreditNote.referenceNo,
|
||||||
|
|
||||||
|
// createdAt: refundCreditNote.createdAt,
|
||||||
|
// indexGroup: 10,
|
||||||
|
|
||||||
|
// credit: 0,
|
||||||
|
// debit: 0,
|
||||||
|
|
||||||
|
// note: refundCreditNote.description,
|
||||||
|
// branchId: refundCreditNote.branchId,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the refudn credit receivable GL entry.
|
||||||
|
// * @param {IRefundCreditNote} refundCreditNote
|
||||||
|
// * @param {number} ARAccountId
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// private getRefundCreditGLReceivableEntry = (
|
||||||
|
// refundCreditNote: IRefundCreditNote,
|
||||||
|
// ARAccountId: number
|
||||||
|
// ): ILedgerEntry => {
|
||||||
|
// const commonEntry = this.getRefundCreditCommonGLEntry(refundCreditNote);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntry,
|
||||||
|
// debit: refundCreditNote.amount,
|
||||||
|
// accountId: ARAccountId,
|
||||||
|
// contactId: refundCreditNote.creditNote.customerId,
|
||||||
|
// index: 1,
|
||||||
|
// accountNormal: AccountNormal.DEBIT,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the refund credit withdrawal GL entry.
|
||||||
|
// * @param {number} refundCreditNote
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// private getRefundCreditGLWithdrawalEntry = (
|
||||||
|
// refundCreditNote: IRefundCreditNote
|
||||||
|
// ): ILedgerEntry => {
|
||||||
|
// const commonEntry = this.getRefundCreditCommonGLEntry(refundCreditNote);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntry,
|
||||||
|
// credit: refundCreditNote.amount,
|
||||||
|
// accountId: refundCreditNote.fromAccountId,
|
||||||
|
// index: 2,
|
||||||
|
// accountNormal: AccountNormal.DEBIT,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieve the refund credit note GL entries.
|
||||||
|
// * @param {IRefundCreditNote} refundCreditNote
|
||||||
|
// * @param {number} receivableAccount
|
||||||
|
// * @returns {ILedgerEntry[]}
|
||||||
|
// */
|
||||||
|
// public getRefundCreditGLEntries(
|
||||||
|
// refundCreditNote: IRefundCreditNote,
|
||||||
|
// ARAccountId: number
|
||||||
|
// ): ILedgerEntry[] {
|
||||||
|
// const receivableEntry = this.getRefundCreditGLReceivableEntry(
|
||||||
|
// refundCreditNote,
|
||||||
|
// ARAccountId
|
||||||
|
// );
|
||||||
|
// const withdrawalEntry =
|
||||||
|
// this.getRefundCreditGLWithdrawalEntry(refundCreditNote);
|
||||||
|
|
||||||
|
// return [receivableEntry, withdrawalEntry];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Creates refund credit GL entries.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {IRefundCreditNote} refundCreditNote
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public createRefundCreditGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// refundCreditNoteId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ) => {
|
||||||
|
// const { Account, RefundCreditNote } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// // Retrieve the refund with associated credit note.
|
||||||
|
// const refundCreditNote = await RefundCreditNote.query(trx)
|
||||||
|
// .findById(refundCreditNoteId)
|
||||||
|
// .withGraphFetched('creditNote');
|
||||||
|
|
||||||
|
// // Receivable account A/R.
|
||||||
|
// const receivableAccount = await Account.query().findOne(
|
||||||
|
// 'slug',
|
||||||
|
// 'accounts-receivable'
|
||||||
|
// );
|
||||||
|
// // Retrieve refund credit GL entries.
|
||||||
|
// const refundGLEntries = this.getRefundCreditGLEntries(
|
||||||
|
// refundCreditNote,
|
||||||
|
// receivableAccount.id
|
||||||
|
// );
|
||||||
|
// const ledger = new Ledger(refundGLEntries);
|
||||||
|
|
||||||
|
// // Saves refund ledger entries.
|
||||||
|
// await this.ledgerStorage.commit(tenantId, ledger, trx);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Reverts refund credit note GL entries.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {number} refundCreditNoteId
|
||||||
|
// * @param {number} receivableAccount
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public revertRefundCreditGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// refundCreditNoteId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ) => {
|
||||||
|
// await this.ledgerStorage.deleteByReference(
|
||||||
|
// tenantId,
|
||||||
|
// refundCreditNoteId,
|
||||||
|
// 'RefundCreditNote',
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
// }
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RefundSyncCreditNoteBalance {
|
||||||
|
/**
|
||||||
|
* @param {typeof CreditNote} creditNoteModel - The credit note model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private readonly creditNoteModel: typeof CreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the refund amount of the credit note.
|
||||||
|
* @param {number} creditNoteId - The credit note ID.
|
||||||
|
* @param {number} amount - The amount to increment.
|
||||||
|
* @param {Knex.Transaction} trx - The knex transaction.
|
||||||
|
*/
|
||||||
|
public incrementCreditNoteRefundAmount = async (
|
||||||
|
creditNoteId: number,
|
||||||
|
amount: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<void> => {
|
||||||
|
await this.creditNoteModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.increment('refunded_amount', amount);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements the refund amount of the credit note.
|
||||||
|
* @param {number} creditNoteId - The credit note ID.
|
||||||
|
* @param {number} amount - The amount to decrement.
|
||||||
|
* @param {Knex.Transaction} trx - The knex transaction.
|
||||||
|
*/
|
||||||
|
public decrementCreditNoteRefundAmount = async (
|
||||||
|
creditNoteId: number,
|
||||||
|
amount: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<void> => {
|
||||||
|
await this.creditNoteModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.decrement('refunded_amount', amount);
|
||||||
|
};
|
||||||
|
}
|
||||||
132
packages/server-nest/src/modules/CreditNotes/constants.ts
Normal file
132
packages/server-nest/src/modules/CreditNotes/constants.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
export const ERRORS = {
|
||||||
|
CREDIT_NOTE_NOT_FOUND: 'CREDIT_NOTE_NOT_FOUND',
|
||||||
|
REFUND_CREDIT_NOTE_NOT_FOUND: 'REFUND_CREDIT_NOTE_NOT_FOUND',
|
||||||
|
CREDIT_NOTE_ALREADY_OPENED: 'CREDIT_NOTE_ALREADY_OPENED',
|
||||||
|
ACCOUNT_INVALID_TYPE: 'ACCOUNT_INVALID_TYPE',
|
||||||
|
CREDIT_NOTE_HAS_NO_REMAINING_AMOUNT: 'CREDIT_NOTE_HAS_NO_REMAINING_AMOUNT',
|
||||||
|
INVOICES_HAS_NO_REMAINING_AMOUNT: 'INVOICES_HAS_NO_REMAINING_AMOUNT',
|
||||||
|
CREDIT_NOTE_APPLY_TO_INVOICES_NOT_FOUND:
|
||||||
|
'CREDIT_NOTE_APPLY_TO_INVOICES_NOT_FOUND',
|
||||||
|
CREDIT_NOTE_HAS_REFUNDS_TRANSACTIONS: 'CREDIT_NOTE_HAS_REFUNDS_TRANSACTIONS',
|
||||||
|
CREDIT_NOTE_HAS_APPLIED_INVOICES: 'CREDIT_NOTE_HAS_APPLIED_INVOICES',
|
||||||
|
CUSTOMER_HAS_LINKED_CREDIT_NOTES: 'CUSTOMER_HAS_LINKED_CREDIT_NOTES',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_VIEW_COLUMNS = [];
|
||||||
|
export const DEFAULT_VIEWS = [
|
||||||
|
{
|
||||||
|
name: 'credit_note.view.draft',
|
||||||
|
slug: 'draft',
|
||||||
|
rolesLogicExpression: '1',
|
||||||
|
roles: [
|
||||||
|
{ index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' },
|
||||||
|
],
|
||||||
|
columns: DEFAULT_VIEW_COLUMNS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'credit_note.view.published',
|
||||||
|
slug: 'published',
|
||||||
|
rolesLogicExpression: '1',
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
fieldKey: 'status',
|
||||||
|
comparator: 'equals',
|
||||||
|
value: 'published',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
columns: DEFAULT_VIEW_COLUMNS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'credit_note.view.open',
|
||||||
|
slug: 'open',
|
||||||
|
rolesLogicExpression: '1',
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
fieldKey: 'status',
|
||||||
|
comparator: 'equals',
|
||||||
|
value: 'open',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
columns: DEFAULT_VIEW_COLUMNS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'credit_note.view.closed',
|
||||||
|
slug: 'closed',
|
||||||
|
rolesLogicExpression: '1',
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
fieldKey: 'status',
|
||||||
|
comparator: 'equals',
|
||||||
|
value: 'closed',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
columns: DEFAULT_VIEW_COLUMNS,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const defaultCreditNoteBrandingAttributes = {
|
||||||
|
// # Colors
|
||||||
|
primaryColor: '',
|
||||||
|
secondaryColor: '',
|
||||||
|
|
||||||
|
// # Company logo
|
||||||
|
showCompanyLogo: true,
|
||||||
|
companyLogoKey: '',
|
||||||
|
companyLogoUri: '',
|
||||||
|
|
||||||
|
// # Company name
|
||||||
|
companyName: 'Bigcapital Technology, Inc.',
|
||||||
|
|
||||||
|
// # Customer address
|
||||||
|
showCustomerAddress: true,
|
||||||
|
customerAddress: '',
|
||||||
|
|
||||||
|
// # Company address
|
||||||
|
showCompanyAddress: true,
|
||||||
|
companyAddress: '',
|
||||||
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
|
// Total
|
||||||
|
total: '$1000.00',
|
||||||
|
totalLabel: 'Total',
|
||||||
|
showTotal: true,
|
||||||
|
|
||||||
|
// Subtotal
|
||||||
|
subtotal: '1000/00',
|
||||||
|
subtotalLabel: 'Subtotal',
|
||||||
|
showSubtotal: true,
|
||||||
|
|
||||||
|
// Customer note
|
||||||
|
showCustomerNote: true,
|
||||||
|
customerNote:
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||||
|
customerNoteLabel: 'Customer Note',
|
||||||
|
|
||||||
|
// Terms & conditions
|
||||||
|
showTermsConditions: true,
|
||||||
|
termsConditions:
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||||
|
termsConditionsLabel: 'Terms & Conditions',
|
||||||
|
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
item: 'Simply dummy text',
|
||||||
|
description: 'Simply dummy text of the printing and typesetting',
|
||||||
|
rate: '1',
|
||||||
|
quantity: '1000',
|
||||||
|
total: '$1000.00',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// Credit note number.
|
||||||
|
showCreditNoteNumber: true,
|
||||||
|
creditNoteNumberLabel: 'Credit Note Number',
|
||||||
|
creditNoteNumebr: '346D3D40-0001',
|
||||||
|
|
||||||
|
// Credit note date.
|
||||||
|
creditNoteDate: 'September 3, 2024',
|
||||||
|
showCreditNoteDate: true,
|
||||||
|
creditNoteDateLabel: 'Credit Note Date',
|
||||||
|
};
|
||||||
@@ -4,8 +4,17 @@ import { mixin, Model } from 'objection';
|
|||||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||||
// import ModelSearchable from './ModelSearchable';
|
// import ModelSearchable from './ModelSearchable';
|
||||||
import { BaseModel } from '@/models/Model';
|
import { BaseModel } from '@/models/Model';
|
||||||
|
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||||
|
import { CreditNote } from './CreditNote';
|
||||||
|
|
||||||
export class CreditNoteAppliedInvoice extends BaseModel {
|
export class CreditNoteAppliedInvoice extends BaseModel {
|
||||||
|
public amount: number;
|
||||||
|
public creditNoteId: number;
|
||||||
|
public invoiceId: number;
|
||||||
|
|
||||||
|
public saleInvoice!: SaleInvoice;
|
||||||
|
public creditNote!: CreditNote;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { Model, mixin } from 'objection';
|
||||||
|
// import TenantModel from 'models/TenantModel';
|
||||||
|
// import ModelSetting from './ModelSetting';
|
||||||
|
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||||
|
// import ModelSearchable from './ModelSearchable';
|
||||||
|
import { BaseModel } from '@/models/Model';
|
||||||
|
|
||||||
|
export class RefundCreditNote extends BaseModel{
|
||||||
|
date: Date;
|
||||||
|
referenceNo: string;
|
||||||
|
amount: number;
|
||||||
|
currencyCode: string;
|
||||||
|
exchangeRate: number;
|
||||||
|
fromAccountId: number;
|
||||||
|
description: string;
|
||||||
|
creditNoteId: number;
|
||||||
|
|
||||||
|
userId?: number;
|
||||||
|
branchId?: number;
|
||||||
|
|
||||||
|
createdAt?: Date | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table name.
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'refund_credit_note_transactions';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamps columns.
|
||||||
|
*/
|
||||||
|
get timestamps() {
|
||||||
|
return ['created_at', 'updated_at'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const { Account } = require('../../Accounts/models/Account.model');
|
||||||
|
const { CreditNote } = require('../../CreditNotes/models/CreditNote');
|
||||||
|
|
||||||
|
return {
|
||||||
|
fromAccount: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Account,
|
||||||
|
join: {
|
||||||
|
from: 'refund_credit_note_transactions.fromAccountId',
|
||||||
|
to: 'accounts.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
creditNote: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: CreditNote,
|
||||||
|
join: {
|
||||||
|
from: 'refund_credit_note_transactions.creditNoteId',
|
||||||
|
to: 'credit_notes.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Transformer } from '../../Transformer/Transformer';
|
||||||
|
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||||
|
|
||||||
|
export class CreditNoteAppliedInvoiceTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Includeded attributes.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formttedAmount',
|
||||||
|
'creditNoteNumber',
|
||||||
|
'creditNoteDate',
|
||||||
|
'invoiceNumber',
|
||||||
|
'invoiceReferenceNo',
|
||||||
|
'formattedCreditNoteDate',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude attributes.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
public excludeAttributes = (): string[] => {
|
||||||
|
return ['saleInvoice', 'creditNote'];
|
||||||
|
};
|
||||||
|
|
||||||
|
public formttedAmount = (item: CreditNoteAppliedInvoice) => {
|
||||||
|
return this.formatNumber(item.amount, {
|
||||||
|
currencyCode: item.creditNote.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
public creditNoteNumber = (item: CreditNoteAppliedInvoice) => {
|
||||||
|
return item.creditNote.creditNoteNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
public creditNoteDate = (item: CreditNoteAppliedInvoice) => {
|
||||||
|
return item.creditNote.creditNoteDate;
|
||||||
|
};
|
||||||
|
|
||||||
|
public invoiceNumber = (item: CreditNoteAppliedInvoice) => {
|
||||||
|
return item.saleInvoice.invoiceNo;
|
||||||
|
};
|
||||||
|
|
||||||
|
public invoiceReferenceNo = (item: CreditNoteAppliedInvoice) => {
|
||||||
|
return item.saleInvoice.referenceNo;
|
||||||
|
};
|
||||||
|
|
||||||
|
public formattedCreditNoteDate = (item: CreditNoteAppliedInvoice) => {
|
||||||
|
return this.formatDate(item.creditNote.creditNoteDate);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { defaultCreditNoteBrandingAttributes } from '../constants';
|
||||||
|
import { GetPdfTemplateService } from '../../PdfTemplate/queries/GetPdfTemplate.service';
|
||||||
|
import { GetOrganizationBrandingAttributesService } from '../../PdfTemplate/queries/GetOrganizationBrandingAttributes.service';
|
||||||
|
import { mergePdfTemplateWithDefaultAttributes } from '../../SaleInvoices/utils';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreditNoteBrandingTemplate {
|
||||||
|
constructor(
|
||||||
|
private getPdfTemplateService: GetPdfTemplateService,
|
||||||
|
private getOrgBrandingAttributes: GetOrganizationBrandingAttributesService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the credit note branding template.
|
||||||
|
* @param {number} templateId
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
public async getCreditNoteBrandingTemplate(templateId: number) {
|
||||||
|
const template =
|
||||||
|
await this.getPdfTemplateService.getPdfTemplate(templateId);
|
||||||
|
|
||||||
|
// Retrieves the organization branding attributes.
|
||||||
|
const commonOrgBrandingAttrs =
|
||||||
|
await this.getOrgBrandingAttributes.getOrganizationBrandingAttributes();
|
||||||
|
|
||||||
|
// Merges the default branding attributes with common organization branding attrs.
|
||||||
|
const organizationBrandingAttrs = {
|
||||||
|
...defaultCreditNoteBrandingAttributes,
|
||||||
|
...commonOrgBrandingAttrs,
|
||||||
|
};
|
||||||
|
const brandingTemplateAttrs = {
|
||||||
|
...template.attributes,
|
||||||
|
companyLogoUri: template.companyLogoUri,
|
||||||
|
};
|
||||||
|
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||||
|
brandingTemplateAttrs,
|
||||||
|
organizationBrandingAttrs,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...template,
|
||||||
|
attributes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
import { AttachmentTransformer } from "@/modules/Attachments/Attachment.transformer";
|
||||||
|
import { ItemEntryTransformer } from "@/modules/TransactionItemEntry/ItemEntry.transformer";
|
||||||
|
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||||
|
|
||||||
|
export class CreditNoteTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale credit note object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formattedCreditsRemaining',
|
||||||
|
'formattedCreditNoteDate',
|
||||||
|
'formattedCreatedAt',
|
||||||
|
'formattedCreatedAt',
|
||||||
|
'formattedAmount',
|
||||||
|
'formattedCreditsUsed',
|
||||||
|
'formattedSubtotal',
|
||||||
|
|
||||||
|
'discountAmountFormatted',
|
||||||
|
'discountAmountLocalFormatted',
|
||||||
|
|
||||||
|
'discountPercentageFormatted',
|
||||||
|
|
||||||
|
'adjustmentFormatted',
|
||||||
|
'adjustmentLocalFormatted',
|
||||||
|
|
||||||
|
'totalFormatted',
|
||||||
|
'totalLocalFormatted',
|
||||||
|
|
||||||
|
'entries',
|
||||||
|
'attachments',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted credit note date.
|
||||||
|
* @param {ICreditNote} credit
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedCreditNoteDate = (credit): string => {
|
||||||
|
return this.formatDate(credit.creditNoteDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted created at date.
|
||||||
|
* @param credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedCreatedAt = (credit): string => {
|
||||||
|
return this.formatDate(credit.createdAt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted invoice amount.
|
||||||
|
* @param {ICreditNote} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (credit): string => {
|
||||||
|
return this.formatNumber(credit.amount, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted credits remaining.
|
||||||
|
* @param {ICreditNote} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedCreditsRemaining = (credit) => {
|
||||||
|
return this.formatNumber(credit.creditsRemaining, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted credits used.
|
||||||
|
* @param {ICreditNote} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedCreditsUsed = (credit) => {
|
||||||
|
return this.formatNumber(credit.creditsUsed, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted subtotal.
|
||||||
|
* @param {ICreditNote} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedSubtotal = (credit): string => {
|
||||||
|
return this.formatNumber(credit.amount, { money: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves formatted discount amount.
|
||||||
|
* @param 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 {ICreditNote} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected discountAmountLocalFormatted = (credit): string => {
|
||||||
|
return this.formatNumber(credit.discountAmountLocal, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
excerptZero: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves formatted discount percentage.
|
||||||
|
* @param credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected discountPercentageFormatted = (credit): string => {
|
||||||
|
return credit.discountPercentage ? `${credit.discountPercentage}%` : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves formatted adjustment amount.
|
||||||
|
* @param credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected adjustmentFormatted = (credit): string => {
|
||||||
|
return this.formatMoney(credit.adjustment, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
excerptZero: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted adjustment amount in local currency.
|
||||||
|
* @param {ICreditNote} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected adjustmentLocalFormatted = (credit): string => {
|
||||||
|
return this.formatNumber(credit.adjustmentLocal, {
|
||||||
|
currencyCode: this.context.organization.baseCurrency,
|
||||||
|
excerptZero: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted total.
|
||||||
|
* @param credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected totalFormatted = (credit): string => {
|
||||||
|
return this.formatNumber(credit.total, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted total in local currency.
|
||||||
|
* @param credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected totalLocalFormatted = (credit): string => {
|
||||||
|
return this.formatNumber(credit.totalLocal, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the entries of the credit note.
|
||||||
|
* @param {ICreditNote} credit
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
protected entries = (credit) => {
|
||||||
|
return this.item(credit.entries, new ItemEntryTransformer(), {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the credit note attachments.
|
||||||
|
* @param {ISaleInvoice} invoice
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected attachments = (creditNote) => {
|
||||||
|
return this.item(creditNote.attachments, new AttachmentTransformer());
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { CreditNoteTransformer } from './CreditNoteTransformer';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetCreditNote {
|
||||||
|
constructor(
|
||||||
|
private readonly transformer: TransformerInjectable,
|
||||||
|
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private readonly creditNoteModel: typeof CreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the credit note graph.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async getCreditNote(creditNoteId: number) {
|
||||||
|
// Retrieve the vendor credit model graph.
|
||||||
|
const creditNote = await this.creditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.withGraphFetched('entries.item')
|
||||||
|
.withGraphFetched('customer')
|
||||||
|
.withGraphFetched('branch')
|
||||||
|
.withGraphFetched('attachments');
|
||||||
|
|
||||||
|
if (!creditNote) {
|
||||||
|
throw new ServiceError(ERRORS.CREDIT_NOTE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
// Transforms the credit note model to POJO.
|
||||||
|
return this.transformer.transform(creditNote, new CreditNoteTransformer());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { CreditNoteAppliedInvoiceTransformer } from './CreditNoteAppliedInvoiceTransformer';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { TransformerInjectable } from '../../Transformer/TransformerInjectable.service';
|
||||||
|
import { CreditNoteAppliedInvoice } from '../models/CreditNoteAppliedInvoice';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetCreditNoteAssociatedAppliedInvoices {
|
||||||
|
constructor(
|
||||||
|
private readonly transformer: TransformerInjectable,
|
||||||
|
private readonly creditNoteAppliedInvoiceModel: typeof CreditNoteAppliedInvoice,
|
||||||
|
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private readonly creditNoteModel: typeof CreditNote
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve credit note associated invoices to apply.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @returns {Promise<CreditNoteAppliedInvoice[]>}
|
||||||
|
*/
|
||||||
|
public async getCreditAssociatedAppliedInvoices(
|
||||||
|
creditNoteId: number
|
||||||
|
): Promise<CreditNoteAppliedInvoice[]> {
|
||||||
|
// Retrieve credit note or throw not found service error.
|
||||||
|
const creditNote = await this.creditNoteModel.query()
|
||||||
|
.findById(creditNoteId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
const appliedToInvoices = await this.creditNoteAppliedInvoiceModel.query()
|
||||||
|
.where('credit_note_id', creditNoteId)
|
||||||
|
.withGraphFetched('saleInvoice')
|
||||||
|
.withGraphFetched('creditNote');
|
||||||
|
|
||||||
|
// Transforms credit note applied to invoices.
|
||||||
|
return this.transformer.transform(
|
||||||
|
appliedToInvoices,
|
||||||
|
new CreditNoteAppliedInvoiceTransformer()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { CreditNoteWithInvoicesToApplyTransformer } from '../commands/CreditNoteWithInvoicesToApplyTransformer';
|
||||||
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
|
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||||
|
import { GetCreditNote } from './GetCreditNote.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetCreditNoteAssociatedInvoicesToApply {
|
||||||
|
constructor(
|
||||||
|
private transformer: TransformerInjectable,
|
||||||
|
private getCreditNote: GetCreditNote,
|
||||||
|
|
||||||
|
@Inject(SaleInvoice.name)
|
||||||
|
private saleInvoiceModel: typeof SaleInvoice,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve credit note associated invoices to apply.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @returns {Promise<ISaleInvoice[]>}
|
||||||
|
*/
|
||||||
|
public async getCreditAssociatedInvoicesToApply(
|
||||||
|
creditNoteId: number,
|
||||||
|
): Promise<SaleInvoice[]> {
|
||||||
|
// Retrieve credit note or throw not found service error.
|
||||||
|
const creditNote = await this.getCreditNote.getCreditNote(creditNoteId);
|
||||||
|
|
||||||
|
// Retrieves the published due invoices that associated to the given customer.
|
||||||
|
const saleInvoices = await this.saleInvoiceModel
|
||||||
|
.query()
|
||||||
|
.where('customerId', creditNote.customerId)
|
||||||
|
.modify('dueInvoices')
|
||||||
|
.modify('published');
|
||||||
|
|
||||||
|
// Transforms the sale invoices models to POJO.
|
||||||
|
return this.transformer.transform(
|
||||||
|
saleInvoices,
|
||||||
|
new CreditNoteWithInvoicesToApplyTransformer(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { GetCreditNote } from './GetCreditNote.service';
|
||||||
|
import { CreditNoteBrandingTemplate } from './CreditNoteBrandingTemplate.service';
|
||||||
|
import { transformCreditNoteToPdfTemplate } from '../utils';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { ChromiumlyTenancy } from '@/modules/ChromiumlyTenancy/ChromiumlyTenancy.service';
|
||||||
|
import { TemplateInjectable } from '@/modules/TemplateInjectable/TemplateInjectable.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
|
||||||
|
import { CreditNotePdfTemplateAttributes } from '../types/CreditNotes.types';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetCreditNotePdf {
|
||||||
|
constructor(
|
||||||
|
private readonly chromiumlyTenancy: ChromiumlyTenancy,
|
||||||
|
private readonly templateInjectable: TemplateInjectable,
|
||||||
|
private readonly getCreditNoteService: GetCreditNote,
|
||||||
|
private readonly creditNoteBrandingTemplate: CreditNoteBrandingTemplate,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(CreditNote.name)
|
||||||
|
private readonly creditNoteModel: typeof CreditNote,
|
||||||
|
|
||||||
|
@Inject(PdfTemplateModel.name)
|
||||||
|
private readonly pdfTemplateModel: typeof PdfTemplateModel,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves sale invoice pdf content.
|
||||||
|
* @param {number} creditNoteId - Credit note id.
|
||||||
|
* @returns {Promise<[Buffer, string]>}
|
||||||
|
*/
|
||||||
|
public async getCreditNotePdf(
|
||||||
|
creditNoteId: number,
|
||||||
|
): Promise<[Buffer, string]> {
|
||||||
|
const brandingAttributes =
|
||||||
|
await this.getCreditNoteBrandingAttributes(creditNoteId);
|
||||||
|
const htmlContent = await this.templateInjectable.render(
|
||||||
|
'modules/credit-note-standard',
|
||||||
|
brandingAttributes,
|
||||||
|
);
|
||||||
|
const filename = await this.getCreditNoteFilename(creditNoteId);
|
||||||
|
|
||||||
|
const document =
|
||||||
|
await this.chromiumlyTenancy.convertHtmlContent(htmlContent);
|
||||||
|
const eventPayload = { creditNoteId };
|
||||||
|
|
||||||
|
// Triggers the `onCreditNotePdfViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.creditNote.onPdfViewed,
|
||||||
|
eventPayload,
|
||||||
|
);
|
||||||
|
return [document, filename];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the filename pdf document of the given credit note.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
public async getCreditNoteFilename(creditNoteId: number): Promise<string> {
|
||||||
|
const creditNote = await this.creditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(creditNoteId);
|
||||||
|
return `Credit-${creditNote.creditNoteNumber}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves credit note branding attributes.
|
||||||
|
* @param {number} creditNoteId - The ID of the credit note.
|
||||||
|
* @returns {Promise<CreditNotePdfTemplateAttributes>} The credit note branding attributes.
|
||||||
|
*/
|
||||||
|
public async getCreditNoteBrandingAttributes(
|
||||||
|
creditNoteId: number,
|
||||||
|
): Promise<CreditNotePdfTemplateAttributes> {
|
||||||
|
const creditNote =
|
||||||
|
await this.getCreditNoteService.getCreditNote(creditNoteId);
|
||||||
|
|
||||||
|
// Retrieve the invoice template id of not found get the default template id.
|
||||||
|
const templateId =
|
||||||
|
creditNote.pdfTemplateId ??
|
||||||
|
(
|
||||||
|
await this.pdfTemplateModel.query().findOne({
|
||||||
|
resource: 'CreditNote',
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
)?.id;
|
||||||
|
// Retrieves the credit note branding template.
|
||||||
|
const brandingTemplate =
|
||||||
|
await this.creditNoteBrandingTemplate.getCreditNoteBrandingTemplate(
|
||||||
|
templateId,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...brandingTemplate.attributes,
|
||||||
|
...transformCreditNoteToPdfTemplate(creditNote),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import RefundCreditNoteTransformer from './RefundCreditNoteTransformer';
|
||||||
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
|
import { RefundCreditNote } from '../models/RefundCreditNote';
|
||||||
|
import { IRefundCreditNotePOJO } from '../types/CreditNotes.types';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ListCreditNoteRefunds {
|
||||||
|
constructor(
|
||||||
|
private readonly transformer: TransformerInjectable,
|
||||||
|
|
||||||
|
@Inject(RefundCreditNote.name)
|
||||||
|
private readonly refundCreditNoteModel: typeof RefundCreditNote,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the credit note graph.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @returns {Promise<IRefundCreditNotePOJO[]>}
|
||||||
|
*/
|
||||||
|
public async getCreditNoteRefunds(
|
||||||
|
creditNoteId: number,
|
||||||
|
): Promise<IRefundCreditNotePOJO[]> {
|
||||||
|
// Retrieve refund credit notes associated to the given credit note.
|
||||||
|
const refundCreditTransactions = await this.refundCreditNoteModel
|
||||||
|
.query()
|
||||||
|
.where('creditNoteId', creditNoteId)
|
||||||
|
.withGraphFetched('creditNote')
|
||||||
|
.withGraphFetched('fromAccount');
|
||||||
|
|
||||||
|
// Transforms refund credit note models to POJO objects.
|
||||||
|
return this.transformer.transform(
|
||||||
|
refundCreditTransactions,
|
||||||
|
new RefundCreditNoteTransformer(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ICreditNoteState } from '../types/CreditNotes.types';
|
||||||
|
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetCreditNoteState {
|
||||||
|
constructor(
|
||||||
|
@Inject(PdfTemplateModel.name)
|
||||||
|
private pdfTemplateModel: typeof PdfTemplateModel,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the create/edit initial state of the payment received.
|
||||||
|
* @return {Promise<ICreditNoteState>}
|
||||||
|
*/
|
||||||
|
public async getCreditNoteState(): Promise<ICreditNoteState> {
|
||||||
|
const defaultPdfTemplate = await this.pdfTemplateModel.query()
|
||||||
|
.findOne({ resource: 'CreditNote' })
|
||||||
|
.modify('default');
|
||||||
|
|
||||||
|
return {
|
||||||
|
defaultTemplateId: defaultPdfTemplate?.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// import { Service, Inject } from 'typedi';
|
||||||
|
// import * as R from 'ramda';
|
||||||
|
// import { ICreditNotesQueryDTO } from '@/interfaces';
|
||||||
|
// import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||||
|
// import BaseCreditNotes from './commands/CommandCreditNoteDTOTransform.service';
|
||||||
|
// import { CreditNoteTransformer } from './queries/CreditNoteTransformer';
|
||||||
|
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export default class ListCreditNotes extends BaseCreditNotes {
|
||||||
|
// @Inject()
|
||||||
|
// private dynamicListService: DynamicListingService;
|
||||||
|
|
||||||
|
// @Inject()
|
||||||
|
// private transformer: TransformerInjectable;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Parses the sale invoice list filter DTO.
|
||||||
|
// * @param filterDTO
|
||||||
|
// * @returns
|
||||||
|
// */
|
||||||
|
// private parseListFilterDTO = (filterDTO) => {
|
||||||
|
// return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the paginated and filterable credit notes list.
|
||||||
|
// * @param {number} tenantId -
|
||||||
|
// * @param {ICreditNotesQueryDTO} creditNotesQuery -
|
||||||
|
// */
|
||||||
|
// public getCreditNotesList = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// creditNotesQuery: ICreditNotesQueryDTO
|
||||||
|
// ) => {
|
||||||
|
// const { CreditNote } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// // Parses stringified filter roles.
|
||||||
|
// const filter = this.parseListFilterDTO(creditNotesQuery);
|
||||||
|
|
||||||
|
// // Dynamic list service.
|
||||||
|
// const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||||
|
// tenantId,
|
||||||
|
// CreditNote,
|
||||||
|
// filter
|
||||||
|
// );
|
||||||
|
// const { results, pagination } = await CreditNote.query()
|
||||||
|
// .onBuild((builder) => {
|
||||||
|
// builder.withGraphFetched('entries.item');
|
||||||
|
// builder.withGraphFetched('customer');
|
||||||
|
// dynamicFilter.buildQuery()(builder);
|
||||||
|
// creditNotesQuery?.filterQuery && creditNotesQuery?.filterQuery(builder);
|
||||||
|
// })
|
||||||
|
// .pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
|
// // Transforomes the credit notes to POJO.
|
||||||
|
// const creditNotes = await this.transformer.transform(
|
||||||
|
// tenantId,
|
||||||
|
// results,
|
||||||
|
// new CreditNoteTransformer()
|
||||||
|
// );
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// creditNotes,
|
||||||
|
// pagination,
|
||||||
|
// filterMeta: dynamicFilter.getResponseMeta(),
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
// }
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { IRefundCreditNote } from '../types/CreditNotes.types';
|
||||||
|
import { RefundCreditNote } from '../models/RefundCreditNote';
|
||||||
|
import { RefundCreditNoteTransformer } from './RefundCreditNoteTransformer';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetRefundCreditNoteTransaction {
|
||||||
|
/**
|
||||||
|
* @param {RefundCreditNoteTransformer} transformer
|
||||||
|
* @param {typeof RefundCreditNote} refundCreditNoteModel
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly transformer: RefundCreditNoteTransformer,
|
||||||
|
|
||||||
|
@Inject(RefundCreditNote.name)
|
||||||
|
private readonly refundCreditNoteModel: typeof RefundCreditNote,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve credit note associated invoices to apply.
|
||||||
|
* @param {number} refundCreditId
|
||||||
|
* @returns {Promise<IRefundCreditNote>}
|
||||||
|
*/
|
||||||
|
public async getRefundCreditTransaction(
|
||||||
|
refundCreditId: number
|
||||||
|
): Promise<IRefundCreditNote> {
|
||||||
|
const refundCreditNote = await this.refundCreditNoteModel
|
||||||
|
.query()
|
||||||
|
.findById(refundCreditId)
|
||||||
|
.withGraphFetched('fromAccount')
|
||||||
|
.withGraphFetched('creditNote')
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
return this.transformer.transform(refundCreditNote);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||||
|
|
||||||
|
export class RefundCreditNoteTransformer extends Transformer{
|
||||||
|
/**
|
||||||
|
* Includeded attributes.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return ['formttedAmount', 'formattedDate'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatted amount.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formttedAmount = (item) => {
|
||||||
|
return this.formatNumber(item.amount, {
|
||||||
|
currencyCode: item.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatted date.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDate = (item) => {
|
||||||
|
return this.formatDate(item.date);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import { sumBy } from 'lodash';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import {
|
||||||
|
IApplyCreditToInvoicesCreatedPayload,
|
||||||
|
IApplyCreditToInvoicesDeletedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import CreditNoteApplySyncCredit from '../commands/CreditNoteApplySyncCredit.service';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class CreditNoteApplySyncCreditSubscriber {
|
||||||
|
@Inject()
|
||||||
|
syncInvoicedAmountWithCredit: CreditNoteApplySyncCredit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param bus
|
||||||
|
*/
|
||||||
|
attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onApplyToInvoicesCreated,
|
||||||
|
this.incrementCreditedAmountOnceApplyToInvoicesCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onApplyToInvoicesDeleted,
|
||||||
|
this.decrementCreditedAmountOnceApplyToInvoicesDeleted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment credited amount of credit note transaction once the transaction created.
|
||||||
|
* @param {IApplyCreditToInvoicesCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
private incrementCreditedAmountOnceApplyToInvoicesCreated = async ({
|
||||||
|
trx,
|
||||||
|
creditNote,
|
||||||
|
tenantId,
|
||||||
|
creditNoteAppliedInvoices,
|
||||||
|
}: IApplyCreditToInvoicesCreatedPayload) => {
|
||||||
|
const totalCredited = sumBy(creditNoteAppliedInvoices, 'amount');
|
||||||
|
|
||||||
|
await this.syncInvoicedAmountWithCredit.incrementCreditNoteInvoicedAmount(
|
||||||
|
tenantId,
|
||||||
|
creditNote.id,
|
||||||
|
totalCredited,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement credited amount of credit note transaction once the transaction deleted.
|
||||||
|
* @param {IApplyCreditToInvoicesDeletedPayload} payload -
|
||||||
|
*/
|
||||||
|
private decrementCreditedAmountOnceApplyToInvoicesDeleted = async ({
|
||||||
|
tenantId,
|
||||||
|
creditNote,
|
||||||
|
creditNoteAppliedToInvoice,
|
||||||
|
trx,
|
||||||
|
}: IApplyCreditToInvoicesDeletedPayload) => {
|
||||||
|
await this.syncInvoicedAmountWithCredit.decrementCreditNoteInvoicedAmount(
|
||||||
|
tenantId,
|
||||||
|
creditNote.id,
|
||||||
|
creditNoteAppliedToInvoice.amount,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import {
|
||||||
|
IApplyCreditToInvoicesCreatedPayload,
|
||||||
|
IApplyCreditToInvoicesDeletedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import CreditNoteApplySyncInvoicesCreditedAmount from '../commands/CreditNoteApplySyncInvoices.service';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class CreditNoteApplySyncInvoicesCreditedAmountSubscriber {
|
||||||
|
@Inject()
|
||||||
|
private syncInvoicesWithCreditNote: CreditNoteApplySyncInvoicesCreditedAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onApplyToInvoicesCreated,
|
||||||
|
this.incrementAppliedInvoicesOnceCreditCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onApplyToInvoicesDeleted,
|
||||||
|
this.decrementAppliedInvoicesOnceCreditDeleted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment invoices credited amount once the credit note apply to invoices transaction
|
||||||
|
* @param {IApplyCreditToInvoicesCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
private incrementAppliedInvoicesOnceCreditCreated = async ({
|
||||||
|
trx,
|
||||||
|
tenantId,
|
||||||
|
creditNoteAppliedInvoices,
|
||||||
|
}: IApplyCreditToInvoicesCreatedPayload) => {
|
||||||
|
await this.syncInvoicesWithCreditNote.incrementInvoicesCreditedAmount(
|
||||||
|
tenantId,
|
||||||
|
creditNoteAppliedInvoices,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {IApplyCreditToInvoicesDeletedPayload} payload -
|
||||||
|
*/
|
||||||
|
private decrementAppliedInvoicesOnceCreditDeleted = async ({
|
||||||
|
trx,
|
||||||
|
creditNoteAppliedToInvoice,
|
||||||
|
tenantId,
|
||||||
|
}: IApplyCreditToInvoicesDeletedPayload) => {
|
||||||
|
// Decrement invoice credited amount.
|
||||||
|
await this.syncInvoicesWithCreditNote.decrementInvoiceCreditedAmount(
|
||||||
|
tenantId,
|
||||||
|
creditNoteAppliedToInvoice.invoiceId,
|
||||||
|
creditNoteAppliedToInvoice.amount,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import BaseCreditNotes from '../commands/CommandCreditNoteDTOTransform.service';
|
||||||
|
import { ICreditNoteCreatedPayload } from '@/interfaces';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class CreditNoteAutoSerialSubscriber {
|
||||||
|
@Inject()
|
||||||
|
creditNotesService: BaseCreditNotes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onCreated,
|
||||||
|
this.autoSerialIncrementOnceCreated
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto serial increment once credit note created.
|
||||||
|
* @param {ICreditNoteCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
private autoSerialIncrementOnceCreated = async ({
|
||||||
|
tenantId,
|
||||||
|
}: ICreditNoteCreatedPayload) => {
|
||||||
|
await this.creditNotesService.incrementSerialNumber(tenantId);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import {
|
||||||
|
ICreditNoteCreatedPayload,
|
||||||
|
ICreditNoteDeletedPayload,
|
||||||
|
ICreditNoteEditedPayload,
|
||||||
|
ICreditNoteOpenedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import CreditNoteGLEntries from '../commands/CreditNoteGLEntries';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class CreditNoteGLEntriesSubscriber {
|
||||||
|
@Inject()
|
||||||
|
private creditNoteGLEntries: CreditNoteGLEntries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
* @param bus
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onCreated,
|
||||||
|
this.writeGlEntriesOnceCreditNoteCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onOpened,
|
||||||
|
this.writeGLEntriesOnceCreditNoteOpened
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onEdited,
|
||||||
|
this.editVendorCreditGLEntriesOnceEdited
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onDeleted,
|
||||||
|
this.revertGLEntriesOnceCreditNoteDeleted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the GL entries once the credit note transaction created or open.
|
||||||
|
* @private
|
||||||
|
* @param {ICreditNoteCreatedPayload|ICreditNoteOpenedPayload} payload -
|
||||||
|
*/
|
||||||
|
private writeGlEntriesOnceCreditNoteCreated = async ({
|
||||||
|
tenantId,
|
||||||
|
creditNote,
|
||||||
|
creditNoteId,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteCreatedPayload | ICreditNoteOpenedPayload) => {
|
||||||
|
// Can't continue if the credit note is not published yet.
|
||||||
|
if (!creditNote.isPublished) return;
|
||||||
|
|
||||||
|
await this.creditNoteGLEntries.createVendorCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
creditNoteId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the GL entries once the vendor credit transaction opened.
|
||||||
|
* @param {ICreditNoteOpenedPayload} payload
|
||||||
|
*/
|
||||||
|
private writeGLEntriesOnceCreditNoteOpened = async ({
|
||||||
|
tenantId,
|
||||||
|
creditNoteId,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteOpenedPayload) => {
|
||||||
|
await this.creditNoteGLEntries.createVendorCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
creditNoteId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts GL entries once credit note deleted.
|
||||||
|
*/
|
||||||
|
private revertGLEntriesOnceCreditNoteDeleted = async ({
|
||||||
|
tenantId,
|
||||||
|
oldCreditNote,
|
||||||
|
creditNoteId,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteDeletedPayload) => {
|
||||||
|
// Can't continue if the credit note is not published yet.
|
||||||
|
if (!oldCreditNote.isPublished) return;
|
||||||
|
|
||||||
|
await this.creditNoteGLEntries.revertVendorCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
creditNoteId
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits vendor credit associated GL entries once the transaction edited.
|
||||||
|
* @param {ICreditNoteEditedPayload} payload -
|
||||||
|
*/
|
||||||
|
private editVendorCreditGLEntriesOnceEdited = async ({
|
||||||
|
tenantId,
|
||||||
|
creditNote,
|
||||||
|
creditNoteId,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteEditedPayload) => {
|
||||||
|
// Can't continue if the credit note is not published yet.
|
||||||
|
if (!creditNote.isPublished) return;
|
||||||
|
|
||||||
|
await this.creditNoteGLEntries.editVendorCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
creditNoteId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import CreditNoteInventoryTransactions from '../commands/CreditNotesInventoryTransactions';
|
||||||
|
import {
|
||||||
|
ICreditNoteCreatedPayload,
|
||||||
|
ICreditNoteDeletedPayload,
|
||||||
|
ICreditNoteEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class CreditNoteInventoryTransactionsSubscriber {
|
||||||
|
@Inject()
|
||||||
|
inventoryTransactions: CreditNoteInventoryTransactions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with publisher.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onCreated,
|
||||||
|
this.writeInventoryTranscationsOnceCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onEdited,
|
||||||
|
this.rewriteInventoryTransactionsOnceEdited
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onDeleted,
|
||||||
|
this.revertInventoryTransactionsOnceDeleted
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onOpened,
|
||||||
|
this.writeInventoryTranscationsOnceCreated
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes inventory transactions once credit note created.
|
||||||
|
* @param {ICreditNoteCreatedPayload} payload -
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public writeInventoryTranscationsOnceCreated = async ({
|
||||||
|
tenantId,
|
||||||
|
creditNote,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteCreatedPayload) => {
|
||||||
|
// Can't continue if the credit note is open yet.
|
||||||
|
if (!creditNote.isOpen) return;
|
||||||
|
|
||||||
|
await this.inventoryTransactions.createInventoryTransactions(
|
||||||
|
tenantId,
|
||||||
|
creditNote,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrites inventory transactions once credit note edited.
|
||||||
|
* @param {ICreditNoteEditedPayload} payload -
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public rewriteInventoryTransactionsOnceEdited = async ({
|
||||||
|
tenantId,
|
||||||
|
creditNoteId,
|
||||||
|
creditNote,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteEditedPayload) => {
|
||||||
|
// Can't continue if the credit note is open yet.
|
||||||
|
if (!creditNote.isOpen) return;
|
||||||
|
|
||||||
|
await this.inventoryTransactions.editInventoryTransactions(
|
||||||
|
tenantId,
|
||||||
|
creditNoteId,
|
||||||
|
creditNote,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts inventory transactions once credit note deleted.
|
||||||
|
* @param {ICreditNoteDeletedPayload} payload -
|
||||||
|
*/
|
||||||
|
public revertInventoryTransactionsOnceDeleted = async ({
|
||||||
|
tenantId,
|
||||||
|
creditNoteId,
|
||||||
|
oldCreditNote,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteDeletedPayload) => {
|
||||||
|
// Can't continue if the credit note is open yet.
|
||||||
|
if (!oldCreditNote.isOpen) return;
|
||||||
|
|
||||||
|
await this.inventoryTransactions.deleteInventoryTransactions(
|
||||||
|
tenantId,
|
||||||
|
creditNoteId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { ServiceError } from '@/exceptions';
|
||||||
|
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { ICustomerDeletingPayload } from '@/interfaces';
|
||||||
|
import DeleteCustomerLinkedCreidtNote from '../commands/DeleteCustomerLinkedCreditNote.service';
|
||||||
|
|
||||||
|
const ERRORS = {
|
||||||
|
CUSTOMER_HAS_TRANSACTIONS: 'CUSTOMER_HAS_TRANSACTIONS',
|
||||||
|
};
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class DeleteCustomerLinkedCreditSubscriber {
|
||||||
|
@Inject()
|
||||||
|
tenancy: TenancyService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
deleteCustomerLinkedCredit: DeleteCustomerLinkedCreidtNote;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
* @param bus
|
||||||
|
*/
|
||||||
|
public attach = (bus) => {
|
||||||
|
bus.subscribe(
|
||||||
|
events.customers.onDeleting,
|
||||||
|
this.validateCustomerHasNoLinkedCreditsOnDeleting
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate vendor has no associated credit transaction once the vendor deleting.
|
||||||
|
* @param {IVendorEventDeletingPayload} payload -
|
||||||
|
*/
|
||||||
|
public validateCustomerHasNoLinkedCreditsOnDeleting = async ({
|
||||||
|
tenantId,
|
||||||
|
customerId,
|
||||||
|
}: ICustomerDeletingPayload) => {
|
||||||
|
try {
|
||||||
|
await this.deleteCustomerLinkedCredit.validateCustomerHasNoCreditTransaction(
|
||||||
|
tenantId,
|
||||||
|
customerId
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw new ServiceError(ERRORS.CUSTOMER_HAS_TRANSACTIONS);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import RefundCreditNoteGLEntries from '../commands/RefundCreditNoteGLEntries';
|
||||||
|
import {
|
||||||
|
IRefundCreditNoteCreatedPayload,
|
||||||
|
IRefundCreditNoteDeletedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class RefundCreditNoteGLEntriesSubscriber {
|
||||||
|
@Inject()
|
||||||
|
refundCreditGLEntries: RefundCreditNoteGLEntries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
*/
|
||||||
|
public attach = (bus) => {
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onRefundCreated,
|
||||||
|
this.writeRefundCreditGLEntriesOnceCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onRefundDeleted,
|
||||||
|
this.revertRefundCreditGLEntriesOnceDeleted
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes refund credit note GL entries once the transaction created.
|
||||||
|
* @param {IRefundCreditNoteCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
private writeRefundCreditGLEntriesOnceCreated = async ({
|
||||||
|
trx,
|
||||||
|
refundCreditNote,
|
||||||
|
creditNote,
|
||||||
|
tenantId,
|
||||||
|
}: IRefundCreditNoteCreatedPayload) => {
|
||||||
|
await this.refundCreditGLEntries.createRefundCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
refundCreditNote.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts refund credit note GL entries once the transaction deleted.
|
||||||
|
* @param {IRefundCreditNoteDeletedPayload} payload -
|
||||||
|
*/
|
||||||
|
private revertRefundCreditGLEntriesOnceDeleted = async ({
|
||||||
|
trx,
|
||||||
|
refundCreditId,
|
||||||
|
oldRefundCredit,
|
||||||
|
tenantId,
|
||||||
|
}: IRefundCreditNoteDeletedPayload) => {
|
||||||
|
await this.refundCreditGLEntries.revertRefundCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
refundCreditId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import {
|
||||||
|
IRefundCreditNoteCreatedPayload,
|
||||||
|
IRefundCreditNoteDeletedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import RefundSyncCreditNoteBalance from '../commands/RefundSyncCreditNoteBalance';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class RefundSyncCreditNoteBalanceSubscriber {
|
||||||
|
@Inject()
|
||||||
|
refundSyncCreditBalance: RefundSyncCreditNoteBalance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
*/
|
||||||
|
attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onRefundCreated,
|
||||||
|
this.incrementRefundedAmountOnceRefundCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onRefundDeleted,
|
||||||
|
this.decrementRefundedAmountOnceRefundDeleted
|
||||||
|
);
|
||||||
|
return bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment credit note refunded amount once associated refund transaction created.
|
||||||
|
* @param {IRefundCreditNoteCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
private incrementRefundedAmountOnceRefundCreated = async ({
|
||||||
|
trx,
|
||||||
|
refundCreditNote,
|
||||||
|
tenantId,
|
||||||
|
}: IRefundCreditNoteCreatedPayload) => {
|
||||||
|
await this.refundSyncCreditBalance.incrementCreditNoteRefundAmount(
|
||||||
|
tenantId,
|
||||||
|
refundCreditNote.creditNoteId,
|
||||||
|
refundCreditNote.amount,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement credit note refunded amount once associated refuned transaction deleted.
|
||||||
|
* @param {IRefundCreditNoteDeletedPayload} payload -
|
||||||
|
*/
|
||||||
|
private decrementRefundedAmountOnceRefundDeleted = async ({
|
||||||
|
trx,
|
||||||
|
oldRefundCredit,
|
||||||
|
tenantId,
|
||||||
|
}: IRefundCreditNoteDeletedPayload) => {
|
||||||
|
await this.refundSyncCreditBalance.decrementCreditNoteRefundAmount(
|
||||||
|
tenantId,
|
||||||
|
oldRefundCredit.creditNoteId,
|
||||||
|
oldRefundCredit.amount,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,307 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { DiscountType, IDynamicListFilter, IItemEntry } from '@/interfaces';
|
||||||
|
import { ILedgerEntry } from './Ledger';
|
||||||
|
import { AttachmentLinkDTO } from './Attachments';
|
||||||
|
import { CreditNote } from '../models/CreditNote';
|
||||||
|
import { RefundCreditNote } from '../models/RefundCreditNote';
|
||||||
|
|
||||||
|
export interface ICreditNoteEntryNewDTO {
|
||||||
|
index?: number;
|
||||||
|
itemId: number;
|
||||||
|
rate: number;
|
||||||
|
quantity: number;
|
||||||
|
discount: number;
|
||||||
|
description: string;
|
||||||
|
warehouseId?: number;
|
||||||
|
}
|
||||||
|
export interface ICreditNoteNewDTO {
|
||||||
|
customerId: number;
|
||||||
|
exchangeRate?: number;
|
||||||
|
creditNoteDate: Date;
|
||||||
|
creditNoteNumber: string;
|
||||||
|
note: string;
|
||||||
|
open: boolean;
|
||||||
|
entries: ICreditNoteEntryNewDTO[];
|
||||||
|
branchId?: number;
|
||||||
|
warehouseId?: number;
|
||||||
|
attachments?: AttachmentLinkDTO[];
|
||||||
|
discount?: number;
|
||||||
|
discountType?: DiscountType;
|
||||||
|
adjustment?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteEditDTO {
|
||||||
|
customerId: number;
|
||||||
|
exchangeRate?: number;
|
||||||
|
creditNoteDate: Date;
|
||||||
|
creditNoteNumber: string;
|
||||||
|
note: string;
|
||||||
|
open: boolean;
|
||||||
|
entries: ICreditNoteEntryNewDTO[];
|
||||||
|
branchId?: number;
|
||||||
|
warehouseId?: number;
|
||||||
|
attachments?: AttachmentLinkDTO[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteEntry extends IItemEntry {}
|
||||||
|
|
||||||
|
export interface ICreditNote {
|
||||||
|
id?: number;
|
||||||
|
customerId: number;
|
||||||
|
amount: number;
|
||||||
|
refundedAmount: number;
|
||||||
|
currencyCode: string;
|
||||||
|
exchangeRate: number;
|
||||||
|
creditNoteDate: Date;
|
||||||
|
creditNoteNumber: string;
|
||||||
|
referenceNo?: string;
|
||||||
|
// note?: string;
|
||||||
|
openedAt: Date | null;
|
||||||
|
entries: ICreditNoteEntry[];
|
||||||
|
isOpen: boolean;
|
||||||
|
isClosed: boolean;
|
||||||
|
isDraft: boolean;
|
||||||
|
isPublished: boolean;
|
||||||
|
creditsRemaining: number;
|
||||||
|
localAmount?: number;
|
||||||
|
branchId?: number;
|
||||||
|
warehouseId: number;
|
||||||
|
createdAt?: Date;
|
||||||
|
termsConditions: string;
|
||||||
|
note: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CreditNoteAction {
|
||||||
|
Create = 'Create',
|
||||||
|
Edit = 'Edit',
|
||||||
|
Delete = 'Delete',
|
||||||
|
View = 'View',
|
||||||
|
Refund = 'Refund',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteDeletingPayload {
|
||||||
|
tenantId: number;
|
||||||
|
oldCreditNote: CreditNote;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteDeletedPayload {
|
||||||
|
tenantId: number;
|
||||||
|
oldCreditNote: CreditNote;
|
||||||
|
creditNoteId: number;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteEditingPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
oldCreditNote: CreditNote;
|
||||||
|
creditNoteEditDTO: ICreditNoteEditDTO;
|
||||||
|
tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteEditedPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
oldCreditNote: CreditNote;
|
||||||
|
// creditNoteId: number;
|
||||||
|
creditNote: CreditNote;
|
||||||
|
creditNoteEditDTO: ICreditNoteEditDTO;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteCreatedPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
creditNoteDTO: ICreditNoteNewDTO;
|
||||||
|
creditNote: CreditNote;
|
||||||
|
// creditNoteId: number;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteCreatingPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
creditNoteDTO: ICreditNoteNewDTO;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteOpeningPayload {
|
||||||
|
tenantId: number;
|
||||||
|
creditNoteId: number;
|
||||||
|
oldCreditNote: CreditNote;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteOpenedPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
creditNote: CreditNote;
|
||||||
|
// creditNoteId: number;
|
||||||
|
oldCreditNote: CreditNote;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNotesQueryDTO {
|
||||||
|
filterQuery?: (query: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNotesQueryDTO extends IDynamicListFilter {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
searchKeyword?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteRefundDTO {
|
||||||
|
fromAccountId: number;
|
||||||
|
amount: number;
|
||||||
|
exchangeRate?: number;
|
||||||
|
referenceNo: string;
|
||||||
|
description: string;
|
||||||
|
date: Date;
|
||||||
|
branchId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteApplyInvoiceDTO {
|
||||||
|
entries: { invoiceId: number; amount: number }[];
|
||||||
|
}
|
||||||
|
export interface IRefundCreditNotePOJO {
|
||||||
|
formattedAmount: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundCreditNoteDeletedPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
refundCreditId: number;
|
||||||
|
oldRefundCredit: RefundCreditNote;
|
||||||
|
tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundCreditNoteDeletingPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
refundCreditId: number;
|
||||||
|
oldRefundCredit: RefundCreditNote;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundCreditNoteCreatingPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
creditNote: CreditNote;
|
||||||
|
newCreditNoteDTO: ICreditNoteRefundDTO;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundCreditNoteCreatedPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
refundCreditNote: RefundCreditNote;
|
||||||
|
creditNote: CreditNote;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundCreditNoteOpenedPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
creditNoteId: number;
|
||||||
|
oldCreditNote: CreditNote;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApplyCreditToInvoiceEntryDTO {
|
||||||
|
amount: number;
|
||||||
|
invoiceId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApplyCreditToInvoicesDTO {
|
||||||
|
entries: IApplyCreditToInvoiceEntryDTO[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApplyCreditToInvoicesCreatedPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
creditNote: ICreditNote;
|
||||||
|
creditNoteAppliedInvoices: ICreditNoteAppliedToInvoice[];
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
export interface IApplyCreditToInvoicesDeletedPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
creditNote: ICreditNote;
|
||||||
|
creditNoteAppliedToInvoice: ICreditNoteAppliedToInvoice;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteAppliedToInvoice {
|
||||||
|
invoiceId: number;
|
||||||
|
amount: number;
|
||||||
|
creditNoteId: number;
|
||||||
|
}
|
||||||
|
export interface ICreditNoteAppliedToInvoiceModel {
|
||||||
|
amount: number;
|
||||||
|
entries: ICreditNoteAppliedToInvoice[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ICreditNoteGLCommonEntry = Pick<
|
||||||
|
ILedgerEntry,
|
||||||
|
| 'date'
|
||||||
|
| 'userId'
|
||||||
|
| 'currencyCode'
|
||||||
|
| 'exchangeRate'
|
||||||
|
| 'transactionType'
|
||||||
|
| 'transactionId'
|
||||||
|
| 'transactionNumber'
|
||||||
|
| 'referenceNumber'
|
||||||
|
| 'createdAt'
|
||||||
|
| 'indexGroup'
|
||||||
|
| 'credit'
|
||||||
|
| 'debit'
|
||||||
|
| 'branchId'
|
||||||
|
>;
|
||||||
|
|
||||||
|
export interface CreditNotePdfTemplateAttributes {
|
||||||
|
// # Primary color
|
||||||
|
primaryColor: string;
|
||||||
|
secondaryColor: string;
|
||||||
|
|
||||||
|
// # Company logo
|
||||||
|
showCompanyLogo: boolean;
|
||||||
|
companyLogo: string;
|
||||||
|
|
||||||
|
// # Company name
|
||||||
|
companyName: string;
|
||||||
|
|
||||||
|
// # Customer Address
|
||||||
|
showCustomerAddress: boolean;
|
||||||
|
customerAddress: string;
|
||||||
|
|
||||||
|
// # Company address
|
||||||
|
showCompanyAddress: boolean;
|
||||||
|
companyAddress: string;
|
||||||
|
billedToLabel: string;
|
||||||
|
|
||||||
|
total: string;
|
||||||
|
totalLabel: string;
|
||||||
|
showTotal: boolean;
|
||||||
|
|
||||||
|
subtotal: string;
|
||||||
|
subtotalLabel: string;
|
||||||
|
showSubtotal: boolean;
|
||||||
|
|
||||||
|
showCustomerNote: boolean;
|
||||||
|
customerNote: string;
|
||||||
|
customerNoteLabel: string;
|
||||||
|
|
||||||
|
showTermsConditions: boolean;
|
||||||
|
termsConditions: string;
|
||||||
|
termsConditionsLabel: string;
|
||||||
|
|
||||||
|
lines: Array<{
|
||||||
|
item: string;
|
||||||
|
description: string;
|
||||||
|
rate: string;
|
||||||
|
quantity: string;
|
||||||
|
total: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
showCreditNoteNumber: boolean;
|
||||||
|
creditNoteNumberLabel: string;
|
||||||
|
creditNoteNumebr: string;
|
||||||
|
|
||||||
|
creditNoteDate: string;
|
||||||
|
showCreditNoteDate: boolean;
|
||||||
|
creditNoteDateLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreditNoteState {
|
||||||
|
defaultTemplateId: number;
|
||||||
|
}
|
||||||
25
packages/server-nest/src/modules/CreditNotes/utils.ts
Normal file
25
packages/server-nest/src/modules/CreditNotes/utils.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { CreditNotePdfTemplateAttributes, ICreditNote } from '@/interfaces';
|
||||||
|
import { contactAddressTextFormat } from '@/utils/address-text-format';
|
||||||
|
|
||||||
|
export const transformCreditNoteToPdfTemplate = (
|
||||||
|
creditNote: ICreditNote
|
||||||
|
): Partial<CreditNotePdfTemplateAttributes> => {
|
||||||
|
return {
|
||||||
|
creditNoteDate: creditNote.formattedCreditNoteDate,
|
||||||
|
creditNoteNumebr: creditNote.creditNoteNumber,
|
||||||
|
|
||||||
|
total: creditNote.formattedAmount,
|
||||||
|
subtotal: creditNote.formattedSubtotal,
|
||||||
|
|
||||||
|
lines: creditNote.entries?.map((entry) => ({
|
||||||
|
item: entry.item.name,
|
||||||
|
description: entry.description,
|
||||||
|
rate: entry.rateFormatted,
|
||||||
|
quantity: entry.quantityFormatted,
|
||||||
|
total: entry.totalFormatted,
|
||||||
|
})),
|
||||||
|
customerNote: creditNote.note,
|
||||||
|
termsConditions: creditNote.termsConditions,
|
||||||
|
customerAddress: contactAddressTextFormat(creditNote.customer),
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -14,7 +14,7 @@ import { SaleEstimatesImportable } from '../Sales/Estimates/SaleEstimatesImporta
|
|||||||
import { BillPaymentsImportable } from '../Purchases/BillPayments/BillPaymentsImportable';
|
import { BillPaymentsImportable } from '../Purchases/BillPayments/BillPaymentsImportable';
|
||||||
import { VendorCreditsImportable } from '../Purchases/VendorCredits/VendorCreditsImportable';
|
import { VendorCreditsImportable } from '../Purchases/VendorCredits/VendorCreditsImportable';
|
||||||
import { PaymentsReceivedImportable } from '../Sales/PaymentReceived/PaymentsReceivedImportable';
|
import { PaymentsReceivedImportable } from '../Sales/PaymentReceived/PaymentsReceivedImportable';
|
||||||
import { CreditNotesImportable } from '../CreditNotes/CreditNotesImportable';
|
import { CreditNotesImportable } from '../CreditNotes/commands/CreditNotesImportable';
|
||||||
import { SaleReceiptsImportable } from '../Sales/Receipts/SaleReceiptsImportable';
|
import { SaleReceiptsImportable } from '../Sales/Receipts/SaleReceiptsImportable';
|
||||||
import { TaxRatesImportable } from '../TaxRates/TaxRatesImportable';
|
import { TaxRatesImportable } from '../TaxRates/TaxRatesImportable';
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,25 @@
|
|||||||
import { BaseModel } from '@/models/Model';
|
import { BaseModel } from '@/models/Model';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
// import TenantModel from 'models/TenantModel';
|
import { ItemEntry } from '../../Items/models/ItemEntry';
|
||||||
// import { defaultToTransform } from 'utils';
|
import { Customer } from '../../Customers/models/Customer';
|
||||||
// import SaleEstimateSettings from './SaleEstimate.Settings';
|
import { Branch } from '../../Branches/models/Branch.model';
|
||||||
// import ModelSetting from './ModelSetting';
|
import { Warehouse } from '../../Warehouses/models/Warehouse.model';
|
||||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
import { Document } from '../../ChromiumlyTenancy/models/Document';
|
||||||
// import { DEFAULT_VIEWS } from '@/services/Sales/Estimates/constants';
|
import { Injectable } from '@nestjs/common';
|
||||||
// import ModelSearchable from './ModelSearchable';
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class SaleEstimate extends BaseModel {
|
export class SaleEstimate extends BaseModel {
|
||||||
|
constructor(
|
||||||
|
private readonly itemEntryModel: typeof ItemEntry,
|
||||||
|
private readonly customerModel: typeof Customer,
|
||||||
|
private readonly branchModel: typeof Branch,
|
||||||
|
private readonly warehouseModel: typeof Warehouse,
|
||||||
|
private readonly documentModel: typeof Document,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
exchangeRate!: number;
|
exchangeRate!: number;
|
||||||
amount!: number;
|
amount!: number;
|
||||||
|
|
||||||
@@ -207,16 +217,10 @@ export class SaleEstimate extends BaseModel {
|
|||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const { ItemEntry } = require('../../Items/models/ItemEntry');
|
|
||||||
const { Customer } = require('../../Customers/models/Customer');
|
|
||||||
const { Branch } = require('../../Branches/models/Branch.model');
|
|
||||||
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
|
|
||||||
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
customer: {
|
customer: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Customer,
|
modelClass: this.customerModel,
|
||||||
join: {
|
join: {
|
||||||
from: 'sales_estimates.customerId',
|
from: 'sales_estimates.customerId',
|
||||||
to: 'contacts.id',
|
to: 'contacts.id',
|
||||||
@@ -227,8 +231,7 @@ export class SaleEstimate extends BaseModel {
|
|||||||
},
|
},
|
||||||
entries: {
|
entries: {
|
||||||
relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
modelClass: ItemEntry,
|
modelClass: this.itemEntryModel,
|
||||||
|
|
||||||
join: {
|
join: {
|
||||||
from: 'sales_estimates.id',
|
from: 'sales_estimates.id',
|
||||||
to: 'items_entries.referenceId',
|
to: 'items_entries.referenceId',
|
||||||
@@ -238,37 +241,25 @@ export class SaleEstimate extends BaseModel {
|
|||||||
builder.orderBy('index', 'ASC');
|
builder.orderBy('index', 'ASC');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Sale estimate may belongs to branch.
|
|
||||||
*/
|
|
||||||
branch: {
|
branch: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Branch,
|
modelClass: this.branchModel,
|
||||||
join: {
|
join: {
|
||||||
from: 'sales_estimates.branchId',
|
from: 'sales_estimates.branchId',
|
||||||
to: 'branches.id',
|
to: 'branches.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Sale estimate may has associated warehouse.
|
|
||||||
*/
|
|
||||||
warehouse: {
|
warehouse: {
|
||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Warehouse,
|
modelClass: this.warehouseModel,
|
||||||
join: {
|
join: {
|
||||||
from: 'sales_estimates.warehouseId',
|
from: 'sales_estimates.warehouseId',
|
||||||
to: 'warehouses.id',
|
to: 'warehouses.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Sale estimate transaction may has many attached attachments.
|
|
||||||
*/
|
|
||||||
attachments: {
|
attachments: {
|
||||||
relation: Model.ManyToManyRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
modelClass: Document,
|
modelClass: this.documentModel,
|
||||||
join: {
|
join: {
|
||||||
from: 'sales_estimates.id',
|
from: 'sales_estimates.id',
|
||||||
through: {
|
through: {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { BillPayment } from '@/modules/BillPayments/models/BillPayment';
|
|||||||
import { BillPaymentEntry } from '@/modules/BillPayments/models/BillPaymentEntry';
|
import { BillPaymentEntry } from '@/modules/BillPayments/models/BillPaymentEntry';
|
||||||
import { BillLandedCostEntry } from '@/modules/BillLandedCosts/models/BillLandedCostEntry';
|
import { BillLandedCostEntry } from '@/modules/BillLandedCosts/models/BillLandedCostEntry';
|
||||||
import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost';
|
import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost';
|
||||||
import { VendorCreditAppliedBill } from '@/modules/VendorCredit/models/VendorCreditAppliedBill';
|
import { VendorCreditAppliedBill } from '@/modules/VendorCreditsApplyBills/models/VendorCreditAppliedBill';
|
||||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||||
import { PaymentReceivedEntry } from '@/modules/PaymentReceived/models/PaymentReceivedEntry';
|
import { PaymentReceivedEntry } from '@/modules/PaymentReceived/models/PaymentReceivedEntry';
|
||||||
import { CreditNoteAppliedInvoice } from '@/modules/CreditNotes/models/CreditNoteAppliedInvoice';
|
import { CreditNoteAppliedInvoice } from '@/modules/CreditNotes/models/CreditNoteAppliedInvoice';
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common';
|
||||||
|
import { VendorCreditsApplicationService } from './VendorCreditsApplication.service';
|
||||||
|
import { IVendorCreditCreateDTO, IVendorCreditEditDTO } from './types/VendorCredit.types';
|
||||||
|
|
||||||
|
@Controller('vendor-credits')
|
||||||
|
export class VendorCreditsController {
|
||||||
|
constructor(
|
||||||
|
private readonly vendorCreditsApplication: VendorCreditsApplicationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
async createVendorCredit(@Body() dto: IVendorCreditCreateDTO) {
|
||||||
|
return this.vendorCreditsApplication.createVendorCredit(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put(':id')
|
||||||
|
async editVendorCredit(
|
||||||
|
@Param('id') vendorCreditId: number,
|
||||||
|
@Body() dto: IVendorCreditEditDTO,
|
||||||
|
) {
|
||||||
|
return this.vendorCreditsApplication.editVendorCredit(vendorCreditId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
async deleteVendorCredit(@Param('id') vendorCreditId: number) {
|
||||||
|
return this.vendorCreditsApplication.deleteVendorCredit(vendorCreditId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
async getVendorCredit(@Param('id') vendorCreditId: number) {
|
||||||
|
return this.vendorCreditsApplication.getVendorCredit(vendorCreditId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [
|
||||||
|
CreateVendorCreditService,
|
||||||
|
DeleteVendorCreditService,
|
||||||
|
EditVendorCreditService,
|
||||||
|
VendorCreditDTOTransformService,
|
||||||
|
VendorCreditAutoIncrementService,
|
||||||
|
GetRefundVendorCreditService,
|
||||||
|
VendorCreditDTOTransformService,
|
||||||
|
GetVendorCreditService
|
||||||
|
],
|
||||||
|
controllers: [],
|
||||||
|
})
|
||||||
|
export class VendorCreditsModule {}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
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 } from './types/VendorCredit.types';
|
||||||
|
import { IVendorCreditCreateDTO } from './types/VendorCredit.types';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class VendorCreditsApplicationService {
|
||||||
|
constructor(
|
||||||
|
private readonly createVendorCreditService: CreateVendorCreditService,
|
||||||
|
private readonly editVendorCreditService: EditVendorCreditService,
|
||||||
|
private readonly deleteVendorCreditService: DeleteVendorCreditService,
|
||||||
|
private readonly getVendorCreditService: GetVendorCreditService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new vendor credit.
|
||||||
|
* @param {IVendorCreditCreateDTO} dto - The vendor credit create DTO.
|
||||||
|
* @param {Knex.Transaction} trx - The transaction.
|
||||||
|
* @returns {Promise<VendorCredit>} The created vendor credit.
|
||||||
|
*/
|
||||||
|
createVendorCredit(dto: IVendorCreditCreateDTO, trx?: Knex.Transaction) {
|
||||||
|
return this.createVendorCreditService.newVendorCredit(dto, trx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits the given vendor credit.
|
||||||
|
* @param {number} vendorCreditId - The vendor credit id.
|
||||||
|
* @param {IVendorCreditEditDTO} dto - The vendor credit edit DTO.
|
||||||
|
* @param {Knex.Transaction} trx - The transaction.
|
||||||
|
* @returns {Promise<VendorCredit>} The edited vendor credit.
|
||||||
|
*/
|
||||||
|
editVendorCredit(
|
||||||
|
vendorCreditId: number,
|
||||||
|
dto: IVendorCreditEditDTO,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
IVendorCreditCreatedPayload,
|
||||||
|
IVendorCreditCreateDTO,
|
||||||
|
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';
|
||||||
|
|
||||||
|
@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,
|
||||||
|
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
|
||||||
|
@Inject(Vendor.name) private readonly vendorModel: typeof Vendor,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new vendor credit.
|
||||||
|
* @param {IVendorCreditCreateDTO} vendorCreditCreateDTO -
|
||||||
|
* @param {Knex.Transaction} trx -
|
||||||
|
*/
|
||||||
|
public newVendorCredit = async (
|
||||||
|
vendorCreditCreateDTO: IVendorCreditCreateDTO,
|
||||||
|
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 = this.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);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
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 '../models/RefundVendorCredit';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@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 vendorCreditModel: typeof VendorCredit,
|
||||||
|
private itemEntryModel: typeof ItemEntry,
|
||||||
|
private eventPublisher: EventEmitter2,
|
||||||
|
private uow: UnitOfWork,
|
||||||
|
|
||||||
|
@Inject(RefundVendorCredit.name)
|
||||||
|
private refundVendorCreditModel: typeof RefundVendorCredit,
|
||||||
|
|
||||||
|
@Inject(VendorCreditAppliedBill.name)
|
||||||
|
private vendorCreditAppliedBillModel: 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.refundVendorCreditModel
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
IVendorCreditEditDTO,
|
||||||
|
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';
|
||||||
|
|
||||||
|
@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,
|
||||||
|
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
|
||||||
|
@Inject(Contact.name)
|
||||||
|
private readonly contactModel: typeof Contact,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given vendor credit.
|
||||||
|
* @param {number} vendorCreditId - Vendor credit id.
|
||||||
|
*/
|
||||||
|
public editVendorCredit = async (
|
||||||
|
vendorCreditId: number,
|
||||||
|
vendorCreditDTO: IVendorCreditEditDTO,
|
||||||
|
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 = this.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,
|
||||||
|
vendorCreditId,
|
||||||
|
vendorCreditDTO,
|
||||||
|
trx,
|
||||||
|
} as IVendorCreditEditedPayload);
|
||||||
|
|
||||||
|
return vendorCredit;
|
||||||
|
}, trx);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
@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: typeof VendorCredit,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given credit note.
|
||||||
|
* @param {number} vendorCreditId -
|
||||||
|
* @returns {Promise<IVendorCredit>}
|
||||||
|
*/
|
||||||
|
public openVendorCredit = async (
|
||||||
|
vendorCreditId: number,
|
||||||
|
): 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)
|
||||||
|
.update({
|
||||||
|
openedAt: new Date(),
|
||||||
|
});
|
||||||
|
// Triggers `onVendorCreditOpened` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.vendorCredit.onOpened, {
|
||||||
|
...eventPayload,
|
||||||
|
vendorCredit,
|
||||||
|
} as IVendorCreditOpenedPayload);
|
||||||
|
|
||||||
|
return vendorCredit;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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 = (): string => {
|
||||||
|
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||||
|
'vendor_credit',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the vendor credit serial next number.
|
||||||
|
* @param {number} tenantId -
|
||||||
|
*/
|
||||||
|
public incrementSerialNumber = () => {
|
||||||
|
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||||
|
'vendor_credit',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import { omit } from 'lodash';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import {
|
||||||
|
IVendorCreditCreateDTO,
|
||||||
|
IVendorCreditEditDTO,
|
||||||
|
IVendorCreditEntryDTO,
|
||||||
|
} from '../types/VendorCredit.types';
|
||||||
|
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';
|
||||||
|
|
||||||
|
|
||||||
|
@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
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes the credit/edit vendor credit DTO to model.
|
||||||
|
* @param {IVendorCreditCreateDTO | IVendorCreditEditDTO} vendorCreditDTO
|
||||||
|
* @param {string} vendorCurrencyCode -
|
||||||
|
* @param {IVendorCredit} oldVendorCredit -
|
||||||
|
* @returns {VendorCredit}
|
||||||
|
*/
|
||||||
|
public transformCreateEditDTOToModel = (
|
||||||
|
vendorCreditDTO: IVendorCreditCreateDTO | IVendorCreditEditDTO,
|
||||||
|
vendorCurrencyCode: string,
|
||||||
|
oldVendorCredit?: VendorCredit
|
||||||
|
): 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: IVendorCreditEntryDTO) => ({
|
||||||
|
referenceType: 'VendorCredit',
|
||||||
|
...entry,
|
||||||
|
}))
|
||||||
|
)(vendorCreditDTO.entries);
|
||||||
|
|
||||||
|
// Retreive the next vendor credit number.
|
||||||
|
const autoNextNumber = 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 R.compose(
|
||||||
|
this.branchDTOTransform.transformDTO<VendorCredit>,
|
||||||
|
this.warehouseDTOTransform.transformDTO<VendorCredit>(
|
||||||
|
)(initialDTO);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
// import { Inject, Service } from 'typedi';
|
||||||
|
// import { Knex } from 'knex';
|
||||||
|
// import * as R from 'ramda';
|
||||||
|
// import {
|
||||||
|
// IVendorCredit,
|
||||||
|
// ILedgerEntry,
|
||||||
|
// AccountNormal,
|
||||||
|
// IItemEntry,
|
||||||
|
// } from '@/interfaces';
|
||||||
|
// import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
// import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||||
|
// import Ledger from '@/services/Accounting/Ledger';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export default class VendorCreditGLEntries {
|
||||||
|
// @Inject()
|
||||||
|
// private ledgerStorage: LedgerStorageService;
|
||||||
|
|
||||||
|
// @Inject()
|
||||||
|
// private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieve the vendor credit GL common entry.
|
||||||
|
// * @param {IVendorCredit} vendorCredit
|
||||||
|
// * @returns {}
|
||||||
|
// */
|
||||||
|
// public getVendorCreditGLCommonEntry = (vendorCredit: IVendorCredit) => {
|
||||||
|
// return {
|
||||||
|
// date: vendorCredit.vendorCreditDate,
|
||||||
|
// currencyCode: vendorCredit.currencyCode,
|
||||||
|
// exchangeRate: vendorCredit.exchangeRate,
|
||||||
|
|
||||||
|
// transactionId: vendorCredit.id,
|
||||||
|
// transactionType: 'VendorCredit',
|
||||||
|
// transactionNumber: vendorCredit.vendorCreditNumber,
|
||||||
|
// referenceNumber: vendorCredit.referenceNo,
|
||||||
|
|
||||||
|
// credit: 0,
|
||||||
|
// debit: 0,
|
||||||
|
|
||||||
|
// branchId: vendorCredit.branchId,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the vendor credit payable GL entry.
|
||||||
|
// * @param {IVendorCredit} vendorCredit
|
||||||
|
// * @param {number} APAccountId
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// public getVendorCreditPayableGLEntry = (
|
||||||
|
// vendorCredit: IVendorCredit,
|
||||||
|
// APAccountId: number
|
||||||
|
// ): ILedgerEntry => {
|
||||||
|
// const commonEntity = this.getVendorCreditGLCommonEntry(vendorCredit);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntity,
|
||||||
|
// debit: vendorCredit.totalLocal,
|
||||||
|
// accountId: APAccountId,
|
||||||
|
// contactId: vendorCredit.vendorId,
|
||||||
|
// accountNormal: AccountNormal.CREDIT,
|
||||||
|
// index: 1,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the vendor credit item GL entry.
|
||||||
|
// * @param {IVendorCredit} vendorCredit
|
||||||
|
// * @param {IItemEntry} entry
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// public getVendorCreditGLItemEntry = R.curry(
|
||||||
|
// (
|
||||||
|
// vendorCredit: IVendorCredit,
|
||||||
|
// entry: IItemEntry,
|
||||||
|
// index: number
|
||||||
|
// ): ILedgerEntry => {
|
||||||
|
// const commonEntity = this.getVendorCreditGLCommonEntry(vendorCredit);
|
||||||
|
// const totalLocal = entry.totalExcludingTax * 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.
|
||||||
|
// * @param {IVendorCredit} vendorCredit
|
||||||
|
// * @param {number} discountAccountId
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// public getDiscountEntry = (
|
||||||
|
// vendorCredit: IVendorCredit,
|
||||||
|
// purchaseDiscountAccountId: number
|
||||||
|
// ) => {
|
||||||
|
// const commonEntry = this.getVendorCreditGLCommonEntry(vendorCredit);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntry,
|
||||||
|
// debit: vendorCredit.discountAmountLocal,
|
||||||
|
// accountId: purchaseDiscountAccountId,
|
||||||
|
// accountNormal: AccountNormal.DEBIT,
|
||||||
|
// index: 1,
|
||||||
|
// indexGroup: 40,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieves the vendor credit adjustment GL entry.
|
||||||
|
// * @param {IVendorCredit} vendorCredit
|
||||||
|
// * @param {number} adjustmentAccountId
|
||||||
|
// * @returns {ILedgerEntry}
|
||||||
|
// */
|
||||||
|
// public getAdjustmentEntry = (
|
||||||
|
// vendorCredit: IVendorCredit,
|
||||||
|
// otherExpensesAccountId: number
|
||||||
|
// ) => {
|
||||||
|
// const commonEntry = this.getVendorCreditGLCommonEntry(vendorCredit);
|
||||||
|
// const adjustmentAmount = Math.abs(vendorCredit.adjustmentLocal);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...commonEntry,
|
||||||
|
// credit: vendorCredit.adjustmentLocal > 0 ? adjustmentAmount : 0,
|
||||||
|
// debit: vendorCredit.adjustmentLocal < 0 ? adjustmentAmount : 0,
|
||||||
|
// accountId: otherExpensesAccountId,
|
||||||
|
// accountNormal: AccountNormal.DEBIT,
|
||||||
|
// index: 1,
|
||||||
|
// indexGroup: 40,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieve the vendor credit GL entries.
|
||||||
|
// * @param {IVendorCredit} vendorCredit -
|
||||||
|
// * @param {number} receivableAccount -
|
||||||
|
// * @return {ILedgerEntry[]}
|
||||||
|
// */
|
||||||
|
// public getVendorCreditGLEntries = (
|
||||||
|
// vendorCredit: IVendorCredit,
|
||||||
|
// payableAccountId: number,
|
||||||
|
// purchaseDiscountAccountId: number,
|
||||||
|
// otherExpensesAccountId: number
|
||||||
|
// ): ILedgerEntry[] => {
|
||||||
|
// const payableEntry = this.getVendorCreditPayableGLEntry(
|
||||||
|
// vendorCredit,
|
||||||
|
// payableAccountId
|
||||||
|
// );
|
||||||
|
// const getItemEntry = this.getVendorCreditGLItemEntry(vendorCredit);
|
||||||
|
// const itemsEntries = vendorCredit.entries.map(getItemEntry);
|
||||||
|
|
||||||
|
// const discountEntry = this.getDiscountEntry(
|
||||||
|
// vendorCredit,
|
||||||
|
// purchaseDiscountAccountId
|
||||||
|
// );
|
||||||
|
// const adjustmentEntry = this.getAdjustmentEntry(
|
||||||
|
// vendorCredit,
|
||||||
|
// otherExpensesAccountId
|
||||||
|
// );
|
||||||
|
// return [payableEntry, discountEntry, adjustmentEntry, ...itemsEntries];
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Reverts the vendor credit associated GL entries.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {number} vendorCreditId
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public revertVendorCreditGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// vendorCreditId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// await this.ledgerStorage.deleteByReference(
|
||||||
|
// tenantId,
|
||||||
|
// vendorCreditId,
|
||||||
|
// 'VendorCredit',
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Creates vendor credit associated GL entries.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {number} vendorCreditId
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public writeVendorCreditGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// vendorCreditId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ) => {
|
||||||
|
// const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
// const { VendorCredit } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// // Vendor credit with entries items.
|
||||||
|
// const vendorCredit = await VendorCredit.query(trx)
|
||||||
|
// .findById(vendorCreditId)
|
||||||
|
// .withGraphFetched('entries.item');
|
||||||
|
|
||||||
|
// // Retrieve the payable account (A/P) account.
|
||||||
|
// const APAccount = await accountRepository.findOrCreateAccountsPayable(
|
||||||
|
// vendorCredit.currencyCode,
|
||||||
|
// {},
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// const purchaseDiscountAccount =
|
||||||
|
// await accountRepository.findOrCreatePurchaseDiscountAccount({}, trx);
|
||||||
|
|
||||||
|
// const otherExpensesAccount =
|
||||||
|
// await accountRepository.findOrCreateOtherExpensesAccount({}, trx);
|
||||||
|
// // Saves the vendor credit GL entries.
|
||||||
|
// const ledgerEntries = this.getVendorCreditGLEntries(
|
||||||
|
// vendorCredit,
|
||||||
|
// APAccount.id,
|
||||||
|
// purchaseDiscountAccount.id,
|
||||||
|
// otherExpensesAccount.id
|
||||||
|
// );
|
||||||
|
// const ledger = new Ledger(ledgerEntries);
|
||||||
|
|
||||||
|
// // Commits the ledger entries to the storage.
|
||||||
|
// await this.ledgerStorage.commit(tenantId, ledger, trx);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Edits vendor credit associated GL entries.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {number} vendorCreditId
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public rewriteVendorCreditGLEntries = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// vendorCreditId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ) => {
|
||||||
|
// // Reverts the GL entries.
|
||||||
|
// await this.revertVendorCreditGLEntries(tenantId, vendorCreditId, trx);
|
||||||
|
|
||||||
|
// // Re-write the GL entries.
|
||||||
|
// await this.writeVendorCreditGLEntries(tenantId, vendorCreditId, trx);
|
||||||
|
// };
|
||||||
|
// }
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// import { Knex } from 'knex';
|
||||||
|
// import { Service, Inject } from 'typedi';
|
||||||
|
// import { IVendorCredit } from '@/interfaces';
|
||||||
|
// import InventoryService from '@/services/Inventory/Inventory';
|
||||||
|
// import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export default class VendorCreditInventoryTransactions {
|
||||||
|
// @Inject()
|
||||||
|
// inventoryService: InventoryService;
|
||||||
|
|
||||||
|
// @Inject()
|
||||||
|
// itemsEntriesService: ItemsEntriesService;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Creates vendor credit associated inventory transactions.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {IVnedorCredit} vendorCredit
|
||||||
|
// * @param {Knex.Transaction} trx
|
||||||
|
// */
|
||||||
|
// public createInventoryTransactions = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// vendorCredit: IVendorCredit,
|
||||||
|
// trx: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// // Loads the inventory items entries of the given sale invoice.
|
||||||
|
// const inventoryEntries =
|
||||||
|
// await this.itemsEntriesService.filterInventoryEntries(
|
||||||
|
// tenantId,
|
||||||
|
// 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(
|
||||||
|
// tenantId,
|
||||||
|
// transaction,
|
||||||
|
// false,
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Edits vendor credit associated inventory transactions.
|
||||||
|
// * @param {number} tenantId
|
||||||
|
// * @param {number} creditNoteId
|
||||||
|
// * @param {ICreditNote} creditNote
|
||||||
|
// * @param {Knex.Transactions} trx
|
||||||
|
// */
|
||||||
|
// public editInventoryTransactions = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// vendorCreditId: number,
|
||||||
|
// vendorCredit: IVendorCredit,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// // Deletes inventory transactions.
|
||||||
|
// await this.deleteInventoryTransactions(tenantId, vendorCreditId, trx);
|
||||||
|
|
||||||
|
// // Re-write inventory transactions.
|
||||||
|
// await this.createInventoryTransactions(tenantId, vendorCredit, trx);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Deletes credit note associated inventory transactions.
|
||||||
|
// * @param {number} tenantId - Tenant id.
|
||||||
|
// * @param {number} creditNoteId - Credit note id.
|
||||||
|
// * @param {Knex.Transaction} trx -
|
||||||
|
// */
|
||||||
|
// public deleteInventoryTransactions = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// vendorCreditId: number,
|
||||||
|
// trx?: Knex.Transaction
|
||||||
|
// ): Promise<void> => {
|
||||||
|
// // Deletes the inventory transactions by the given reference id and type.
|
||||||
|
// await this.inventoryService.deleteInventoryTransactions(
|
||||||
|
// tenantId,
|
||||||
|
// vendorCreditId,
|
||||||
|
// 'VendorCredit',
|
||||||
|
// trx
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
// }
|
||||||
@@ -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);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -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;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
82
packages/server-nest/src/modules/VendorCredit/constants.ts
Normal file
82
packages/server-nest/src/modules/VendorCredit/constants.ts
Normal 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,
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { Model, mixin } from 'objection';
|
||||||
|
// import TenantModel from 'models/TenantModel';
|
||||||
|
// import ModelSetting from './ModelSetting';
|
||||||
|
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||||
|
// import ModelSearchable from './ModelSearchable';
|
||||||
|
import { BaseModel } from '@/models/Model';
|
||||||
|
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||||
|
import { VendorCredit } from './VendorCredit';
|
||||||
|
|
||||||
|
export class RefundVendorCredit extends BaseModel {
|
||||||
|
public vendorCreditId!: number;
|
||||||
|
public amount!: number;
|
||||||
|
public currencyCode!: string;
|
||||||
|
public exchangeRate!: number;
|
||||||
|
public referenceNo!: string;
|
||||||
|
public depositAccountId!: number;
|
||||||
|
public description!: string;
|
||||||
|
public branchId!: number;
|
||||||
|
|
||||||
|
public vendorCredit!: VendorCredit;
|
||||||
|
public depositAccount!: Account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table name.
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'refund_vendor_credit_transactions';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamps columns.
|
||||||
|
*/
|
||||||
|
get timestamps() {
|
||||||
|
return ['created_at', 'updated_at'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const { VendorCredit } = require('./VendorCredit');
|
||||||
|
const { Account } = require('../../Accounts/models/Account.model');
|
||||||
|
|
||||||
|
return {
|
||||||
|
depositAccount: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Account,
|
||||||
|
join: {
|
||||||
|
from: 'refund_vendor_credit_transactions.depositAccountId',
|
||||||
|
to: 'accounts.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vendorCredit: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: VendorCredit,
|
||||||
|
join: {
|
||||||
|
from: 'refund_vendor_credit_transactions.vendorCreditId',
|
||||||
|
to: 'vendor_credits.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,9 @@ export class VendorCredit extends BaseModel {
|
|||||||
openedAt: Date;
|
openedAt: Date;
|
||||||
userId: number;
|
userId: number;
|
||||||
|
|
||||||
|
branchId: number;
|
||||||
|
warehouseId: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -183,83 +186,85 @@ export class VendorCredit extends BaseModel {
|
|||||||
/**
|
/**
|
||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
// static get relationMappings() {
|
static get relationMappings() {
|
||||||
// const Vendor = require('models/Vendor');
|
const { Vendor } = require('../../Vendors/models/Vendor');
|
||||||
// const ItemEntry = require('models/ItemEntry');
|
const {
|
||||||
// const Branch = require('models/Branch');
|
ItemEntry,
|
||||||
// const Document = require('models/Document');
|
} = require('../../TransactionItemEntry/models/ItemEntry');
|
||||||
// const Warehouse = require('models/Warehouse');
|
const { Branch } = require('../../Branches/models/Branch.model');
|
||||||
|
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
||||||
|
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
|
||||||
|
|
||||||
// return {
|
return {
|
||||||
// vendor: {
|
vendor: {
|
||||||
// relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
// modelClass: Vendor.default,
|
modelClass: Vendor,
|
||||||
// join: {
|
join: {
|
||||||
// from: 'vendor_credits.vendorId',
|
from: 'vendor_credits.vendorId',
|
||||||
// to: 'contacts.id',
|
to: 'contacts.id',
|
||||||
// },
|
},
|
||||||
// filter(query) {
|
filter(query) {
|
||||||
// query.where('contact_service', 'vendor');
|
query.where('contact_service', 'vendor');
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
|
|
||||||
// entries: {
|
entries: {
|
||||||
// relation: Model.HasManyRelation,
|
relation: Model.HasManyRelation,
|
||||||
// modelClass: ItemEntry.default,
|
modelClass: ItemEntry,
|
||||||
// join: {
|
join: {
|
||||||
// from: 'vendor_credits.id',
|
from: 'vendor_credits.id',
|
||||||
// to: 'items_entries.referenceId',
|
to: 'items_entries.referenceId',
|
||||||
// },
|
},
|
||||||
// filter(builder) {
|
filter(builder) {
|
||||||
// builder.where('reference_type', 'VendorCredit');
|
builder.where('reference_type', 'VendorCredit');
|
||||||
// builder.orderBy('index', 'ASC');
|
builder.orderBy('index', 'ASC');
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Vendor credit may belongs to branch.
|
* Vendor credit may belongs to branch.
|
||||||
// */
|
*/
|
||||||
// branch: {
|
branch: {
|
||||||
// relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
// modelClass: Branch.default,
|
modelClass: Branch,
|
||||||
// join: {
|
join: {
|
||||||
// from: 'vendor_credits.branchId',
|
from: 'vendor_credits.branchId',
|
||||||
// to: 'branches.id',
|
to: 'branches.id',
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Vendor credit may has associated warehouse.
|
* Vendor credit may has associated warehouse.
|
||||||
// */
|
*/
|
||||||
// warehouse: {
|
warehouse: {
|
||||||
// relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
// modelClass: Warehouse.default,
|
modelClass: Warehouse,
|
||||||
// join: {
|
join: {
|
||||||
// from: 'vendor_credits.warehouseId',
|
from: 'vendor_credits.warehouseId',
|
||||||
// to: 'warehouses.id',
|
to: 'warehouses.id',
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Vendor credit may has many attached attachments.
|
* Vendor credit may has many attached attachments.
|
||||||
// */
|
*/
|
||||||
// attachments: {
|
attachments: {
|
||||||
// relation: Model.ManyToManyRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
// modelClass: Document.default,
|
modelClass: Document,
|
||||||
// join: {
|
join: {
|
||||||
// from: 'vendor_credits.id',
|
from: 'vendor_credits.id',
|
||||||
// through: {
|
through: {
|
||||||
// from: 'document_links.modelId',
|
from: 'document_links.modelId',
|
||||||
// to: 'document_links.documentId',
|
to: 'document_links.documentId',
|
||||||
// },
|
},
|
||||||
// to: 'documents.id',
|
to: 'documents.id',
|
||||||
// },
|
},
|
||||||
// filter(query) {
|
filter(query) {
|
||||||
// query.where('model_ref', 'VendorCredit');
|
query.where('model_ref', 'VendorCredit');
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
// };
|
};
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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 { Knex } from 'knex';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class GetVendorCreditService {
|
||||||
|
constructor(
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
private readonly transformer: TransformerInjectable,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the given vendor credit.
|
||||||
|
* @param {number} vendorCreditId - Vendor credit id.
|
||||||
|
*/
|
||||||
|
public async getVendorCredit(
|
||||||
|
vendorCreditId: number,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
) {
|
||||||
|
// Retrieve the vendor credit model graph.
|
||||||
|
const vendorCredit = await this.vendorCreditModel
|
||||||
|
.query()
|
||||||
|
.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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// import * as R from 'ramda';
|
||||||
|
// import { Service, Inject } from 'typedi';
|
||||||
|
// import BaseVendorCredit from '../commands/BaseVendorCredit';
|
||||||
|
// import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||||
|
// import { IVendorCreditsQueryDTO } from '@/interfaces';
|
||||||
|
// import { VendorCreditTransformer } from './VendorCreditTransformer';
|
||||||
|
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
|
||||||
|
// @Service()
|
||||||
|
// export default class ListVendorCredits extends BaseVendorCredit {
|
||||||
|
// @Inject()
|
||||||
|
// private dynamicListService: DynamicListingService;
|
||||||
|
|
||||||
|
// @Inject()
|
||||||
|
// private transformer: TransformerInjectable;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 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 {number} tenantId - Tenant id.
|
||||||
|
// * @param {IVendorCreditsQueryDTO} vendorCreditQuery -
|
||||||
|
// */
|
||||||
|
// public getVendorCredits = async (
|
||||||
|
// tenantId: number,
|
||||||
|
// vendorCreditQuery: IVendorCreditsQueryDTO
|
||||||
|
// ) => {
|
||||||
|
// const { VendorCredit } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// // Parses stringified filter roles.
|
||||||
|
// const filter = this.parseListFilterDTO(vendorCreditQuery);
|
||||||
|
|
||||||
|
// // Dynamic list service.
|
||||||
|
// const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||||
|
// tenantId,
|
||||||
|
// VendorCredit,
|
||||||
|
// filter
|
||||||
|
// );
|
||||||
|
// const { results, pagination } = await VendorCredit.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(
|
||||||
|
// tenantId,
|
||||||
|
// results,
|
||||||
|
// new VendorCreditTransformer()
|
||||||
|
// );
|
||||||
|
// return {
|
||||||
|
// vendorCredits,
|
||||||
|
// pagination,
|
||||||
|
// filterMeta: dynamicFilter.getResponseMeta(),
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
// }
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
import { AttachmentTransformer } from "@/modules/Attachments/Attachment.transformer";
|
||||||
|
import { ItemEntryTransformer } from "@/modules/TransactionItemEntry/ItemEntry.transformer";
|
||||||
|
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||||
|
|
||||||
|
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 {IVendorCredit} credit
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedVendorCreditDate = (vendorCredit): string => {
|
||||||
|
return this.formatDate(vendorCredit.vendorCreditDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retireve formatted created at date.
|
||||||
|
* @param vendorCredit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedCreatedAt = (vendorCredit): string => {
|
||||||
|
return this.formatDate(vendorCredit.createdAt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted vendor credit amount.
|
||||||
|
* @param {IVendorCredit} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (vendorCredit): string => {
|
||||||
|
return this.formatNumber(vendorCredit.amount, {
|
||||||
|
currencyCode: vendorCredit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the vendor credit formatted subtotal.
|
||||||
|
* @param {IVendorCredit} vendorCredit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedSubtotal = (vendorCredit): string => {
|
||||||
|
return this.formatNumber(vendorCredit.amount, { money: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted credits remaining.
|
||||||
|
* @param {IVendorCredit} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedCreditsRemaining = (credit) => {
|
||||||
|
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 {IVendorCredit} 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 {IVendorCredit} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected discountPercentageFormatted = (credit): string => {
|
||||||
|
return credit.discountPercentage ? `${credit.discountPercentage}%` : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted adjustment amount.
|
||||||
|
* @param {IVendorCredit} 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 {IVendorCredit} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected adjustmentLocalFormatted = (credit): 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) => {
|
||||||
|
return this.formatNumber(credit.invoicedAmount, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the formatted total.
|
||||||
|
* @param {IVendorCredit} credit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected totalFormatted = (credit) => {
|
||||||
|
return this.formatNumber(credit.total, {
|
||||||
|
currencyCode: credit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the entries of the bill.
|
||||||
|
* @param {IVendorCredit} vendorCredit
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
protected entries = (vendorCredit) => {
|
||||||
|
return this.item(vendorCredit.entries, new ItemEntryTransformer(), {
|
||||||
|
currencyCode: vendorCredit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the vendor credit attachments.
|
||||||
|
* @param {IVendorCredit} invoice
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected attachments = (vendorCredit) => {
|
||||||
|
return this.item(vendorCredit.attachments, new AttachmentTransformer());
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import {
|
||||||
|
IVendorCreditCreatedPayload,
|
||||||
|
IVendorCreditDeletedPayload,
|
||||||
|
IVendorCreditEditedPayload,
|
||||||
|
IVendorCreditOpenedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import VendorCreditGLEntries from '../commands/VendorCreditGLEntries';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class VendorCreditGlEntriesSubscriber {
|
||||||
|
@Inject()
|
||||||
|
private vendorCreditGLEntries: VendorCreditGLEntries;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Attaches events with handlers.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onCreated,
|
||||||
|
this.writeGLEntriesOnceVendorCreditCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onOpened,
|
||||||
|
this.writeGLEntgriesOnceVendorCreditOpened
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onEdited,
|
||||||
|
this.editGLEntriesOnceVendorCreditEdited
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onDeleted,
|
||||||
|
this.revertGLEntriesOnceDeleted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes GL entries of vendor credit once the transaction created.
|
||||||
|
* @param {IVendorCreditCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
private writeGLEntriesOnceVendorCreditCreated = async ({
|
||||||
|
tenantId,
|
||||||
|
vendorCredit,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditCreatedPayload): Promise<void> => {
|
||||||
|
// Can't continue if the vendor credit is not open yet.
|
||||||
|
if (!vendorCredit.isPublished) return;
|
||||||
|
|
||||||
|
await this.vendorCreditGLEntries.writeVendorCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
vendorCredit.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes Gl entries of vendor credit once the transaction opened.
|
||||||
|
* @param {IVendorCreditOpenedPayload} payload -
|
||||||
|
*/
|
||||||
|
private writeGLEntgriesOnceVendorCreditOpened = async ({
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditOpenedPayload) => {
|
||||||
|
await this.vendorCreditGLEntries.writeVendorCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits associated GL entries once vendor credit edited.
|
||||||
|
* @param {IVendorCreditEditedPayload} payload
|
||||||
|
*/
|
||||||
|
private editGLEntriesOnceVendorCreditEdited = async ({
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId,
|
||||||
|
vendorCredit,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditEditedPayload) => {
|
||||||
|
// Can't continue if the vendor credit is not open yet.
|
||||||
|
if (!vendorCredit.isPublished) return;
|
||||||
|
|
||||||
|
await this.vendorCreditGLEntries.rewriteVendorCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts the GL entries once vendor credit deleted.
|
||||||
|
* @param {IVendorCreditDeletedPayload} payload -
|
||||||
|
*/
|
||||||
|
private revertGLEntriesOnceDeleted = async ({
|
||||||
|
vendorCreditId,
|
||||||
|
tenantId,
|
||||||
|
oldVendorCredit,
|
||||||
|
}: IVendorCreditDeletedPayload): Promise<void> => {
|
||||||
|
// Can't continue of the vendor credit is not open yet.
|
||||||
|
if (!oldVendorCredit.isPublished) return;
|
||||||
|
|
||||||
|
await this.vendorCreditGLEntries.revertVendorCreditGLEntries(
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import {
|
||||||
|
IVendorCreditCreatedPayload,
|
||||||
|
IVendorCreditDeletedPayload,
|
||||||
|
IVendorCreditEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import VendorCreditInventoryTransactions from '../commands/VendorCreditInventoryTransactions';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class VendorCreditInventoryTransactionsSubscriber {
|
||||||
|
@Inject()
|
||||||
|
private inventoryTransactions: VendorCreditInventoryTransactions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
* @param bus
|
||||||
|
*/
|
||||||
|
attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onCreated,
|
||||||
|
this.writeInventoryTransactionsOnceCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onOpened,
|
||||||
|
this.writeInventoryTransactionsOnceCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onEdited,
|
||||||
|
this.rewriteInventroyTransactionsOnceEdited
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onDeleted,
|
||||||
|
this.revertInventoryTransactionsOnceDeleted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes inventory transactions once vendor created created.
|
||||||
|
* @param {IVendorCreditCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
private writeInventoryTransactionsOnceCreated = async ({
|
||||||
|
tenantId,
|
||||||
|
vendorCredit,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditCreatedPayload) => {
|
||||||
|
// Can't continue if vendor credit is not opened.
|
||||||
|
if (!vendorCredit.openedAt) return null;
|
||||||
|
|
||||||
|
await this.inventoryTransactions.createInventoryTransactions(
|
||||||
|
tenantId,
|
||||||
|
vendorCredit,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrites inventory transactions once vendor credit edited.
|
||||||
|
* @param {IVendorCreditEditedPayload} payload -
|
||||||
|
*/
|
||||||
|
private rewriteInventroyTransactionsOnceEdited = async ({
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId,
|
||||||
|
vendorCredit,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditEditedPayload) => {
|
||||||
|
// Can't continue if vendor credit is not opened.
|
||||||
|
if (!vendorCredit.openedAt) return null;
|
||||||
|
|
||||||
|
await this.inventoryTransactions.editInventoryTransactions(
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId,
|
||||||
|
vendorCredit,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts inventory transactions once vendor credit deleted.
|
||||||
|
* @param {IVendorCreditDeletedPayload} payload -
|
||||||
|
*/
|
||||||
|
private revertInventoryTransactionsOnceDeleted = async ({
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditDeletedPayload) => {
|
||||||
|
await this.inventoryTransactions.deleteInventoryTransactions(
|
||||||
|
tenantId,
|
||||||
|
vendorCreditId,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
import { DiscountType, IDynamicListFilter, IItemEntry, IItemEntryDTO } from '@/interfaces';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { VendorCredit } from '../models/VendorCredit';
|
||||||
|
|
||||||
|
export enum VendorCreditAction {
|
||||||
|
Create = 'Create',
|
||||||
|
Edit = 'Edit',
|
||||||
|
Delete = 'Delete',
|
||||||
|
View = 'View',
|
||||||
|
Refund = 'Refund',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface IVendorCreditEntryDTO extends IItemEntryDTO {}
|
||||||
|
|
||||||
|
export interface IRefundVendorCredit {
|
||||||
|
id?: number | null;
|
||||||
|
date: Date;
|
||||||
|
referenceNo: string;
|
||||||
|
amount: number;
|
||||||
|
currencyCode: string;
|
||||||
|
exchangeRate: number;
|
||||||
|
depositAccountId: number;
|
||||||
|
description: string;
|
||||||
|
vendorCreditId: number;
|
||||||
|
createdAt: Date | null;
|
||||||
|
userId: number;
|
||||||
|
branchId?: number;
|
||||||
|
|
||||||
|
vendorCredit?: VendorCredit
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// tenantId: number;
|
||||||
|
refundVendorCreditDTO: IRefundVendorCreditDTO;
|
||||||
|
vendorCreditId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditCreatingPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
vendorCreditId: number;
|
||||||
|
vendorCreditCreateDTO: IVendorCreditCreateDTO;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditCreatedPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
vendorCreditCreateDTO: IVendorCreditCreateDTO;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditCreatedPayload {}
|
||||||
|
export interface IVendorCreditDeletedPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
// tenantId: number;
|
||||||
|
vendorCreditId: number;
|
||||||
|
oldVendorCredit: VendorCredit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditDeletingPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
// tenantId: number;
|
||||||
|
oldVendorCredit: VendorCredit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditsQueryDTO extends IDynamicListFilter {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
searchKeyword?: string;
|
||||||
|
filterQuery?: (q: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditEditingPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
oldVendorCredit: VendorCredit;
|
||||||
|
vendorCreditDTO: IVendorCreditEditDTO;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditEditedPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
oldVendorCredit: VendorCredit;
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
vendorCreditId: number;
|
||||||
|
vendorCreditDTO: IVendorCreditEditDTO;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundVendorCreditDTO {
|
||||||
|
amount: number;
|
||||||
|
exchangeRate?: number;
|
||||||
|
depositAccountId: number;
|
||||||
|
description: string;
|
||||||
|
date: Date;
|
||||||
|
branchId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundVendorCreditDeletedPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
refundCreditId: number;
|
||||||
|
oldRefundCredit: IRefundVendorCredit;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundVendorCreditDeletePayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
refundCreditId: number;
|
||||||
|
oldRefundCredit: IRefundVendorCredit;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
export interface IRefundVendorCreditDeletingPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
refundCreditId: number;
|
||||||
|
oldRefundCredit: IRefundVendorCredit;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundVendorCreditCreatingPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
refundVendorCreditDTO: IRefundVendorCreditDTO;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRefundVendorCreditCreatedPayload {
|
||||||
|
refundVendorCredit: IRefundVendorCredit;
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
// tenantId: number;
|
||||||
|
}
|
||||||
|
export interface IRefundVendorCreditPOJO {}
|
||||||
|
|
||||||
|
export interface IApplyCreditToBillEntryDTO {
|
||||||
|
amount: number;
|
||||||
|
billId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApplyCreditToBillsDTO {
|
||||||
|
entries: IApplyCreditToBillEntryDTO[];
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToBillsCreatedPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
vendorCreditAppliedBills: IVendorCreditAppliedBill[];
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
export interface IVendorCreditApplyToBillsCreatingPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
export interface IVendorCreditApplyToBillsCreatePayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
export interface IVendorCreditApplyToBillDeletedPayload {
|
||||||
|
// tenantId: number;
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
oldCreditAppliedToBill: IVendorCreditAppliedBill;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToInvoiceDTO {
|
||||||
|
amount: number;
|
||||||
|
billId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToInvoicesDTO {
|
||||||
|
entries: IVendorCreditApplyToInvoiceDTO[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToInvoiceModel {
|
||||||
|
billId: number;
|
||||||
|
amount: number;
|
||||||
|
vendorCreditId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToInvoicesModel {
|
||||||
|
entries: IVendorCreditApplyToInvoiceModel[];
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditAppliedBill {
|
||||||
|
billId: number;
|
||||||
|
amount: number;
|
||||||
|
vendorCreditId: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { ApplyVendorCreditSyncBillsService } from "./command/ApplyVendorCreditSyncBills.service";
|
||||||
|
import { ApplyVendorCreditSyncInvoicedService } from "./command/ApplyVendorCreditSyncInvoiced.service";
|
||||||
|
import { DeleteApplyVendorCreditToBillService } from "./command/DeleteApplyVendorCreditToBill.service";
|
||||||
|
import { ApplyVendorCreditToBillsService } from "./command/ApplyVendorCreditToBills.service";
|
||||||
|
import { GetAppliedBillsToVendorCreditService } from "./queries/GetAppliedBillsToVendorCredit.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ApplyVendorCreditSyncBillsService,
|
||||||
|
ApplyVendorCreditSyncInvoicedService,
|
||||||
|
ApplyVendorCreditToBillsService,
|
||||||
|
DeleteApplyVendorCreditToBillService,
|
||||||
|
GetAppliedBillsToVendorCreditService
|
||||||
|
],
|
||||||
|
controllers: [],
|
||||||
|
})
|
||||||
|
export class VendorCreditApplyBillsModule {}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
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',
|
||||||
|
};
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import Bluebird from 'bluebird';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { IVendorCreditAppliedBill } from '../types/VendorCreditApplyBills.types';
|
||||||
|
import { Bill } from '@/modules/Bills/models/Bill';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ApplyVendorCreditSyncBillsService {
|
||||||
|
constructor(
|
||||||
|
@Inject(Bill.name)
|
||||||
|
private readonly billModel: typeof Bill,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment bills credited amount.
|
||||||
|
* @param {IVendorCreditAppliedBill[]} vendorCreditAppliedBills
|
||||||
|
* @param {Knex.Transaction} trx
|
||||||
|
*/
|
||||||
|
public incrementBillsCreditedAmount = async (
|
||||||
|
vendorCreditAppliedBills: IVendorCreditAppliedBill[],
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
) => {
|
||||||
|
await Bluebird.each(
|
||||||
|
vendorCreditAppliedBills,
|
||||||
|
(vendorCreditAppliedBill: IVendorCreditAppliedBill) => {
|
||||||
|
return this.billModel
|
||||||
|
.query(trx)
|
||||||
|
.where('id', vendorCreditAppliedBill.billId)
|
||||||
|
.increment('creditedAmount', vendorCreditAppliedBill.amount);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement bill credited amount.
|
||||||
|
* @param {IVendorCreditAppliedBill} vendorCreditAppliedBill
|
||||||
|
* @param {Knex.Transaction} trx
|
||||||
|
*/
|
||||||
|
public decrementBillCreditedAmount = async (
|
||||||
|
vendorCreditAppliedBill: IVendorCreditAppliedBill,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
) => {
|
||||||
|
await this.billModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(vendorCreditAppliedBill.billId)
|
||||||
|
.decrement('creditedAmount', vendorCreditAppliedBill.amount);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ApplyVendorCreditSyncInvoicedService {
|
||||||
|
constructor(
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment vendor credit invoiced amount.
|
||||||
|
* @param {number} vendorCreditId - Vendor credit id.
|
||||||
|
* @param {number} amount - Amount to increment.
|
||||||
|
* @param {Knex.Transaction} trx - Knex transaction.
|
||||||
|
*/
|
||||||
|
public incrementVendorCreditInvoicedAmount = async (
|
||||||
|
vendorCreditId: number,
|
||||||
|
amount: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
) => {
|
||||||
|
await this.vendorCreditModel.query(trx)
|
||||||
|
.findById(vendorCreditId)
|
||||||
|
.increment('invoicedAmount', amount);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement credit note invoiced amount.
|
||||||
|
* @param {number} vendorCreditId - Vendor credit id.
|
||||||
|
* @param {number} amount - Amount to decrement.
|
||||||
|
* @param {Knex.Transaction} trx - Knex transaction.
|
||||||
|
*/
|
||||||
|
public decrementVendorCreditInvoicedAmount = async (
|
||||||
|
vendorCreditId: number,
|
||||||
|
amount: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
) => {
|
||||||
|
await this.vendorCreditModel.query(trx)
|
||||||
|
.findById(vendorCreditId)
|
||||||
|
.decrement('invoicedAmount', amount);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { sumBy } from 'lodash';
|
||||||
|
import {
|
||||||
|
IVendorCreditApplyToBillsCreatedPayload,
|
||||||
|
IVendorCreditApplyToInvoicesDTO,
|
||||||
|
IVendorCreditApplyToInvoicesModel,
|
||||||
|
} from '../types/VendorCreditApplyBills.types';
|
||||||
|
import { ERRORS } from '../VendorCreditsApplyBills.constants';
|
||||||
|
import { VendorCreditAppliedBill } from '../models/VendorCreditAppliedBill';
|
||||||
|
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
||||||
|
import { BillPaymentValidators } from '@/modules/BillPayments/commands/BillPaymentValidators.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { Bill } from '@/modules/Bills/models/Bill';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ApplyVendorCreditToBillsService {
|
||||||
|
/**
|
||||||
|
* @param {UnitOfWork} uow - The unit of work service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - The event emitter service.
|
||||||
|
* @param {BillPaymentValidators} billPaymentValidators - The bill payment validators service.
|
||||||
|
* @param {typeof VendorCreditAppliedBill} vendorCreditAppliedBillModel - The vendor credit applied bill model.
|
||||||
|
* @param {typeof VendorCredit} vendorCreditModel - The vendor credit model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
private readonly billPaymentValidators: BillPaymentValidators,
|
||||||
|
private readonly vendorCreditAppliedBillModel: typeof VendorCreditAppliedBill,
|
||||||
|
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply credit note to the given invoices.
|
||||||
|
* @param {number} creditNoteId
|
||||||
|
* @param {IApplyCreditToInvoicesDTO} applyCreditToInvoicesDTO
|
||||||
|
*/
|
||||||
|
public applyVendorCreditToBills = async (
|
||||||
|
vendorCreditId: number,
|
||||||
|
applyCreditToBillsDTO: IVendorCreditApplyToInvoicesDTO,
|
||||||
|
): Promise<void> => {
|
||||||
|
// Retrieves the vendor credit or throw not found service error.
|
||||||
|
const vendorCredit = await this.vendorCreditModel
|
||||||
|
.query()
|
||||||
|
.findById(vendorCreditId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Transfomes credit apply to bills DTO to model object.
|
||||||
|
const vendorCreditAppliedModel = this.transformApplyDTOToModel(
|
||||||
|
applyCreditToBillsDTO,
|
||||||
|
vendorCredit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validate bills entries existance.
|
||||||
|
const appliedBills =
|
||||||
|
await this.billPaymentValidators.validateBillsExistance(
|
||||||
|
vendorCreditAppliedModel.entries,
|
||||||
|
vendorCredit.vendorId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validate bills has remaining amount to apply.
|
||||||
|
this.validateBillsRemainingAmount(
|
||||||
|
appliedBills,
|
||||||
|
vendorCreditAppliedModel.amount,
|
||||||
|
);
|
||||||
|
// Validate vendor credit remaining credit amount.
|
||||||
|
this.validateCreditRemainingAmount(
|
||||||
|
vendorCredit,
|
||||||
|
vendorCreditAppliedModel.amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Saves vendor credit applied to bills under unit-of-work envirement.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Inserts vendor credit applied to bills graph to the storage layer.
|
||||||
|
const vendorCreditAppliedBills = await this.vendorCreditAppliedBillModel
|
||||||
|
.query(trx)
|
||||||
|
.insertGraph(vendorCreditAppliedModel.entries);
|
||||||
|
|
||||||
|
// Triggers `IVendorCreditApplyToBillsCreatedPayload` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.vendorCredit.onApplyToInvoicesCreated,
|
||||||
|
{
|
||||||
|
trx,
|
||||||
|
vendorCredit,
|
||||||
|
vendorCreditAppliedBills,
|
||||||
|
} as IVendorCreditApplyToBillsCreatedPayload,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes apply DTO to model.
|
||||||
|
* @param {IApplyCreditToInvoicesDTO} applyDTO
|
||||||
|
* @param {ICreditNote} creditNote
|
||||||
|
* @returns {IVendorCreditApplyToInvoicesModel}
|
||||||
|
*/
|
||||||
|
private transformApplyDTOToModel = (
|
||||||
|
applyDTO: IVendorCreditApplyToInvoicesDTO,
|
||||||
|
vendorCredit: VendorCredit,
|
||||||
|
): IVendorCreditApplyToInvoicesModel => {
|
||||||
|
const entries = applyDTO.entries.map((entry) => ({
|
||||||
|
billId: entry.billId,
|
||||||
|
amount: entry.amount,
|
||||||
|
vendorCreditId: vendorCredit.id,
|
||||||
|
}));
|
||||||
|
const amount = sumBy(applyDTO.entries, 'amount');
|
||||||
|
|
||||||
|
return {
|
||||||
|
amount,
|
||||||
|
entries,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate bills remaining amount.
|
||||||
|
* @param {IBill[]} bills
|
||||||
|
* @param {number} amount
|
||||||
|
*/
|
||||||
|
private validateBillsRemainingAmount = (bills: Bill[], amount: number) => {
|
||||||
|
const invalidBills = bills.filter((bill) => bill.dueAmount < amount);
|
||||||
|
if (invalidBills.length > 0) {
|
||||||
|
throw new ServiceError(ERRORS.BILLS_HAS_NO_REMAINING_AMOUNT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { IVendorCreditApplyToBillDeletedPayload } from '../types/VendorCreditApplyBills.types';
|
||||||
|
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
||||||
|
import { ERRORS } from '../VendorCreditsApplyBills.constants';
|
||||||
|
import { VendorCreditAppliedBill } from '../models/VendorCreditAppliedBill';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeleteApplyVendorCreditToBillService {
|
||||||
|
/**
|
||||||
|
* @param {UnitOfWork} uow - The unit of work service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - The event emitter service.
|
||||||
|
* @param {typeof VendorCreditAppliedBill} vendorCreditAppliedBillModel - The vendor credit applied bill model.
|
||||||
|
* @param {typeof VendorCredit} vendorCreditModel - The vendor credit model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
private readonly vendorCreditAppliedBillModel: typeof VendorCreditAppliedBill,
|
||||||
|
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete apply vendor credit to bill transaction.
|
||||||
|
* @param {number} appliedCreditToBillId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async deleteApplyVendorCreditToBills(appliedCreditToBillId: number) {
|
||||||
|
const oldCreditAppliedToBill = await this.vendorCreditAppliedBillModel
|
||||||
|
.query()
|
||||||
|
.findById(appliedCreditToBillId);
|
||||||
|
|
||||||
|
if (!oldCreditAppliedToBill) {
|
||||||
|
throw new ServiceError(ERRORS.VENDOR_CREDIT_APPLY_TO_BILLS_NOT_FOUND);
|
||||||
|
}
|
||||||
|
// Retrieve the vendor credit or throw not found service error.
|
||||||
|
const vendorCredit = await this.vendorCreditModel
|
||||||
|
.query()
|
||||||
|
.findById(oldCreditAppliedToBill.vendorCreditId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Deletes vendor credit apply under unit-of-work environment.
|
||||||
|
return this.uow.withTransaction(async (trx) => {
|
||||||
|
// Delete vendor credit applied to bill transaction.
|
||||||
|
await this.vendorCreditAppliedBillModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(appliedCreditToBillId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Triggers `onVendorCreditApplyToInvoiceDeleted` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.vendorCredit.onApplyToInvoicesDeleted,
|
||||||
|
{
|
||||||
|
vendorCredit,
|
||||||
|
oldCreditAppliedToBill,
|
||||||
|
trx,
|
||||||
|
} as IVendorCreditApplyToBillDeletedPayload,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
import { mixin, Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
// import TenantModel from 'models/TenantModel';
|
// import TenantModel from 'models/TenantModel';
|
||||||
// import ModelSetting from './ModelSetting';
|
// import ModelSetting from './ModelSetting';
|
||||||
// import CustomViewBaseModel from './CustomViewBaseModel';
|
// import CustomViewBaseModel from './CustomViewBaseModel';
|
||||||
// import ModelSearchable from './ModelSearchable';
|
// import ModelSearchable from './ModelSearchable';
|
||||||
import { BaseModel } from '@/models/Model';
|
import { BaseModel } from '@/models/Model';
|
||||||
|
import { VendorCredit } from '../../VendorCredit/models/VendorCredit';
|
||||||
|
import { Bill } from '@/modules/Bills/models/Bill';
|
||||||
|
|
||||||
export class VendorCreditAppliedBill extends BaseModel {
|
export class VendorCreditAppliedBill extends BaseModel {
|
||||||
|
public amount!: number;
|
||||||
|
public billId!: number;
|
||||||
|
public vendorCreditId!: number;
|
||||||
|
|
||||||
|
public vendorCredit!: VendorCredit;
|
||||||
|
public bill!: Bill;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
@@ -16,7 +25,7 @@ export class VendorCreditAppliedBill extends BaseModel {
|
|||||||
/**
|
/**
|
||||||
* Timestamps columns.
|
* Timestamps columns.
|
||||||
*/
|
*/
|
||||||
get timestamps() {
|
public get timestamps() {
|
||||||
return ['created_at', 'updated_at'];
|
return ['created_at', 'updated_at'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { VendorCreditAppliedBillTransformer } from './VendorCreditAppliedBillTransformer';
|
||||||
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
|
import { VendorCreditAppliedBill } from '@/modules/VendorCreditsApplyBills/models/VendorCreditAppliedBill';
|
||||||
|
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetAppliedBillsToVendorCreditService {
|
||||||
|
constructor(
|
||||||
|
private readonly transformer: TransformerInjectable,
|
||||||
|
private readonly vendorCreditAppliedBillModel: typeof VendorCreditAppliedBill,
|
||||||
|
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get applied bills to vendor credit.
|
||||||
|
* @param {number} vendorCreditId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public getAppliedBills = async (vendorCreditId: number) => {
|
||||||
|
const vendorCredit = await this.vendorCreditModel
|
||||||
|
.query()
|
||||||
|
.findById(vendorCreditId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
const appliedToBills = await this.vendorCreditAppliedBillModel
|
||||||
|
.query()
|
||||||
|
.where('vendorCreditId', vendorCreditId)
|
||||||
|
.withGraphFetched('bill')
|
||||||
|
.withGraphFetched('vendorCredit');
|
||||||
|
|
||||||
|
// Transforms the models to POJO.
|
||||||
|
return this.transformer.transform(
|
||||||
|
appliedToBills,
|
||||||
|
new VendorCreditAppliedBillTransformer(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { VendorCreditToApplyBillTransformer } from './VendorCreditToApplyBillTransformer';
|
||||||
|
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
||||||
|
import { Bill } from '@/modules/Bills/models/Bill';
|
||||||
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetVendorCreditToApplyBills {
|
||||||
|
/**
|
||||||
|
* @param {TransformerService} transformerService - The transformer service.
|
||||||
|
* @param {typeof Bill} billModel - The bill model.
|
||||||
|
* @param {typeof VendorCredit} vendorCreditModel - The vendor credit model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly transformerService: TransformerInjectable,
|
||||||
|
@Inject(Bill.name) private readonly billModel: typeof Bill,
|
||||||
|
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve bills that valid apply to the given vendor credit.
|
||||||
|
* @param {number} vendorCreditId
|
||||||
|
* @returns {Promise<any[]>}
|
||||||
|
*/
|
||||||
|
public async getCreditToApplyBills(vendorCreditId: number) {
|
||||||
|
// Retrieve vendor credit or throw not found service error.
|
||||||
|
const vendorCredit = await this.vendorCreditModel
|
||||||
|
.query()
|
||||||
|
.findById(vendorCreditId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Retrieve open bills associated to the given vendor.
|
||||||
|
const openBills = await this.billModel
|
||||||
|
.query()
|
||||||
|
.where('vendor_id', vendorCredit.vendorId)
|
||||||
|
.modify('dueBills')
|
||||||
|
.modify('published');
|
||||||
|
|
||||||
|
// Transform the bills to POJO.
|
||||||
|
return this.transformerService.transform(
|
||||||
|
openBills,
|
||||||
|
new VendorCreditToApplyBillTransformer(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||||
|
import { VendorCreditAppliedBill } from "@/modules/VendorCreditsApplyBills/models/VendorCreditAppliedBill";
|
||||||
|
|
||||||
|
export class VendorCreditAppliedBillTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Includeded attributes.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formattedAmount',
|
||||||
|
'vendorCreditNumber',
|
||||||
|
'vendorCreditDate',
|
||||||
|
'billNumber',
|
||||||
|
'billReferenceNo',
|
||||||
|
'formattedVendorCreditDate',
|
||||||
|
'formattedBillDate',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude attributes.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
public excludeAttributes = (): string[] => {
|
||||||
|
return ['bill', 'vendorCredit'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (item: VendorCreditAppliedBill) => {
|
||||||
|
return this.formatNumber(item.amount, {
|
||||||
|
currencyCode: item.vendorCredit.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
protected vendorCreditNumber = (item) => {
|
||||||
|
return item.vendorCredit.vendorCreditNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected vendorCreditDate = (item) => {
|
||||||
|
return item.vendorCredit.vendorCreditDate;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected formattedVendorCreditDate = (item) => {
|
||||||
|
return this.formatDate(item.vendorCredit.vendorCreditDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected billNumber = (item) => {
|
||||||
|
return item.bill.billNo;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected billReferenceNo = (item) => {
|
||||||
|
return item.bill.referenceNo;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected BillDate = (item) => {
|
||||||
|
return item.bill.billDate;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected formattedBillDate = (item) => {
|
||||||
|
return this.formatDate(item.bill.billDate);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { Bill } from '@/modules/Bills/models/Bill';
|
||||||
|
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||||
|
|
||||||
|
export class VendorCreditToApplyBillTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'formattedBillDate',
|
||||||
|
'formattedDueDate',
|
||||||
|
'formattedAmount',
|
||||||
|
'formattedDueAmount',
|
||||||
|
'formattedPaymentAmount',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted bill date.
|
||||||
|
* @param {Bill} bill
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected formattedBillDate = (bill: Bill): string => {
|
||||||
|
return this.formatDate(bill.billDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted due date.
|
||||||
|
* @param {Bill} bill
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDueDate = (bill: Bill): string => {
|
||||||
|
return this.formatDate(bill.dueDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted bill amount.
|
||||||
|
* @param {Bill} bill
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedAmount = (bill: Bill): string => {
|
||||||
|
return this.formatNumber(bill.amount, {
|
||||||
|
currencyCode: bill.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted bill due amount.
|
||||||
|
* @param {Bill} bill
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedDueAmount = (bill: Bill): string => {
|
||||||
|
return this.formatNumber(bill.dueAmount, {
|
||||||
|
currencyCode: bill.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve formatted payment amount.
|
||||||
|
* @param {Bill} bill
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected formattedPaymentAmount = (bill: Bill): string => {
|
||||||
|
return this.formatNumber(bill.paymentAmount, {
|
||||||
|
currencyCode: bill.currencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectModel } from '@nestjs/objection';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import {
|
||||||
|
IVendorCreditApplyToBillDeletedPayload,
|
||||||
|
IVendorCreditApplyToBillsCreatedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import { ApplyVendorCreditSyncBillsService } from '../command/ApplyVendorCreditSyncBills.service';
|
||||||
|
import { VendorCreditApplyToBill } from '../models/VendorCreditApplyToBill';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class ApplyVendorCreditSyncBillsSubscriber {
|
||||||
|
constructor(
|
||||||
|
private readonly syncBillsWithVendorCredit: ApplyVendorCreditSyncBillsService,
|
||||||
|
@InjectModel(VendorCreditApplyToBill)
|
||||||
|
private readonly vendorCreditApplyToBillModel: typeof VendorCreditApplyToBill,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
*/
|
||||||
|
attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onApplyToInvoicesCreated,
|
||||||
|
this.incrementAppliedBillsOnceCreditCreated
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onApplyToInvoicesDeleted,
|
||||||
|
this.decrementAppliedBillsOnceCreditDeleted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment credited amount of applied bills once the vendor credit transaction created.
|
||||||
|
* @param {IVendorCreditApplyToBillsCreatedPayload} paylaod -
|
||||||
|
*/
|
||||||
|
private incrementAppliedBillsOnceCreditCreated = async ({
|
||||||
|
vendorCreditAppliedBills,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditApplyToBillsCreatedPayload) => {
|
||||||
|
await this.syncBillsWithVendorCredit.incrementBillsCreditedAmount(
|
||||||
|
vendorCreditAppliedBills,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement credited amount of applied bills once the vendor credit
|
||||||
|
* transaction delted.
|
||||||
|
* @param {IVendorCreditApplyToBillDeletedPayload} payload
|
||||||
|
*/
|
||||||
|
private decrementAppliedBillsOnceCreditDeleted = async ({
|
||||||
|
oldCreditAppliedToBill,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditApplyToBillDeletedPayload) => {
|
||||||
|
await this.syncBillsWithVendorCredit.decrementBillCreditedAmount(
|
||||||
|
oldCreditAppliedToBill,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import { sumBy } from 'lodash';
|
||||||
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
|
import ApplyVendorCreditSyncInvoiced from '../command/ApplyVendorCreditSyncInvoiced.service';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import {
|
||||||
|
IVendorCreditApplyToBillDeletedPayload,
|
||||||
|
IVendorCreditApplyToBillsCreatedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class ApplyVendorCreditSyncInvoicedSubscriber {
|
||||||
|
@Inject()
|
||||||
|
tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
syncCreditWithInvoiced: ApplyVendorCreditSyncInvoiced;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches events with handlers.
|
||||||
|
*/
|
||||||
|
attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onApplyToInvoicesCreated,
|
||||||
|
this.incrementBillInvoicedOnceCreditApplied
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onApplyToInvoicesDeleted,
|
||||||
|
this.decrementBillInvoicedOnceCreditApplyDeleted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment vendor credit invoiced amount once the apply transaction created.
|
||||||
|
* @param {IVendorCreditApplyToBillsCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
private incrementBillInvoicedOnceCreditApplied = async ({
|
||||||
|
vendorCredit,
|
||||||
|
tenantId,
|
||||||
|
vendorCreditAppliedBills,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditApplyToBillsCreatedPayload) => {
|
||||||
|
const amount = sumBy(vendorCreditAppliedBills, 'amount');
|
||||||
|
|
||||||
|
await this.syncCreditWithInvoiced.incrementVendorCreditInvoicedAmount(
|
||||||
|
tenantId,
|
||||||
|
vendorCredit.id,
|
||||||
|
amount,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement vendor credit invoiced amount once the apply transaction deleted.
|
||||||
|
* @param {IVendorCreditApplyToBillDeletedPayload} payload -
|
||||||
|
*/
|
||||||
|
private decrementBillInvoicedOnceCreditApplyDeleted = async ({
|
||||||
|
tenantId,
|
||||||
|
vendorCredit,
|
||||||
|
oldCreditAppliedToBill,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditApplyToBillDeletedPayload) => {
|
||||||
|
await this.syncCreditWithInvoiced.decrementVendorCreditInvoicedAmount(
|
||||||
|
tenantId,
|
||||||
|
oldCreditAppliedToBill.vendorCreditId,
|
||||||
|
oldCreditAppliedToBill.amount,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToBillsCreatedPayload {
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
vendorCreditAppliedBills: IVendorCreditAppliedBill[];
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToBillsCreatingPayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToBillsCreatePayload {
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToBillDeletedPayload {
|
||||||
|
vendorCredit: VendorCredit;
|
||||||
|
oldCreditAppliedToBill: IVendorCreditAppliedBill;
|
||||||
|
trx: Knex.Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToInvoiceDTO {
|
||||||
|
amount: number;
|
||||||
|
billId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToInvoicesDTO {
|
||||||
|
entries: IVendorCreditApplyToInvoiceDTO[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToInvoiceModel {
|
||||||
|
billId: number;
|
||||||
|
amount: number;
|
||||||
|
vendorCreditId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditApplyToInvoicesModel {
|
||||||
|
entries: IVendorCreditApplyToInvoiceModel[];
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVendorCreditAppliedBill {
|
||||||
|
billId: number;
|
||||||
|
amount: number;
|
||||||
|
vendorCreditId: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import {
|
||||||
|
IRefundVendorCredit,
|
||||||
|
IRefundVendorCreditCreatedPayload,
|
||||||
|
IRefundVendorCreditCreatingPayload,
|
||||||
|
IRefundVendorCreditDTO,
|
||||||
|
IVendorCreditCreatePayload,
|
||||||
|
} from '@/modules/VendorCredit/types/VendorCredit.types';
|
||||||
|
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||||
|
import { VendorCredit } from '../../models/VendorCredit';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { RefundVendorCredit } from '../../models/RefundVendorCredit';
|
||||||
|
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreateRefundVendorCredit {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
private readonly branchDTOTransform: BranchTransactionDTOTransformer,
|
||||||
|
|
||||||
|
@Inject(RefundVendorCredit.name)
|
||||||
|
private readonly refundVendorCreditModel: typeof RefundVendorCredit,
|
||||||
|
|
||||||
|
@Inject(Account.name)
|
||||||
|
private readonly accountModel: typeof Account,
|
||||||
|
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private readonly vendorCreditModel: typeof VendorCredit,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a refund vendor credit.
|
||||||
|
* @param {number} vendorCreditId
|
||||||
|
* @param {IRefundVendorCreditDTO} refundVendorCreditDTO
|
||||||
|
* @returns {Promise<IRefundVendorCredit>}
|
||||||
|
*/
|
||||||
|
public createRefund = async (
|
||||||
|
vendorCreditId: number,
|
||||||
|
refundVendorCreditDTO: IRefundVendorCreditDTO
|
||||||
|
): Promise<IRefundVendorCredit> => {
|
||||||
|
// Retrieve the vendor credit or throw not found service error.
|
||||||
|
const vendorCredit = await this.vendorCreditModel.query()
|
||||||
|
.findById(vendorCreditId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Retrieve the deposit account or throw not found service error.
|
||||||
|
const depositAccount = await this.accountModel.query()
|
||||||
|
.findById(refundVendorCreditDTO.depositAccountId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Validate vendor credit has remaining credit.
|
||||||
|
this.validateVendorCreditRemainingCredit(
|
||||||
|
vendorCredit,
|
||||||
|
refundVendorCreditDTO.amount
|
||||||
|
);
|
||||||
|
// Validate refund deposit account type.
|
||||||
|
this.validateRefundDepositAccountType(depositAccount);
|
||||||
|
|
||||||
|
// Triggers `onVendorCreditRefundCreate` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.vendorCredit.onRefundCreate, {
|
||||||
|
vendorCreditId,
|
||||||
|
refundVendorCreditDTO,
|
||||||
|
} as IVendorCreditCreatePayload);
|
||||||
|
|
||||||
|
const refundCreditObj = this.transformDTOToModel(
|
||||||
|
vendorCredit,
|
||||||
|
refundVendorCreditDTO
|
||||||
|
);
|
||||||
|
// Saves refund vendor credit with associated transactions.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
const eventPayload = {
|
||||||
|
vendorCredit,
|
||||||
|
trx,
|
||||||
|
refundVendorCreditDTO,
|
||||||
|
} as IRefundVendorCreditCreatingPayload;
|
||||||
|
|
||||||
|
// Triggers `onVendorCreditRefundCreating` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.vendorCredit.onRefundCreating,
|
||||||
|
eventPayload as IRefundVendorCreditCreatingPayload
|
||||||
|
);
|
||||||
|
// Inserts refund vendor credit to the storage layer.
|
||||||
|
const refundVendorCredit =
|
||||||
|
await this.refundVendorCreditModel.query().insertAndFetch(refundCreditObj);
|
||||||
|
|
||||||
|
// Triggers `onVendorCreditCreated` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.vendorCredit.onRefundCreated, {
|
||||||
|
...eventPayload,
|
||||||
|
refundVendorCredit,
|
||||||
|
} as IRefundVendorCreditCreatedPayload);
|
||||||
|
|
||||||
|
return refundVendorCredit;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes the refund DTO to refund vendor credit model.
|
||||||
|
* @param {IVendorCredit} vendorCredit -
|
||||||
|
* @param {IRefundVendorCreditDTO} vendorCreditDTO
|
||||||
|
* @returns {IRefundVendorCredit}
|
||||||
|
*/
|
||||||
|
public transformDTOToModel = (
|
||||||
|
vendorCredit: VendorCredit,
|
||||||
|
vendorCreditDTO: IRefundVendorCreditDTO
|
||||||
|
) => {
|
||||||
|
const initialDTO = {
|
||||||
|
vendorCreditId: vendorCredit.id,
|
||||||
|
...vendorCreditDTO,
|
||||||
|
currencyCode: vendorCredit.currencyCode,
|
||||||
|
exchangeRate: vendorCreditDTO.exchangeRate || 1,
|
||||||
|
};
|
||||||
|
return R.compose(this.branchDTOTransform.transformDTO())(
|
||||||
|
initialDTO
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
IRefundVendorCreditDeletedPayload,
|
||||||
|
IRefundVendorCreditDeletePayload,
|
||||||
|
IRefundVendorCreditDeletingPayload,
|
||||||
|
} from '@/modules/VendorCredit/types/VendorCredit.types';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { RefundVendorCredit } from '../../models/RefundVendorCredit';
|
||||||
|
import { RefundVendorCreditService } from './RefundVendorCredit.service';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeleteRefundVendorCreditService {
|
||||||
|
/**
|
||||||
|
* @param {UnitOfWork} uow - Unit of work service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - Event emitter service.
|
||||||
|
* @param {typeof RefundVendorCredit} refundVendorCreditModel - Refund vendor credit model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(RefundVendorCredit.name)
|
||||||
|
private readonly refundVendorCreditModel: typeof RefundVendorCredit,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the refund vendor credit.
|
||||||
|
* @param {number} refundCreditId - Refund credit id.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async deleteRefundVendorCreditRefund(
|
||||||
|
refundCreditId: number
|
||||||
|
): Promise<void> {
|
||||||
|
// Retrieve the old credit note or throw not found service error.
|
||||||
|
const oldRefundCredit = await this.getRefundVendorCreditOrThrowError(
|
||||||
|
refundCreditId
|
||||||
|
);
|
||||||
|
// Triggers `onVendorCreditRefundDelete` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.vendorCredit.onRefundDelete, {
|
||||||
|
refundCreditId,
|
||||||
|
oldRefundCredit,
|
||||||
|
} as IRefundVendorCreditDeletePayload);
|
||||||
|
|
||||||
|
// Deletes the refund vendor credit under unit-of-work environment.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
const eventPayload = {
|
||||||
|
trx,
|
||||||
|
refundCreditId,
|
||||||
|
oldRefundCredit,
|
||||||
|
} as IRefundVendorCreditDeletingPayload;
|
||||||
|
|
||||||
|
// Triggers `onVendorCreditRefundDeleting` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.vendorCredit.onRefundDeleting,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
// Deletes the refund vendor credit graph from the storage.
|
||||||
|
await this.refundVendorCreditModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(refundCreditId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Triggers `onVendorCreditRefundDeleted` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.vendorCredit.onRefundDeleted,
|
||||||
|
eventPayload as IRefundVendorCreditDeletedPayload
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Transaction } from 'objection';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { VendorCredit } from '@/modules/VendorCredit/models/VendorCredit';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RefundSyncCreditRefundedAmount {
|
||||||
|
/**
|
||||||
|
* @param {typeof VendorCredit} vendorCreditModel - Vendor credit model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
@Inject(VendorCredit.name)
|
||||||
|
private vendorCreditModel: typeof VendorCredit,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment vendor credit refunded amount.
|
||||||
|
* @param {number} vendorCreditId - Vendor credit id.
|
||||||
|
* @param {number} amount - Amount.
|
||||||
|
* @param {Transaction} trx - Objection transaction.
|
||||||
|
*/
|
||||||
|
public incrementCreditRefundedAmount = async (
|
||||||
|
vendorCreditId: number,
|
||||||
|
amount: number,
|
||||||
|
trx?: Transaction,
|
||||||
|
): Promise<void> => {
|
||||||
|
await this.vendorCreditModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(vendorCreditId)
|
||||||
|
.increment('refundedAmount', amount);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrement vendor credit refunded amount.
|
||||||
|
* @param {number} vendorCreditId - Vendor credit id.
|
||||||
|
* @param {number} amount - Amount.
|
||||||
|
* @param {Transaction} trx - Objection transaction.
|
||||||
|
*/
|
||||||
|
public decrementCreditNoteRefundAmount = async (
|
||||||
|
vendorCreditId: number,
|
||||||
|
amount: number,
|
||||||
|
trx?: Transaction,
|
||||||
|
): Promise<void> => {
|
||||||
|
await this.vendorCreditModel
|
||||||
|
.query(trx)
|
||||||
|
.findById(vendorCreditId)
|
||||||
|
.decrement('refundedAmount', amount);
|
||||||
|
};
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user