feat: auto-increment invoices.

This commit is contained in:
a.bouhuolia
2021-03-04 16:31:21 +02:00
parent 4f98db4c4b
commit 6d58767e9f
5 changed files with 183 additions and 32 deletions

View File

@@ -0,0 +1,71 @@
import { Service, Inject } from 'typedi';
import TenancyService from 'services/Tenancy/TenancyService';
import { transactionIncrement } from 'utils';
/**
* Auto increment orders service.
*/
@Service()
export default class AutoIncrementOrdersService {
@Inject()
tenancy: TenancyService;
/**
* Retrieve the next service transaction number.
* @param {number} tenantId
* @param {string} settingsGroup
* @param {Function} getMaxTransactionNo
* @return {Promise<[string, string]>}
*/
async getNextTransactionNumber(
tenantId: number,
settingsGroup: string,
getOrderTransaction: (prefix: string, number: string) => Promise<boolean>,
getMaxTransactionNumber: (prefix: string, number: string) => Promise<string>
): Promise<[string, string]> {
const settings = this.tenancy.settings(tenantId);
const group = settingsGroup;
// Settings service transaction number and prefix.
const settingNo = settings.get({ group, key: 'next_number' });
const settingPrefix = settings.get({ group, key: 'number_prefix' });
let nextInvoiceNumber = settingNo;
const orderTransaction = await getOrderTransaction(
settingPrefix,
settingNo
);
if (orderTransaction) {
// Retrieve the max invoice number in the given prefix.
const maxInvoiceNo = await getMaxTransactionNumber(
settingPrefix,
settingNo
);
if (maxInvoiceNo) {
nextInvoiceNumber = transactionIncrement(maxInvoiceNo);
}
}
return [settingPrefix, nextInvoiceNumber];
}
/**
* Increment setting next number.
* @param {number} tenantId -
* @param {string} orderGroup - Order group.
* @param {string} orderNumber -Order number.
*/
async incrementSettingsNextNumber(
tenantId,
orderGroup: string,
orderNumber: string
) {
const settings = this.tenancy.settings(tenantId);
settings.set(
{ group: orderGroup, key: 'next_number' },
transactionIncrement(orderNumber)
);
await settings.save();
}
}

View File

@@ -1,5 +1,5 @@
import { Service, Inject } from 'typedi';
import { omit, sumBy } from 'lodash';
import { omit, sumBy, join } from 'lodash';
import moment from 'moment';
import {
EventDispatcher,
@@ -21,14 +21,15 @@ import JournalCommands from 'services/Accounting/JournalCommands';
import events from 'subscribers/events';
import InventoryService from 'services/Inventory/Inventory';
import TenancyService from 'services/Tenancy/TenancyService';
import DynamicListingService from 'services/DynamicListing/DynamicListService';
import { formatDateFields } from 'utils';
import DynamicListingService from 'services/DynamicListing/DynamicListService';
import { ServiceError } from 'exceptions';
import ItemsService from 'services/Items/ItemsService';
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
import CustomersService from 'services/Contacts/CustomersService';
import SaleEstimateService from 'services/Sales/SalesEstimate';
import JournalPosterService from './JournalPosterService';
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
import { ERRORS } from './constants';
/**
@@ -67,6 +68,9 @@ export default class SaleInvoicesService {
@Inject()
journalService: JournalPosterService;
@Inject()
autoIncrementOrdersService: AutoIncrementOrdersService;
/**
* Validate whether sale invoice number unqiue on the storage.
*/
@@ -153,6 +157,33 @@ export default class SaleInvoicesService {
return saleInvoice;
}
/**
* Retrieve the next unique invoice number.
* @param {number} tenantId - Tenant id.
* @return {string}
*/
async getNextInvoiceNumber(tenantId: number): Promise<[string, string]> {
const { SaleInvoice } = this.tenancy.models(tenantId);
// Retrieve the max invoice number in the given prefix.
const getMaxInvoicesNo = (prefix, number) => {
return SaleInvoice.query()
.modify('maxInvoiceNo', prefix, number)
.then((res) => res?.invNumber);
};
// Retrieve the order transaction number by number.
const getTransactionNumber = (prefix, number) => {
return SaleInvoice.query().modify('byPrefixAndNumber', prefix, number);
};
return this.autoIncrementOrdersService.getNextTransactionNumber(
tenantId,
'sales_invoices',
getTransactionNumber,
getMaxInvoicesNo
);
}
/**
* Transform DTO object to model object.
* @param {number} tenantId - Tenant id.
@@ -161,7 +192,8 @@ export default class SaleInvoicesService {
transformDTOToModel(
tenantId: number,
saleInvoiceDTO: ISaleInvoiceCreateDTO | ISaleInvoiceEditDTO,
oldSaleInvoice?: ISaleInvoice
oldSaleInvoice?: ISaleInvoice,
autoNextNumber?: [string, string] // prefix, number
): ISaleInvoice {
const { ItemEntry } = this.tenancy.models(tenantId);
const balance = sumBy(saleInvoiceDTO.entries, (e) =>
@@ -180,6 +212,13 @@ export default class SaleInvoicesService {
}),
balance,
paymentAmount: 0,
...(saleInvoiceDTO.invoiceNo || autoNextNumber
? {
invoiceNo: saleInvoiceDTO.invoiceNo
? saleInvoiceDTO.invoiceNo
: join(autoNextNumber, ''),
}
: {}),
entries: saleInvoiceDTO.entries.map((entry) => ({
referenceType: 'SaleInvoice',
...omit(entry, ['amount', 'id']),
@@ -202,9 +241,18 @@ export default class SaleInvoicesService {
): Promise<ISaleInvoice> {
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
// Transform DTO object to model object.
const saleInvoiceObj = this.transformDTOToModel(tenantId, saleInvoiceDTO);
// The next invoice number automattically or manually.
const autoNextNumber = !saleInvoiceDTO.invoiceNo
? await this.getNextInvoiceNumber(tenantId)
: null;
// Transform DTO object to model object.
const saleInvoiceObj = this.transformDTOToModel(
tenantId,
saleInvoiceDTO,
null,
autoNextNumber
);
// Validate customer existance.
await this.customersService.getCustomerByIdOrThrowError(
tenantId,
@@ -248,6 +296,7 @@ export default class SaleInvoicesService {
saleInvoiceDTO,
saleInvoiceId: saleInvoice.id,
authorizedUser,
autoNextNumber,
});
this.logger.info('[sale_invoice] successfully inserted.', {
tenantId,