lbx-invoice
Version:
Provides functionality around generating invoices.
204 lines • 9.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseInvoiceNumberService = void 0;
const tslib_1 = require("tslib");
const core_1 = require("@loopback/core");
const rest_1 = require("@loopback/rest");
const keys_1 = require("../keys");
const repositories_1 = require("../repositories");
/**
* Handles generating unique and consecutive numbers for invoices.
*/
let BaseInvoiceNumberService = class BaseInvoiceNumberService {
constructor(invoiceRepository, numberInvoicesRepository) {
this.invoiceRepository = invoiceRepository;
this.numberInvoicesRepository = numberInvoicesRepository;
/**
* The number of digits used to generate the consecutive number.
* @default 4
*/
this.NUMBER_OF_DIGITS = 4;
/**
* The separator for the parts of the invoice number.
* @default '-'
*/
this.SEPARATOR = '-';
/**
* How many characters of the company name should be used in the invoice number.
* @default 6
*/
this.NUMBER_COMPANY_ABBREVIATION_CHARACTERS = 6;
/**
* How many characters of the private customer first and last name should be used in the invoice number.
* @default 3
*/
this.NUMBER_PRIVATE_CUSTOMER_ABBREVIATION_CHARACTERS = 3;
}
/**
* Generates a new invoice number.
* @param recipientId - The id of the recipient of the invoice.
* @param invoiceAddress - The address data of the customer.
* @param transaction - An optional transaction from outside to make sure any changes only apply when the transaction is committed.
* @param nameAbbreviation - An optional name abbreviation if you don't want to generate one.
* @returns A promise of the new invoice number.
*/
async generateInvoiceNumber(recipientId, invoiceAddress, transaction, nameAbbreviation) {
const currentYear = `${new Date().getFullYear()}`;
const customerNameAbbreviation = nameAbbreviation !== null && nameAbbreviation !== void 0 ? nameAbbreviation : this.getCustomerNameAbbreviation(invoiceAddress);
const consecutiveNumber = await this.getConsecutiveNumber(recipientId, transaction);
const result = `${currentYear}${this.SEPARATOR}${customerNameAbbreviation}${this.SEPARATOR}${consecutiveNumber}`;
await this.validateInvoiceNumber(result);
return result;
}
/**
* Generates a temporary invoice number with the prefix temp.
* This does not increase the number of invoices which can be helpful if you create an invoice that might not be sent out.
* @param recipientId - The id of the recipient of the invoice.
* @param invoiceAddress - The address data of the customer.
* @param nameAbbreviation - An optional name abbreviation if you don't want to generate one.
* @returns A promise of the new temporary invoice number.
*/
async generateTemporaryInvoiceNumber(recipientId, invoiceAddress, nameAbbreviation) {
const currentYear = `${new Date().getFullYear()}`;
const customerNameAbbreviation = nameAbbreviation !== null && nameAbbreviation !== void 0 ? nameAbbreviation : this.getCustomerNameAbbreviation(invoiceAddress);
const consecutiveNumber = await this.getTemporaryConsecutiveNumber(recipientId);
// eslint-disable-next-line stylistic/max-len
const result = `TEMP${this.SEPARATOR}${currentYear}${this.SEPARATOR}${customerNameAbbreviation}${this.SEPARATOR}${consecutiveNumber}`;
if (!await this.invoiceRepository.findOne({ where: { number: result } })) {
return result;
}
let suffix = 2;
while (await this.invoiceRepository.findOne({ where: { number: `${result}${this.SEPARATOR}${suffix}` } })) {
suffix++;
}
return `${result}${this.SEPARATOR}${suffix}`;
}
/**
* Gets the temporary consecutive number of the invoices in this year and for the provided recipientId.
* Prefixes it with zeroes until the result has the same length as this.NUMBER_OF_DIGITS.
* @param recipientId - The id of the recipient of the invoice.
* @returns The number of invoices over a year filled up with zeroes to match this.NUMBER_OF_DIGITS.
*/
async getTemporaryConsecutiveNumber(recipientId) {
const currentYear = new Date(Date.now()).getFullYear();
let numberOfInvoices = await this.numberInvoicesRepository.findOne({
where: {
year: currentYear,
recipientId: recipientId
}
});
if (numberOfInvoices) {
numberOfInvoices.number++;
}
else {
numberOfInvoices = {
number: 1,
year: currentYear,
recipientId: recipientId
};
}
const consecutiveNumber = `${numberOfInvoices.number}`;
if (consecutiveNumber.length > this.NUMBER_OF_DIGITS) {
return consecutiveNumber;
}
const difference = this.NUMBER_OF_DIGITS - consecutiveNumber.length;
let prefix = '';
for (let i = 0; i < difference; i++) {
prefix = `${prefix}0`;
}
return `${prefix}${consecutiveNumber}`;
}
/**
* Gets the consecutive number of the invoices in this year.
* Prefixes it with zeroes until the result has the same length as this.NUMBER_OF_DIGITS.
* @param recipientId - The id of the recipient of the invoice.
* @param transaction - An optional transaction from outside to make sure any changes only apply when the transaction is committed.
* @returns The number of invoices over a year filled up with zeroes to match this.NUMBER_OF_DIGITS.
*/
async getConsecutiveNumber(recipientId, transaction) {
const currentYear = new Date(Date.now()).getFullYear();
let numberOfInvoices = await this.numberInvoicesRepository.findOne({
where: {
year: currentYear,
recipientId: recipientId
}
});
if (numberOfInvoices) {
numberOfInvoices.number++;
await this.numberInvoicesRepository.updateById(numberOfInvoices.id, numberOfInvoices, { transaction: transaction });
}
else {
numberOfInvoices = await this.numberInvoicesRepository.create({
number: 1,
year: currentYear,
recipientId: recipientId
}, { transaction: transaction });
}
const consecutiveNumber = `${numberOfInvoices.number}`;
if (consecutiveNumber.length > this.NUMBER_OF_DIGITS) {
return consecutiveNumber;
}
const difference = this.NUMBER_OF_DIGITS - consecutiveNumber.length;
let prefix = '';
for (let i = 0; i < difference; i++) {
prefix = `${prefix}0`;
}
return `${prefix}${consecutiveNumber}`;
}
/**
* Gets the customer name abbreviation.
* @param invoiceAddress - The address data to get the name abbreviation from.
* @returns The first char of first and last name for private customers
* and the first two chars of the company name for company customers.
*/
getCustomerNameAbbreviation(invoiceAddress) {
if (invoiceAddress.company && invoiceAddress.companyName) {
return this.getCompanyNameAbbreviation(invoiceAddress.companyName);
}
return this.getPrivateCustomerAbbreviation(invoiceAddress);
}
getPrivateCustomerAbbreviation(invoiceAddress) {
let res = '';
for (let i = 0; i < this.NUMBER_PRIVATE_CUSTOMER_ABBREVIATION_CHARACTERS; i++) {
if (invoiceAddress.firstName[i]) {
res += invoiceAddress.firstName[i];
}
}
for (let i = 0; i < this.NUMBER_PRIVATE_CUSTOMER_ABBREVIATION_CHARACTERS; i++) {
if (invoiceAddress.lastName[i]) {
res += invoiceAddress.lastName[i];
}
}
return res.toUpperCase();
}
getCompanyNameAbbreviation(companyName) {
companyName = companyName.replaceAll(/\s/g, '');
companyName = companyName.replaceAll(/[^\da-z]/gi, '');
let res = '';
for (let i = 0; i < this.NUMBER_COMPANY_ABBREVIATION_CHARACTERS; i++) {
if (companyName[i]) {
res += companyName[i];
}
}
return res.toUpperCase();
}
// TODO: Is this still needed?
/**
* Validates the given invoice number.
* @param invoiceNumber - The invoice number to validate.
*/
async validateInvoiceNumber(invoiceNumber) {
if (await this.invoiceRepository.findOne({ where: { number: invoiceNumber } })) {
throw new rest_1.HttpErrors.Conflict(`The generated invoice-number ${invoiceNumber} already exists!`);
}
}
};
exports.BaseInvoiceNumberService = BaseInvoiceNumberService;
exports.BaseInvoiceNumberService = BaseInvoiceNumberService = tslib_1.__decorate([
(0, core_1.injectable)({ scope: core_1.BindingScope.TRANSIENT }),
tslib_1.__param(0, (0, core_1.inject)(keys_1.LbxInvoiceBindings.INVOICE_REPOSITORY)),
tslib_1.__param(1, (0, core_1.inject)(keys_1.LbxInvoiceBindings.NUMBER_INVOICES_REPOSITORY)),
tslib_1.__metadata("design:paramtypes", [repositories_1.BaseInvoiceRepository,
repositories_1.NumberInvoicesRepository])
], BaseInvoiceNumberService);
//# sourceMappingURL=base-invoice-number.service.js.map