From d0785c65db415932a54b902346de916c935edd10 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 26 Oct 2020 13:47:42 +0200 Subject: [PATCH] feat: add sale receipt number. --- .../api/controllers/Sales/SalesReceipts.ts | 7 ++- ...200713213303_create_sales_receipt_table.js | 1 + server/src/interfaces/SaleReceipt.ts | 1 + server/src/services/Sales/SalesInvoices.ts | 3 -- server/src/services/Sales/SalesReceipts.ts | 43 +++++++++++++++++++ 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/server/src/api/controllers/Sales/SalesReceipts.ts b/server/src/api/controllers/Sales/SalesReceipts.ts index a2ad3562e..a24b48f21 100644 --- a/server/src/api/controllers/Sales/SalesReceipts.ts +++ b/server/src/api/controllers/Sales/SalesReceipts.ts @@ -66,6 +66,7 @@ export default class SalesReceiptsController extends BaseController{ check('deposit_account_id').exists().isNumeric().toInt(), check('receipt_date').exists().isISO8601(), check('send_to_email').optional().isEmail(), + check('receipt_number').optional().trim().escape(), check('reference_no').optional().trim().escape(), check('entries').exists().isArray({ min: 1 }), @@ -262,8 +263,12 @@ export default class SalesReceiptsController extends BaseController{ errors: [{ type: 'DEPOSIT.ACCOUNT.NOT.EXISTS', code: 800 }], }); } + if (error.errorType === 'SALE_RECEIPT_NUMBER_NOT_UNIQUE') { + return res.boom.badRequest(null, { + errors: [{ type: 'SALE_RECEIPT_NUMBER_NOT_UNIQUE', code: 900 }], + }); + } } - console.log(error); next(error); } }; diff --git a/server/src/database/migrations/20200713213303_create_sales_receipt_table.js b/server/src/database/migrations/20200713213303_create_sales_receipt_table.js index 6b53bdd4d..79a9980ca 100644 --- a/server/src/database/migrations/20200713213303_create_sales_receipt_table.js +++ b/server/src/database/migrations/20200713213303_create_sales_receipt_table.js @@ -6,6 +6,7 @@ exports.up = function(knex) { table.integer('deposit_account_id').unsigned().index().references('id').inTable('accounts'); table.integer('customer_id').unsigned().index().references('id').inTable('contacts'); table.date('receipt_date').index(); + table.string('receipt_number'); table.string('reference_no'); table.string('email_send_to'); table.text('receipt_message'); diff --git a/server/src/interfaces/SaleReceipt.ts b/server/src/interfaces/SaleReceipt.ts index 1866a6cfd..3586c8055 100644 --- a/server/src/interfaces/SaleReceipt.ts +++ b/server/src/interfaces/SaleReceipt.ts @@ -9,6 +9,7 @@ export interface ISaleReceipt { sendToEmail: string, referenceNo: string, receiptMessage: string, + receiptNumber: string, statement: string, entries: any[], }; diff --git a/server/src/services/Sales/SalesInvoices.ts b/server/src/services/Sales/SalesInvoices.ts index d7915cf08..01c690881 100644 --- a/server/src/services/Sales/SalesInvoices.ts +++ b/server/src/services/Sales/SalesInvoices.ts @@ -66,9 +66,6 @@ export default class SaleInvoicesService extends SalesInvoicesCost { /** * * Validate whether sale invoice number unqiue on the storage. - * @param {Request} req - * @param {Response} res - * @param {Function} next */ async validateInvoiceNumberUnique(tenantId: number, invoiceNumber: string, notInvoiceId?: number) { const { SaleInvoice } = this.tenancy.models(tenantId); diff --git a/server/src/services/Sales/SalesReceipts.ts b/server/src/services/Sales/SalesReceipts.ts index 3d665d8a2..584174791 100644 --- a/server/src/services/Sales/SalesReceipts.ts +++ b/server/src/services/Sales/SalesReceipts.ts @@ -19,6 +19,7 @@ const ERRORS = { SALE_RECEIPT_NOT_FOUND: 'SALE_RECEIPT_NOT_FOUND', DEPOSIT_ACCOUNT_NOT_FOUND: 'DEPOSIT_ACCOUNT_NOT_FOUND', DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET: 'DEPOSIT_ACCOUNT_NOT_CURRENT_ASSET', + SALE_RECEIPT_NUMBER_NOT_UNIQUE: 'SALE_RECEIPT_NUMBER_NOT_UNIQUE' }; @Service() export default class SalesReceiptService { @@ -77,6 +78,30 @@ export default class SalesReceiptService { } } + /** + * Validate sale receipt number uniquiness on the storage. + * @param {number} tenantId - + * @param {string} receiptNumber - + * @param {number} notReceiptId - + */ + async validateReceiptNumberUnique(tenantId: number, receiptNumber: string, notReceiptId?: number) { + const { SaleReceipt } = this.tenancy.models(tenantId); + + this.logger.info('[sale_receipt] validate receipt number uniquiness.', { tenantId, receiptNumber }); + const saleReceipt = await SaleReceipt.query() + .findOne('receipt_number', receiptNumber) + .onBuild((builder) => { + if (notReceiptId) { + builder.whereNot('id', notReceiptId); + } + }); + + if (saleReceipt) { + this.logger.info('[sale_receipt] sale receipt number not unique.', { tenantId }); + throw new ServiceError(ERRORS.SALE_RECEIPT_NUMBER_NOT_UNIQUE); + } + } + /** * Creates a new sale receipt with associated entries. * @async @@ -91,10 +116,19 @@ export default class SalesReceiptService { amount, ...formatDateFields(saleReceiptDTO, ['receipt_date']) }; + + // Validate receipt deposit account existance and type. await this.validateReceiptDepositAccountExistance(tenantId, saleReceiptDTO.depositAccountId); + + // Validate items IDs existance on the storage. await this.itemsEntriesService.validateItemsIdsExistance(tenantId, saleReceiptDTO.entries); + + // Validate the sellable items. await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, saleReceiptDTO.entries); + // Validate sale receipt number uniuqiness. + await this.validateReceiptNumberUnique(tenantId, saleReceiptDTO.receiptNumber); + this.logger.info('[sale_receipt] trying to insert sale receipt graph.', { tenantId, saleReceiptDTO }); const saleReceipt = await SaleReceipt.query() .insertGraphAndFetch({ @@ -126,12 +160,21 @@ export default class SalesReceiptService { amount, ...formatDateFields(saleReceiptDTO, ['receipt_date']) }; + // Retrieve sale receipt or throw not found service error. const oldSaleReceipt = await this.getSaleReceiptOrThrowError(tenantId, saleReceiptId); + // Validate receipt deposit account existance and type. await this.validateReceiptDepositAccountExistance(tenantId, saleReceiptDTO.depositAccountId); + + // Validate items IDs existance on the storage. await this.itemsEntriesService.validateItemsIdsExistance(tenantId, saleReceiptDTO.entries); + + // Validate the sellable items. await this.itemsEntriesService.validateNonSellableEntriesItems(tenantId, saleReceiptDTO.entries); + // Validate sale receipt number uniuqiness. + await this.validateReceiptNumberUnique(tenantId, saleReceiptDTO.receiptNumber, saleReceiptId); + const saleReceipt = await SaleReceipt.query() .upsertGraphAndFetch({ id: saleReceiptId,