mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-11 10:20:30 +00:00
161 lines
4.8 KiB
TypeScript
161 lines
4.8 KiB
TypeScript
import { Service, Inject } from 'typedi';
|
|
import moment from 'moment';
|
|
import { omit } from 'lodash';
|
|
import * as R from 'ramda';
|
|
import composeAsync from 'async/compose';
|
|
import { ServiceError } from '@/exceptions';
|
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
|
import { ERRORS } from './constants';
|
|
import {
|
|
ICreditNote,
|
|
ICreditNoteEditDTO,
|
|
ICreditNoteEntryNewDTO,
|
|
ICreditNoteNewDTO,
|
|
} from '@/interfaces';
|
|
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
|
import AutoIncrementOrdersService from '@/services/Sales/AutoIncrementOrdersService';
|
|
import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
|
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
|
import { assocItemEntriesDefaultIndex } from '../Items/utils';
|
|
import { BrandingTemplateDTOTransformer } from '../PdfTemplate/BrandingTemplateDTOTransformer';
|
|
|
|
@Service()
|
|
export default class BaseCreditNotes {
|
|
@Inject()
|
|
private tenancy: HasTenancyService;
|
|
|
|
@Inject()
|
|
private itemsEntriesService: ItemsEntriesService;
|
|
|
|
@Inject()
|
|
private autoIncrementOrdersService: AutoIncrementOrdersService;
|
|
|
|
@Inject()
|
|
private branchDTOTransform: BranchTransactionDTOTransform;
|
|
|
|
@Inject()
|
|
private warehouseDTOTransform: WarehouseTransactionDTOTransform;
|
|
|
|
@Inject()
|
|
private brandingTemplatesTransformer: BrandingTemplateDTOTransformer;
|
|
|
|
/**
|
|
* Transformes the credit/edit DTO to model.
|
|
* @param {ICreditNoteNewDTO | ICreditNoteEditDTO} creditNoteDTO
|
|
* @param {string} customerCurrencyCode -
|
|
*/
|
|
protected transformCreateEditDTOToModel = async (
|
|
tenantId: number,
|
|
creditNoteDTO: ICreditNoteNewDTO | ICreditNoteEditDTO,
|
|
customerCurrencyCode: string,
|
|
oldCreditNote?: ICreditNote
|
|
): Promise<ICreditNote> => {
|
|
// 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.getNextCreditNumber(tenantId);
|
|
|
|
// 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(
|
|
tenantId,
|
|
'CreditNote'
|
|
)
|
|
)(initialDTO);
|
|
|
|
return R.compose(
|
|
this.branchDTOTransform.transformDTO<ICreditNote>(tenantId),
|
|
this.warehouseDTOTransform.transformDTO<ICreditNote>(tenantId)
|
|
)(initialAsyncDTO);
|
|
};
|
|
|
|
/**
|
|
* Retrieve the given credit note or throw not found service error.
|
|
* @param {number} tenantId -
|
|
* @param {number} creditNoteId -
|
|
*/
|
|
protected getCreditNoteOrThrowError = async (
|
|
tenantId: number,
|
|
creditNoteId: number
|
|
) => {
|
|
const { CreditNote } = this.tenancy.models(tenantId);
|
|
|
|
const creditNote = await CreditNote.query().findById(creditNoteId);
|
|
|
|
if (!creditNote) {
|
|
throw new ServiceError(ERRORS.CREDIT_NOTE_NOT_FOUND);
|
|
}
|
|
return creditNote;
|
|
};
|
|
|
|
/**
|
|
* Retrieve the next unique credit number.
|
|
* @param {number} tenantId - Tenant id.
|
|
* @return {string}
|
|
*/
|
|
private getNextCreditNumber = (tenantId: number): string => {
|
|
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
|
tenantId,
|
|
'credit_note'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Increment the credit note serial next number.
|
|
* @param {number} tenantId -
|
|
*/
|
|
public incrementSerialNumber = (tenantId: number) => {
|
|
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
|
tenantId,
|
|
'credit_note'
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Validate the credit note remaining amount.
|
|
* @param {ICreditNote} creditNote
|
|
* @param {number} amount
|
|
*/
|
|
public validateCreditRemainingAmount = (
|
|
creditNote: ICreditNote,
|
|
amount: number
|
|
) => {
|
|
if (creditNote.creditsRemaining < amount) {
|
|
throw new ServiceError(ERRORS.CREDIT_NOTE_HAS_NO_REMAINING_AMOUNT);
|
|
}
|
|
};
|
|
}
|