mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
Merge branch 'master' of https://github.com/abouolia/Bigcapital
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Request, Response, Router, NextFunction } from 'express';
|
||||
import asyncMiddleware from 'api/middleware/asyncMiddleware';
|
||||
import BaseController from 'api/controllers/BaseController';
|
||||
|
||||
@@ -17,33 +17,32 @@ export default class CurrenciesController extends BaseController {
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get('/', [
|
||||
...this.listSchema,
|
||||
],
|
||||
router.get(
|
||||
'/',
|
||||
[...this.listSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.all.bind(this))
|
||||
);
|
||||
router.post('/', [
|
||||
...this.currencyDTOSchemaValidation,
|
||||
],
|
||||
router.post(
|
||||
'/',
|
||||
[...this.currencyDTOSchemaValidation],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.newCurrency.bind(this)),
|
||||
this.handlerServiceError,
|
||||
this.handlerServiceError
|
||||
);
|
||||
router.post('/:id', [
|
||||
...this.currencyIdParamSchema,
|
||||
...this.currencyEditDTOSchemaValidation
|
||||
],
|
||||
router.post(
|
||||
'/:id',
|
||||
[...this.currencyIdParamSchema, ...this.currencyEditDTOSchemaValidation],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.editCurrency.bind(this)),
|
||||
this.handlerServiceError,
|
||||
this.handlerServiceError
|
||||
);
|
||||
router.delete('/:currency_code', [
|
||||
...this.currencyParamSchema,
|
||||
],
|
||||
router.delete(
|
||||
'/:currency_code',
|
||||
[...this.currencyParamSchema],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.deleteCurrency.bind(this)),
|
||||
this.handlerServiceError,
|
||||
this.handlerServiceError
|
||||
);
|
||||
return router;
|
||||
}
|
||||
@@ -56,21 +55,15 @@ export default class CurrenciesController extends BaseController {
|
||||
}
|
||||
|
||||
get currencyEditDTOSchemaValidation(): ValidationChain[] {
|
||||
return [
|
||||
check('currency_name').exists().trim().escape(),
|
||||
];
|
||||
return [check('currency_name').exists().trim().escape()];
|
||||
}
|
||||
|
||||
get currencyIdParamSchema(): ValidationChain[] {
|
||||
return [
|
||||
param('id').exists().isNumeric().toInt(),
|
||||
];
|
||||
return [param('id').exists().isNumeric().toInt()];
|
||||
}
|
||||
|
||||
|
||||
get currencyParamSchema(): ValidationChain[] {
|
||||
return [
|
||||
param('currency_code').exists().trim().escape(),
|
||||
];
|
||||
return [param('currency_code').exists().trim().escape()];
|
||||
}
|
||||
|
||||
get listSchema(): ValidationChain[] {
|
||||
@@ -82,16 +75,16 @@ export default class CurrenciesController extends BaseController {
|
||||
|
||||
/**
|
||||
* Retrieve all registered currency details.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async all(req: Request, res: Response, next: NextFunction) {
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
const currencies = await this.currenciesService.listCurrencies(tenantId);
|
||||
return res.status(200).send({ currencies: [ ...currencies, ] });
|
||||
return res.status(200).send({ currencies: [...currencies] });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -99,9 +92,9 @@ export default class CurrenciesController extends BaseController {
|
||||
|
||||
/**
|
||||
* Creates a new currency on the storage.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async newCurrency(req: Request, res: Response, next: Function) {
|
||||
const { tenantId } = req;
|
||||
@@ -121,13 +114,13 @@ export default class CurrenciesController extends BaseController {
|
||||
|
||||
/**
|
||||
* Edits details of the given currency.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async deleteCurrency(req: Request, res: Response, next: Function) {
|
||||
const { tenantId } = req;
|
||||
const { currency_code: currencyCode } = req.params;
|
||||
const { currency_code: currencyCode } = req.params;
|
||||
|
||||
try {
|
||||
await this.currenciesService.deleteCurrency(tenantId, currencyCode);
|
||||
@@ -142,9 +135,9 @@ export default class CurrenciesController extends BaseController {
|
||||
|
||||
/**
|
||||
* Deletes the currency.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
async editCurrency(req: Request, res: Response, next: Function) {
|
||||
const { tenantId } = req;
|
||||
@@ -152,7 +145,11 @@ export default class CurrenciesController extends BaseController {
|
||||
const { body: editCurrencyDTO } = req;
|
||||
|
||||
try {
|
||||
const currency = await this.currenciesService.editCurrency(tenantId, currencyId, editCurrencyDTO);
|
||||
const currency = await this.currenciesService.editCurrency(
|
||||
tenantId,
|
||||
currencyId,
|
||||
editCurrencyDTO
|
||||
);
|
||||
return res.status(200).send({
|
||||
currency_code: currency.currencyCode,
|
||||
message: 'The currency has been edited successfully.',
|
||||
@@ -164,24 +161,29 @@ export default class CurrenciesController extends BaseController {
|
||||
|
||||
/**
|
||||
* Handles currencies service error.
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
handlerServiceError(error: Error, req: Request, res: Response, next: NextFunction) {
|
||||
handlerServiceError(
|
||||
error: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
if (error instanceof ServiceError) {
|
||||
if (error.errorType === 'currency_not_found') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CURRENCY_NOT_FOUND', code: 100, }],
|
||||
errors: [{ type: 'CURRENCY_NOT_FOUND', code: 100 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'currency_code_exists') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'CURRENCY_CODE_EXISTS', code: 200, }],
|
||||
errors: [{ type: 'CURRENCY_CODE_EXISTS', code: 200 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -444,6 +444,11 @@ export default class PaymentReceivesController extends BaseController {
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'PAYMENT_RECEIVE_NO_IS_REQUIRED') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'PAYMENT_RECEIVE_NO_IS_REQUIRED', code: 1100 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ export default class SalesEstimatesController extends BaseController {
|
||||
check('estimate_date').exists().isISO8601(),
|
||||
check('expiration_date').optional().isISO8601(),
|
||||
check('reference').optional(),
|
||||
check('estimate_number').exists().trim().escape(),
|
||||
check('estimate_number').optional().trim().escape(),
|
||||
check('delivered').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('entries').exists().isArray({ min: 1 }),
|
||||
@@ -401,6 +401,11 @@ export default class SalesEstimatesController extends BaseController {
|
||||
errors: [{ type: 'CUSTOMER_NOT_FOUND', code: 1300 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'SALE_ESTIMATE_NO_IS_REQUIRED') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'SALE_ESTIMATE_NO_IS_REQUIRED', code: 1400 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -432,6 +432,13 @@ export default class SaleInvoicesController extends BaseController {
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'SALE_INVOICE_NO_IS_REQUIRED') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [
|
||||
{ type: 'SALE_INVOICE_NO_IS_REQUIRED', code: 1500 },
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface ISaleEstimate {
|
||||
amount: number,
|
||||
customerId: number,
|
||||
estimateDate: Date,
|
||||
estimateNumber: string,
|
||||
reference: string,
|
||||
note: string,
|
||||
termsConditions: string,
|
||||
@@ -19,8 +20,8 @@ export interface ISaleEstimate {
|
||||
export interface ISaleEstimateDTO {
|
||||
customerId: number,
|
||||
estimateDate?: Date,
|
||||
reference: string,
|
||||
estimateNumber: string,
|
||||
reference?: string,
|
||||
estimateNumber?: string,
|
||||
entries: IItemEntry[],
|
||||
note: string,
|
||||
termsConditions: string,
|
||||
|
||||
@@ -22,7 +22,8 @@ export interface ISaleReceiptDTO {
|
||||
depositAccountId: number;
|
||||
receiptDate: Date;
|
||||
sendToEmail: string;
|
||||
referenceNo: string;
|
||||
referenceNo?: string;
|
||||
receiptNumber?: string,
|
||||
receiptMessage: string;
|
||||
statement: string;
|
||||
closed: boolean;
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { Model } from 'objection';
|
||||
import { omit, isEmpty } from 'lodash';
|
||||
import {
|
||||
IMetadata,
|
||||
IMetaQuery,
|
||||
IMetableStore,
|
||||
} from 'interfaces';
|
||||
import { IMetadata, IMetaQuery, IMetableStore } from 'interfaces';
|
||||
import { itemsStartWith } from 'utils';
|
||||
|
||||
export default class MetableStore implements IMetableStore{
|
||||
export default class MetableStore implements IMetableStore {
|
||||
metadata: IMetadata[];
|
||||
model: Model;
|
||||
extraColumns: string[];
|
||||
@@ -31,18 +27,19 @@ export default class MetableStore implements IMetableStore{
|
||||
|
||||
/**
|
||||
* Find the given metadata key.
|
||||
* @param {string|IMetaQuery} query -
|
||||
* @returns {IMetadata} - Metadata object.
|
||||
* @param {string|IMetaQuery} query -
|
||||
* @returns {IMetadata} - Metadata object.
|
||||
*/
|
||||
find(query: string|IMetaQuery): IMetadata {
|
||||
find(query: string | IMetaQuery): IMetadata {
|
||||
const { key, value, ...extraColumns } = this.parseQuery(query);
|
||||
|
||||
return this.metadata.find((meta: IMetadata) => {
|
||||
const isSameKey = meta.key === key;
|
||||
const sameExtraColumns = this.extraColumns
|
||||
.some((extraColumn: string) => extraColumns[extraColumn] === meta[extraColumn]);
|
||||
const sameExtraColumns = this.extraColumns.some(
|
||||
(extraColumn: string) => extraColumns[extraColumn] === meta[extraColumn]
|
||||
);
|
||||
|
||||
const isSameExtraColumns = (sameExtraColumns || isEmpty(extraColumns));
|
||||
const isSameExtraColumns = sameExtraColumns || isEmpty(extraColumns);
|
||||
|
||||
return isSameKey && isSameExtraColumns;
|
||||
});
|
||||
@@ -55,10 +52,9 @@ export default class MetableStore implements IMetableStore{
|
||||
all(): IMetadata[] {
|
||||
return this.metadata
|
||||
.filter((meta: IMetadata) => !meta._markAsDeleted)
|
||||
.map((meta: IMetadata) => omit(
|
||||
meta,
|
||||
itemsStartWith(Object.keys(meta), '_')
|
||||
));
|
||||
.map((meta: IMetadata) =>
|
||||
omit(meta, itemsStartWith(Object.keys(meta), '_'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,16 +62,20 @@ export default class MetableStore implements IMetableStore{
|
||||
* @param {String} key -
|
||||
* @param {Mixied} defaultValue -
|
||||
*/
|
||||
get(query: string|IMetaQuery, defaultValue: any): any|false {
|
||||
get(query: string | IMetaQuery, defaultValue: any): any | false {
|
||||
const metadata = this.find(query);
|
||||
return metadata ? metadata.value : defaultValue || false;
|
||||
return metadata
|
||||
? metadata.value
|
||||
: typeof defaultValue !== 'undefined'
|
||||
? defaultValue
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Markes the metadata to should be deleted.
|
||||
* @param {String} key -
|
||||
*/
|
||||
remove(query: string|IMetaQuery): void {
|
||||
remove(query: string | IMetaQuery): void {
|
||||
const metadata: IMetadata = this.find(query);
|
||||
|
||||
if (metadata) {
|
||||
@@ -99,7 +99,7 @@ export default class MetableStore implements IMetableStore{
|
||||
* @param {String} key -
|
||||
* @param {String} value -
|
||||
*/
|
||||
set(query: IMetaQuery|IMetadata[]|string, metaValue?: any): void {
|
||||
set(query: IMetaQuery | IMetadata[] | string, metaValue?: any): void {
|
||||
if (Array.isArray(query)) {
|
||||
const metadata = query;
|
||||
|
||||
@@ -127,10 +127,10 @@ export default class MetableStore implements IMetableStore{
|
||||
|
||||
/**
|
||||
* Parses query query.
|
||||
* @param query
|
||||
* @param value
|
||||
* @param query
|
||||
* @param value
|
||||
*/
|
||||
parseQuery(query: string|IMetaQuery): IMetaQuery {
|
||||
parseQuery(query: string | IMetaQuery): IMetaQuery {
|
||||
return typeof query !== 'object' ? { key: query } : { ...query };
|
||||
}
|
||||
|
||||
@@ -141,9 +141,9 @@ export default class MetableStore implements IMetableStore{
|
||||
* @return {string|number|boolean} -
|
||||
*/
|
||||
static formatMetaValue(
|
||||
value: string|boolean|number,
|
||||
value: string | boolean | number,
|
||||
valueType: string
|
||||
) : string|number|boolean {
|
||||
): string | number | boolean {
|
||||
let parsedValue;
|
||||
|
||||
switch (valueType) {
|
||||
@@ -168,7 +168,9 @@ export default class MetableStore implements IMetableStore{
|
||||
* @param {Array} collection -
|
||||
*/
|
||||
mapMetadataToCollection(metadata: IMetadata[], parseType: string = 'parse') {
|
||||
return metadata.map((model) => this.mapMetadataToCollection(model, parseType));
|
||||
return metadata.map((model) =>
|
||||
this.mapMetadataToCollection(model, parseType)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,14 +179,16 @@ export default class MetableStore implements IMetableStore{
|
||||
*/
|
||||
from(meta: []) {
|
||||
if (Array.isArray(meta)) {
|
||||
meta.forEach((m) => { this.from(m); });
|
||||
meta.forEach((m) => {
|
||||
this.from(m);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.metadata.push(meta);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @returns {array}
|
||||
*/
|
||||
toArray(): IMetadata[] {
|
||||
@@ -193,7 +197,7 @@ export default class MetableStore implements IMetableStore{
|
||||
|
||||
/**
|
||||
* Static method to load metadata to the collection.
|
||||
* @param {Array} meta
|
||||
* @param {Array} meta
|
||||
*/
|
||||
static from(meta) {
|
||||
const collection = new MetableCollection();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import { transactionIncrement } from 'utils';
|
||||
import { transactionIncrement, parseBoolean } from 'utils';
|
||||
|
||||
/**
|
||||
* Auto increment orders service.
|
||||
@@ -15,38 +15,18 @@ export default class AutoIncrementOrdersService {
|
||||
* @param {number} tenantId
|
||||
* @param {string} settingsGroup
|
||||
* @param {Function} getMaxTransactionNo
|
||||
* @return {Promise<[string, string]>}
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
async getNextTransactionNumber(
|
||||
tenantId: number,
|
||||
settingsGroup: string,
|
||||
getOrderTransaction: (prefix: string, number: string) => Promise<boolean>,
|
||||
getMaxTransactionNumber: (prefix: string, number: string) => Promise<string>
|
||||
): Promise<[string, string]> {
|
||||
getNextTransactionNumber(tenantId: number, settingsGroup: 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' });
|
||||
const autoIncrement = settings.get({ group, key: 'auto_increment' }, false);
|
||||
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];
|
||||
return parseBoolean(autoIncrement, false) ? `${settingPrefix}${settingNo}` : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,16 +35,17 @@ export default class AutoIncrementOrdersService {
|
||||
* @param {string} orderGroup - Order group.
|
||||
* @param {string} orderNumber -Order number.
|
||||
*/
|
||||
async incrementSettingsNextNumber(
|
||||
tenantId,
|
||||
orderGroup: string,
|
||||
orderNumber: string
|
||||
) {
|
||||
async incrementSettingsNextNumber(tenantId: number, group: string) {
|
||||
const settings = this.tenancy.settings(tenantId);
|
||||
const settingNo = settings.get({ group, key: 'next_number' });
|
||||
const autoIncrement = settings.get({ group, key: 'auto_increment' });
|
||||
|
||||
// Can't continue if the auto-increment of the service was disabled.
|
||||
if (!autoIncrement) return;
|
||||
|
||||
settings.set(
|
||||
{ group: orderGroup, key: 'next_number' },
|
||||
transactionIncrement(orderNumber)
|
||||
{ group, key: 'next_number' },
|
||||
transactionIncrement(settingNo)
|
||||
);
|
||||
await settings.save();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import CustomersService from 'services/Contacts/CustomersService';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||
import { ACCOUNT_PARENT_TYPE } from 'data/AccountTypes';
|
||||
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
||||
|
||||
const ERRORS = {
|
||||
PAYMENT_RECEIVE_NO_EXISTS: 'PAYMENT_RECEIVE_NO_EXISTS',
|
||||
@@ -41,6 +42,7 @@ const ERRORS = {
|
||||
INVOICES_IDS_NOT_FOUND: 'INVOICES_IDS_NOT_FOUND',
|
||||
ENTRIES_IDS_NOT_EXISTS: 'ENTRIES_IDS_NOT_EXISTS',
|
||||
INVOICES_NOT_DELIVERED_YET: 'INVOICES_NOT_DELIVERED_YET',
|
||||
PAYMENT_RECEIVE_NO_IS_REQUIRED: 'PAYMENT_RECEIVE_NO_IS_REQUIRED'
|
||||
};
|
||||
/**
|
||||
* Payment receive service.
|
||||
@@ -66,6 +68,9 @@ export default class PaymentReceiveService {
|
||||
@Inject()
|
||||
dynamicListService: DynamicListingService;
|
||||
|
||||
@Inject()
|
||||
autoIncrementOrdersService: AutoIncrementOrdersService;
|
||||
|
||||
@Inject('logger')
|
||||
logger: any;
|
||||
|
||||
@@ -144,7 +149,8 @@ export default class PaymentReceiveService {
|
||||
/**
|
||||
* Validates the invoices IDs existance.
|
||||
* @param {number} tenantId -
|
||||
* @param {} paymentReceiveEntries -
|
||||
* @param {number} customerId -
|
||||
* @param {IPaymentReceiveEntryDTO[]} paymentReceiveEntries -
|
||||
*/
|
||||
async validateInvoicesIDsExistance(
|
||||
tenantId: number,
|
||||
@@ -225,6 +231,61 @@ export default class PaymentReceiveService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the next unique payment receive number.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @return {string}
|
||||
*/
|
||||
getNextPaymentReceiveNumber(tenantId: number): string {
|
||||
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||
tenantId,
|
||||
'payment_receives'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the payment receive next number.
|
||||
* @param {number} tenantId
|
||||
*/
|
||||
incrementNextPaymentReceiveNumber(tenantId: number) {
|
||||
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||
tenantId,
|
||||
'payment_receives'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the payment receive number require.
|
||||
* @param {IPaymentReceive} paymentReceiveObj
|
||||
*/
|
||||
validatePaymentReceiveNoRequire(paymentReceiveObj: IPaymentReceive) {
|
||||
if (!paymentReceiveObj.paymentReceiveNo) {
|
||||
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NO_IS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve estimate number to object model.
|
||||
* @param {number} tenantId
|
||||
* @param {IPaymentReceiveCreateDTO | IPaymentReceiveEditDTO} paymentReceiveDTO - Payment receive DTO.
|
||||
* @param {IPaymentReceive} oldPaymentReceive - Old payment model object.
|
||||
*/
|
||||
transformPaymentNumberToModel(
|
||||
tenantId: number,
|
||||
paymentReceiveDTO: IPaymentReceiveCreateDTO | IPaymentReceiveEditDTO,
|
||||
oldPaymentReceive?: IPaymentReceive
|
||||
): string {
|
||||
// Retreive the next invoice number.
|
||||
const autoNextNumber = this.getNextPaymentReceiveNumber(tenantId);
|
||||
|
||||
if (paymentReceiveDTO.paymentReceiveNo) {
|
||||
return paymentReceiveDTO.paymentReceiveNo;
|
||||
}
|
||||
return oldPaymentReceive
|
||||
? oldPaymentReceive.paymentReceiveNo
|
||||
: autoNextNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the payment receive entries IDs existance.
|
||||
* @param {number} tenantId
|
||||
@@ -246,7 +307,6 @@ export default class PaymentReceiveService {
|
||||
'payment_receive_id',
|
||||
paymentReceiveId
|
||||
);
|
||||
|
||||
const storedEntriesIds = storedEntries.map((entry: any) => entry.id);
|
||||
const notFoundEntriesIds = difference(entriesIds, storedEntriesIds);
|
||||
|
||||
@@ -255,6 +315,36 @@ export default class PaymentReceiveService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformes the create payment receive DTO to model object.
|
||||
* @param {number} tenantId
|
||||
* @param {IPaymentReceiveCreateDTO} paymentReceiveDTO
|
||||
*/
|
||||
transformPaymentReceiveDTOToModel(
|
||||
tenantId: number,
|
||||
paymentReceiveDTO: IPaymentReceiveCreateDTO | IPaymentReceiveEditDTO,
|
||||
oldPaymentReceive?: IPaymentReceive
|
||||
): IPaymentReceive {
|
||||
const paymentAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
|
||||
|
||||
// Retrieve the next payment receive number.
|
||||
const paymentReceiveNo = this.transformPaymentNumberToModel(
|
||||
tenantId,
|
||||
paymentReceiveDTO,
|
||||
oldPaymentReceive
|
||||
);
|
||||
return {
|
||||
amount: paymentAmount,
|
||||
...formatDateFields(omit(paymentReceiveDTO, ['entries']), [
|
||||
'paymentDate',
|
||||
]),
|
||||
...(paymentReceiveNo ? { paymentReceiveNo } : {}),
|
||||
entries: paymentReceiveDTO.entries.map((entry) => ({
|
||||
...omit(entry, ['id']),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new payment receive and store it to the storage
|
||||
* with associated invoices payment and journal transactions.
|
||||
@@ -268,34 +358,36 @@ export default class PaymentReceiveService {
|
||||
authorizedUser: ISystemUser
|
||||
) {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
const paymentAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
|
||||
|
||||
// Transformes the payment receive DTO to model.
|
||||
const paymentReceiveObj = this.transformPaymentReceiveDTOToModel(
|
||||
tenantId,
|
||||
paymentReceiveDTO
|
||||
);
|
||||
// Validate payment receive is required.
|
||||
this.validatePaymentReceiveNoRequire(paymentReceiveObj);
|
||||
|
||||
// Validate payment receive number uniquiness.
|
||||
if (paymentReceiveDTO.paymentReceiveNo) {
|
||||
await this.validatePaymentReceiveNoExistance(
|
||||
tenantId,
|
||||
paymentReceiveDTO.paymentReceiveNo
|
||||
);
|
||||
}
|
||||
await this.validatePaymentReceiveNoExistance(
|
||||
tenantId,
|
||||
paymentReceiveObj.paymentReceiveNo
|
||||
);
|
||||
// Validate customer existance.
|
||||
await this.customersService.getCustomerByIdOrThrowError(
|
||||
tenantId,
|
||||
paymentReceiveDTO.customerId
|
||||
);
|
||||
|
||||
// Validate the deposit account existance and type.
|
||||
await this.getDepositAccountOrThrowError(
|
||||
tenantId,
|
||||
paymentReceiveDTO.depositAccountId
|
||||
);
|
||||
|
||||
// Validate payment receive invoices IDs existance.
|
||||
await this.validateInvoicesIDsExistance(
|
||||
tenantId,
|
||||
paymentReceiveDTO.customerId,
|
||||
paymentReceiveDTO.entries
|
||||
);
|
||||
|
||||
// Validate invoice payment amount.
|
||||
await this.validateInvoicesPaymentsAmount(
|
||||
tenantId,
|
||||
@@ -304,15 +396,9 @@ export default class PaymentReceiveService {
|
||||
|
||||
this.logger.info('[payment_receive] inserting to the storage.');
|
||||
const paymentReceive = await PaymentReceive.query().insertGraphAndFetch({
|
||||
amount: paymentAmount,
|
||||
...formatDateFields(omit(paymentReceiveDTO, ['entries']), [
|
||||
'paymentDate',
|
||||
]),
|
||||
entries: paymentReceiveDTO.entries.map((entry) => ({
|
||||
...omit(entry, ['id']),
|
||||
})),
|
||||
...paymentReceiveObj,
|
||||
});
|
||||
|
||||
// Triggers `onPaymentReceiveCreated` event.
|
||||
await this.eventDispatcher.dispatch(events.paymentReceive.onCreated, {
|
||||
tenantId,
|
||||
paymentReceive,
|
||||
@@ -349,19 +435,26 @@ export default class PaymentReceiveService {
|
||||
authorizedUser: ISystemUser
|
||||
) {
|
||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||
const paymentAmount = sumBy(paymentReceiveDTO.entries, 'paymentAmount');
|
||||
|
||||
this.logger.info('[payment_receive] trying to edit payment receive.', {
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
paymentReceiveDTO,
|
||||
});
|
||||
|
||||
// Validate the payment receive existance.
|
||||
const oldPaymentReceive = await this.getPaymentReceiveOrThrowError(
|
||||
tenantId,
|
||||
paymentReceiveId
|
||||
);
|
||||
// Transformes the payment receive DTO to model.
|
||||
const paymentReceiveObj = this.transformPaymentReceiveDTOToModel(
|
||||
tenantId,
|
||||
paymentReceiveDTO,
|
||||
oldPaymentReceive
|
||||
);
|
||||
// Validate payment receive number existance.
|
||||
this.validatePaymentReceiveNoRequire(paymentReceiveObj);
|
||||
|
||||
// Validate payment receive number uniquiness.
|
||||
if (paymentReceiveDTO.paymentReceiveNo) {
|
||||
await this.validatePaymentReceiveNoExistance(
|
||||
@@ -375,7 +468,6 @@ export default class PaymentReceiveService {
|
||||
tenantId,
|
||||
paymentReceiveDTO.depositAccountId
|
||||
);
|
||||
|
||||
// Validate the entries ids existance on payment receive type.
|
||||
await this.validateEntriesIdsExistance(
|
||||
tenantId,
|
||||
@@ -397,11 +489,7 @@ export default class PaymentReceiveService {
|
||||
// Update the payment receive transaction.
|
||||
const paymentReceive = await PaymentReceive.query().upsertGraphAndFetch({
|
||||
id: paymentReceiveId,
|
||||
amount: paymentAmount,
|
||||
...formatDateFields(omit(paymentReceiveDTO, ['entries']), [
|
||||
'paymentDate',
|
||||
]),
|
||||
entries: paymentReceiveDTO.entries,
|
||||
...paymentReceiveObj,
|
||||
});
|
||||
|
||||
await this.eventDispatcher.dispatch(events.paymentReceive.onEdited, {
|
||||
|
||||
@@ -19,6 +19,7 @@ import events from 'subscribers/events';
|
||||
import { ServiceError } from 'exceptions';
|
||||
import CustomersService from 'services/Contacts/CustomersService';
|
||||
import moment from 'moment';
|
||||
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
||||
|
||||
const ERRORS = {
|
||||
SALE_ESTIMATE_NOT_FOUND: 'SALE_ESTIMATE_NOT_FOUND',
|
||||
@@ -30,6 +31,7 @@ const ERRORS = {
|
||||
SALE_ESTIMATE_ALREADY_REJECTED: 'SALE_ESTIMATE_ALREADY_REJECTED',
|
||||
SALE_ESTIMATE_ALREADY_APPROVED: 'SALE_ESTIMATE_ALREADY_APPROVED',
|
||||
SALE_ESTIMATE_NOT_DELIVERED: 'SALE_ESTIMATE_NOT_DELIVERED',
|
||||
SALE_ESTIMATE_NO_IS_REQUIRED: 'SALE_ESTIMATE_NO_IS_REQUIRED'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -56,6 +58,9 @@ export default class SaleEstimateService {
|
||||
@EventDispatcher()
|
||||
eventDispatcher: EventDispatcherInterface;
|
||||
|
||||
@Inject()
|
||||
autoIncrementOrdersService: AutoIncrementOrdersService;
|
||||
|
||||
/**
|
||||
* Retrieve sale estimate or throw service error.
|
||||
* @param {number} tenantId
|
||||
@@ -100,7 +105,7 @@ export default class SaleEstimateService {
|
||||
|
||||
/**
|
||||
* Validates the given sale estimate not already converted to invoice.
|
||||
* @param {ISaleEstimate} saleEstimate -
|
||||
* @param {ISaleEstimate} saleEstimate -
|
||||
*/
|
||||
validateEstimateNotConverted(saleEstimate: ISaleEstimate) {
|
||||
if (saleEstimate.isConvertedToInvoice) {
|
||||
@@ -108,6 +113,49 @@ export default class SaleEstimateService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the next unique estimate number.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @return {string}
|
||||
*/
|
||||
getNextEstimateNumber(tenantId: number): string {
|
||||
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||
tenantId,
|
||||
'sales_estimates'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the estimate next number.
|
||||
* @param {number} tenantId -
|
||||
*/
|
||||
incrementNextEstimateNumber(tenantId: number) {
|
||||
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||
tenantId,
|
||||
'sales_estimates'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve estimate number to object model.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleEstimateDTO} saleEstimateDTO
|
||||
* @param {ISaleEstimate} oldSaleEstimate
|
||||
*/
|
||||
transformEstimateNumberToModel(
|
||||
tenantId: number,
|
||||
saleEstimateDTO: ISaleEstimateDTO,
|
||||
oldSaleEstimate?: ISaleEstimate
|
||||
): string {
|
||||
// Retreive the next invoice number.
|
||||
const autoNextNumber = this.getNextEstimateNumber(tenantId);
|
||||
|
||||
if (saleEstimateDTO.estimateNumber) {
|
||||
return saleEstimateDTO.estimateNumber;
|
||||
}
|
||||
return oldSaleEstimate ? oldSaleEstimate.estimateNumber : autoNextNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform DTO object ot model object.
|
||||
* @param {number} tenantId
|
||||
@@ -123,17 +171,24 @@ export default class SaleEstimateService {
|
||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||
const amount = sumBy(estimateDTO.entries, (e) => ItemEntry.calcAmount(e));
|
||||
|
||||
// Retreive the next estimate number.
|
||||
const estimateNumber = this.transformEstimateNumberToModel(
|
||||
tenantId,
|
||||
estimateDTO,
|
||||
oldSaleEstimate
|
||||
);
|
||||
|
||||
return {
|
||||
amount,
|
||||
...formatDateFields(omit(estimateDTO, ['delivered', 'entries']), [
|
||||
'estimateDate',
|
||||
'expirationDate',
|
||||
]),
|
||||
...(estimateNumber ? { estimateNumber } : {}),
|
||||
entries: estimateDTO.entries.map((entry) => ({
|
||||
reference_type: 'SaleEstimate',
|
||||
...omit(entry, ['total', 'amount', 'id']),
|
||||
})),
|
||||
|
||||
// Avoid rewrite the deliver date in edit mode when already published.
|
||||
...(estimateDTO.delivered &&
|
||||
!oldSaleEstimate?.deliveredAt && {
|
||||
@@ -141,6 +196,16 @@ export default class SaleEstimateService {
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the sale estimate number require.
|
||||
* @param {ISaleEstimate} saleInvoiceObj
|
||||
*/
|
||||
validateEstimateNoRequire(saleInvoiceObj: ISaleEstimate) {
|
||||
if (!saleInvoiceObj.estimateNumber) {
|
||||
throw new ServiceError(ERRORS.SALE_ESTIMATE_NO_IS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new estimate with associated entries.
|
||||
@@ -160,11 +225,14 @@ export default class SaleEstimateService {
|
||||
// Transform DTO object ot model object.
|
||||
const estimateObj = this.transformDTOToModel(tenantId, estimateDTO);
|
||||
|
||||
// Validate the sale estimate number require.
|
||||
this.validateEstimateNoRequire(estimateObj);
|
||||
|
||||
// Validate estimate number uniquiness on the storage.
|
||||
if (estimateDTO.estimateNumber) {
|
||||
if (estimateObj.estimateNumber) {
|
||||
await this.validateEstimateNumberExistance(
|
||||
tenantId,
|
||||
estimateDTO.estimateNumber
|
||||
estimateObj.estimateNumber
|
||||
);
|
||||
}
|
||||
// Retrieve the given customer or throw not found service error.
|
||||
@@ -221,6 +289,9 @@ export default class SaleEstimateService {
|
||||
estimateDTO,
|
||||
oldSaleEstimate
|
||||
);
|
||||
// Validate the sale estimate number require.
|
||||
this.validateEstimateNoRequire(estimateObj);
|
||||
|
||||
// Validate estimate number uniquiness on the storage.
|
||||
if (estimateDTO.estimateNumber) {
|
||||
await this.validateEstimateNumberExistance(
|
||||
|
||||
@@ -162,28 +162,44 @@ export default class SaleInvoicesService {
|
||||
* @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);
|
||||
};
|
||||
|
||||
getNextInvoiceNumber(tenantId: number): string {
|
||||
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||
tenantId,
|
||||
'sales_invoices',
|
||||
getTransactionNumber,
|
||||
getMaxInvoicesNo
|
||||
'sales_invoices'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the invoice next number.
|
||||
* @param {number} tenantId -
|
||||
*/
|
||||
incrementNextInvoiceNumber(tenantId: number) {
|
||||
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||
tenantId,
|
||||
'sales_invoices'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve invoice number to object model.
|
||||
* @param tenantId
|
||||
* @param saleInvoiceDTO
|
||||
* @param oldSaleInvoice
|
||||
*/
|
||||
transformInvoiceNumberToModel(
|
||||
tenantId: number,
|
||||
saleInvoiceDTO: ISaleInvoiceCreateDTO | ISaleInvoiceEditDTO,
|
||||
oldSaleInvoice?: ISaleInvoice
|
||||
): string {
|
||||
// Retreive the next invoice number.
|
||||
const autoNextNumber = this.getNextInvoiceNumber(tenantId);
|
||||
|
||||
if (saleInvoiceDTO.invoiceNo) {
|
||||
return saleInvoiceDTO.invoiceNo;
|
||||
}
|
||||
return oldSaleInvoice ? oldSaleInvoice.invoiceNo : autoNextNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform DTO object to model object.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
@@ -192,33 +208,37 @@ export default class SaleInvoicesService {
|
||||
transformDTOToModel(
|
||||
tenantId: number,
|
||||
saleInvoiceDTO: ISaleInvoiceCreateDTO | ISaleInvoiceEditDTO,
|
||||
oldSaleInvoice?: ISaleInvoice,
|
||||
autoNextNumber?: [string, string] // prefix, number
|
||||
oldSaleInvoice?: ISaleInvoice
|
||||
): ISaleInvoice {
|
||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||
|
||||
const balance = sumBy(saleInvoiceDTO.entries, (e) =>
|
||||
ItemEntry.calcAmount(e)
|
||||
);
|
||||
|
||||
const invoiceNo = this.transformInvoiceNumberToModel(
|
||||
tenantId,
|
||||
saleInvoiceDTO,
|
||||
oldSaleInvoice
|
||||
);
|
||||
return {
|
||||
...formatDateFields(
|
||||
omit(saleInvoiceDTO, ['delivered', 'entries', 'fromEstimateId']),
|
||||
['invoiceDate', 'dueDate']
|
||||
),
|
||||
// Avoid rewrite the deliver date in edit mode when already published.
|
||||
balance,
|
||||
...(saleInvoiceDTO.delivered &&
|
||||
!oldSaleInvoice?.deliveredAt && {
|
||||
deliveredAt: moment().toMySqlDateTime(),
|
||||
}),
|
||||
balance,
|
||||
paymentAmount: 0,
|
||||
...(saleInvoiceDTO.invoiceNo || autoNextNumber
|
||||
// Avoid add payment amount in edit mode.
|
||||
...(!oldSaleInvoice
|
||||
? {
|
||||
invoiceNo: saleInvoiceDTO.invoiceNo
|
||||
? saleInvoiceDTO.invoiceNo
|
||||
: join(autoNextNumber, ''),
|
||||
paymentAmount: 0,
|
||||
}
|
||||
: {}),
|
||||
...(invoiceNo ? { invoiceNo } : {}),
|
||||
entries: saleInvoiceDTO.entries.map((entry) => ({
|
||||
referenceType: 'SaleInvoice',
|
||||
...omit(entry, ['amount', 'id']),
|
||||
@@ -226,6 +246,16 @@ export default class SaleInvoicesService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the invoice number require.
|
||||
* @param {ISaleInvoice} saleInvoiceObj
|
||||
*/
|
||||
validateInvoiceNoRequire(saleInvoiceObj: ISaleInvoice) {
|
||||
if (!saleInvoiceObj.invoiceNo) {
|
||||
throw new ServiceError(ERRORS.SALE_INVOICE_NO_IS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new sale invoices and store it to the storage
|
||||
* with associated to entries and journal transactions.
|
||||
@@ -241,28 +271,25 @@ export default class SaleInvoicesService {
|
||||
): Promise<ISaleInvoice> {
|
||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// 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
|
||||
null
|
||||
);
|
||||
this.validateInvoiceNoRequire(saleInvoiceObj);
|
||||
|
||||
// Validate customer existance.
|
||||
await this.customersService.getCustomerByIdOrThrowError(
|
||||
tenantId,
|
||||
saleInvoiceDTO.customerId
|
||||
);
|
||||
|
||||
// Validate sale invoice number uniquiness.
|
||||
if (saleInvoiceDTO.invoiceNo) {
|
||||
if (saleInvoiceObj.invoiceNo) {
|
||||
await this.validateInvoiceNumberUnique(
|
||||
tenantId,
|
||||
saleInvoiceDTO.invoiceNo
|
||||
saleInvoiceObj.invoiceNo
|
||||
);
|
||||
}
|
||||
// Validate the from estimate id exists on the storage.
|
||||
@@ -296,7 +323,6 @@ export default class SaleInvoicesService {
|
||||
saleInvoiceDTO,
|
||||
saleInvoiceId: saleInvoice.id,
|
||||
authorizedUser,
|
||||
autoNextNumber,
|
||||
});
|
||||
this.logger.info('[sale_invoice] successfully inserted.', {
|
||||
tenantId,
|
||||
@@ -317,11 +343,12 @@ export default class SaleInvoicesService {
|
||||
public async editSaleInvoice(
|
||||
tenantId: number,
|
||||
saleInvoiceId: number,
|
||||
saleInvoiceDTO: any,
|
||||
saleInvoiceDTO: ISaleInvoiceEditDTO,
|
||||
authorizedUser: ISystemUser
|
||||
): Promise<ISaleInvoice> {
|
||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Retrieve the sale invoice or throw not found service error.
|
||||
const oldSaleInvoice = await this.getInvoiceOrThrowError(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
|
||||
@@ -19,6 +19,7 @@ import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
import { ItemEntry } from 'models';
|
||||
import InventoryService from 'services/Inventory/Inventory';
|
||||
import { ACCOUNT_PARENT_TYPE } from 'data/AccountTypes';
|
||||
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
|
||||
|
||||
const ERRORS = {
|
||||
SALE_RECEIPT_NOT_FOUND: 'SALE_RECEIPT_NOT_FOUND',
|
||||
@@ -26,6 +27,7 @@ const ERRORS = {
|
||||
DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET: 'DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET',
|
||||
SALE_RECEIPT_NUMBER_NOT_UNIQUE: 'SALE_RECEIPT_NUMBER_NOT_UNIQUE',
|
||||
SALE_RECEIPT_IS_ALREADY_CLOSED: 'SALE_RECEIPT_IS_ALREADY_CLOSED',
|
||||
SALE_RECEIPT_NO_IS_REQUIRED: 'SALE_RECEIPT_NO_IS_REQUIRED'
|
||||
};
|
||||
|
||||
@Service()
|
||||
@@ -51,6 +53,9 @@ export default class SalesReceiptService {
|
||||
@Inject('logger')
|
||||
logger: any;
|
||||
|
||||
@Inject()
|
||||
autoIncrementOrdersService: AutoIncrementOrdersService;
|
||||
|
||||
/**
|
||||
* Validate whether sale receipt exists on the storage.
|
||||
* @param {number} tenantId -
|
||||
@@ -130,6 +135,59 @@ export default class SalesReceiptService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the sale receipt number require.
|
||||
* @param {ISaleReceipt} saleReceipt
|
||||
*/
|
||||
validateReceiptNoRequire(saleReceipt: ISaleReceipt) {
|
||||
if (!saleReceipt.receiptNumber) {
|
||||
throw new ServiceError(ERRORS.SALE_RECEIPT_NO_IS_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the next unique receipt number.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @return {string}
|
||||
*/
|
||||
getNextReceiptNumber(tenantId: number): string {
|
||||
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||
tenantId,
|
||||
'sales_receipts'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the receipt next number.
|
||||
* @param {number} tenantId -
|
||||
*/
|
||||
incrementNextReceiptNumber(tenantId: number) {
|
||||
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||
tenantId,
|
||||
'sales_receipts'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve estimate number to object model.
|
||||
* @param {number} tenantId
|
||||
* @param {ISaleReceiptDTO} saleReceiptDTO - Sale receipt DTO.
|
||||
* @param {ISaleReceipt} oldSaleReceipt - Old receipt model object.
|
||||
*/
|
||||
transformReceiptNumberToModel(
|
||||
tenantId: number,
|
||||
saleReceiptDTO: ISaleReceiptDTO,
|
||||
oldSaleReceipt?: ISaleReceipt
|
||||
): string {
|
||||
// Retreive the next invoice number.
|
||||
const autoNextNumber = this.getNextReceiptNumber(tenantId);
|
||||
|
||||
if (saleReceiptDTO.receiptNumber) {
|
||||
return saleReceiptDTO.receiptNumber;
|
||||
}
|
||||
return oldSaleReceipt ? oldSaleReceipt.receiptNumber : autoNextNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform DTO object to model object.
|
||||
* @param {ISaleReceiptDTO} saleReceiptDTO -
|
||||
@@ -137,18 +195,26 @@ export default class SalesReceiptService {
|
||||
* @returns {ISaleReceipt}
|
||||
*/
|
||||
transformObjectDTOToModel(
|
||||
tenantId: number,
|
||||
saleReceiptDTO: ISaleReceiptDTO,
|
||||
oldSaleReceipt?: ISaleReceipt
|
||||
): ISaleReceipt {
|
||||
const amount = sumBy(saleReceiptDTO.entries, (e) =>
|
||||
ItemEntry.calcAmount(e)
|
||||
);
|
||||
// Retreive the receipt number.
|
||||
const receiptNumber = this.transformReceiptNumberToModel(
|
||||
tenantId,
|
||||
saleReceiptDTO,
|
||||
oldSaleReceipt
|
||||
);
|
||||
|
||||
return {
|
||||
amount,
|
||||
...formatDateFields(omit(saleReceiptDTO, ['closed', 'entries']), [
|
||||
'receiptDate',
|
||||
]),
|
||||
...(receiptNumber ? { receiptNumber } : {}),
|
||||
// Avoid rewrite the deliver date in edit mode when already published.
|
||||
...(saleReceiptDTO.closed &&
|
||||
!oldSaleReceipt?.closedAt && {
|
||||
@@ -174,7 +240,12 @@ export default class SalesReceiptService {
|
||||
const { SaleReceipt } = this.tenancy.models(tenantId);
|
||||
|
||||
// Transform sale receipt DTO to model.
|
||||
const saleReceiptObj = this.transformObjectDTOToModel(saleReceiptDTO);
|
||||
const saleReceiptObj = this.transformObjectDTOToModel(
|
||||
tenantId,
|
||||
saleReceiptDTO
|
||||
);
|
||||
// Validate receipt number is required.
|
||||
this.validateReceiptNoRequire(saleReceiptObj);
|
||||
|
||||
// Validate receipt deposit account existance and type.
|
||||
await this.validateReceiptDepositAccountExistance(
|
||||
@@ -238,9 +309,13 @@ export default class SalesReceiptService {
|
||||
);
|
||||
// Transform sale receipt DTO to model.
|
||||
const saleReceiptObj = this.transformObjectDTOToModel(
|
||||
tenantId,
|
||||
saleReceiptDTO,
|
||||
oldSaleReceipt
|
||||
);
|
||||
// Validate receipt number is required.
|
||||
this.validateReceiptNoRequire(saleReceiptObj);
|
||||
|
||||
// Validate receipt deposit account existance and type.
|
||||
await this.validateReceiptDepositAccountExistance(
|
||||
tenantId,
|
||||
@@ -309,7 +384,7 @@ export default class SalesReceiptService {
|
||||
await this.eventDispatcher.dispatch(events.saleReceipt.onDeleted, {
|
||||
tenantId,
|
||||
saleReceiptId,
|
||||
oldSaleReceipt
|
||||
oldSaleReceipt,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -463,7 +538,7 @@ export default class SalesReceiptService {
|
||||
'SaleReceipt',
|
||||
saleReceipt.receiptDate,
|
||||
'OUT',
|
||||
override,
|
||||
override
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,4 +9,5 @@ export const ERRORS = {
|
||||
'INVOICE_AMOUNT_SMALLER_THAN_PAYMENT_AMOUNT',
|
||||
INVOICE_HAS_ASSOCIATED_PAYMENT_ENTRIES:
|
||||
'INVOICE_HAS_ASSOCIATED_PAYMENT_ENTRIES',
|
||||
SALE_INVOICE_NO_IS_REQUIRED: 'SALE_INVOICE_NO_IS_REQUIRED'
|
||||
};
|
||||
|
||||
@@ -50,17 +50,9 @@ export default class SaleInvoiceSubscriber {
|
||||
@On(events.saleInvoice.onCreated)
|
||||
public async handleInvoiceNextNumberIncrement({
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
saleInvoice,
|
||||
saleInvoiceDTO,
|
||||
autoNextNumber,
|
||||
}) {
|
||||
if (saleInvoiceDTO.invoiceNo || !autoNextNumber) return;
|
||||
|
||||
await this.saleInvoicesService.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||
await this.saleInvoicesService.incrementNextInvoiceNumber(
|
||||
tenantId,
|
||||
'sales_invoices',
|
||||
autoNextNumber[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,9 +130,6 @@ export default class PaymentReceivesSubscriber {
|
||||
tenantId,
|
||||
paymentReceiveId,
|
||||
}) {
|
||||
await this.settingsService.incrementNextNumber(tenantId, {
|
||||
key: 'next_number',
|
||||
group: 'payment_receives',
|
||||
});
|
||||
await this.paymentReceivesService.incrementNextPaymentReceiveNumber(tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +289,30 @@ const transformToMap = (objects, key) => {
|
||||
|
||||
const transactionIncrement = (s) => s.replace(/([0-8]|\d?9+)?$/, (e) => ++e);
|
||||
|
||||
const booleanValuesRepresentingTrue: string[] = [
|
||||
'true',
|
||||
'1',
|
||||
];
|
||||
const booleanValuesRepresentingFalse: string[] = [
|
||||
'false',
|
||||
'0',
|
||||
];
|
||||
|
||||
const normalizeValue = (value: any): string => value.toString().trim().toLowerCase();
|
||||
|
||||
const booleanValues: string[] = [
|
||||
...booleanValuesRepresentingTrue,
|
||||
...booleanValuesRepresentingFalse,
|
||||
].map((value) => normalizeValue(value));
|
||||
|
||||
export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
|
||||
const normalizedValue = normalizeValue(value);
|
||||
if (booleanValues.indexOf(normalizedValue) === -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;
|
||||
};
|
||||
|
||||
export {
|
||||
hashPassword,
|
||||
origin,
|
||||
Reference in New Issue
Block a user