From ca251a2d28c2389c0b79c081ade69da7b73f012a Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 30 Aug 2020 22:11:14 +0200 Subject: [PATCH] - feat: Optimize tenancy software architecture. --- server/src/http/controllers/Accounting.js | 9 - .../{BaseController.js => BaseController.ts} | 3 - server/src/http/controllers/Items.ts | 107 +++---- .../Purchases/{Bills.js => Bills.ts} | 157 +++++----- .../controllers/Purchases/BillsPayments.ts | 145 ++++++---- .../Purchases/{index.js => index.ts} | 5 +- ...{PaymentReceives.js => PaymentReceives.ts} | 268 ++++++++++-------- .../{SalesEstimates.js => SalesEstimates.ts} | 138 +++++---- .../{SalesInvoices.js => SalesInvoices.ts} | 189 ++++++------ .../{SalesReceipts.js => SalesReceipts.ts} | 147 ++++++---- .../controllers/Sales/{index.js => index.ts} | 9 +- .../http/controllers/Subscription/Vouchers.ts | 37 ++- server/src/http/index.js | 4 +- .../src/http/middleware/TenancyMiddleware.js | 5 + server/src/interfaces/Bill.ts | 3 + server/src/interfaces/BillPayment.ts | 4 +- server/src/interfaces/PaymentReceive.ts | 4 + server/src/interfaces/SaleEstimate.ts | 4 + server/src/interfaces/index.ts | 22 +- .../src/jobs/MailNotifcationSubscribeEnd.ts | 8 + .../src/jobs/MailNotificationSubscribeEnd.ts | 6 + .../src/jobs/SMSNotificationSubscribeEnd.ts | 13 + server/src/jobs/SMSNotificationTrialEnd.ts | 8 + server/src/loaders/jobs.ts | 18 +- server/src/models/Bill.js | 5 +- server/src/models/Customer.js | 28 +- server/src/models/ItemEntry.js | 11 +- server/src/models/SaleInvoice.js | 3 +- server/src/models/Vendor.js | 3 +- .../src/services/Accounts/AccountsService.js | 22 -- .../src/services/Accounts/AccountsService.ts | 29 ++ .../services/Customers/CustomersService.js | 2 +- server/src/services/Inventory/Inventory.ts | 57 ++-- .../Inventory/InventoryAverageCost.ts | 17 +- .../Inventory/InventoryCostLotTracker.ts | 45 ++- .../services/Inventory/InventoryCostMethod.ts | 28 +- server/src/services/Items/ItemsService.js | 59 ---- server/src/services/Items/ItemsService.ts | 70 +++++ server/src/services/Payment/Voucher.ts | 26 ++ .../{BillPayments.js => BillPayments.ts} | 100 ++++--- .../services/Purchases/{Bills.js => Bills.ts} | 172 ++++++----- server/src/services/Sales/HasItemsEntries.ts | 25 +- ...sterService.js => JournalPosterService.ts} | 20 +- server/src/services/Sales/PaymentsReceives.ts | 144 ++++++---- server/src/services/Sales/SalesEstimate.ts | 102 ++++--- server/src/services/Sales/SalesInvoices.ts | 175 +++++++----- .../src/services/Sales/SalesInvoicesCost.ts | 46 +-- server/src/services/Sales/SalesReceipts.ts | 93 +++--- server/src/services/Tenancy/TenancyService.ts | 21 ++ .../Subscriptions/{Voucher.ts => Voucher.js} | 12 +- server/src/system/models/SystemModel.js | 1 + server/src/utils/index.js | 2 +- server/tsconfig.json | 5 + 53 files changed, 1581 insertions(+), 1055 deletions(-) rename server/src/http/controllers/{BaseController.js => BaseController.ts} (93%) rename server/src/http/controllers/Purchases/{Bills.js => Bills.ts} (70%) rename server/src/http/controllers/Purchases/{index.js => index.ts} (60%) rename server/src/http/controllers/Sales/{PaymentReceives.js => PaymentReceives.ts} (67%) rename server/src/http/controllers/Sales/{SalesEstimates.js => SalesEstimates.ts} (69%) rename server/src/http/controllers/Sales/{SalesInvoices.js => SalesInvoices.ts} (70%) rename server/src/http/controllers/Sales/{SalesReceipts.js => SalesReceipts.ts} (71%) rename server/src/http/controllers/Sales/{index.js => index.ts} (52%) create mode 100644 server/src/interfaces/Bill.ts create mode 100644 server/src/interfaces/PaymentReceive.ts create mode 100644 server/src/interfaces/SaleEstimate.ts create mode 100644 server/src/jobs/MailNotifcationSubscribeEnd.ts create mode 100644 server/src/jobs/MailNotificationSubscribeEnd.ts create mode 100644 server/src/jobs/SMSNotificationSubscribeEnd.ts create mode 100644 server/src/jobs/SMSNotificationTrialEnd.ts delete mode 100644 server/src/services/Accounts/AccountsService.js create mode 100644 server/src/services/Accounts/AccountsService.ts delete mode 100644 server/src/services/Items/ItemsService.js create mode 100644 server/src/services/Items/ItemsService.ts rename server/src/services/Purchases/{BillPayments.js => BillPayments.ts} (74%) rename server/src/services/Purchases/{Bills.js => Bills.ts} (67%) rename server/src/services/Sales/{JournalPosterService.js => JournalPosterService.ts} (55%) create mode 100644 server/src/services/Tenancy/TenancyService.ts rename server/src/system/models/Subscriptions/{Voucher.ts => Voucher.js} (85%) diff --git a/server/src/http/controllers/Accounting.js b/server/src/http/controllers/Accounting.js index 9c5f2ac92..fd0e0c911 100644 --- a/server/src/http/controllers/Accounting.js +++ b/server/src/http/controllers/Accounting.js @@ -28,13 +28,11 @@ export default { this.getManualJournal.validation, asyncMiddleware(this.getManualJournal.handler) ); - router.get( '/manual-journals', this.manualJournals.validation, asyncMiddleware(this.manualJournals.handler) ); - router.post( '/make-journal-entries', this.validateMediaIds, @@ -42,13 +40,11 @@ export default { this.makeJournalEntries.validation, asyncMiddleware(this.makeJournalEntries.handler) ); - router.post( '/manual-journals/:id/publish', this.publishManualJournal.validation, asyncMiddleware(this.publishManualJournal.handler) ); - router.post( '/manual-journals/:id', this.validateMediaIds, @@ -56,31 +52,26 @@ export default { this.editManualJournal.validation, asyncMiddleware(this.editManualJournal.handler) ); - router.delete( '/manual-journals/:id', this.deleteManualJournal.validation, asyncMiddleware(this.deleteManualJournal.handler) ); - router.delete( '/manual-journals', this.deleteBulkManualJournals.validation, asyncMiddleware(this.deleteBulkManualJournals.handler) ); - router.post( '/recurring-journal-entries', this.recurringJournalEntries.validation, asyncMiddleware(this.recurringJournalEntries.handler) ); - router.post( 'quick-journal-entries', this.quickJournalEntries.validation, asyncMiddleware(this.quickJournalEntries.handler) ); - return router; }, diff --git a/server/src/http/controllers/BaseController.js b/server/src/http/controllers/BaseController.ts similarity index 93% rename from server/src/http/controllers/BaseController.js rename to server/src/http/controllers/BaseController.ts index 17a2ade45..a1aea3170 100644 --- a/server/src/http/controllers/BaseController.js +++ b/server/src/http/controllers/BaseController.ts @@ -1,6 +1,3 @@ - - export default class BaseController { - } \ No newline at end of file diff --git a/server/src/http/controllers/Items.ts b/server/src/http/controllers/Items.ts index 550addb25..583b1accd 100644 --- a/server/src/http/controllers/Items.ts +++ b/server/src/http/controllers/Items.ts @@ -1,5 +1,6 @@ +import { Inject, Service } from 'typedi'; import { Router, Request, Response } from 'express'; -import { check, param, query, ValidationChain } from 'express-validator'; +import { check, param, query, ValidationChain, matchedData } from 'express-validator'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; import validateMiddleware from '@/http/middleware/validateMiddleware'; import ItemsService from '@/services/Items/ItemsService'; @@ -7,23 +8,27 @@ import DynamicListing from '@/services/DynamicListing/DynamicListing'; import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder'; import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing'; +@Service() export default class ItemsController { + @Inject() + itemsService: ItemsService; + /** * Router constructor. */ - static router() { + router() { const router = Router(); router.post( '/', this.validateItemSchema, validateMiddleware, - asyncMiddleware(this.validateCategoryExistance), - asyncMiddleware(this.validateCostAccountExistance), - asyncMiddleware(this.validateSellAccountExistance), - asyncMiddleware(this.validateInventoryAccountExistance), - asyncMiddleware(this.validateItemNameExistance), - asyncMiddleware(this.newItem), + asyncMiddleware(this.validateCategoryExistance.bind(this)), + asyncMiddleware(this.validateCostAccountExistance.bind(this)), + asyncMiddleware(this.validateSellAccountExistance.bind(this)), + asyncMiddleware(this.validateInventoryAccountExistance.bind(this)), + asyncMiddleware(this.validateItemNameExistance.bind(this)), + asyncMiddleware(this.newItem.bind(this)), ); router.post( '/:id', [ @@ -31,49 +36,41 @@ export default class ItemsController { ...this.validateSpecificItemSchema, ], validateMiddleware, - asyncMiddleware(this.validateItemExistance), - asyncMiddleware(this.validateCategoryExistance), - asyncMiddleware(this.validateCostAccountExistance), - asyncMiddleware(this.validateSellAccountExistance), - asyncMiddleware(this.validateInventoryAccountExistance), - asyncMiddleware(this.validateItemNameExistance), - asyncMiddleware(this.editItem), + asyncMiddleware(this.validateItemExistance.bind(this)), + asyncMiddleware(this.validateCategoryExistance.bind(this)), + asyncMiddleware(this.validateCostAccountExistance.bind(this)), + asyncMiddleware(this.validateSellAccountExistance.bind(this)), + asyncMiddleware(this.validateInventoryAccountExistance.bind(this)), + asyncMiddleware(this.validateItemNameExistance.bind(this)), + asyncMiddleware(this.editItem.bind(this)), ); router.delete( '/:id', this.validateSpecificItemSchema, validateMiddleware, - asyncMiddleware(this.validateItemExistance), - asyncMiddleware(this.deleteItem), + asyncMiddleware(this.validateItemExistance.bind(this)), + asyncMiddleware(this.deleteItem.bind(this)), ); router.get( '/:id', this.validateSpecificItemSchema, validateMiddleware, - asyncMiddleware(this.validateItemExistance), - asyncMiddleware(this.getItem), + asyncMiddleware(this.validateItemExistance.bind(this)), + asyncMiddleware(this.getItem.bind(this)), ); router.get( '/', this.validateListQuerySchema, validateMiddleware, - asyncMiddleware(this.listItems), + asyncMiddleware(this.listItems.bind(this)), ); return router; } /** * Validate item schema. - * - * @param {Request} req - - * @param {Response} res - - * @return {ValidationChain[]} - validation chain. */ - static get validateItemSchema( - req: Request, - res: Response, - next: Function, - ): ValidationChain[] { + get validateItemSchema(): ValidationChain[] { return [ check('name').exists(), check('type').exists().trim().escape() @@ -122,7 +119,7 @@ export default class ItemsController { /** * Validate specific item params schema. */ - static get validateSpecificItemSchema(): ValidationChain[] { + get validateSpecificItemSchema(): ValidationChain[] { return [ param('id').exists().isNumeric().toInt(), ]; @@ -132,7 +129,7 @@ export default class ItemsController { /** * Validate list query schema */ - static get validateListQuerySchema() { + get validateListQuerySchema() { return [ query('column_sort_order').optional().isIn(['created_at', 'name', 'amount', 'sku']), query('sort_order').optional().isIn(['desc', 'asc']), @@ -149,7 +146,7 @@ export default class ItemsController { * @param {Response} res - * @param {NextFunction} next - */ - static async validateItemExistance(req: Request, res: Response, next: Function) { + async validateItemExistance(req: Request, res: Response, next: Function) { const { Item } = req.models; const itemId: number = req.params.id; @@ -169,7 +166,7 @@ export default class ItemsController { * @param {Response} res * @param {NextFunction} next */ - static async validateItemNameExistance(req: Request, res: Response, next: Function) { + async validateItemNameExistance(req: Request, res: Response, next: Function) { const { Item } = req.models; const item = req.body; const itemId: number = req.params.id; @@ -195,7 +192,7 @@ export default class ItemsController { * @param {Response} res * @param {Function} next */ - static async validateCategoryExistance(req: Request, res: Response, next: Function) { + async validateCategoryExistance(req: Request, res: Response, next: Function) { const { ItemCategory } = req.models; const item = req.body; @@ -217,7 +214,7 @@ export default class ItemsController { * @param {Response} res * @param {Function} next */ - static async validateCostAccountExistance(req: Request, res: Response, next: Function) { + async validateCostAccountExistance(req: Request, res: Response, next: Function) { const { Account, AccountType } = req.models; const item = req.body; @@ -244,7 +241,7 @@ export default class ItemsController { * @param {Response} res * @param {NextFunction} next */ - static async validateSellAccountExistance(req: Request, res: Response, next: Function) { + async validateSellAccountExistance(req: Request, res: Response, next: Function) { const { Account, AccountType } = req.models; const item = req.body; @@ -271,7 +268,7 @@ export default class ItemsController { * @param {Response} res * @param {NextFunction} next */ - static async validateInventoryAccountExistance(req: Request, res: Response, next: Function) { + async validateInventoryAccountExistance(req: Request, res: Response, next: Function) { const { Account, AccountType } = req.models; const item = req.body; @@ -297,9 +294,14 @@ export default class ItemsController { * @param {Request} req * @param {Response} res */ - static async newItem(req: Request, res: Response,) { - const item = req.body; - const storedItem = await ItemsService.newItem(item); + async newItem(req: Request, res: Response,) { + const { tenantId } = req; + + const item = matchedData(req, { + locations: ['body'], + includeOptionals: true + }); + const storedItem = await this.itemsService.newItem(tenantId, item); return res.status(200).send({ id: storedItem.id }); } @@ -309,10 +311,15 @@ export default class ItemsController { * @param {Request} req * @param {Response} res */ - static async editItem(req: Request, res: Response) { - const item = req.body; + async editItem(req: Request, res: Response) { + const { tenantId } = req; + const itemId: number = req.params.id; - const updatedItem = await ItemsService.editItem(item, itemId); + const item = matchedData(req, { + locations: ['body'], + includeOptionals: true + }); + const updatedItem = await this.itemsService.editItem(tenantId, item, itemId); return res.status(200).send({ id: itemId }); } @@ -322,9 +329,11 @@ export default class ItemsController { * @param {Request} req * @param {Response} res */ - static async deleteItem(req: Request, res: Response) { + async deleteItem(req: Request, res: Response) { const itemId: number = req.params.id; - await ItemsService.deleteItem(itemId); + const { tenantId } = req; + + await this.itemsService.deleteItem(tenantId, itemId); return res.status(200).send({ id: itemId }); } @@ -335,9 +344,11 @@ export default class ItemsController { * @param {Response} res * @return {Response} */ - static async getItem(req: Request, res: Response) { + async getItem(req: Request, res: Response) { const itemId: number = req.params.id; - const storedItem = await ItemsService.getItemWithMetadata(itemId); + const { tenantId } = req; + + const storedItem = await this.itemsService.getItemWithMetadata(tenantId, itemId); return res.status(200).send({ item: storedItem }); } @@ -347,7 +358,7 @@ export default class ItemsController { * @param {Request} req * @param {Response} res */ - static async listItems(req: Request, res: Response) { + async listItems(req: Request, res: Response) { const filter = { filter_roles: [], sort_order: 'asc', diff --git a/server/src/http/controllers/Purchases/Bills.js b/server/src/http/controllers/Purchases/Bills.ts similarity index 70% rename from server/src/http/controllers/Purchases/Bills.js rename to server/src/http/controllers/Purchases/Bills.ts index c23418b87..1a659601d 100644 --- a/server/src/http/controllers/Purchases/Bills.js +++ b/server/src/http/controllers/Purchases/Bills.ts @@ -1,63 +1,75 @@ -import express from 'express'; -import { check, param, query } from 'express-validator'; +import { Router, Request, Response } from 'express'; +import { check, param, query, matchedData } from 'express-validator'; +import { Service, Inject } from 'typedi'; +import { difference } from 'lodash'; +import { BillOTD } from '@/interfaces'; import validateMiddleware from '@/http/middleware/validateMiddleware'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; import BillsService from '@/services/Purchases/Bills'; import BaseController from '@/http/controllers/BaseController'; -import VendorsServices from '@/services/Vendors/VendorsService'; import ItemsService from '@/services/Items/ItemsService'; +import TenancyService from '@/services/Tenancy/TenancyService'; import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder'; import DynamicListing from '@/services/DynamicListing/DynamicListing'; import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/HasDynamicListing'; -import { difference } from 'lodash'; +@Service() export default class BillsController extends BaseController { + @Inject() + itemsService: ItemsService; + + @Inject() + billsService: BillsService; + + @Inject() + tenancy: TenancyService; + /** * Router constructor. */ - static router() { - const router = express.Router(); + router() { + const router = Router(); router.post( '/', [...this.billValidationSchema], validateMiddleware, - asyncMiddleware(this.validateVendorExistance), - asyncMiddleware(this.validateItemsIds), - asyncMiddleware(this.validateBillNumberExists), - asyncMiddleware(this.validateNonPurchasableEntriesItems), - asyncMiddleware(this.newBill) + asyncMiddleware(this.validateVendorExistance.bind(this)), + asyncMiddleware(this.validateItemsIds.bind(this)), + asyncMiddleware(this.validateBillNumberExists.bind(this)), + asyncMiddleware(this.validateNonPurchasableEntriesItems.bind(this)), + asyncMiddleware(this.newBill.bind(this)) ); router.post( '/:id', [...this.billValidationSchema, ...this.specificBillValidationSchema], validateMiddleware, - asyncMiddleware(this.validateBillExistance), - asyncMiddleware(this.validateVendorExistance), - asyncMiddleware(this.validateItemsIds), - asyncMiddleware(this.validateEntriesIdsExistance), - asyncMiddleware(this.validateNonPurchasableEntriesItems), - asyncMiddleware(this.editBill) + asyncMiddleware(this.validateBillExistance.bind(this)), + asyncMiddleware(this.validateVendorExistance.bind(this)), + asyncMiddleware(this.validateItemsIds.bind(this)), + asyncMiddleware(this.validateEntriesIdsExistance.bind(this)), + asyncMiddleware(this.validateNonPurchasableEntriesItems.bind(this)), + asyncMiddleware(this.editBill.bind(this)) ); router.get( '/:id', [...this.specificBillValidationSchema], validateMiddleware, - asyncMiddleware(this.validateBillExistance), - asyncMiddleware(this.getBill) + asyncMiddleware(this.validateBillExistance.bind(this)), + asyncMiddleware(this.getBill.bind(this)) ); router.get( '/', [...this.billsListingValidationSchema], validateMiddleware, - asyncMiddleware(this.listingBills) + asyncMiddleware(this.listingBills.bind(this)) ); router.delete( '/:id', [...this.specificBillValidationSchema], validateMiddleware, - asyncMiddleware(this.validateBillExistance), - asyncMiddleware(this.deleteBill) + asyncMiddleware(this.validateBillExistance.bind(this)), + asyncMiddleware(this.deleteBill.bind(this)) ); return router; } @@ -65,7 +77,7 @@ export default class BillsController extends BaseController { /** * Common validation schema. */ - static get billValidationSchema() { + get billValidationSchema() { return [ check('bill_number').exists().trim().escape(), check('bill_date').exists().isISO8601(), @@ -87,14 +99,14 @@ export default class BillsController extends BaseController { /** * Bill validation schema. */ - static get specificBillValidationSchema() { + get specificBillValidationSchema() { return [param('id').exists().isNumeric().toInt()]; } /** * Bills list validation schema. */ - static get billsListingValidationSchema() { + get billsListingValidationSchema() { return [ query('custom_view_id').optional().isNumeric().toInt(), query('stringified_filter_roles').optional().isJSON(), @@ -107,14 +119,17 @@ export default class BillsController extends BaseController { /** * Validates whether the vendor is exist. + * @async * @param {Request} req * @param {Response} res * @param {Function} next */ - static async validateVendorExistance(req, res, next) { - const isVendorExists = await VendorsServices.isVendorExists( - req.body.vendor_id - ); + async validateVendorExistance(req: Request, res: Response, next: Function) { + const { tenantId } = req; + const { Vendor } = req.models; + + const isVendorExists = await Vendor.query().findById(req.body.vendor_id); + if (!isVendorExists) { return res.status(400).send({ errors: [{ type: 'VENDOR.ID.NOT.FOUND', code: 300 }], @@ -125,12 +140,17 @@ export default class BillsController extends BaseController { /** * Validates the given bill existance. + * @async * @param {Request} req * @param {Response} res * @param {Function} next */ - static async validateBillExistance(req, res, next) { - const isBillExists = await BillsService.isBillExists(req.params.id); + async validateBillExistance(req: Request, res: Response, next: Function) { + const billId: number = req.params.id; + const { tenantId } = req; + + const isBillExists = await this.billsService.isBillExists(tenantId, billId); + if (!isBillExists) { return res.status(400).send({ errors: [{ type: 'BILL.NOT.FOUND', code: 200 }], @@ -141,13 +161,17 @@ export default class BillsController extends BaseController { /** * Validates the entries items ids. + * @async * @param {Request} req * @param {Response} res * @param {Function} next */ - static async validateItemsIds(req, res, next) { + async validateItemsIds(req: Request, res: Response, next: Function) { + const { tenantId } = req; const itemsIds = req.body.entries.map((e) => e.item_id); - const notFoundItemsIds = await ItemsService.isItemsIdsExists(itemsIds); + + const notFoundItemsIds = await this.itemsService.isItemsIdsExists(tenantId, itemsIds); + if (notFoundItemsIds.length > 0) { return res.status(400).send({ errors: [{ type: 'ITEMS.IDS.NOT.FOUND', code: 400 }], @@ -158,15 +182,17 @@ export default class BillsController extends BaseController { /** * Validates the bill number existance. + * @async * @param {Request} req * @param {Response} res * @param {Function} next */ - static async validateBillNumberExists(req, res, next) { - const isBillNoExists = await BillsService.isBillNoExists( - req.body.bill_number - ); + async validateBillNumberExists(req: Request, res: Response, next: Function) { + const { tenantId } = req; + const isBillNoExists = await this.billsService.isBillNoExists( + tenantId, req.body.bill_number, + ); if (isBillNoExists) { return res.status(400).send({ errors: [{ type: 'BILL.NUMBER.EXISTS', code: 500 }], @@ -181,20 +207,20 @@ export default class BillsController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateEntriesIdsExistance(req, res, next) { + async validateEntriesIdsExistance(req: Request, res: Response, next: Function) { const { id: billId } = req.params; const bill = { ...req.body }; const { ItemEntry } = req.models; const entriesIds = bill.entries.filter((e) => e.id).map((e) => e.id); - const storedEntries = await ItemEntry.tenant() - .query() + const storedEntries = await ItemEntry.query() .whereIn('reference_id', [billId]) .whereIn('reference_type', ['Bill']); const storedEntriesIds = storedEntries.map((entry) => entry.id); const notFoundEntriesIds = difference(entriesIds, storedEntriesIds); + if (notFoundEntriesIds.length > 0) { return res.status(400).send({ errors: [{ type: 'BILL.ENTRIES.IDS.NOT.FOUND', code: 600 }], @@ -209,7 +235,7 @@ export default class BillsController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateNonPurchasableEntriesItems(req, res, next) { + async validateNonPurchasableEntriesItems(req: Request, res: Response, next: Function) { const { Item } = req.models; const bill = { ...req.body }; const itemsIds = bill.entries.map(e => e.item_id); @@ -235,17 +261,15 @@ export default class BillsController extends BaseController { * @param {Response} res * @param {Function} next */ - static async newBill(req, res, next) { + async newBill(req: Request, res: Response, next: Function) { + const { tenantId } = req; const { ItemEntry } = req.models; - const bill = { - ...req.body, - entries: req.body.entries.map((entry) => ({ - ...entry, - amount: ItemEntry.calcAmount(entry), - })), - }; - const storedBill = await BillsService.createBill(bill); + const billOTD: BillOTD = matchedData(req, { + locations: ['body'], + includeOptionals: true + }); + const storedBill = await this.billsService.createBill(tenantId, billOTD); return res.status(200).send({ id: storedBill.id }); } @@ -255,17 +279,16 @@ export default class BillsController extends BaseController { * @param {Request} req * @param {Response} res */ - static async editBill(req, res) { - const { ItemEntry } = req.models; + async editBill(req: Request, res: Response) { const { id: billId } = req.params; - const bill = { - ...req.body, - entries: req.body.entries.map((entry) => ({ - ...entry, - amount: ItemEntry.calcAmount(entry), - })), - }; - const editedBill = await BillsService.editBill(billId, bill); + const { ItemEntry } = req.models; + const { tenantId } = req; + + const billOTD: BillOTD = matchedData(req, { + locations: ['body'], + includeOptionals: true + }); + const editedBill = await this.billsService.editBill(tenantId, billId, billOTD); return res.status(200).send({ id: billId }); } @@ -276,9 +299,11 @@ export default class BillsController extends BaseController { * @param {Response} res * @return {Response} */ - static async getBill(req, res) { + async getBill(req: Request, res: Response) { + const { tenantId } = req; const { id: billId } = req.params; - const bill = await BillsService.getBillWithMetadata(billId); + + const bill = await this.billsService.getBillWithMetadata(tenantId, billId); return res.status(200).send({ bill }); } @@ -289,9 +314,11 @@ export default class BillsController extends BaseController { * @param {Response} res - * @return {Response} */ - static async deleteBill(req, res) { + async deleteBill(req: Request, res: Response) { const billId = req.params.id; - await BillsService.deleteBill(billId); + const { tenantId } = req; + + await this.billsService.deleteBill(tenantId, billId); return res.status(200).send({ id: billId }); } @@ -302,7 +329,7 @@ export default class BillsController extends BaseController { * @param {Response} res - * @return {Response} */ - static async listingBills(req, res) { + async listingBills(req: Request, res: Response) { const filter = { filter_roles: [], sort_order: 'asc', diff --git a/server/src/http/controllers/Purchases/BillsPayments.ts b/server/src/http/controllers/Purchases/BillsPayments.ts index 521377e04..bb583eead 100644 --- a/server/src/http/controllers/Purchases/BillsPayments.ts +++ b/server/src/http/controllers/Purchases/BillsPayments.ts @@ -1,6 +1,7 @@ import { Router } from 'express'; -import { check, param, query, ValidationChain } from 'express-validator'; +import { Service, Inject } from 'typedi'; +import { check, param, query, ValidationChain, matchedData } from 'express-validator'; import { difference } from 'lodash'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; import validateMiddleware from '@/http/middleware/validateMiddleware'; @@ -13,55 +14,62 @@ import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDyn /** * Bills payments controller. - * @controller + * @service */ +@Service() export default class BillsPayments extends BaseController { + @Inject() + billPaymentService: BillPaymentsService; + + @Inject() + accountsService: AccountsService; + /** * Router constructor. */ - static router() { + router() { const router = Router(); router.post('/', [ ...this.billPaymentSchemaValidation, ], validateMiddleware, - asyncMiddleware(this.validateBillPaymentVendorExistance), - asyncMiddleware(this.validatePaymentAccount), - asyncMiddleware(this.validatePaymentNumber), - asyncMiddleware(this.validateEntriesBillsExistance), - asyncMiddleware(this.validateVendorsDueAmount), - asyncMiddleware(this.createBillPayment), + asyncMiddleware(this.validateBillPaymentVendorExistance.bind(this)), + asyncMiddleware(this.validatePaymentAccount.bind(this)), + asyncMiddleware(this.validatePaymentNumber.bind(this)), + asyncMiddleware(this.validateEntriesBillsExistance.bind(this)), + asyncMiddleware(this.validateVendorsDueAmount.bind(this)), + asyncMiddleware(this.createBillPayment.bind(this)), ); router.post('/:id', [ ...this.billPaymentSchemaValidation, ...this.specificBillPaymentValidateSchema, ], validateMiddleware, - asyncMiddleware(this.validateBillPaymentVendorExistance), - asyncMiddleware(this.validatePaymentAccount), - asyncMiddleware(this.validatePaymentNumber), - asyncMiddleware(this.validateEntriesIdsExistance), - asyncMiddleware(this.validateEntriesBillsExistance), - asyncMiddleware(this.validateVendorsDueAmount), - asyncMiddleware(this.editBillPayment), + asyncMiddleware(this.validateBillPaymentVendorExistance.bind(this)), + asyncMiddleware(this.validatePaymentAccount.bind(this)), + asyncMiddleware(this.validatePaymentNumber.bind(this)), + asyncMiddleware(this.validateEntriesIdsExistance.bind(this)), + asyncMiddleware(this.validateEntriesBillsExistance.bind(this)), + asyncMiddleware(this.validateVendorsDueAmount.bind(this)), + asyncMiddleware(this.editBillPayment.bind(this)), ) router.delete('/:id', this.specificBillPaymentValidateSchema, validateMiddleware, - asyncMiddleware(this.validateBillPaymentExistance), - asyncMiddleware(this.deleteBillPayment), + asyncMiddleware(this.validateBillPaymentExistance.bind(this)), + asyncMiddleware(this.deleteBillPayment.bind(this)), ); router.get('/:id', this.specificBillPaymentValidateSchema, validateMiddleware, - asyncMiddleware(this.validateBillPaymentExistance), - asyncMiddleware(this.getBillPayment), + asyncMiddleware(this.validateBillPaymentExistance.bind(this)), + asyncMiddleware(this.getBillPayment.bind(this)), ); router.get('/', this.listingValidationSchema, validateMiddleware, - asyncMiddleware(this.getBillsPayments) + asyncMiddleware(this.getBillsPayments.bind(this)) ); return router; } @@ -69,7 +77,7 @@ export default class BillsPayments extends BaseController { /** * Bill payments schema validation. */ - static get billPaymentSchemaValidation(): ValidationChain[] { + get billPaymentSchemaValidation(): ValidationChain[] { return [ check('vendor_id').exists().isNumeric().toInt(), check('payment_account_id').exists().isNumeric().toInt(), @@ -87,19 +95,33 @@ export default class BillsPayments extends BaseController { /** * Specific bill payment schema validation. */ - static get specificBillPaymentValidateSchema(): ValidationChain[] { + get specificBillPaymentValidateSchema(): ValidationChain[] { return [ param('id').exists().isNumeric().toInt(), ]; } + /** + * Bills payment list validation schema. + */ + get listingValidationSchema(): ValidationChain[] { + return [ + query('custom_view_id').optional().isNumeric().toInt(), + query('stringified_filter_roles').optional().isJSON(), + query('column_sort_by').optional(), + query('sort_order').optional().isIn(['desc', 'asc']), + query('page').optional().isNumeric().toInt(), + query('page_size').optional().isNumeric().toInt(), + ]; + } + /** * Validate whether the bill payment vendor exists on the storage. * @param {Request} req * @param {Response} res * @param {Function} next */ - static async validateBillPaymentVendorExistance(req: Request, res: Response, next: any ) { + async validateBillPaymentVendorExistance(req: Request, res: Response, next: any ) { const billPayment = req.body; const { Vendor } = req.models; const isVendorExists = await Vendor.query().findById(billPayment.vendor_id); @@ -118,7 +140,7 @@ export default class BillsPayments extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateBillPaymentExistance(req: Request, res: Response, next: any ) { + async validateBillPaymentExistance(req: Request, res: Response, next: any ) { const { id: billPaymentId } = req.params; const { BillPayment } = req.models; const foundBillPayment = await BillPayment.query().findById(billPaymentId); @@ -137,11 +159,15 @@ export default class BillsPayments extends BaseController { * @param {Response} res * @param {Function} next */ - static async validatePaymentAccount(req: Request, res: Response, next: any) { + async validatePaymentAccount(req: Request, res: Response, next: any) { + const { tenantId } = req; const billPayment = { ...req.body }; - const isAccountExists = await AccountsService.isAccountExists( + + const isAccountExists = await this.accountsService.isAccountExists( + tenantId, billPayment.payment_account_id ); + if (!isAccountExists) { return res.status(400).send({ errors: [{ type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 200 }], @@ -156,7 +182,7 @@ export default class BillsPayments extends BaseController { * @param {Response} res * @param {Function} res */ - static async validatePaymentNumber(req: Request, res: Response, next: any) { + async validatePaymentNumber(req: Request, res: Response, next: any) { const billPayment = { ...req.body }; const { id: billPaymentId } = req.params; const { BillPayment } = req.models; @@ -164,7 +190,6 @@ export default class BillsPayments extends BaseController { const foundBillPayment = await BillPayment.query() .onBuild((builder: any) => { builder.where('payment_number', billPayment.payment_number) - if (billPaymentId) { builder.whereNot('id', billPaymentId); } @@ -185,7 +210,7 @@ export default class BillsPayments extends BaseController { * @param {Response} res * @param {NextFunction} next */ - static async validateEntriesBillsExistance(req: Request, res: Response, next: any) { + async validateEntriesBillsExistance(req: Request, res: Response, next: any) { const { Bill } = req.models; const billPayment = { ...req.body }; const entriesBillsIds = billPayment.entries.map((e: any) => e.bill_id); @@ -210,7 +235,7 @@ export default class BillsPayments extends BaseController { * @param {NextFunction} next * @return {void} */ - static async validateVendorsDueAmount(req: Request, res: Response, next: Function) { + async validateVendorsDueAmount(req: Request, res: Response, next: Function) { const { Bill } = req.models; const billsIds = req.body.entries.map((entry: any) => entry.bill_id); const storedBills = await Bill.query() @@ -248,7 +273,7 @@ export default class BillsPayments extends BaseController { * @param {Response} res * @return {Response} */ - static async validateEntriesIdsExistance(req: Request, res: Response, next: Function) { + async validateEntriesIdsExistance(req: Request, res: Response, next: Function) { const { BillPaymentEntry } = req.models; const billPayment = { id: req.params.id, ...req.body }; @@ -256,7 +281,7 @@ export default class BillsPayments extends BaseController { .filter((entry: any) => entry.id) .map((entry: any) => entry.id); - const storedEntries = await BillPaymentEntry.tenant().query() + const storedEntries = await BillPaymentEntry.query() .where('bill_payment_id', billPayment.id); const storedEntriesIds = storedEntries.map((entry: any) => entry.id); @@ -277,9 +302,15 @@ export default class BillsPayments extends BaseController { * @param {Response} res * @param {Response} res */ - static async createBillPayment(req: Request, res: Response) { - const billPayment = { ...req.body }; - const storedPayment = await BillPaymentsService.createBillPayment(billPayment); + async createBillPayment(req: Request, res: Response) { + const { tenantId } = req; + + const billPayment = matchedData(req, { + locations: ['body'], + includeOptionals: true, + }); + const storedPayment = await this.billPaymentService + .createBillPayment(tenantId, billPayment); return res.status(200).send({ id: storedPayment.id }); } @@ -289,10 +320,14 @@ export default class BillsPayments extends BaseController { * @param {Request} req * @param {Response} res */ - static async editBillPayment(req: Request, res: Response) { - const billPayment = { ...req.body }; - const { id: billPaymentId } = req.params; + async editBillPayment(req: Request, res: Response) { + const { tenantId } = req; + const billPayment = matchedData(req, { + locations: ['body'], + includeOptionals: true, + }); + const { id: billPaymentId } = req.params; const { BillPayment } = req.models; const oldBillPayment = await BillPayment.query() @@ -300,7 +335,8 @@ export default class BillsPayments extends BaseController { .withGraphFetched('entries') .first(); - await BillPaymentsService.editBillPayment( + await this.billPaymentService.editBillPayment( + tenantId, billPaymentId, billPayment, oldBillPayment, @@ -315,11 +351,13 @@ export default class BillsPayments extends BaseController { * @param {Response} res - * @return {Response} res - */ - static async deleteBillPayment(req: Request, res: Response) { + async deleteBillPayment(req: Request, res: Response) { + const { tenantId } = req; + const { id: billPaymentId } = req.params; const billPayment = req.body; - await BillPaymentsService.deleteBillPayment(billPaymentId); + await this.billPaymentService.deleteBillPayment(tenantId, billPaymentId); return res.status(200).send({ id: billPaymentId }); } @@ -329,34 +367,23 @@ export default class BillsPayments extends BaseController { * @param {Request} req * @param {Response} res */ - static async getBillPayment(req: Request, res: Response) { + async getBillPayment(req: Request, res: Response) { + const { tenantId } = req; const { id: billPaymentId } = req.params; - const billPayment = await BillPaymentsService.getBillPaymentWithMetadata(billPaymentId); + + const billPayment = await this.billPaymentService + .getBillPaymentWithMetadata(tenantId, billPaymentId); return res.status(200).send({ bill_payment: { ...billPayment } }); } - /** - * Bills payment list validation schema. - */ - static get listingValidationSchema(): ValidationChain[] { - return [ - query('custom_view_id').optional().isNumeric().toInt(), - query('stringified_filter_roles').optional().isJSON(), - query('column_sort_by').optional(), - query('sort_order').optional().isIn(['desc', 'asc']), - query('page').optional().isNumeric().toInt(), - query('page_size').optional().isNumeric().toInt(), - ]; - } - /** * Retrieve bills payments listing with pagination metadata. * @param {Request} req - * @param {Response} res - * @return {Response} */ - static async getBillsPayments(req: Request, res: Response) { + async getBillsPayments(req: Request, res: Response) { const filter = { filter_roles: [], sort_order: 'asc', diff --git a/server/src/http/controllers/Purchases/index.js b/server/src/http/controllers/Purchases/index.ts similarity index 60% rename from server/src/http/controllers/Purchases/index.js rename to server/src/http/controllers/Purchases/index.ts index 5c8a3cb24..9660dda47 100644 --- a/server/src/http/controllers/Purchases/index.js +++ b/server/src/http/controllers/Purchases/index.ts @@ -1,4 +1,5 @@ import express from 'express'; +import { Container } from 'typedi'; import Bills from '@/http/controllers/Purchases/Bills' import BillPayments from '@/http/controllers/Purchases/BillsPayments'; @@ -7,8 +8,8 @@ export default { router() { const router = express.Router(); - router.use('/bills', Bills.router()); - router.use('/bill_payments', BillPayments.router()); + router.use('/bills', Container.get(Bills).router()); + router.use('/bill_payments', Container.get(BillPayments).router()); return router; } diff --git a/server/src/http/controllers/Sales/PaymentReceives.js b/server/src/http/controllers/Sales/PaymentReceives.ts similarity index 67% rename from server/src/http/controllers/Sales/PaymentReceives.js rename to server/src/http/controllers/Sales/PaymentReceives.ts index 973eeb00e..f54a7af60 100644 --- a/server/src/http/controllers/Sales/PaymentReceives.js +++ b/server/src/http/controllers/Sales/PaymentReceives.ts @@ -1,13 +1,14 @@ -import express from 'express'; -import { check, param, query } from 'express-validator'; +import { Router, Request, Response } from 'express'; +import { check, param, query, ValidationChain, matchedData } from 'express-validator'; import { difference } from 'lodash'; -import { PaymentReceiveEntry } from '@/models'; +import { Inject, Service } from 'typedi'; +import { IPaymentReceive, IPaymentReceiveOTD } from '@/interfaces'; import BaseController from '@/http/controllers/BaseController'; import validateMiddleware from '@/http/middleware/validateMiddleware'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; import PaymentReceiveService from '@/services/Sales/PaymentsReceives'; import CustomersService from '@/services/Customers/CustomersService'; -import SaleInvoicesService from '@/services/Sales/SalesInvoices'; +import SaleInvoiceService from '@/services/Sales/SalesInvoices'; import AccountsService from '@/services/Accounts/AccountsService'; import DynamicListing from '@/services/DynamicListing/DynamicListing'; import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder'; @@ -15,70 +16,134 @@ import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDyn /** * Payments receives controller. - * @controller + * @service */ +@Service() export default class PaymentReceivesController extends BaseController { + @Inject() + paymentReceiveService: PaymentReceiveService; + + @Inject() + customersService: CustomersService; + + @Inject() + accountsService: AccountsService; + + @Inject() + saleInvoiceService: SaleInvoiceService; + /** * Router constructor. */ - static router() { - const router = express.Router(); + router() { + const router = Router(); router.post( '/:id', this.editPaymentReceiveValidation, validateMiddleware, - asyncMiddleware(this.validatePaymentReceiveExistance), - asyncMiddleware(this.validatePaymentReceiveNoExistance), - asyncMiddleware(this.validateCustomerExistance), - asyncMiddleware(this.validateDepositAccount), - asyncMiddleware(this.validateInvoicesIDs), - asyncMiddleware(this.validateEntriesIdsExistance), - asyncMiddleware(this.validateInvoicesPaymentsAmount), - asyncMiddleware(this.editPaymentReceive), + asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)), + asyncMiddleware(this.validatePaymentReceiveNoExistance.bind(this)), + asyncMiddleware(this.validateCustomerExistance.bind(this)), + asyncMiddleware(this.validateDepositAccount.bind(this)), + asyncMiddleware(this.validateInvoicesIDs.bind(this)), + asyncMiddleware(this.validateEntriesIdsExistance.bind(this)), + asyncMiddleware(this.validateInvoicesPaymentsAmount.bind(this)), + asyncMiddleware(this.editPaymentReceive.bind(this)), ); router.post( '/', this.newPaymentReceiveValidation, validateMiddleware, - asyncMiddleware(this.validatePaymentReceiveNoExistance), - asyncMiddleware(this.validateCustomerExistance), - asyncMiddleware(this.validateDepositAccount), - asyncMiddleware(this.validateInvoicesIDs), - asyncMiddleware(this.validateInvoicesPaymentsAmount), - asyncMiddleware(this.newPaymentReceive), + asyncMiddleware(this.validatePaymentReceiveNoExistance.bind(this)), + asyncMiddleware(this.validateCustomerExistance.bind(this)), + asyncMiddleware(this.validateDepositAccount.bind(this)), + asyncMiddleware(this.validateInvoicesIDs.bind(this)), + asyncMiddleware(this.validateInvoicesPaymentsAmount.bind(this)), + asyncMiddleware(this.newPaymentReceive.bind(this)), ); router.get( '/:id', this.paymentReceiveValidation, validateMiddleware, - asyncMiddleware(this.validatePaymentReceiveExistance), - asyncMiddleware(this.getPaymentReceive) + asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)), + asyncMiddleware(this.getPaymentReceive.bind(this)) ); router.get( '/', this.validatePaymentReceiveList, validateMiddleware, - asyncMiddleware(this.getPaymentReceiveList), + asyncMiddleware(this.getPaymentReceiveList.bind(this)), ); router.delete( '/:id', this.paymentReceiveValidation, validateMiddleware, - asyncMiddleware(this.validatePaymentReceiveExistance), - asyncMiddleware(this.deletePaymentReceive), + asyncMiddleware(this.validatePaymentReceiveExistance.bind(this)), + asyncMiddleware(this.deletePaymentReceive.bind(this)), ); return router; } + /** + * Payment receive schema. + * @return {Array} + */ + get paymentReceiveSchema(): ValidationChain[] { + return [ + check('customer_id').exists().isNumeric().toInt(), + check('payment_date').exists(), + check('reference_no').optional(), + check('deposit_account_id').exists().isNumeric().toInt(), + check('payment_receive_no').exists().trim().escape(), + check('statement').optional().trim().escape(), + + check('entries').isArray({ min: 1 }), + + check('entries.*.invoice_id').exists().isNumeric().toInt(), + check('entries.*.payment_amount').exists().isNumeric().toInt(), + ]; + } + + /** + * Payment receive list validation schema. + */ + get validatePaymentReceiveList(): ValidationChain[] { + return [ + query('custom_view_id').optional().isNumeric().toInt(), + query('stringified_filter_roles').optional().isJSON(), + query('column_sort_by').optional(), + query('sort_order').optional().isIn(['desc', 'asc']), + query('page').optional().isNumeric().toInt(), + query('page_size').optional().isNumeric().toInt(), + ] + } + + /** + * Validate payment receive parameters. + */ + get paymentReceiveValidation() { + return [param('id').exists().isNumeric().toInt()]; + } + + /** + * New payment receive validation schema. + * @return {Array} + */ + get newPaymentReceiveValidation() { + return [...this.paymentReceiveSchema]; + } + /** * Validates the payment receive number existance. * @param {Request} req * @param {Response} res * @param {Function} next */ - static async validatePaymentReceiveNoExistance(req, res, next) { - const isPaymentNoExists = await PaymentReceiveService.isPaymentReceiveNoExists( + async validatePaymentReceiveNoExistance(req: Request, res: Response, next: Function) { + const tenantId = req.tenantId; + const isPaymentNoExists = await this.paymentReceiveService.isPaymentReceiveNoExists( + tenantId, req.body.payment_receive_no, req.params.id, ); @@ -96,13 +161,16 @@ export default class PaymentReceivesController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validatePaymentReceiveExistance(req, res, next) { - const isPaymentNoExists = await PaymentReceiveService.isPaymentReceiveExists( - req.params.id - ); + async validatePaymentReceiveExistance(req: Request, res: Response, next: Function) { + const tenantId = req.tenantId; + const isPaymentNoExists = await this.paymentReceiveService + .isPaymentReceiveExists( + tenantId, + req.params.id + ); if (!isPaymentNoExists) { return res.status(400).send({ - errors: [{ type: 'PAYMENT.RECEIVE.NO.EXISTS', code: 600 }], + errors: [{ type: 'PAYMENT.RECEIVE.NOT.EXISTS', code: 600 }], }); } next(); @@ -114,8 +182,10 @@ export default class PaymentReceivesController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateDepositAccount(req, res, next) { - const isDepositAccExists = await AccountsService.isAccountExists( + async validateDepositAccount(req: Request, res: Response, next: Function) { + const tenantId = req.tenantId; + const isDepositAccExists = await this.accountsService.isAccountExists( + tenantId, req.body.deposit_account_id ); if (!isDepositAccExists) { @@ -132,10 +202,11 @@ export default class PaymentReceivesController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateCustomerExistance(req, res, next) { - const isCustomerExists = await CustomersService.isCustomerExists( - req.body.customer_id - ); + async validateCustomerExistance(req: Request, res: Response, next: Function) { + const { Customer } = req.models; + + const isCustomerExists = await Customer.query().findById(req.body.customer_id); + if (!isCustomerExists) { return res.status(400).send({ errors: [{ type: 'CUSTOMER.ID.NOT.EXISTS', code: 200 }], @@ -150,10 +221,14 @@ export default class PaymentReceivesController extends BaseController { * @param {Response} res - * @param {Function} next - */ - static async validateInvoicesIDs(req, res, next) { + async validateInvoicesIDs(req: Request, res: Response, next: Function) { const paymentReceive = { ...req.body }; - const invoicesIds = paymentReceive.entries.map((e) => e.invoice_id); - const notFoundInvoicesIDs = await SaleInvoicesService.isInvoicesExist( + const { tenantId } = req; + const invoicesIds = paymentReceive.entries + .map((e) => e.invoice_id); + + const notFoundInvoicesIDs = await this.saleInvoiceService.isInvoicesExist( + tenantId, invoicesIds, paymentReceive.customer_id, ); @@ -171,19 +246,19 @@ export default class PaymentReceivesController extends BaseController { * @param {Response} res - * @param {Function} next - */ - static async validateInvoicesPaymentsAmount(req, res, next) { + async validateInvoicesPaymentsAmount(req: Request, res: Response, next: Function) { const { SaleInvoice } = req.models; const invoicesIds = req.body.entries.map((e) => e.invoice_id); - const storedInvoices = await SaleInvoice.tenant() - .query() + + const storedInvoices = await SaleInvoice.query() .whereIn('id', invoicesIds); const storedInvoicesMap = new Map( storedInvoices.map((invoice) => [invoice.id, invoice]) ); - const hasWrongPaymentAmount = []; + const hasWrongPaymentAmount: any[] = []; - req.body.entries.forEach((entry, index) => { + req.body.entries.forEach((entry, index: number) => { const entryInvoice = storedInvoicesMap.get(entry.invoice_id); const { dueAmount } = entryInvoice; @@ -211,13 +286,15 @@ export default class PaymentReceivesController extends BaseController { * @param {Response} res * @return {Response} */ - static async validateEntriesIdsExistance(req, res, next) { + async validateEntriesIdsExistance(req: Request, res: Response, next: Function) { const paymentReceive = { id: req.params.id, ...req.body }; const entriesIds = paymentReceive.entries .filter(entry => entry.id) .map(entry => entry.id); - const storedEntries = await PaymentReceiveEntry.tenant().query() + const { PaymentReceiveEntry } = req.models; + + const storedEntries = await PaymentReceiveEntry.query() .where('payment_receive_id', paymentReceive.id); const storedEntriesIds = storedEntries.map((entry) => entry.id); @@ -231,49 +308,28 @@ export default class PaymentReceivesController extends BaseController { next(); } - /** - * Payment receive schema. - * @return {Array} - */ - static get paymentReceiveSchema() { - return [ - check('customer_id').exists().isNumeric().toInt(), - check('payment_date').exists(), - check('reference_no').optional(), - check('deposit_account_id').exists().isNumeric().toInt(), - check('payment_receive_no').exists().trim().escape(), - check('statement').optional().trim().escape(), - - check('entries').isArray({ min: 1 }), - - check('entries.*.invoice_id').exists().isNumeric().toInt(), - check('entries.*.payment_amount').exists().isNumeric().toInt(), - ]; - } - - /** - * New payment receive validation schema. - * @return {Array} - */ - static get newPaymentReceiveValidation() { - return [...this.paymentReceiveSchema]; - } - /** * Records payment receive to the given customer with associated invoices. */ - static async newPaymentReceive(req, res) { - const paymentReceive = { ...req.body }; - const storedPaymentReceive = await PaymentReceiveService.createPaymentReceive( - paymentReceive - ); + async newPaymentReceive(req: Request, res: Response) { + const { tenantId } = req; + const paymentReceive: IPaymentReceiveOTD = matchedData(req, { + locations: ['body'], + includeOptionals: true, + }); + + const storedPaymentReceive = await this.paymentReceiveService + .createPaymentReceive( + tenantId, + paymentReceive, + ); return res.status(200).send({ id: storedPaymentReceive.id }); } /** * Edit payment receive validation. */ - static get editPaymentReceiveValidation() { + get editPaymentReceiveValidation() { return [ param('id').exists().isNumeric().toInt(), ...this.paymentReceiveSchema, @@ -286,18 +342,23 @@ export default class PaymentReceivesController extends BaseController { * @param {Response} res * @return {Response} */ - static async editPaymentReceive(req, res) { - const paymentReceive = { ...req.body }; + async editPaymentReceive(req: Request, res: Response) { + const { tenantId } = req; const { id: paymentReceiveId } = req.params; const { PaymentReceive } = req.models; + const paymentReceive: IPaymentReceiveOTD = matchedData(req, { + locations: ['body'], + }); + // Retrieve the payment receive before updating. - const oldPaymentReceive = await PaymentReceive.query() + const oldPaymentReceive: IPaymentReceive = await PaymentReceive.query() .where('id', paymentReceiveId) .withGraphFetched('entries') .first(); - await PaymentReceiveService.editPaymentReceive( + await this.paymentReceiveService.editPaymentReceive( + tenantId, paymentReceiveId, paymentReceive, oldPaymentReceive, @@ -305,28 +366,23 @@ export default class PaymentReceivesController extends BaseController { return res.status(200).send({ id: paymentReceiveId }); } - /** - * Validate payment receive parameters. - */ - static get paymentReceiveValidation() { - return [param('id').exists().isNumeric().toInt()]; - } - /** * Delets the given payment receive id. * @param {Request} req * @param {Response} res */ - static async deletePaymentReceive(req, res) { + async deletePaymentReceive(req: Request, res: Response) { + const { tenantId } = req; const { id: paymentReceiveId } = req.params; - const { PaymentReceive } = req.models; + const storedPaymentReceive = await PaymentReceive.query() .where('id', paymentReceiveId) .withGraphFetched('entries') .first(); - await PaymentReceiveService.deletePaymentReceive( + await this.paymentReceiveService.deletePaymentReceive( + tenantId, paymentReceiveId, storedPaymentReceive ); @@ -339,7 +395,7 @@ export default class PaymentReceivesController extends BaseController { * @param {Request} req - * @param {Response} res - */ - static async getPaymentReceive(req, res) { + async getPaymentReceive(req: Request, res: Response) { const { id: paymentReceiveId } = req.params; const paymentReceive = await PaymentReceiveService.getPaymentReceive( paymentReceiveId @@ -347,27 +403,13 @@ export default class PaymentReceivesController extends BaseController { return res.status(200).send({ paymentReceive }); } - /** - * Payment receive list validation schema. - */ - static get validatePaymentReceiveList() { - return [ - query('custom_view_id').optional().isNumeric().toInt(), - query('stringified_filter_roles').optional().isJSON(), - query('column_sort_by').optional(), - query('sort_order').optional().isIn(['desc', 'asc']), - query('page').optional().isNumeric().toInt(), - query('page_size').optional().isNumeric().toInt(), - ] - } - /** * Retrieve payment receive list with pagination metadata. * @param {Request} req * @param {Response} res * @return {Response} */ - static async getPaymentReceiveList(req, res) { + async getPaymentReceiveList(req: Request, res: Response) { const filter = { filter_roles: [], sort_order: 'asc', diff --git a/server/src/http/controllers/Sales/SalesEstimates.js b/server/src/http/controllers/Sales/SalesEstimates.ts similarity index 69% rename from server/src/http/controllers/Sales/SalesEstimates.js rename to server/src/http/controllers/Sales/SalesEstimates.ts index 3885aa8d0..2fbb83405 100644 --- a/server/src/http/controllers/Sales/SalesEstimates.js +++ b/server/src/http/controllers/Sales/SalesEstimates.ts @@ -1,6 +1,7 @@ -import express from 'express'; -import { check, param, query } from 'express-validator'; -import { ItemEntry } from '@/models'; +import { Router, Request, Response } from 'express'; +import { check, param, query, matchedData } from 'express-validator'; +import { Inject, Service } from 'typedi'; +import { ISaleEstimate, ISaleEstimateOTD } from '@/interfaces'; import BaseController from '@/http/controllers/BaseController' import validateMiddleware from '@/http/middleware/validateMiddleware'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; @@ -10,21 +11,31 @@ import ItemsService from '@/services/Items/ItemsService'; import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder'; import DynamicListing from '@/services/DynamicListing/DynamicListing'; +@Service() export default class SalesEstimatesController extends BaseController { + @Inject() + saleEstimateService: SaleEstimateService; + + @Inject() + itemsService: ItemsService; + + @Inject() + customersService: CustomersService; + /** * Router constructor. */ - static router() { - const router = express.Router(); + router() { + const router = Router(); router.post( '/', this.estimateValidationSchema, validateMiddleware, - asyncMiddleware(this.validateEstimateCustomerExistance), - asyncMiddleware(this.validateEstimateNumberExistance), - asyncMiddleware(this.validateEstimateEntriesItemsExistance), - asyncMiddleware(this.newEstimate) + asyncMiddleware(this.validateEstimateCustomerExistance.bind(this)), + asyncMiddleware(this.validateEstimateNumberExistance.bind(this)), + asyncMiddleware(this.validateEstimateEntriesItemsExistance.bind(this)), + asyncMiddleware(this.newEstimate.bind(this)) ); router.post( '/:id', [ @@ -32,33 +43,33 @@ export default class SalesEstimatesController extends BaseController { ...this.estimateValidationSchema, ], validateMiddleware, - asyncMiddleware(this.validateEstimateIdExistance), - asyncMiddleware(this.validateEstimateCustomerExistance), - asyncMiddleware(this.validateEstimateNumberExistance), - asyncMiddleware(this.validateEstimateEntriesItemsExistance), - asyncMiddleware(this.valdiateInvoiceEntriesIdsExistance), - asyncMiddleware(this.editEstimate) + asyncMiddleware(this.validateEstimateIdExistance.bind(this)), + asyncMiddleware(this.validateEstimateCustomerExistance.bind(this)), + asyncMiddleware(this.validateEstimateNumberExistance.bind(this)), + asyncMiddleware(this.validateEstimateEntriesItemsExistance.bind(this)), + asyncMiddleware(this.valdiateInvoiceEntriesIdsExistance.bind(this)), + asyncMiddleware(this.editEstimate.bind(this)) ); router.delete( '/:id', [ this.validateSpecificEstimateSchema, ], validateMiddleware, - asyncMiddleware(this.validateEstimateIdExistance), - asyncMiddleware(this.deleteEstimate) + asyncMiddleware(this.validateEstimateIdExistance.bind(this)), + asyncMiddleware(this.deleteEstimate.bind(this)) ); router.get( '/:id', this.validateSpecificEstimateSchema, validateMiddleware, - asyncMiddleware(this.validateEstimateIdExistance), - asyncMiddleware(this.getEstimate) + asyncMiddleware(this.validateEstimateIdExistance.bind(this)), + asyncMiddleware(this.getEstimate.bind(this)) ); router.get( '/', this.validateEstimateListSchema, validateMiddleware, - asyncMiddleware(this.getEstimates) + asyncMiddleware(this.getEstimates.bind(this)) ); return router; } @@ -66,7 +77,7 @@ export default class SalesEstimatesController extends BaseController { /** * Estimate validation schema. */ - static get estimateValidationSchema() { + get estimateValidationSchema() { return [ check('customer_id').exists().isNumeric().toInt(), check('estimate_date').exists().isISO8601(), @@ -90,7 +101,7 @@ export default class SalesEstimatesController extends BaseController { /** * Specific sale estimate validation schema. */ - static get validateSpecificEstimateSchema() { + get validateSpecificEstimateSchema() { return [ param('id').exists().isNumeric().toInt(), ]; @@ -99,7 +110,7 @@ export default class SalesEstimatesController extends BaseController { /** * Sales estimates list validation schema. */ - static get validateEstimateListSchema() { + get validateEstimateListSchema() { return [ query('custom_view_id').optional().isNumeric().toInt(), query('stringified_filter_roles').optional().isJSON(), @@ -116,12 +127,13 @@ export default class SalesEstimatesController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateEstimateCustomerExistance(req, res, next) { + async validateEstimateCustomerExistance(req: Request, res: Response, next: Function) { const estimate = { ...req.body }; - const isCustomerExists = await CustomersService.isCustomerExists( - estimate.customer_id - ); - if (!isCustomerExists) { + const { Customer } = req.models + + const foundCustomer = await Customer.query().findById(estimate.customer_id); + + if (!foundCustomer) { return res.status(404).send({ errors: [{ type: 'CUSTOMER.ID.NOT.FOUND', code: 200 }], }); @@ -135,10 +147,12 @@ export default class SalesEstimatesController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateEstimateNumberExistance(req, res, next) { + async validateEstimateNumberExistance(req: Request, res: Response, next: Function) { const estimate = { ...req.body }; + const { tenantId } = req; - const isEstNumberUnqiue = await SaleEstimateService.isEstimateNumberUnique( + const isEstNumberUnqiue = await this.saleEstimateService.isEstimateNumberUnique( + tenantId, estimate.estimate_number, req.params.id, ); @@ -147,7 +161,7 @@ export default class SalesEstimatesController extends BaseController { errors: [{ type: 'ESTIMATE.NUMBER.IS.NOT.UNQIUE', code: 300 }], }); } - next(); + next(); } /** @@ -156,12 +170,13 @@ export default class SalesEstimatesController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateEstimateEntriesItemsExistance(req, res, next) { + async validateEstimateEntriesItemsExistance(req: Request, res: Response, next: Function) { + const tenantId = req.tenantId; const estimate = { ...req.body }; const estimateItemsIds = estimate.entries.map(e => e.item_id); // Validate items ids in estimate entries exists. - const notFoundItemsIds = await ItemsService.isItemsIdsExists(estimateItemsIds); + const notFoundItemsIds = await this.itemsService.isItemsIdsExists(tenantId, estimateItemsIds); if (notFoundItemsIds.length > 0) { return res.boom.badRequest(null, { @@ -177,9 +192,12 @@ export default class SalesEstimatesController extends BaseController { * @param {Response} res * @param {Function} next */ - static async validateEstimateIdExistance(req, res, next) { + async validateEstimateIdExistance(req: Request, res: Response, next: Function) { const { id: estimateId } = req.params; - const storedEstimate = await SaleEstimateService.getEstimate(estimateId); + const { tenantId } = req; + + const storedEstimate = await this.saleEstimateService + .getEstimate(tenantId, estimateId); if (!storedEstimate) { return res.status(404).send({ @@ -195,14 +213,16 @@ export default class SalesEstimatesController extends BaseController { * @param {Response} res * @param {Function} next */ - static async valdiateInvoiceEntriesIdsExistance(req, res, next) { + async valdiateInvoiceEntriesIdsExistance(req: Request, res: Response, next: Function) { + const { ItemEntry } = req.models; + const { id: saleInvoiceId } = req.params; const saleInvoice = { ...req.body }; const entriesIds = saleInvoice.entries .filter(e => e.id) .map((e) => e.id); - const foundEntries = await ItemEntry.tenant().query() + const foundEntries = await ItemEntry.query() .whereIn('id', entriesIds) .where('reference_type', 'SaleInvoice') .where('reference_id', saleInvoiceId); @@ -221,15 +241,14 @@ export default class SalesEstimatesController extends BaseController { * @param {Response} res - * @return {Response} res - */ - static async newEstimate(req, res) { - const estimate = { - ...req.body, - entries: req.body.entries.map((entry) => ({ - ...entry, - amount: ItemEntry.calcAmount(entry), - })), - }; - const storedEstimate = await SaleEstimateService.createEstimate(estimate); + async newEstimate(req: Request, res: Response) { + const { tenantId } = req; + const estimateOTD: ISaleEstimateOTD = matchedData(req, { + locations: ['body'], + includeOptionals: true, + }); + const storedEstimate = await this.saleEstimateService + .createEstimate(tenantId, estimateOTD); return res.status(200).send({ id: storedEstimate.id }); } @@ -239,12 +258,16 @@ export default class SalesEstimatesController extends BaseController { * @param {Request} req * @param {Response} res */ - static async editEstimate(req, res) { + async editEstimate(req: Request, res: Response) { const { id: estimateId } = req.params; - const estimate = { ...req.body }; + const { tenantId } = req; + const estimateOTD: ISaleEstimateOTD = matchedData(req, { + locations: ['body'], + includeOptionals: true, + }); // Update estimate with associated estimate entries. - await SaleEstimateService.editEstimate(estimateId, estimate); + await this.saleEstimateService.editEstimate(tenantId, estimateId, estimateOTD); return res.status(200).send({ id: estimateId }); } @@ -254,9 +277,11 @@ export default class SalesEstimatesController extends BaseController { * @param {Request} req * @param {Response} res */ - static async deleteEstimate(req, res) { + async deleteEstimate(req: Request, res: Response) { const { id: estimateId } = req.params; - await SaleEstimateService.deleteEstimate(estimateId); + const { tenantId } = req; + + await this.saleEstimateService.deleteEstimate(tenantId, estimateId); return res.status(200).send({ id: estimateId }); } @@ -264,9 +289,12 @@ export default class SalesEstimatesController extends BaseController { /** * Retrieve the given estimate with associated entries. */ - static async getEstimate(req, res) { + async getEstimate(req: Request, res: Response) { const { id: estimateId } = req.params; - const estimate = await SaleEstimateService.getEstimateWithEntries(estimateId); + const { tenantId } = req; + + const estimate = await this.saleEstimateService + .getEstimateWithEntries(tenantId, estimateId); return res.status(200).send({ estimate }); } @@ -276,7 +304,7 @@ export default class SalesEstimatesController extends BaseController { * @param {Request} req * @param {Response} res */ - static async getEstimates(req, res) { + async getEstimates(req: Request, res: Response) { const filter = { filter_roles: [], sort_order: 'asc', @@ -288,7 +316,7 @@ export default class SalesEstimatesController extends BaseController { filter.filter_roles = JSON.parse(filter.stringified_filter_roles); } const { SaleEstimate, Resource, View } = req.models; - const resource = await Resource.tenant().query() + const resource = await Resource.query() .remember() .where('name', 'sales_estimates') .withGraphFetched('fields') diff --git a/server/src/http/controllers/Sales/SalesInvoices.js b/server/src/http/controllers/Sales/SalesInvoices.ts similarity index 70% rename from server/src/http/controllers/Sales/SalesInvoices.js rename to server/src/http/controllers/Sales/SalesInvoices.ts index 1e77faff7..b0ebb2196 100644 --- a/server/src/http/controllers/Sales/SalesInvoices.js +++ b/server/src/http/controllers/Sales/SalesInvoices.ts @@ -1,34 +1,40 @@ import express from 'express'; -import { check, param, query } from 'express-validator'; +import { check, param, query, matchedData } from 'express-validator'; import { difference } from 'lodash'; import { raw } from 'objection'; -import { ItemEntry } from '@/models'; +import { Service, Inject } from 'typedi'; import validateMiddleware from '@/http/middleware/validateMiddleware'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; import SaleInvoiceService from '@/services/Sales/SalesInvoices'; import ItemsService from '@/services/Items/ItemsService'; -import CustomersService from '@/services/Customers/CustomersService'; import DynamicListing from '@/services/DynamicListing/DynamicListing'; import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder'; import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing'; -import { Customer, Item } from '../../../models'; +import { ISaleInvoiceOTD } from '@/interfaces'; +@Service() export default class SaleInvoicesController { + @Inject() + itemsService: ItemsService; + + @Inject() + saleInvoiceService: SaleInvoiceService; + /** * Router constructor. */ - static router() { + router() { const router = express.Router(); router.post( '/', this.saleInvoiceValidationSchema, validateMiddleware, - asyncMiddleware(this.validateInvoiceCustomerExistance), - asyncMiddleware(this.validateInvoiceNumberUnique), - asyncMiddleware(this.validateInvoiceItemsIdsExistance), - asyncMiddleware(this.validateNonSellableEntriesItems), - asyncMiddleware(this.newSaleInvoice) + asyncMiddleware(this.validateInvoiceCustomerExistance.bind(this)), + asyncMiddleware(this.validateInvoiceNumberUnique.bind(this)), + asyncMiddleware(this.validateInvoiceItemsIdsExistance.bind(this)), + asyncMiddleware(this.validateNonSellableEntriesItems.bind(this)), + asyncMiddleware(this.newSaleInvoice.bind(this)) ); router.post( '/:id', @@ -37,38 +43,38 @@ export default class SaleInvoicesController { ...this.specificSaleInvoiceValidation, ], validateMiddleware, - asyncMiddleware(this.validateInvoiceExistance), - asyncMiddleware(this.validateInvoiceCustomerExistance), - asyncMiddleware(this.validateInvoiceNumberUnique), - asyncMiddleware(this.validateInvoiceItemsIdsExistance), - asyncMiddleware(this.valdiateInvoiceEntriesIdsExistance), - asyncMiddleware(this.validateEntriesIdsExistance), - asyncMiddleware(this.validateNonSellableEntriesItems), - asyncMiddleware(this.editSaleInvoice) + asyncMiddleware(this.validateInvoiceExistance.bind(this)), + asyncMiddleware(this.validateInvoiceCustomerExistance.bind(this)), + asyncMiddleware(this.validateInvoiceNumberUnique.bind(this)), + asyncMiddleware(this.validateInvoiceItemsIdsExistance.bind(this)), + asyncMiddleware(this.valdiateInvoiceEntriesIdsExistance.bind(this)), + asyncMiddleware(this.validateEntriesIdsExistance.bind(this)), + asyncMiddleware(this.validateNonSellableEntriesItems.bind(this)), + asyncMiddleware(this.editSaleInvoice.bind(this)) ); router.delete( '/:id', this.specificSaleInvoiceValidation, validateMiddleware, - asyncMiddleware(this.validateInvoiceExistance), - asyncMiddleware(this.deleteSaleInvoice) + asyncMiddleware(this.validateInvoiceExistance.bind(this)), + asyncMiddleware(this.deleteSaleInvoice.bind(this)) ); router.get( '/due_invoices', this.dueSalesInvoicesListValidationSchema, - asyncMiddleware(this.getDueSalesInvoice), + asyncMiddleware(this.getDueSalesInvoice.bind(this)), ); router.get( '/:id', this.specificSaleInvoiceValidation, validateMiddleware, - asyncMiddleware(this.validateInvoiceExistance), - asyncMiddleware(this.getSaleInvoice) + asyncMiddleware(this.validateInvoiceExistance.bind(this)), + asyncMiddleware(this.getSaleInvoice.bind(this)) ); router.get( '/', this.saleInvoiceListValidationSchema, - asyncMiddleware(this.getSalesInvoices) + asyncMiddleware(this.getSalesInvoices.bind(this)) ) return router; } @@ -76,7 +82,7 @@ export default class SaleInvoicesController { /** * Sale invoice validation schema. */ - static get saleInvoiceValidationSchema() { + get saleInvoiceValidationSchema() { return [ check('customer_id').exists().isNumeric().toInt(), check('invoice_date').exists().isISO8601(), @@ -102,14 +108,14 @@ export default class SaleInvoicesController { /** * Specific sale invoice validation schema. */ - static get specificSaleInvoiceValidation() { + get specificSaleInvoiceValidation() { return [param('id').exists().isNumeric().toInt()]; } /** * Sales invoices list validation schema. */ - static get saleInvoiceListValidationSchema() { + get saleInvoiceListValidationSchema() { return [ query('custom_view_id').optional().isNumeric().toInt(), query('stringified_filter_roles').optional().isJSON(), @@ -120,8 +126,10 @@ export default class SaleInvoicesController { ]; } - - static get dueSalesInvoicesListValidationSchema() { + /** + * Due sale invoice list validation schema. + */ + get dueSalesInvoicesListValidationSchema() { return [ query('customer_id').optional().isNumeric().toInt(), ] @@ -133,11 +141,12 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async validateInvoiceCustomerExistance(req, res, next) { + async validateInvoiceCustomerExistance(req: Request, res: Response, next: Function) { const saleInvoice = { ...req.body }; - const isCustomerIDExists = await CustomersService.isCustomerExists( - saleInvoice.customer_id - ); + const { Customer } = req.models; + + const isCustomerIDExists = await Customer.query().findById(saleInvoice.customer_id); + if (!isCustomerIDExists) { return res.status(400).send({ errors: [{ type: 'CUSTOMER.ID.NOT.EXISTS', code: 200 }], @@ -148,15 +157,17 @@ export default class SaleInvoicesController { /** * Validate whether sale invoice items ids esits on the storage. - * @param {Request} req - * @param {Response} res - * @param {Function} next + * @param {Request} req - + * @param {Response} res - + * @param {Function} next - */ - static async validateInvoiceItemsIdsExistance(req, res, next) { + async validateInvoiceItemsIdsExistance(req: Request, res: Response, next: Function) { + const { tenantId } = req; const saleInvoice = { ...req.body }; const entriesItemsIds = saleInvoice.entries.map((e) => e.item_id); - const isItemsIdsExists = await ItemsService.isItemsIdsExists( - entriesItemsIds + + const isItemsIdsExists = await this.itemsService.isItemsIdsExists( + tenantId, entriesItemsIds, ); if (isItemsIdsExists.length > 0) { return res.status(400).send({ @@ -173,9 +184,12 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async validateInvoiceNumberUnique(req, res, next) { + async validateInvoiceNumberUnique(req: Request, res: Response, next: Function) { + const { tenantId } = req; const saleInvoice = { ...req.body }; - const isInvoiceNoExists = await SaleInvoiceService.isSaleInvoiceNumberExists( + + const isInvoiceNoExists = await this.saleInvoiceService.isSaleInvoiceNumberExists( + tenantId, saleInvoice.invoice_no, req.params.id ); @@ -195,10 +209,12 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async validateInvoiceExistance(req, res, next) { + async validateInvoiceExistance(req: Request, res: Response, next: Function) { const { id: saleInvoiceId } = req.params; - const isSaleInvoiceExists = await SaleInvoiceService.isSaleInvoiceExists( - saleInvoiceId + const { tenantId } = req; + + const isSaleInvoiceExists = await this.saleInvoiceService.isSaleInvoiceExists( + tenantId, saleInvoiceId, ); if (!isSaleInvoiceExists) { return res @@ -214,12 +230,13 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async valdiateInvoiceEntriesIdsExistance(req, res, next) { + async valdiateInvoiceEntriesIdsExistance(req: Request, res: Response, next: Function) { + const { tenantId } = req; const saleInvoice = { ...req.body }; const entriesItemsIds = saleInvoice.entries.map((e) => e.item_id); - const isItemsIdsExists = await ItemsService.isItemsIdsExists( - entriesItemsIds + const isItemsIdsExists = await this.itemsService.isItemsIdsExists( + tenantId, entriesItemsIds, ); if (isItemsIdsExists.length > 0) { return res.status(400).send({ @@ -235,14 +252,16 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async validateEntriesIdsExistance(req, res, next) { + async validateEntriesIdsExistance(req: Request, res: Response, next: Function) { + const { ItemEntry } = req.models; const { id: saleInvoiceId } = req.params; const saleInvoice = { ...req.body }; + const entriesIds = saleInvoice.entries .filter(e => e.id) .map(e => e.id); - - const storedEntries = await ItemEntry.tenant().query() + + const storedEntries = await ItemEntry.query() .whereIn('reference_id', [saleInvoiceId]) .whereIn('reference_type', ['SaleInvoice']); @@ -265,7 +284,7 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async validateNonSellableEntriesItems(req, res, next) { + async validateNonSellableEntriesItems(req: Request, res: Response, next: Function) { const { Item } = req.models; const saleInvoice = { ...req.body }; const itemsIds = saleInvoice.entries.map(e => e.item_id); @@ -291,22 +310,17 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async newSaleInvoice(req, res) { - const errorReasons = []; - const saleInvoice = { - ...req.body, - entries: req.body.entries.map((entry) => ({ - ...entry, - amount: ItemEntry.calcAmount(entry), - })), - }; + async newSaleInvoice(req: Request, res: Response) { + const { tenantId } = req; + const saleInvoiceOTD: ISaleInvoiceOTD = matchedData(req, { + locations: ['body'], + includeOptionals: true + }); + // Creates a new sale invoice with associated entries. - const storedSaleInvoice = await SaleInvoiceService.createSaleInvoice( - saleInvoice + const storedSaleInvoice = await this.saleInvoiceService.createSaleInvoice( + tenantId, saleInvoiceOTD, ); - - // InventoryService.trackingInventoryLotsCost(); - return res.status(200).send({ id: storedSaleInvoice.id }); } @@ -316,19 +330,18 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async editSaleInvoice(req, res) { + async editSaleInvoice(req: Request, res: Response) { + const { tenantId } = req; const { id: saleInvoiceId } = req.params; - const saleInvoice = { - ...req.body, - entries: req.body.entries.map((entry) => ({ - ...entry, - amount: ItemEntry.calcAmount(entry), - })), - }; - // Update the given sale invoice details. - await SaleInvoiceService.editSaleInvoice(saleInvoiceId, saleInvoice); - return res.status(200).send({ id: saleInvoice.id }); + const saleInvoiceOTD: ISaleInvoiceOTD = matchedData(req, { + locations: ['body'], + includeOptionals: true + }); + // Update the given sale invoice details. + await this.saleInvoiceService.editSaleInvoice(tenantId, saleInvoiceId, saleInvoiceOTD); + + return res.status(200).send({ id: saleInvoiceId }); } /** @@ -337,10 +350,12 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async deleteSaleInvoice(req, res) { + async deleteSaleInvoice(req: Request, res: Response) { const { id: saleInvoiceId } = req.params; + const { tenantId } = req; + // Deletes the sale invoice with associated entries and journal transaction. - await SaleInvoiceService.deleteSaleInvoice(saleInvoiceId); + await this.saleInvoiceService.deleteSaleInvoice(tenantId, saleInvoiceId); return res.status(200).send({ id: saleInvoiceId }); } @@ -350,10 +365,12 @@ export default class SaleInvoicesController { * @param {Request} req * @param {Response} res */ - static async getSaleInvoice(req, res) { + async getSaleInvoice(req: Request, res: Response) { const { id: saleInvoiceId } = req.params; - const saleInvoice = await SaleInvoiceService.getSaleInvoiceWithEntries( - saleInvoiceId + const { tenantId } = req; + + const saleInvoice = await this.saleInvoiceService.getSaleInvoiceWithEntries( + tenantId, saleInvoiceId, ); return res.status(200).send({ sale_invoice: saleInvoice }); } @@ -363,13 +380,14 @@ export default class SaleInvoicesController { * @param {Request} req * @param {Response} res */ - static async getDueSalesInvoice(req, res) { + async getDueSalesInvoice(req: Request, res: Response) { + const { Customer, SaleInvoice } = req.models; + const { tenantId } = req; + const filter = { customer_id: null, ...req.query, }; - const { Customer, SaleInvoice } = req.models; - if (filter.customer_id) { const foundCustomer = await Customer.query().findById(filter.customer_id); @@ -381,7 +399,6 @@ export default class SaleInvoicesController { } const dueSalesInvoices = await SaleInvoice.query().onBuild((query) => { query.where(raw('BALANCE - PAYMENT_AMOUNT > 0')); - if (filter.customer_id) { query.where('customer_id', filter.customer_id); } @@ -397,7 +414,7 @@ export default class SaleInvoicesController { * @param {Response} res * @param {Function} next */ - static async getSalesInvoices(req, res) { + async getSalesInvoices(req, res) { const filter = { filter_roles: [], sort_order: 'asc', diff --git a/server/src/http/controllers/Sales/SalesReceipts.js b/server/src/http/controllers/Sales/SalesReceipts.ts similarity index 71% rename from server/src/http/controllers/Sales/SalesReceipts.js rename to server/src/http/controllers/Sales/SalesReceipts.ts index 09a6ba3df..b592778bb 100644 --- a/server/src/http/controllers/Sales/SalesReceipts.js +++ b/server/src/http/controllers/Sales/SalesReceipts.ts @@ -1,9 +1,8 @@ -import express from 'express'; -import { check, param, query } from 'express-validator'; -import { ItemEntry } from '@/models'; +import { Router, Request, Response } from 'express'; +import { check, param, query, matchedData } from 'express-validator'; +import { Inject, Service } from 'typedi'; import validateMiddleware from '@/http/middleware/validateMiddleware'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; -import CustomersService from '@/services/Customers/CustomersService'; import AccountsService from '@/services/Accounts/AccountsService'; import ItemsService from '@/services/Items/ItemsService'; import SaleReceiptService from '@/services/Sales/SalesReceipts'; @@ -13,12 +12,22 @@ import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/HasDynamicListing'; +@Service() export default class SalesReceiptsController { + @Inject() + saleReceiptService: SaleReceiptService; + + @Inject() + accountsService: AccountsService; + + @Inject() + itemsService: ItemsService; + /** * Router constructor. */ - static router() { - const router = express.Router(); + router() { + const router = Router(); router.post( '/:id', [ @@ -26,34 +35,34 @@ export default class SalesReceiptsController { ...this.salesReceiptsValidationSchema, ], validateMiddleware, - asyncMiddleware(this.validateSaleReceiptExistance), - asyncMiddleware(this.validateReceiptCustomerExistance), - asyncMiddleware(this.validateReceiptDepositAccountExistance), - asyncMiddleware(this.validateReceiptItemsIdsExistance), - asyncMiddleware(this.validateReceiptEntriesIds), - asyncMiddleware(this.editSaleReceipt) + asyncMiddleware(this.validateSaleReceiptExistance.bind(this)), + asyncMiddleware(this.validateReceiptCustomerExistance.bind(this)), + asyncMiddleware(this.validateReceiptDepositAccountExistance.bind(this)), + asyncMiddleware(this.validateReceiptItemsIdsExistance.bind(this)), + asyncMiddleware(this.validateReceiptEntriesIds.bind(this)), + asyncMiddleware(this.editSaleReceipt.bind(this)) ); router.post( '/', this.salesReceiptsValidationSchema, validateMiddleware, - asyncMiddleware(this.validateReceiptCustomerExistance), - asyncMiddleware(this.validateReceiptDepositAccountExistance), - asyncMiddleware(this.validateReceiptItemsIdsExistance), - asyncMiddleware(this.newSaleReceipt) + asyncMiddleware(this.validateReceiptCustomerExistance.bind(this)), + asyncMiddleware(this.validateReceiptDepositAccountExistance.bind(this)), + asyncMiddleware(this.validateReceiptItemsIdsExistance.bind(this)), + asyncMiddleware(this.newSaleReceipt.bind(this)) ); router.delete( '/:id', this.specificReceiptValidationSchema, validateMiddleware, - asyncMiddleware(this.validateSaleReceiptExistance), - asyncMiddleware(this.deleteSaleReceipt) + asyncMiddleware(this.validateSaleReceiptExistance.bind(this)), + asyncMiddleware(this.deleteSaleReceipt.bind(this)) ); router.get( '/', this.listSalesReceiptsValidationSchema, validateMiddleware, - asyncMiddleware(this.listingSalesReceipts) + asyncMiddleware(this.listingSalesReceipts.bind(this)) ); return router; } @@ -62,7 +71,7 @@ export default class SalesReceiptsController { * Sales receipt validation schema. * @return {Array} */ - static get salesReceiptsValidationSchema() { + get salesReceiptsValidationSchema() { return [ check('customer_id').exists().isNumeric().toInt(), check('deposit_account_id').exists().isNumeric().toInt(), @@ -88,7 +97,7 @@ export default class SalesReceiptsController { /** * Specific sale receipt validation schema. */ - static get specificReceiptValidationSchema() { + get specificReceiptValidationSchema() { return [ param('id').exists().isNumeric().toInt() ]; @@ -97,7 +106,7 @@ export default class SalesReceiptsController { /** * List sales receipts validation schema. */ - static get listSalesReceiptsValidationSchema() { + get listSalesReceiptsValidationSchema() { return [ query('custom_view_id').optional().isNumeric().toInt(), query('stringified_filter_roles').optional().isJSON(), @@ -113,11 +122,15 @@ export default class SalesReceiptsController { * @param {Request} req * @param {Response} res */ - static async validateSaleReceiptExistance(req, res, next) { + async validateSaleReceiptExistance(req: Request, res: Response, next: Function) { + const { tenantId } = req; const { id: saleReceiptId } = req.params; - const isSaleReceiptExists = await SaleReceiptService.isSaleReceiptExists( - saleReceiptId - ); + + const isSaleReceiptExists = await this.saleReceiptService + .isSaleReceiptExists( + tenantId, + saleReceiptId, + ); if (!isSaleReceiptExists) { return res.status(404).send({ errors: [{ type: 'SALE.RECEIPT.NOT.FOUND', code: 200 }], @@ -132,12 +145,13 @@ export default class SalesReceiptsController { * @param {Response} res * @param {Function} next */ - static async validateReceiptCustomerExistance(req, res, next) { + async validateReceiptCustomerExistance(req: Request, res: Response, next: Function) { const saleReceipt = { ...req.body }; - const isCustomerExists = await CustomersService.isCustomerExists( - saleReceipt.customer_id - ); - if (!isCustomerExists) { + const { Customer } = req.models; + + const foundCustomer = await Customer.query().findById(saleReceipt.customer_id); + + if (!foundCustomer) { return res.status(400).send({ errors: [{ type: 'CUSTOMER.ID.NOT.EXISTS', code: 200 }], }); @@ -151,9 +165,12 @@ export default class SalesReceiptsController { * @param {Response} res * @param {Function} next */ - static async validateReceiptDepositAccountExistance(req, res, next) { + async validateReceiptDepositAccountExistance(req: Request, res: Response, next: Function) { + const { tenantId } = req; + const saleReceipt = { ...req.body }; - const isDepositAccountExists = await AccountsService.isAccountExists( + const isDepositAccountExists = await this.accountsService.isAccountExists( + tenantId, saleReceipt.deposit_account_id ); if (!isDepositAccountExists) { @@ -170,10 +187,14 @@ export default class SalesReceiptsController { * @param {Response} res * @param {Function} next */ - static async validateReceiptItemsIdsExistance(req, res, next) { + async validateReceiptItemsIdsExistance(req: Request, res: Response, next: Function) { + const { tenantId } = req; + const saleReceipt = { ...req.body }; const estimateItemsIds = saleReceipt.entries.map((e) => e.item_id); - const notFoundItemsIds = await ItemsService.isItemsIdsExists( + + const notFoundItemsIds = await this.itemsService.isItemsIdsExists( + tenantId, estimateItemsIds ); if (notFoundItemsIds.length > 0) { @@ -188,15 +209,19 @@ export default class SalesReceiptsController { * @param {Response} res * @param {Function} next */ - static async validateReceiptEntriesIds(req, res, next) { + async validateReceiptEntriesIds(req: Request, res: Response, next: Function) { + const { tenantId } = req; + const saleReceipt = { ...req.body }; const { id: saleReceiptId } = req.params; // Validate the entries IDs that not stored or associated to the sale receipt. - const notExistsEntriesIds = await SaleReceiptService.isSaleReceiptEntriesIDsExists( - saleReceiptId, - saleReceipt - ); + const notExistsEntriesIds = await this.saleReceiptService + .isSaleReceiptEntriesIDsExists( + tenantId, + saleReceiptId, + saleReceipt, + ); if (notExistsEntriesIds.length > 0) { return res.status(400).send({ errors: [{ type: 'ENTRIES.IDS.NOT.FOUND', @@ -212,19 +237,19 @@ export default class SalesReceiptsController { * @param {Request} req * @param {Response} res */ - static async newSaleReceipt(req, res) { - const saleReceipt = { - ...req.body, - entries: req.body.entries.map((entry) => ({ - ...entry, - amount: ItemEntry.calcAmount(entry), - })), - }; + async newSaleReceipt(req: Request, res: Response) { + const { tenantId } = req; + const saleReceipt = matchedData(req, { + locations: ['body'], + includeOptionals: true, + }); // Store the given sale receipt details with associated entries. - const storedSaleReceipt = await SaleReceiptService.createSaleReceipt( - saleReceipt - ); + const storedSaleReceipt = await this.saleReceiptService + .createSaleReceipt( + tenantId, + saleReceipt, + ); return res.status(200).send({ id: storedSaleReceipt.id }); } @@ -233,11 +258,12 @@ export default class SalesReceiptsController { * @param {Request} req * @param {Response} res */ - static async deleteSaleReceipt(req, res) { + async deleteSaleReceipt(req: Request, res: Response) { + const { tenantId } = req; const { id: saleReceiptId } = req.params; // Deletes the sale receipt. - await SaleReceiptService.deleteSaleReceipt(saleReceiptId); + await this.saleReceiptService.deleteSaleReceipt(tenantId, saleReceiptId); return res.status(200).send({ id: saleReceiptId }); } @@ -248,9 +274,12 @@ export default class SalesReceiptsController { * @param {Request} req * @param {Response} res */ - static async editSaleReceipt(req, res) { + async editSaleReceipt(req: Request, res: Response) { + const { tenantId } = req; + const { id: saleReceiptId } = req.params; const saleReceipt = { ...req.body }; + const errorReasons = []; // Handle all errors with reasons messages. @@ -258,7 +287,11 @@ export default class SalesReceiptsController { return res.boom.badRequest(null, { errors: errorReasons }); } // Update the given sale receipt details. - await SaleReceiptService.editSaleReceipt(saleReceiptId, saleReceipt); + await this.saleReceiptService.editSaleReceipt( + tenantId, + saleReceiptId, + saleReceipt, + ); return res.status(200).send(); } @@ -268,7 +301,7 @@ export default class SalesReceiptsController { * @param {Request} req * @param {Response} res */ - static async listingSalesReceipts(req, res) { + async listingSalesReceipts(req: Request, res: Response) { const filter = { filter_roles: [], sort_order: 'asc', @@ -280,7 +313,7 @@ export default class SalesReceiptsController { filter.filter_roles = JSON.parse(filter.stringified_filter_roles); } const { SaleReceipt, Resource, View } = req.models; - const resource = await Resource.tenant().query() + const resource = await Resource.query() .remember() .where('name', 'sales_receipts') .withGraphFetched('fields') diff --git a/server/src/http/controllers/Sales/index.js b/server/src/http/controllers/Sales/index.ts similarity index 52% rename from server/src/http/controllers/Sales/index.js rename to server/src/http/controllers/Sales/index.ts index 664391216..146c8dddf 100644 --- a/server/src/http/controllers/Sales/index.js +++ b/server/src/http/controllers/Sales/index.ts @@ -1,4 +1,5 @@ import express from 'express'; +import { Container } from 'typedi'; import SalesEstimates from './SalesEstimates'; import SalesReceipts from './SalesReceipts'; import SalesInvoices from './SalesInvoices' @@ -11,10 +12,10 @@ export default { router() { const router = express.Router(); - router.use('/invoices', SalesInvoices.router()); - router.use('/estimates', SalesEstimates.router()); - router.use('/receipts', SalesReceipts.router()); - router.use('/payment_receives', PaymentReceives.router()); + router.use('/invoices', Container.get(SalesInvoices).router()); + router.use('/estimates', Container.get(SalesEstimates).router()); + router.use('/receipts', Container.get(SalesReceipts).router()); + router.use('/payment_receives', Container.get(PaymentReceives).router()); return router; } diff --git a/server/src/http/controllers/Subscription/Vouchers.ts b/server/src/http/controllers/Subscription/Vouchers.ts index 20b06cc83..37174114a 100644 --- a/server/src/http/controllers/Subscription/Vouchers.ts +++ b/server/src/http/controllers/Subscription/Vouchers.ts @@ -25,14 +25,15 @@ export default class VouchersController { this.generateVoucherSchema, validateMiddleware, PrettierMiddleware, - asyncMiddleware(this.validatePlanExistance), + asyncMiddleware(this.validatePlanExistance.bind(this)), asyncMiddleware(this.generateVoucher.bind(this)), ); router.post( '/disable/:voucherId', + validateMiddleware, PrettierMiddleware, - asyncMiddleware(this.validateVoucherExistance), - asyncMiddleware(this.validateNotDisabledVoucher), + asyncMiddleware(this.validateVoucherExistance.bind(this)), + asyncMiddleware(this.validateNotDisabledVoucher.bind(this)), asyncMiddleware(this.disableVoucher.bind(this)), ); router.post( @@ -45,7 +46,7 @@ export default class VouchersController { router.delete( '/:voucherId', PrettierMiddleware, - asyncMiddleware(this.validateVoucherExistance), + asyncMiddleware(this.validateVoucherExistance.bind(this)), asyncMiddleware(this.deleteVoucher.bind(this)), ); router.get( @@ -158,23 +159,21 @@ export default class VouchersController { * @param {Response} res * @return {Response} */ - async generateVoucher(req: Request, res: Response) { + async generateVoucher(req: Request, res: Response, next: Function) { const { loop = 10, period, periodInterval, planId } = req.body; - const generatedVouchers: string[] = []; - const asyncOpers = []; - times(loop, () => { - const generateOper = this.voucherService - .generateVoucher(period, periodInterval, planId) - .then((generatedVoucher: any) => { - generatedVouchers.push(generatedVoucher) - }); - asyncOpers.push(generateOper); - }); - - return res.status(200).send({ - vouchers: generatedVouchers, - }); + try { + await this.voucherService.generateVouchers( + loop, period, periodInterval, planId, + ); + return res.status(200).send({ + code: 100, + message: 'The vouchers have been generated successfully.' + }); + } catch (error) { + console.log(error); + next(error); + } } /** diff --git a/server/src/http/index.js b/server/src/http/index.js index 1fe16e957..9cb582902 100644 --- a/server/src/http/index.js +++ b/server/src/http/index.js @@ -49,7 +49,7 @@ export default (app) => { dashboard.use('/api/account_types', AccountTypes.router()); dashboard.use('/api/accounting', Accounting.router()); dashboard.use('/api/views', Views.router()); - dashboard.use('/api/items', Items.router()); + dashboard.use('/api/items', Container.get(Items).router()); dashboard.use('/api/item_categories', Container.get(ItemCategories)); dashboard.use('/api/expenses', Expenses.router()); dashboard.use('/api/financial_statements', FinancialStatements.router()); @@ -61,7 +61,7 @@ export default (app) => { dashboard.use('/api/resources', Resources.router()); dashboard.use('/api/exchange_rates', ExchangeRates.router()); dashboard.use('/api/media', Media.router()); - + app.use('/agendash', Agendash.router()); app.use('/', dashboard); }; diff --git a/server/src/http/middleware/TenancyMiddleware.js b/server/src/http/middleware/TenancyMiddleware.js index c57a20319..b68b03578 100644 --- a/server/src/http/middleware/TenancyMiddleware.js +++ b/server/src/http/middleware/TenancyMiddleware.js @@ -2,6 +2,7 @@ import fs from 'fs'; import path from 'path'; import TenantsManager from '@/system/TenantsManager'; import TenantModel from '@/models/TenantModel'; +import { Container } from 'typedi'; function loadModelsFromDirectory() { const models = {}; @@ -45,6 +46,7 @@ export default async (req, res, next) => { req.knex = knex; req.organizationId = organizationId; req.tenant = tenant; + req.tenantId = tenant.id; req.models = { ...Object.values(models).reduce((acc, model) => { if (typeof model.resource.default !== 'undefined' && @@ -56,5 +58,8 @@ export default async (req, res, next) => { return acc; }, {}), }; + Container.of(`tenant-${tenant.id}`).set('models', { + ...req.models, + }); next(); }; \ No newline at end of file diff --git a/server/src/interfaces/Bill.ts b/server/src/interfaces/Bill.ts new file mode 100644 index 000000000..a448bd536 --- /dev/null +++ b/server/src/interfaces/Bill.ts @@ -0,0 +1,3 @@ +export interface IBillOTD {}; +export interface IBill {}; + \ No newline at end of file diff --git a/server/src/interfaces/BillPayment.ts b/server/src/interfaces/BillPayment.ts index 8b6c22098..a08e716a2 100644 --- a/server/src/interfaces/BillPayment.ts +++ b/server/src/interfaces/BillPayment.ts @@ -10,4 +10,6 @@ export interface IBillPayment { reference: string, billNo: string, entries: IBillPaymentEntry[], -} \ No newline at end of file +} + +export interface IBillPaymentOTD {}; \ No newline at end of file diff --git a/server/src/interfaces/PaymentReceive.ts b/server/src/interfaces/PaymentReceive.ts new file mode 100644 index 000000000..99e6c6f9b --- /dev/null +++ b/server/src/interfaces/PaymentReceive.ts @@ -0,0 +1,4 @@ + + +export interface IPaymentReceive { }; +export interface IPaymentReceiveOTD { }; \ No newline at end of file diff --git a/server/src/interfaces/SaleEstimate.ts b/server/src/interfaces/SaleEstimate.ts new file mode 100644 index 000000000..f8fd64b43 --- /dev/null +++ b/server/src/interfaces/SaleEstimate.ts @@ -0,0 +1,4 @@ + + +export interface ISaleEstimate {}; +export interface ISaleEstimateOTD {}; \ No newline at end of file diff --git a/server/src/interfaces/index.ts b/server/src/interfaces/index.ts index cce52ff2c..344e66647 100644 --- a/server/src/interfaces/index.ts +++ b/server/src/interfaces/index.ts @@ -1,5 +1,9 @@ import { IInventoryTransaction, IInventoryLotCost } from './InventoryTransaction'; -import { IBillPaymentEntry, IBillPayment } from './BillPayment'; +import { + IBillPaymentEntry, + IBillPayment, + IBillPaymentOTD, +} from './BillPayment'; import { IInventoryCostMethod } from './InventoryCostMethod'; import { IItemEntry } from './ItemEntry'; import { IItem } from './Item'; @@ -16,10 +20,20 @@ import { ISaleInvoice, ISaleInvoiceOTD, } from './SaleInvoice'; +import { + IPaymentReceive, + IPaymentReceiveOTD, +} from './PaymentReceive'; +import { + ISaleEstimate, + ISaleEstimateOTD, +} from './SaleEstimate'; export { IBillPaymentEntry, IBillPayment, + IBillPaymentOTD, + IInventoryTransaction, IInventoryLotCost, IInventoryCostMethod, @@ -38,4 +52,10 @@ export { ISaleInvoice, ISaleInvoiceOTD, + + ISaleEstimate, + ISaleEstimateOTD, + + IPaymentReceive, + IPaymentReceiveOTD, }; \ No newline at end of file diff --git a/server/src/jobs/MailNotifcationSubscribeEnd.ts b/server/src/jobs/MailNotifcationSubscribeEnd.ts new file mode 100644 index 000000000..eb24a6382 --- /dev/null +++ b/server/src/jobs/MailNotifcationSubscribeEnd.ts @@ -0,0 +1,8 @@ + + +export default class MailNotificationSubscribeEnd { + + handler(job) { + + } +} \ No newline at end of file diff --git a/server/src/jobs/MailNotificationSubscribeEnd.ts b/server/src/jobs/MailNotificationSubscribeEnd.ts new file mode 100644 index 000000000..8a5b072c6 --- /dev/null +++ b/server/src/jobs/MailNotificationSubscribeEnd.ts @@ -0,0 +1,6 @@ + + +export default class MailNotificationSubscribeEnd { + + +} \ No newline at end of file diff --git a/server/src/jobs/SMSNotificationSubscribeEnd.ts b/server/src/jobs/SMSNotificationSubscribeEnd.ts new file mode 100644 index 000000000..fbe8a4f62 --- /dev/null +++ b/server/src/jobs/SMSNotificationSubscribeEnd.ts @@ -0,0 +1,13 @@ + + + + +export default class SMSNotificationSubscribeEnd { + + + + handler(job) { + const { tenantId, subscriptionSlug } = job.attrs.data; + + } +} \ No newline at end of file diff --git a/server/src/jobs/SMSNotificationTrialEnd.ts b/server/src/jobs/SMSNotificationTrialEnd.ts new file mode 100644 index 000000000..eb24a6382 --- /dev/null +++ b/server/src/jobs/SMSNotificationTrialEnd.ts @@ -0,0 +1,8 @@ + + +export default class MailNotificationSubscribeEnd { + + handler(job) { + + } +} \ No newline at end of file diff --git a/server/src/loaders/jobs.ts b/server/src/loaders/jobs.ts index f88686e0b..58f014f65 100644 --- a/server/src/loaders/jobs.ts +++ b/server/src/loaders/jobs.ts @@ -30,6 +30,22 @@ export default ({ agenda }: { agenda: Agenda }) => { 'send-voucher-via-email', { priority: 'high', concurrency: 1, }, new SendVoucherViaEmailJob().handler, - ) + ); + // agenda.define( + // 'send-sms-notification-subscribe-end', + // { priority: 'high', concurrency: 1, }, + // ); + // agenda.define( + // 'send-mail-notification-subscribe-end', + // { priority: 'high', concurrency: 1, }, + // ); + // agenda.define( + // 'send-sms-notification-trial-end', + // { priority: 'high', concurrency: 1, }, + // ); + // agenda.define( + // 'send-mail-notification-trial-end', + // { priority: 'high', concurrency: 1, }, + // ); agenda.start(); }; diff --git a/server/src/models/Bill.js b/server/src/models/Bill.js index 94801da06..bbe44d344 100644 --- a/server/src/models/Bill.js +++ b/server/src/models/Bill.js @@ -74,7 +74,7 @@ export default class Bill extends mixin(TenantModel, [CachableModel]) { * @return {Array} */ static async getNotFoundBills(billsIds, vendorId) { - const storedBills = await this.tenant().query() + const storedBills = await this.query() .onBuild((builder) => { builder.whereIn('id', billsIds); @@ -94,8 +94,7 @@ export default class Bill extends mixin(TenantModel, [CachableModel]) { static changePaymentAmount(billId, amount) { const changeMethod = amount > 0 ? 'increment' : 'decrement'; - return this.tenant() - .query() + return this.query() .where('id', billId) [changeMethod]('payment_amount', Math.abs(amount)); } diff --git a/server/src/models/Customer.js b/server/src/models/Customer.js index 579411741..89daf8152 100644 --- a/server/src/models/Customer.js +++ b/server/src/models/Customer.js @@ -33,10 +33,9 @@ export default class Customer extends TenantModel { * @param {Numeric} amount */ static async changeBalance(customerId, amount) { - const changeMethod = amount > 0 ? 'increment' : 'decrement'; + const changeMethod = (amount > 0) ? 'increment' : 'decrement'; - await this.tenant() - .query() + return this.query() .where('id', customerId) [changeMethod]('balance', Math.abs(amount)); } @@ -47,8 +46,7 @@ export default class Customer extends TenantModel { * @param {Integer} amount */ static async incrementBalance(customerId, amount) { - await this.tenant() - .query() + return this.query() .where('id', customerId) .increment('balance', amount); } @@ -59,9 +57,25 @@ export default class Customer extends TenantModel { * @param {integer} amount - */ static async decrementBalance(customerId, amount) { - await this.tenant() - .query() + await this.query() .where('id', customerId) .decrement('balance', amount); } + + static changeDiffBalance(customerId, oldCustomerId, amount, oldAmount) { + const diffAmount = amount - oldAmount; + const asyncOpers = []; + + if (customerId != oldCustomerId) { + const oldCustomerOper = this.changeBalance(oldCustomerId, (oldAmount * -1)); + const customerOper = this.changeBalance(customerId, amount); + + asyncOpers.push(customerOper); + asyncOpers.push(oldCustomerOper); + } else { + const balanceChangeOper = this.changeBalance(customerId, diffAmount); + asyncOpers.push(balanceChangeOper); + } + return Promise.all(asyncOpers); + } } diff --git a/server/src/models/ItemEntry.js b/server/src/models/ItemEntry.js index 4d4319a9f..b4cffec90 100644 --- a/server/src/models/ItemEntry.js +++ b/server/src/models/ItemEntry.js @@ -17,13 +17,12 @@ export default class ItemEntry extends TenantModel { return ['created_at', 'updated_at']; } - /** - * Relationship mapping. - */ - static get relationMappings() { - return { + static get virtualAttributes() { + return ['amount']; + } - }; + static amount() { + return this.calcAmount(this); } static calcAmount(itemEntry) { diff --git a/server/src/models/SaleInvoice.js b/server/src/models/SaleInvoice.js index 27062766b..f08d9cb5c 100644 --- a/server/src/models/SaleInvoice.js +++ b/server/src/models/SaleInvoice.js @@ -119,8 +119,7 @@ export default class SaleInvoice extends mixin(TenantModel, [CachableModel]) { static async changePaymentAmount(invoiceId, amount) { const changeMethod = amount > 0 ? 'increment' : 'decrement'; - await this.tenant() - .query() + await this.query() .where('id', invoiceId) [changeMethod]('payment_amount', Math.abs(amount)); } diff --git a/server/src/models/Vendor.js b/server/src/models/Vendor.js index 2c162b522..0b66b14d7 100644 --- a/server/src/models/Vendor.js +++ b/server/src/models/Vendor.js @@ -25,8 +25,7 @@ export default class Vendor extends TenantModel { static async changeBalance(vendorId, amount) { const changeMethod = amount > 0 ? 'increment' : 'decrement'; - return this.tenant() - .query() + return this.query() .where('id', vendorId) [changeMethod]('balance', Math.abs(amount)); } diff --git a/server/src/services/Accounts/AccountsService.js b/server/src/services/Accounts/AccountsService.js deleted file mode 100644 index df082024f..000000000 --- a/server/src/services/Accounts/AccountsService.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Account, AccountType } from '@/models'; - -export default class AccountsService { - static async isAccountExists(accountId) { - const foundAccounts = await Account.tenant().query().where('id', accountId); - return foundAccounts.length > 0; - } - - static async getAccountByType(accountTypeKey) { - const accountType = await AccountType.tenant() - .query() - .where('key', accountTypeKey) - .first(); - - const account = await Account.tenant() - .query() - .where('account_type_id', accountType.id) - .first(); - - return account; - } -} diff --git a/server/src/services/Accounts/AccountsService.ts b/server/src/services/Accounts/AccountsService.ts new file mode 100644 index 000000000..a45f21adf --- /dev/null +++ b/server/src/services/Accounts/AccountsService.ts @@ -0,0 +1,29 @@ +import { Inject, Service } from 'typedi'; +import TenancyService from '@/services/Tenancy/TenancyService'; + +@Service() +export default class AccountsService { + @Inject() + tenancy: TenancyService; + + async isAccountExists(tenantId: number, accountId: number) { + const { Account } = this.tenancy.models(tenantId); + const foundAccounts = await Account.query() + .where('id', accountId); + + return foundAccounts.length > 0; + } + + async getAccountByType(tenantId: number, accountTypeKey: string) { + const { AccountType, Account } = this.tenancy.models(tenantId); + const accountType = await AccountType.query() + .where('key', accountTypeKey) + .first(); + + const account = await Account.query() + .where('account_type_id', accountType.id) + .first(); + + return account; + } +} diff --git a/server/src/services/Customers/CustomersService.js b/server/src/services/Customers/CustomersService.js index b5397ad46..7166a4c9a 100644 --- a/server/src/services/Customers/CustomersService.js +++ b/server/src/services/Customers/CustomersService.js @@ -4,7 +4,7 @@ import Customer from "../../models/Customer"; export default class CustomersService { static async isCustomerExists(customerId) { - const foundCustomeres = await Customer.tenant().query().where('id', customerId); + const foundCustomeres = await Customer.query().where('id', customerId); return foundCustomeres.length > 0; } } \ No newline at end of file diff --git a/server/src/services/Inventory/Inventory.ts b/server/src/services/Inventory/Inventory.ts index d58806254..68ad44bd3 100644 --- a/server/src/services/Inventory/Inventory.ts +++ b/server/src/services/Inventory/Inventory.ts @@ -1,23 +1,26 @@ -import { Container } from 'typedi'; -import { - InventoryTransaction, - Item, - Option, -} from '@/models'; +import { Container, Service, Inject } from 'typedi'; import InventoryAverageCost from '@/services/Inventory/InventoryAverageCost'; import InventoryCostLotTracker from '@/services/Inventory/InventoryCostLotTracker'; +import TenancyService from '@/services/Tenancy/TenancyService'; type TCostMethod = 'FIFO' | 'LIFO' | 'AVG'; +@Service() export default class InventoryService { + @Inject() + tenancy: TenancyService; + /** * Computes the given item cost and records the inventory lots transactions * and journal entries based on the cost method FIFO, LIFO or average cost rate. + * @param {number} tenantId - * @param {Date} fromDate - * @param {number} itemId - */ - static async computeItemCost(fromDate: Date, itemId: number) { - const item = await Item.tenant().query() + async computeItemCost(tenantId: number, fromDate: Date, itemId: number) { + const { Item } = this.tenancy.models(tenantId); + + const item = await Item.query() .findById(itemId) .withGraphFetched('category'); @@ -42,30 +45,34 @@ export default class InventoryService { } /** - * SChedule item cost compute job. + * Schedule item cost compute job. + * @param {number} tenantId * @param {number} itemId * @param {Date} startingDate */ - static async scheduleComputeItemCost(itemId: number, startingDate: Date|string) { + async scheduleComputeItemCost(tenantId: number, itemId: number, startingDate: Date|string) { const agenda = Container.get('agenda'); return agenda.schedule('in 3 seconds', 'compute-item-cost', { - startingDate, itemId, + startingDate, itemId, tenantId, }); } /** * Records the inventory transactions. + * @param {number} tenantId - Tenant id. * @param {Bill} bill * @param {number} billId */ - static async recordInventoryTransactions( + async recordInventoryTransactions( + tenantId: number, entries: [], deleteOld: boolean, ) { + const { InventoryTransaction, Item } = this.tenancy.models(tenantId); + const entriesItemsIds = entries.map((e: any) => e.item_id); - const inventoryItems = await Item.tenant() - .query() + const inventoryItems = await Item.query() .whereIn('id', entriesItemsIds) .where('type', 'inventory'); @@ -78,11 +85,12 @@ export default class InventoryService { inventoryEntries.forEach(async (entry: any) => { if (deleteOld) { await this.deleteInventoryTransactions( + tenantId, entry.transactionId, entry.transactionType, ); } - await InventoryTransaction.tenant().query().insert({ + await InventoryTransaction.query().insert({ ...entry, lotNumber: entry.lotNumber, }); @@ -91,15 +99,19 @@ export default class InventoryService { /** * Deletes the given inventory transactions. + * @param {number} tenantId - Tenant id. * @param {string} transactionType * @param {number} transactionId * @return {Promise} */ - static deleteInventoryTransactions( + deleteInventoryTransactions( + tenantId: number, transactionId: number, transactionType: string, ) { - return InventoryTransaction.tenant().query() + const { InventoryTransaction } = this.tenancy.models(tenantId); + + return InventoryTransaction.query() .where('transaction_type', transactionType) .where('transaction_id', transactionId) .delete(); @@ -107,21 +119,24 @@ export default class InventoryService { /** * Retrieve the lot number after the increment. + * @param {number} tenantId - Tenant id. */ - static async nextLotNumber() { + async nextLotNumber(tenantId: number) { + const { Option } = this.tenancy.models(tenantId); + const LOT_NUMBER_KEY = 'lot_number_increment'; - const effectRows = await Option.tenant().query() + const effectRows = await Option.query() .where('key', LOT_NUMBER_KEY) .increment('value', 1); if (effectRows === 0) { - await Option.tenant().query() + await Option.query() .insert({ key: LOT_NUMBER_KEY, value: 1, }); } - const options = await Option.tenant().query(); + const options = await Option.query(); return options.getMeta(LOT_NUMBER_KEY, 1); } } \ No newline at end of file diff --git a/server/src/services/Inventory/InventoryAverageCost.ts b/server/src/services/Inventory/InventoryAverageCost.ts index 87fad4e73..36e0cb9c1 100644 --- a/server/src/services/Inventory/InventoryAverageCost.ts +++ b/server/src/services/Inventory/InventoryAverageCost.ts @@ -1,5 +1,4 @@ import { pick } from 'lodash'; -import { InventoryTransaction } from '@/models'; import { IInventoryTransaction } from '@/interfaces'; import InventoryCostMethod from '@/services/Inventory/InventoryCostMethod'; @@ -10,10 +9,12 @@ export default class InventoryAverageCostMethod extends InventoryCostMethod impl /** * Constructor method. + * @param {number} tenantId - The given tenant id. * @param {Date} startingDate - - * @param {number} itemId - + * @param {number} itemId - The given inventory item id. */ constructor( + tenantId: number, startingDate: Date, itemId: number, ) { @@ -39,11 +40,10 @@ export default class InventoryAverageCostMethod extends InventoryCostMethod impl * @param {string} referenceType */ public async computeItemCost() { + const { InventoryTransaction } = this.tenantModels; const openingAvgCost = await this.getOpeningAvaregeCost(this.startingDate, this.itemId); - const afterInvTransactions: IInventoryTransaction[] = await InventoryTransaction - .tenant() - .query() + const afterInvTransactions: IInventoryTransaction[] = await InventoryTransaction.query() .modify('filterDateRange', this.startingDate) .orderBy('date', 'ASC') .orderByRaw("FIELD(direction, 'IN', 'OUT')") @@ -66,6 +66,7 @@ export default class InventoryAverageCostMethod extends InventoryCostMethod impl * @return {number} */ public async getOpeningAvaregeCost(startingDate: Date, itemId: number) { + const { InventoryTransaction } = this.tenantModels; const commonBuilder = (builder: any) => { if (startingDate) { builder.where('date', '<', startingDate); @@ -81,16 +82,14 @@ export default class InventoryAverageCostMethod extends InventoryCostMethod impl // Calculates the total inventory total quantity and rate `IN` transactions. // @todo total `IN` transactions. - const inInvSumationOper: Promise = InventoryTransaction.tenant() - .query() + const inInvSumationOper: Promise = InventoryTransaction.query() .onBuild(commonBuilder) .where('direction', 'IN') .first(); // Calculates the total inventory total quantity and rate `OUT` transactions. // @todo total `OUT` transactions. - const outInvSumationOper: Promise = InventoryTransaction.tenant() - .query() + const outInvSumationOper: Promise = InventoryTransaction.query() .onBuild(commonBuilder) .where('direction', 'OUT') .first(); diff --git a/server/src/services/Inventory/InventoryCostLotTracker.ts b/server/src/services/Inventory/InventoryCostLotTracker.ts index 98048f060..03e94d950 100644 --- a/server/src/services/Inventory/InventoryCostLotTracker.ts +++ b/server/src/services/Inventory/InventoryCostLotTracker.ts @@ -1,10 +1,5 @@ -import { omit, pick, chain } from 'lodash'; +import { pick, chain } from 'lodash'; import moment from 'moment'; -import { - InventoryTransaction, - InventoryLotCostTracker, - Item, -} from "@/models"; import { IInventoryLotCost, IInventoryTransaction } from "@/interfaces"; import InventoryCostMethod from '@/services/Inventory/InventoryCostMethod'; @@ -28,7 +23,12 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme * @param {number} itemId - * @param {string} costMethod - */ - constructor(startingDate: Date, itemId: number, costMethod: TCostMethod = 'FIFO') { + constructor( + tenantId: number, + startingDate: Date, + itemId: number, + costMethod: TCostMethod = 'FIFO' + ) { super(); this.startingDate = startingDate; @@ -83,13 +83,14 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme * @private */ private async fetchInvINTransactions() { + const { InventoryTransaction, InventoryLotCostTracker } = this.tenantModels; + const commonBuilder = (builder: any) => { builder.orderBy('date', (this.costMethod === 'LIFO') ? 'DESC': 'ASC'); builder.where('item_id', this.itemId); }; const afterInvTransactions: IInventoryTransaction[] = - await InventoryTransaction.tenant() - .query() + await InventoryTransaction.query() .modify('filterDateRange', this.startingDate) .orderByRaw("FIELD(direction, 'IN', 'OUT')") .onBuild(commonBuilder) @@ -97,8 +98,7 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme .withGraphFetched('item'); const availiableINLots: IInventoryLotCost[] = - await InventoryLotCostTracker.tenant() - .query() + await InventoryLotCostTracker.query() .modify('filterDateRange', null, this.startingDate) .orderBy('date', 'ASC') .where('direction', 'IN') @@ -117,9 +117,10 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme * @private */ private async fetchInvOUTTransactions() { + const { InventoryTransaction } = this.tenantModels; + const afterOUTTransactions: IInventoryTransaction[] = - await InventoryTransaction.tenant() - .query() + await InventoryTransaction.query() .modify('filterDateRange', this.startingDate) .orderBy('date', 'ASC') .orderBy('lot_number', 'ASC') @@ -132,8 +133,8 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme private async fetchItemsMapped() { const itemsIds = chain(this.inTransactions).map((e) => e.itemId).uniq().value(); - const storedItems = await Item.tenant() - .query() + const { Item } = this.tenantModels; + const storedItems = await Item.query() .where('type', 'inventory') .whereIn('id', itemsIds); @@ -145,9 +146,9 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme * @private */ private async fetchRevertInvJReferenceIds() { + const { InventoryTransaction } = this.tenantModels; const revertJEntriesTransactions: IInventoryTransaction[] = - await InventoryTransaction.tenant() - .query() + await InventoryTransaction.query() .select(['transactionId', 'transactionType']) .modify('filterDateRange', this.startingDate) .where('direction', 'OUT') @@ -164,16 +165,15 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme * @return {Promise} */ public async revertInventoryLots(startingDate: Date) { + const { InventoryLotCostTracker } = this.tenantModels; const asyncOpers: any[] = []; - const inventoryLotsTrans = await InventoryLotCostTracker.tenant() - .query() + const inventoryLotsTrans = await InventoryLotCostTracker.query() .modify('filterDateRange', this.startingDate) .orderBy('date', 'DESC') .where('item_id', this.itemId) .where('direction', 'OUT'); - const deleteInvLotsTrans = InventoryLotCostTracker.tenant() - .query() + const deleteInvLotsTrans = InventoryLotCostTracker.query() .modify('filterDateRange', this.startingDate) .where('item_id', this.itemId) .delete(); @@ -181,8 +181,7 @@ export default class InventoryCostLotTracker extends InventoryCostMethod impleme inventoryLotsTrans.forEach((inventoryLot: IInventoryLotCost) => { if (!inventoryLot.lotNumber) { return; } - const incrementOper = InventoryLotCostTracker.tenant() - .query() + const incrementOper = InventoryLotCostTracker.query() .where('lot_number', inventoryLot.lotNumber) .where('direction', 'IN') .increment('remaining', inventoryLot.quantity); diff --git a/server/src/services/Inventory/InventoryCostMethod.ts b/server/src/services/Inventory/InventoryCostMethod.ts index b93e860d3..9d8297daa 100644 --- a/server/src/services/Inventory/InventoryCostMethod.ts +++ b/server/src/services/Inventory/InventoryCostMethod.ts @@ -1,28 +1,42 @@ import { omit } from 'lodash'; +import { Inject } from 'typedi'; +import TenancyService from '@/services/Tenancy/TenancyService'; import { IInventoryLotCost } from '@/interfaces'; -import { InventoryLotCostTracker } from '@/models'; export default class InventoryCostMethod { + @Inject() + tenancy: TenancyService; + tenantModels: any; + + /** + * Constructor method. + * @param {number} tenantId - The given tenant id. + */ + constructor(tenantId: number, startingDate: Date, itemId: number) { + this.tenantModels = this.tenantModels.models(tenantId); + } + /** * Stores the inventory lots costs transactions in bulk. * @param {IInventoryLotCost[]} costLotsTransactions * @return {Promise[]} */ public storeInventoryLotsCost(costLotsTransactions: IInventoryLotCost[]): Promise { + const { InventoryLotCostTracker } = this.tenantModels; const opers: any = []; costLotsTransactions.forEach((transaction: IInventoryLotCost) => { if (transaction.lotTransId && transaction.decrement) { - const decrementOper = InventoryLotCostTracker.tenant() - .query() + const decrementOper = InventoryLotCostTracker.query() .where('id', transaction.lotTransId) .decrement('remaining', transaction.decrement); opers.push(decrementOper); + } else if(!transaction.lotTransId) { - const operation = InventoryLotCostTracker.tenant().query() - .insert({ - ...omit(transaction, ['decrement', 'invTransId', 'lotTransId']), - }); + const operation = InventoryLotCostTracker.query() + .insert({ + ...omit(transaction, ['decrement', 'invTransId', 'lotTransId']), + }); opers.push(operation); } }); diff --git a/server/src/services/Items/ItemsService.js b/server/src/services/Items/ItemsService.js deleted file mode 100644 index 00e353bca..000000000 --- a/server/src/services/Items/ItemsService.js +++ /dev/null @@ -1,59 +0,0 @@ -import { difference } from "lodash"; -import { Item, ItemTransaction } from '@/models'; - -export default class ItemsService { - - static async newItem(item) { - const storedItem = await Item.tenant() - .query() - .insertAndFetch({ - ...item, - }); - return storedItem; - } - - static async editItem(item, itemId) { - const updateItem = await Item.tenant() - .query() - .findById(itemId) - .patch({ - ...item, - }); - return updateItem; - } - - static async deleteItem(itemId) { - return Item.tenant() - .query() - .findById(itemId) - .delete(); - } - - static async getItemWithMetadata(itemId) { - return Item.tenant() - .query() - .findById(itemId) - .withGraphFetched( - 'costAccount', - 'sellAccount', - 'inventoryAccount', - 'category' - ); - } - - /** - * Validates the given items IDs exists or not returns the not found ones. - * @param {Array} itemsIDs - * @return {Array} - */ - static async isItemsIdsExists(itemsIDs) { - const storedItems = await Item.tenant().query().whereIn('id', itemsIDs); - const storedItemsIds = storedItems.map((t) => t.id); - - const notFoundItemsIds = difference( - itemsIDs, - storedItemsIds, - ); - return notFoundItemsIds; - } -} \ No newline at end of file diff --git a/server/src/services/Items/ItemsService.ts b/server/src/services/Items/ItemsService.ts new file mode 100644 index 000000000..595993dc2 --- /dev/null +++ b/server/src/services/Items/ItemsService.ts @@ -0,0 +1,70 @@ +import { difference } from "lodash"; +import { Service, Inject } from "typedi"; +import TenancyService from '@/services/Tenancy/TenancyService'; + +@Service() +export default class ItemsService { + @Inject() + tenancy: TenancyService; + + async newItem(tenantId: number, item: any) { + const { Item } = this.tenancy.models(tenantId); + const storedItem = await Item.query() + .insertAndFetch({ + ...item, + }); + return storedItem; + } + + async editItem(tenantId: number, item: any, itemId: number) { + const { Item } = this.tenancy.models(tenantId); + const updateItem = await Item.query() + .findById(itemId) + .patch({ + ...item, + }); + return updateItem; + } + + async deleteItem(tenantId: number, itemId: number) { + const { Item } = this.tenancy.models(tenantId); + return Item.query() + .findById(itemId) + .delete(); + } + + /** + * Retrieve the item details of the given id with associated details. + * @param {number} tenantId + * @param {number} itemId + */ + async getItemWithMetadata(tenantId: number, itemId: number) { + const { Item } = this.tenancy.models(tenantId); + return Item.query() + .findById(itemId) + .withGraphFetched( + 'costAccount', + 'sellAccount', + 'inventoryAccount', + 'category' + ); + } + + /** + * Validates the given items IDs exists or not returns the not found ones. + * @param {Array} itemsIDs + * @return {Array} + */ + async isItemsIdsExists(tenantId: number, itemsIDs: number[]) { + const { Item } = this.tenancy.models(tenantId); + + const storedItems = await Item.query().whereIn('id', itemsIDs); + const storedItemsIds = storedItems.map((t) => t.id); + + const notFoundItemsIds = difference( + itemsIDs, + storedItemsIds, + ); + return notFoundItemsIds; + } +} \ No newline at end of file diff --git a/server/src/services/Payment/Voucher.ts b/server/src/services/Payment/Voucher.ts index ad30e1932..a87597974 100644 --- a/server/src/services/Payment/Voucher.ts +++ b/server/src/services/Payment/Voucher.ts @@ -1,5 +1,6 @@ import { Service, Container, Inject } from 'typedi'; import cryptoRandomString from 'crypto-random-string'; +import { times } from 'lodash'; import { Voucher } from "@/system/models"; import { IVoucher } from '@/interfaces'; import VoucherMailMessages from '@/services/Payment/VoucherMailMessages'; @@ -26,6 +27,8 @@ export default class VoucherService { let voucherCode: string; let repeat: boolean = true; + console.log(Voucher); + while(repeat) { voucherCode = cryptoRandomString({ length: 10, type: 'numeric' }); const foundVouchers = await Voucher.query().where('voucher_code', voucherCode); @@ -39,6 +42,29 @@ export default class VoucherService { }); } + + /** + * + * @param {number} loop + * @param {number} voucherPeriod + * @param {string} periodInterval + * @param {number} planId + */ + async generateVouchers( + loop = 1, + voucherPeriod: numner, + periodInterval: string = 'days', + planId: number, + ) { + const asyncOpers: Promise[] = []; + + times(loop, () => { + const generateOper = this.generateVoucher(voucherPeriod, periodInterval, planId); + asyncOpers.push(generateOper); + }); + return Promise.all(asyncOpers); + } + /** * Disables the given voucher id on the storage. * @param {number} voucherId diff --git a/server/src/services/Purchases/BillPayments.js b/server/src/services/Purchases/BillPayments.ts similarity index 74% rename from server/src/services/Purchases/BillPayments.js rename to server/src/services/Purchases/BillPayments.ts index 65dd1e117..338c0b072 100644 --- a/server/src/services/Purchases/BillPayments.js +++ b/server/src/services/Purchases/BillPayments.ts @@ -1,26 +1,30 @@ -import express from 'express'; +import { Inject, Service } from 'typedi'; import { omit, sumBy } from 'lodash'; import moment from 'moment'; -import { - BillPayment, - BillPaymentEntry, - Vendor, - Bill, - Account, - AccountTransaction, -} from '@/models'; +import { IBillPaymentOTD, IBillPayment } from '@/interfaces'; import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries'; import AccountsService from '@/services/Accounts/AccountsService'; import JournalPoster from '@/services/Accounting/JournalPoster'; import JournalEntry from '@/services/Accounting/JournalEntry'; import JournalPosterService from '@/services/Sales/JournalPosterService'; +import TenancyService from '@/services/Tenancy/TenancyService'; import { formatDateFields } from '@/utils'; /** * Bill payments service. * @service */ +@Service() export default class BillPaymentsService { + @Inject() + accountsService: AccountsService; + + @Inject() + tenancy: TenancyService; + + @Inject() + journalService: JournalPosterService; + /** * Creates a new bill payment transcations and store it to the storage * with associated bills entries and journal transactions. @@ -32,24 +36,24 @@ export default class BillPaymentsService { * - Increment the payment amount of the given vendor bills. * - Decrement the vendor balance. * - Records payment journal entries. - * - * @param {BillPaymentDTO} billPayment + * @param {number} tenantId - Tenant id. + * @param {BillPaymentDTO} billPayment - Bill payment object. */ - static async createBillPayment(billPaymentDTO) { + async createBillPayment(tenantId: number, billPaymentDTO: IBillPaymentOTD) { + const { Bill, BillPayment, BillPaymentEntry, Vendor } = this.tenancy.models(tenantId); + const billPayment = { amount: sumBy(billPaymentDTO.entries, 'payment_amount'), ...formatDateFields(billPaymentDTO, ['payment_date']), } - const storedBillPayment = await BillPayment.tenant() - .query() + const storedBillPayment = await BillPayment.query() .insert({ ...omit(billPayment, ['entries']), }); - const storeOpers = []; + const storeOpers: Promise[] = []; billPayment.entries.forEach((entry) => { - const oper = BillPaymentEntry.tenant() - .query() + const oper = BillPaymentEntry.query() .insert({ bill_payment_id: storedBillPayment.id, ...entry, @@ -69,7 +73,7 @@ export default class BillPaymentsService { ); // Records the journal transactions after bills payment // and change diff acoount balance. - const recordJournalTransaction = this.recordPaymentReceiveJournalEntries({ + const recordJournalTransaction = this.recordPaymentReceiveJournalEntries(tenantId, { id: storedBillPayment.id, ...billPayment, }); @@ -93,18 +97,23 @@ export default class BillPaymentsService { * - Re-insert the journal transactions and update the diff accounts balance. * - Update the diff vendor balance. * - Update the diff bill payment amount. - * + * @param {number} tenantId - Tenant id * @param {Integer} billPaymentId * @param {BillPaymentDTO} billPayment * @param {IBillPayment} oldBillPayment */ - static async editBillPayment(billPaymentId, billPaymentDTO, oldBillPayment) { + async editBillPayment( + tenantId: number, + billPaymentId: number, + billPaymentDTO, + oldBillPayment, + ) { + const { BillPayment, BillPaymentEntry, Vendor } = this.tenancy.models(tenantId); const billPayment = { amount: sumBy(billPaymentDTO.entries, 'payment_amount'), ...formatDateFields(billPaymentDTO, ['payment_date']), }; - const updateBillPayment = await BillPayment.tenant() - .query() + const updateBillPayment = await BillPayment.query() .where('id', billPaymentId) .update({ ...omit(billPayment, ['entries']), @@ -118,22 +127,19 @@ export default class BillPaymentsService { entriesHasIds ); if (entriesIdsShouldDelete.length > 0) { - const deleteOper = BillPaymentEntry.tenant() - .query() + const deleteOper = BillPaymentEntry.query() .bulkDelete(entriesIdsShouldDelete); opers.push(deleteOper); } // Entries that should be update to the storage. if (entriesHasIds.length > 0) { - const updateOper = BillPaymentEntry.tenant() - .query() + const updateOper = BillPaymentEntry.query() .bulkUpdate(entriesHasIds, { where: 'id' }); opers.push(updateOper); } // Entries that should be inserted to the storage. if (entriesHasNoIds.length > 0) { - const insertOper = BillPaymentEntry.tenant() - .query() + const insertOper = BillPaymentEntry.query() .bulkInsert( entriesHasNoIds.map((e) => ({ ...e, bill_payment_id: billPaymentId })) ); @@ -141,7 +147,7 @@ export default class BillPaymentsService { } // Records the journal transactions after bills payment and change // different acoount balance. - const recordJournalTransaction = this.recordPaymentReceiveJournalEntries({ + const recordJournalTransaction = this.recordPaymentReceiveJournalEntries(tenantId, { id: storedBillPayment.id, ...billPayment, }); @@ -161,18 +167,19 @@ export default class BillPaymentsService { /** * Deletes the bill payment and associated transactions. + * @param {number} tenantId - Tenant id. * @param {Integer} billPaymentId - The given bill payment id. * @return {Promise} */ - static async deleteBillPayment(billPaymentId) { - const billPayment = await BillPayment.tenant().query().where('id', billPaymentId).first(); + async deleteBillPayment(tenantId: number, billPaymentId: number) { + const { BillPayment, BillPaymentEntry, Vendor } = this.tenancy.models(tenantId); + const billPayment = await BillPayment.query().where('id', billPaymentId).first(); - await BillPayment.tenant().query() + await BillPayment.query() .where('id', billPaymentId) .delete(); - await BillPaymentEntry.tenant() - .query() + await BillPaymentEntry.query() .where('bill_payment_id', billPaymentId) .delete(); @@ -192,17 +199,21 @@ export default class BillPaymentsService { /** * Records bill payment receive journal transactions. + * @param {number} tenantId - * @param {BillPayment} billPayment * @param {Integer} billPaymentId */ - static async recordPaymentReceiveJournalEntries(billPayment) { + async recordPaymentReceiveJournalEntries(tenantId: number, billPayment) { + const { AccountTransaction, Account } = this.tenancy.models(tenantId); + const paymentAmount = sumBy(billPayment.entries, 'payment_amount'); const formattedDate = moment(billPayment.payment_date).format('YYYY-MM-DD'); - const payableAccount = await AccountsService.getAccountByType( + const payableAccount = await this.accountsService.getAccountByType( + tenantId, 'accounts_payable' ); - const accountsDepGraph = await Account.tenant().depGraph().query(); + const accountsDepGraph = await Account.depGraph().query(); const journal = new JournalPoster(accountsDepGraph); const commonJournal = { @@ -213,8 +224,7 @@ export default class BillPaymentsService { date: formattedDate, }; if (billPayment.id) { - const transactions = await AccountTransaction.tenant() - .query() + const transactions = await AccountTransaction.query() .whereIn('reference_type', ['BillPayment']) .where('reference_id', billPayment.id) .withGraphFetched('account.type'); @@ -246,11 +256,12 @@ export default class BillPaymentsService { /** * Retrieve bill payment with associated metadata. - * @param {number} billPaymentId + * @param {number} billPaymentId - The bill payment id. * @return {object} */ - static async getBillPaymentWithMetadata(billPaymentId) { - const billPayment = await BillPayment.tenant().query() + async getBillPaymentWithMetadata(tenantId: number, billPaymentId: number) { + const { BillPayment } = this.tenancy.models(tenantId); + const billPayment = await BillPayment.query() .where('id', billPaymentId) .withGraphFetched('entries') .withGraphFetched('vendor') @@ -265,8 +276,9 @@ export default class BillPaymentsService { * @param {Integer} billPaymentId * @return {boolean} */ - static async isBillPaymentExists(billPaymentId) { - const billPayment = await BillPayment.tenant().query() + async isBillPaymentExists(tenantId: number, billPaymentId: number) { + const { BillPayment } = this.tenancy.models(tenantId); + const billPayment = await BillPayment.query() .where('id', billPaymentId) .first(); diff --git a/server/src/services/Purchases/Bills.js b/server/src/services/Purchases/Bills.ts similarity index 67% rename from server/src/services/Purchases/Bills.js rename to server/src/services/Purchases/Bills.ts index ff9953f65..da30ad051 100644 --- a/server/src/services/Purchases/Bills.js +++ b/server/src/services/Purchases/Bills.ts @@ -1,13 +1,6 @@ import { omit, sumBy, pick } from 'lodash'; import moment from 'moment'; -import { - Account, - Bill, - Vendor, - ItemEntry, - Item, - AccountTransaction, -} from '@/models'; +import { Inject, Service } from 'typedi'; import JournalPoster from '@/services/Accounting/JournalPoster'; import JournalEntry from '@/services/Accounting/JournalEntry'; import AccountsService from '@/services/Accounts/AccountsService'; @@ -15,13 +8,25 @@ import JournalPosterService from '@/services/Sales/JournalPosterService'; import InventoryService from '@/services/Inventory/Inventory'; import HasItemsEntries from '@/services/Sales/HasItemsEntries'; import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost'; +import TenancyService from '@/services/Tenancy/TenancyService'; import { formatDateFields } from '@/utils'; +import{ IBillOTD } from '@/interfaces'; /** * Vendor bills services. * @service */ +@Service() export default class BillsService extends SalesInvoicesCost { + @Inject() + inventoryService: InventoryService; + + @Inject() + accountsService: AccountsService; + + @Inject() + tenancy: TenancyService; + /** * Creates a new bill and stored it to the storage. * @@ -33,26 +38,29 @@ export default class BillsService extends SalesInvoicesCost { * - Record bill journal transactions on the given accounts. * - Record bill items inventory transactions. * ---- - * @param {IBill} bill - + * @param {number} tenantId - The given tenant id. + * @param {IBillOTD} billDTO - * @return {void} */ - static async createBill(billDTO) { - const invLotNumber = await InventoryService.nextLotNumber(); + async createBill(tenantId: number, billDTO: IBillOTD) { + const { Vendor, Bill, ItemEntry } = this.tenancy.models(tenantId); + + const invLotNumber = await this.inventoryService.nextLotNumber(tenantId); + const amount = sumBy(billDTO.entries, e => ItemEntry.calcAmount(e)); + const bill = { ...formatDateFields(billDTO, ['bill_date', 'due_date']), - amount: sumBy(billDTO.entries, 'amount'), + amount, invLotNumber: billDTO.invLotNumber || invLotNumber }; const saveEntriesOpers = []; - const storedBill = await Bill.tenant() - .query() + const storedBill = await Bill.query() .insert({ ...omit(bill, ['entries']), }); bill.entries.forEach((entry) => { - const oper = ItemEntry.tenant() - .query() + const oper = ItemEntry.query() .insertAndFetch({ reference_type: 'Bill', reference_id: storedBill.id, @@ -70,10 +78,10 @@ export default class BillsService extends SalesInvoicesCost { // Rewrite the inventory transactions for inventory items. const writeInvTransactionsOper = this.recordInventoryTransactions( - bill, storedBill.id + tenantId, bill, storedBill.id ); // Writes the journal entries for the given bill transaction. - const writeJEntriesOper = this.recordJournalTransactions({ + const writeJEntriesOper = this.recordJournalTransactions(tenantId, { id: storedBill.id, ...bill, }); await Promise.all([ @@ -83,7 +91,7 @@ export default class BillsService extends SalesInvoicesCost { ]); // Schedule bill re-compute based on the item cost // method and starting date. - await this.scheduleComputeBillItemsCost(bill); + await this.scheduleComputeBillItemsCost(tenantId, bill); return storedBill; } @@ -100,26 +108,29 @@ export default class BillsService extends SalesInvoicesCost { * - Re-write the inventory transactions. * - Re-write the bill journal transactions. * + * @param {number} tenantId - The given tenant id. * @param {Integer} billId - The given bill id. - * @param {IBill} bill - The given new bill details. + * @param {billDTO} billDTO - The given new bill details. */ - static async editBill(billId, billDTO) { - const oldBill = await Bill.tenant().query().findById(billId); + async editBill(tenantId: number, billId: number, billDTO: billDTO) { + const { Bill, ItemEntry, Vendor } = this.tenancy.models(tenantId); + + const oldBill = await Bill.query().findById(billId); + const amount = sumBy(billDTO.entries, e => ItemEntry.calcAmount(e)); + const bill = { ...formatDateFields(billDTO, ['bill_date', 'due_date']), - amount: sumBy(billDTO.entries, 'amount'), + amount, invLotNumber: oldBill.invLotNumber, }; // Update the bill transaction. - const updatedBill = await Bill.tenant() - .query() + const updatedBill = await Bill.query() .where('id', billId) .update({ ...omit(bill, ['entries', 'invLotNumber']) }); // Old stored entries. - const storedEntries = await ItemEntry.tenant() - .query() + const storedEntries = await ItemEntry.query() .where('reference_id', billId) .where('reference_type', 'Bill'); @@ -135,10 +146,11 @@ export default class BillsService extends SalesInvoicesCost { oldBill.amount, ); // Re-write the inventory transactions for inventory items. - const writeInvTransactionsOper = this.recordInventoryTransactions(bill, billId, true); - + const writeInvTransactionsOper = this.recordInventoryTransactions( + tenantId, bill, billId, true + ); // Writes the journal entries for the given bill transaction. - const writeJEntriesOper = this.recordJournalTransactions({ + const writeJEntriesOper = this.recordJournalTransactions(tenantId, { id: billId, ...bill, }, billId); @@ -151,7 +163,7 @@ export default class BillsService extends SalesInvoicesCost { ]); // Schedule sale invoice re-compute based on the item cost // method and starting date. - await this.scheduleComputeBillItemsCost(bill); + await this.scheduleComputeBillItemsCost(tenantId, bill); } /** @@ -159,21 +171,22 @@ export default class BillsService extends SalesInvoicesCost { * @param {Integer} billId * @return {void} */ - static async deleteBill(billId) { - const bill = await Bill.tenant().query() + async deleteBill(tenantId: number, billId: number) { + const { Bill, ItemEntry, Vendor } = this.tenancy.models(tenantId); + + const bill = await Bill.query() .where('id', billId) .withGraphFetched('entries') .first(); // Delete all associated bill entries. - const deleteBillEntriesOper = ItemEntry.tenant() - .query() + const deleteBillEntriesOper = ItemEntry.query() .where('reference_type', 'Bill') .where('reference_id', billId) .delete(); // Delete the bill transaction. - const deleteBillOper = Bill.tenant().query().where('id', billId).delete(); + const deleteBillOper = Bill.query().where('id', billId).delete(); // Delete associated bill journal transactions. const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions( @@ -181,8 +194,8 @@ export default class BillsService extends SalesInvoicesCost { 'Bill' ); // Delete bill associated inventory transactions. - const deleteInventoryTransOper = InventoryService.deleteInventoryTransactions( - billId, 'Bill' + const deleteInventoryTransOper = this.inventoryService.deleteInventoryTransactions( + tenantId, billId, 'Bill' ); // Revert vendor balance. const revertVendorBalance = Vendor.changeBalance(bill.vendorId, bill.amount * -1); @@ -196,7 +209,7 @@ export default class BillsService extends SalesInvoicesCost { ]); // Schedule sale invoice re-compute based on the item cost // method and starting date. - await this.scheduleComputeBillItemsCost(bill); + await this.scheduleComputeBillItemsCost(tenantId, bill); } /** @@ -204,7 +217,12 @@ export default class BillsService extends SalesInvoicesCost { * @param {Bill} bill * @param {number} billId */ - static recordInventoryTransactions(bill, billId, override) { + recordInventoryTransactions( + tenantId: number, + bill: any, + billId: number, + override?: boolean + ) { const inventoryTransactions = bill.entries .map((entry) => ({ ...pick(entry, ['item_id', 'quantity', 'rate']), @@ -216,8 +234,8 @@ export default class BillsService extends SalesInvoicesCost { entryId: entry.id, })); - return InventoryService.recordInventoryTransactions( - inventoryTransactions, override + return this.inventoryService.recordInventoryTransactions( + tenantId, inventoryTransactions, override ); } @@ -227,19 +245,21 @@ export default class BillsService extends SalesInvoicesCost { * @param {IBill} bill * @param {Integer} billId */ - static async recordJournalTransactions(bill, billId) { + async recordJournalTransactions(tenantId: number, bill: any, billId?: number) { + const { AccountTransaction, Item, Account } = this.tenancy.models(tenantId); + const entriesItemsIds = bill.entries.map((entry) => entry.item_id); const payableTotal = sumBy(bill.entries, 'amount'); const formattedDate = moment(bill.bill_date).format('YYYY-MM-DD'); - const storedItems = await Item.tenant() - .query() + const storedItems = await Item.query() .whereIn('id', entriesItemsIds); const storedItemsMap = new Map(storedItems.map((item) => [item.id, item])); - const payableAccount = await AccountsService.getAccountByType('accounts_payable'); - - const accountsDepGraph = await Account.tenant().depGraph().query(); + const payableAccount = await this.accountsService.getAccountByType( + tenantId, 'accounts_payable' + ); + const accountsDepGraph = await Account.depGraph().query(); const journal = new JournalPoster(accountsDepGraph); const commonJournalMeta = { @@ -251,8 +271,7 @@ export default class BillsService extends SalesInvoicesCost { accural: true, }; if (billId) { - const transactions = await AccountTransaction.tenant() - .query() + const transactions = await AccountTransaction.query() .whereIn('reference_type', ['Bill']) .whereIn('reference_id', [billId]) .withGraphFetched('account.type'); @@ -291,11 +310,14 @@ export default class BillsService extends SalesInvoicesCost { /** * Detarmines whether the bill exists on the storage. - * @param {Integer} billId + * @param {number} tenantId - The given tenant id. + * @param {Integer} billId - The given bill id. * @return {Boolean} */ - static async isBillExists(billId) { - const foundBills = await Bill.tenant().query().where('id', billId); + async isBillExists(tenantId: number, billId: number) { + const { Bill } = this.tenancy.models(tenantId); + + const foundBills = await Bill.query().where('id', billId); return foundBills.length > 0; } @@ -304,18 +326,23 @@ export default class BillsService extends SalesInvoicesCost { * @param {Array} billsIds * @return {Boolean} */ - static async isBillsExist(billsIds) { - const bills = await Bill.tenant().query().whereIn('id', billsIds); + async isBillsExist(tenantId: number, billsIds: number[]) { + const { Bill } = this.tenancy.models(tenantId); + + const bills = await Bill.query().whereIn('id', billsIds); return bills.length > 0; } /** * Detarmines whether the given bill id exists on the storage. + * @param {number} tenantId * @param {Integer} billNumber + * @return {boolean} */ - static async isBillNoExists(billNumber) { - const foundBills = await Bill.tenant() - .query() + async isBillNoExists(tenantId: number, billNumber : string) { + const { Bill } = this.tenancy.models(tenantId); + + const foundBills = await Bill.query() .where('bill_number', billNumber); return foundBills.length > 0; } @@ -325,8 +352,10 @@ export default class BillsService extends SalesInvoicesCost { * @param {Integer} billId - * @returns {Promise} */ - static getBill(billId) { - return Bill.tenant().query().where('id', billId).first(); + getBill(tenantId: number, billId: number) { + const { Bill } = this.tenancy.models(tenantId); + + return Bill.query().where('id', billId).first(); } /** @@ -334,9 +363,10 @@ export default class BillsService extends SalesInvoicesCost { * @param {Integer} billId - * @returns {Promise} */ - static getBillWithMetadata(billId) { - return Bill.tenant() - .query() + getBillWithMetadata(tenantId: number, billId: number) { + const { Bill } = this.tenancy.models(tenantId); + + return Bill.query() .where('id', billId) .withGraphFetched('vendor') .withGraphFetched('entries') @@ -345,21 +375,27 @@ export default class BillsService extends SalesInvoicesCost { /** * Schedules compute bill items cost based on each item cost method. - * @param {IBill} bill + * @param {number} tenantId - + * @param {IBill} bill - * @return {Promise} */ - static async scheduleComputeBillItemsCost(bill) { + async scheduleComputeBillItemsCost(tenantId: number, bill) { + const { Item } = this.tenancy.models(tenantId); const billItemsIds = bill.entries.map((entry) => entry.item_id); // Retrieves inventory items only. - const inventoryItems = await Item.tenant().query() + const inventoryItems = await Item.query() .whereIn('id', billItemsIds) .where('type', 'inventory'); const inventoryItemsIds = inventoryItems.map(i => i.id); if (inventoryItemsIds.length > 0) { - await this.scheduleComputeItemsCost(inventoryItemsIds, bill.bill_date); + await this.scheduleComputeItemsCost( + tenantId, + inventoryItemsIds, + bill.bill_date + ); } } -} +} \ No newline at end of file diff --git a/server/src/services/Sales/HasItemsEntries.ts b/server/src/services/Sales/HasItemsEntries.ts index 43c9de698..59c3b338f 100644 --- a/server/src/services/Sales/HasItemsEntries.ts +++ b/server/src/services/Sales/HasItemsEntries.ts @@ -1,7 +1,13 @@ import { difference, omit } from 'lodash'; +import { Service, Inject } from 'typedi'; +import TenancyService from '@/services/Tenancy/TenancyService'; import { ItemEntry } from '@/models'; +@Service() export default class HasItemEntries { + @Inject() + tenancy: TenancyService; + /** * Patch items entries to the storage. * @@ -9,15 +15,17 @@ export default class HasItemEntries { * @param {Array} oldEntries - * @param {String} referenceType - * @param {String|Number} referenceId - - * * @return {Promise} */ - static async patchItemsEntries( + async patchItemsEntries( + tenantId: number, newEntries: Array, oldEntries: Array, referenceType: string, referenceId: string|number ) { + const { ItemEntry } = this.tenancy.models(tenantId); + const entriesHasIds = newEntries.filter((entry) => entry.id); const entriesHasNoIds = newEntries.filter((entry) => !entry.id); const entriesIds = entriesHasIds.map(entry => entry.id); @@ -31,15 +39,13 @@ export default class HasItemEntries { entriesIds, ); if (entriesIdsShouldDelete.length > 0) { - const deleteOper = ItemEntry.tenant() - .query() + const deleteOper = ItemEntry.query() .whereIn('id', entriesIdsShouldDelete) .delete(); opers.push(deleteOper); } entriesHasIds.forEach((entry) => { - const updateOper = ItemEntry.tenant() - .query() + const updateOper = ItemEntry.query() .where('id', entry.id) .update({ ...omit(entry, excludeAttrs), @@ -47,8 +53,7 @@ export default class HasItemEntries { opers.push(updateOper); }); entriesHasNoIds.forEach((entry) => { - const insertOper = ItemEntry.tenant() - .query() + const insertOper = ItemEntry.query() .insert({ reference_id: referenceId, reference_type: referenceType, @@ -59,7 +64,7 @@ export default class HasItemEntries { return Promise.all([...opers]); } - static filterNonInventoryEntries(entries: [], items: []) { + filterNonInventoryEntries(entries: [], items: []) { const nonInventoryItems = items.filter((item: any) => item.type !== 'inventory'); const nonInventoryItemsIds = nonInventoryItems.map((i: any) => i.id); @@ -69,7 +74,7 @@ export default class HasItemEntries { )); } - static filterInventoryEntries(entries: [], items: []) { + filterInventoryEntries(entries: [], items: []) { const inventoryItems = items.filter((item: any) => item.type === 'inventory'); const inventoryItemsIds = inventoryItems.map((i: any) => i.id); diff --git a/server/src/services/Sales/JournalPosterService.js b/server/src/services/Sales/JournalPosterService.ts similarity index 55% rename from server/src/services/Sales/JournalPosterService.js rename to server/src/services/Sales/JournalPosterService.ts index d7678b657..48dbf4954 100644 --- a/server/src/services/Sales/JournalPosterService.js +++ b/server/src/services/Sales/JournalPosterService.ts @@ -1,12 +1,26 @@ -import { Account, AccountTransaction } from '@/models'; +import { Service, Inject } from 'typedi'; import JournalPoster from '@/services/Accounting/JournalPoster'; +import TenancyService from '@/services/Tenancy/TenancyService'; - +@Service() export default class JournalPosterService { + @Inject() + tenancy: TenancyService; + /** * Deletes the journal transactions that associated to the given reference id. + * @param {number} tenantId - The given tenant id. + * @param {number} referenceId - The transaction reference id. + * @param {string} referenceType - The transaction reference type. + * @return {Promise} */ - static async deleteJournalTransactions(referenceId, referenceType) { + async deleteJournalTransactions( + tenantId: number, + referenceId: number, + referenceType: string + ) { + const { Account, AccountTransaction } = this.tenancy.models(tenantId); + const transactions = await AccountTransaction.tenant() .query() .whereIn('reference_type', [referenceType]) diff --git a/server/src/services/Sales/PaymentsReceives.ts b/server/src/services/Sales/PaymentsReceives.ts index bfa6b1948..72e4bda5e 100644 --- a/server/src/services/Sales/PaymentsReceives.ts +++ b/server/src/services/Sales/PaymentsReceives.ts @@ -1,13 +1,7 @@ import { omit, sumBy, chain } from 'lodash'; import moment from 'moment'; -import { - AccountTransaction, - PaymentReceive, - PaymentReceiveEntry, - SaleInvoice, - Customer, - Account, -} from '@/models'; +import { Service, Inject } from 'typedi'; +import { IPaymentReceiveOTD } from '@/interfaces'; import AccountsService from '@/services/Accounts/AccountsService'; import JournalPoster from '@/services/Accounting/JournalPoster'; import JournalEntry from '@/services/Accounting/JournalEntry'; @@ -15,23 +9,41 @@ import JournalPosterService from '@/services/Sales/JournalPosterService'; import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries'; import PaymentReceiveEntryRepository from '@/repositories/PaymentReceiveEntryRepository'; import CustomerRepository from '@/repositories/CustomerRepository'; +import TenancyService from '@/services/Tenancy/TenancyService'; import { formatDateFields } from '@/utils'; /** * Payment receive service. * @service */ +@Service() export default class PaymentReceiveService { + @Inject() + accountsService: AccountsService; + + @Inject() + tenancy: TenancyService; + + @Inject() + journalService: JournalPosterService; + /** * Creates a new payment receive and store it to the storage * with associated invoices payment and journal transactions. * @async + * @param {number} tenantId - Tenant id. * @param {IPaymentReceive} paymentReceive */ - static async createPaymentReceive(paymentReceive: any) { + async createPaymentReceive(tenantId: number, paymentReceive: IPaymentReceiveOTD) { + const { + PaymentReceive, + PaymentReceiveEntry, + SaleInvoice, + Customer, + } = this.tenancy.models(tenantId); + const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount'); - const storedPaymentReceive = await PaymentReceive.tenant() - .query() + const storedPaymentReceive = await PaymentReceive.query() .insert({ amount: paymentAmount, ...formatDateFields(omit(paymentReceive, ['entries']), ['payment_date']), @@ -39,15 +51,13 @@ export default class PaymentReceiveService { const storeOpers: Array = []; paymentReceive.entries.forEach((entry: any) => { - const oper = PaymentReceiveEntry.tenant() - .query() + const oper = PaymentReceiveEntry.query() .insert({ payment_receive_id: storedPaymentReceive.id, ...entry, }); // Increment the invoice payment amount. - const invoice = SaleInvoice.tenant() - .query() + const invoice = SaleInvoice.query() .where('id', entry.invoice_id) .increment('payment_amount', entry.payment_amount); @@ -59,10 +69,10 @@ export default class PaymentReceiveService { paymentAmount, ); // Records the sale invoice journal transactions. - const recordJournalTransactions = this.recordPaymentReceiveJournalEntries({ - id: storedPaymentReceive.id, - ...paymentReceive, - }); + const recordJournalTransactions = this.recordPaymentReceiveJournalEntries(tenantId,{ + id: storedPaymentReceive.id, + ...paymentReceive, + }); await Promise.all([ ...storeOpers, customerIncrementOper, @@ -82,19 +92,22 @@ export default class PaymentReceiveService { * - Update the different customer balances. * - Update the different invoice payment amount. * @async - * @param {Integer} paymentReceiveId - * @param {IPaymentReceive} paymentReceive - * @param {IPaymentReceive} oldPaymentReceive + * @param {number} tenantId - + * @param {Integer} paymentReceiveId - + * @param {IPaymentReceive} paymentReceive - + * @param {IPaymentReceive} oldPaymentReceive - */ - static async editPaymentReceive( + async editPaymentReceive( + tenantId: number, paymentReceiveId: number, paymentReceive: any, oldPaymentReceive: any ) { + const { PaymentReceive } = this.tenancy.models(tenantId); + const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount'); // Update the payment receive transaction. - const updatePaymentReceive = await PaymentReceive.tenant() - .query() + const updatePaymentReceive = await PaymentReceive.query() .where('id', paymentReceiveId) .update({ amount: paymentAmount, @@ -131,6 +144,7 @@ export default class PaymentReceiveService { } // Re-write the journal transactions of the given payment receive. const recordJournalTransactions = this.recordPaymentReceiveJournalEntries( + tenantId, { id: oldPaymentReceive.id, ...paymentReceive, @@ -147,6 +161,7 @@ export default class PaymentReceiveService { ); // Change the difference between the old and new invoice payment amount. const diffInvoicePaymentAmount = this.saveChangeInvoicePaymentAmount( + tenantId, oldPaymentReceive.entries, paymentReceive.entries ); @@ -169,24 +184,26 @@ export default class PaymentReceiveService { * - Revert the customer balance. * - Revert the payment amount of the associated invoices. * @async - * @param {Integer} paymentReceiveId - * @param {IPaymentReceive} paymentReceive + * @param {number} tenantId - Tenant id. + * @param {Integer} paymentReceiveId - Payment receive id. + * @param {IPaymentReceive} paymentReceive - Payment receive object. */ - static async deletePaymentReceive(paymentReceiveId: number, paymentReceive: any) { + async deletePaymentReceive(tenantId: number, paymentReceiveId: number, paymentReceive: any) { + const { PaymentReceive, PaymentReceiveEntry, Customer } = this.tenancy.models(tenantId); + // Deletes the payment receive transaction. - await PaymentReceive.tenant() - .query() + await PaymentReceive.query() .where('id', paymentReceiveId) .delete(); // Deletes the payment receive associated entries. - await PaymentReceiveEntry.tenant() - .query() + await PaymentReceiveEntry.query() .where('payment_receive_id', paymentReceiveId) .delete(); // Delete all associated journal transactions to payment receive transaction. - const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions( + const deleteTransactionsOper = this.journalService.deleteJournalTransactions( + tenantId, paymentReceiveId, 'PaymentReceive' ); @@ -197,6 +214,7 @@ export default class PaymentReceiveService { ); // Revert the invoices payments amount. const revertInvoicesPaymentAmount = this.revertInvoicePaymentAmount( + tenantId, paymentReceive.entries.map((entry: any) => ({ invoiceId: entry.invoiceId, revertAmount: entry.paymentAmount, @@ -211,11 +229,12 @@ export default class PaymentReceiveService { /** * Retrieve the payment receive details of the given id. - * @param {Integer} paymentReceiveId + * @param {number} tenantId - Tenant id. + * @param {Integer} paymentReceiveId - Payment receive id. */ - static async getPaymentReceive(paymentReceiveId: number) { - const paymentReceive = await PaymentReceive.tenant() - .query() + async getPaymentReceive(tenantId: number, paymentReceiveId: number) { + const { PaymentReceive } = this.tenancy.models(tenantId); + const paymentReceive = await PaymentReceive.query() .where('id', paymentReceiveId) .withGraphFetched('entries.invoice') .first(); @@ -226,9 +245,9 @@ export default class PaymentReceiveService { * Retrieve the payment receive details with associated invoices. * @param {Integer} paymentReceiveId */ - static async getPaymentReceiveWithInvoices(paymentReceiveId: number) { - return PaymentReceive.tenant() - .query() + async getPaymentReceiveWithInvoices(tenantId: number, paymentReceiveId: number) { + const { PaymentReceive } = this.tenancy.models(tenantId); + return PaymentReceive.query() .where('id', paymentReceiveId) .withGraphFetched('invoices') .first(); @@ -236,11 +255,12 @@ export default class PaymentReceiveService { /** * Detarmines whether the payment receive exists on the storage. + * @param {number} tenantId - Tenant id. * @param {Integer} paymentReceiveId */ - static async isPaymentReceiveExists(paymentReceiveId: number) { - const paymentReceives = await PaymentReceive.tenant() - .query() + async isPaymentReceiveExists(tenantId: number, paymentReceiveId: number) { + const { PaymentReceive } = this.tenancy.models(tenantId); + const paymentReceives = await PaymentReceive.query() .where('id', paymentReceiveId); return paymentReceives.length > 0; } @@ -248,15 +268,17 @@ export default class PaymentReceiveService { /** * Detarmines the payment receive number existance. * @async + * @param {number} tenantId - Tenant id. * @param {Integer} paymentReceiveNumber - Payment receive number. * @param {Integer} paymentReceiveId - Payment receive id. */ - static async isPaymentReceiveNoExists( + async isPaymentReceiveNoExists( + tenantId: number, paymentReceiveNumber: string|number, paymentReceiveId: number ) { - const paymentReceives = await PaymentReceive.tenant() - .query() + const { PaymentReceive } = this.tenancy.models(tenantId); + const paymentReceives = await PaymentReceive.query() .where('payment_receive_no', paymentReceiveNumber) .onBuild((query) => { if (paymentReceiveId) { @@ -273,21 +295,25 @@ export default class PaymentReceiveService { * -------- * - Account receivable -> Debit * - Payment account [current asset] -> Credit - * * @async + * @param {number} tenantId - Tenant id. * @param {IPaymentReceive} paymentReceive * @param {Number} paymentReceiveId */ - static async recordPaymentReceiveJournalEntries( + async recordPaymentReceiveJournalEntries( + tenantId: number, paymentReceive: any, paymentReceiveId?: number ) { + const { Account, AccountTransaction } = this.tenancy.models(tenantId); + const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount'); const formattedDate = moment(paymentReceive.payment_date).format('YYYY-MM-DD'); - const receivableAccount = await AccountsService.getAccountByType( + const receivableAccount = await this.accountsService.getAccountByType( + tenantId, 'accounts_receivable' ); - const accountsDepGraph = await Account.tenant().depGraph().query(); + const accountsDepGraph = await Account.depGraph().query(); const journal = new JournalPoster(accountsDepGraph); const commonJournal = { debit: 0, @@ -297,8 +323,7 @@ export default class PaymentReceiveService { date: formattedDate, }; if (paymentReceiveId) { - const transactions = await AccountTransaction.tenant() - .query() + const transactions = await AccountTransaction.query() .whereIn('reference_type', ['PaymentReceive']) .where('reference_id', paymentReceiveId) .withGraphFetched('account.type'); @@ -330,15 +355,18 @@ export default class PaymentReceiveService { /** * Revert the payment amount of the given invoices ids. + * @async + * @param {number} tenantId - Tenant id. * @param {Array} revertInvoices + * @return {Promise} */ - static async revertInvoicePaymentAmount(revertInvoices: any[]) { + async revertInvoicePaymentAmount(tenantId: number, revertInvoices: any[]) { + const { SaleInvoice } = this.tenancy.models(tenantId); const opers: Promise[] = []; revertInvoices.forEach((revertInvoice) => { const { revertAmount, invoiceId } = revertInvoice; - const oper = SaleInvoice.tenant() - .query() + const oper = SaleInvoice.query() .where('id', invoiceId) .decrement('payment_amount', revertAmount); opers.push(oper); @@ -348,14 +376,18 @@ export default class PaymentReceiveService { /** * Saves difference changing between old and new invoice payment amount. + * @async + * @param {number} tenantId - Tenant id. * @param {Array} paymentReceiveEntries * @param {Array} newPaymentReceiveEntries * @return */ - static async saveChangeInvoicePaymentAmount( + async saveChangeInvoicePaymentAmount( + tenantId: number, paymentReceiveEntries: [], newPaymentReceiveEntries: [], ) { + const { SaleInvoice } = this.tenancy.models(tenantId); const opers: Promise[] = []; const newEntriesTable = chain(newPaymentReceiveEntries) .groupBy('invoice_id') diff --git a/server/src/services/Sales/SalesEstimate.ts b/server/src/services/Sales/SalesEstimate.ts index 7bd725335..72596af3e 100644 --- a/server/src/services/Sales/SalesEstimate.ts +++ b/server/src/services/Sales/SalesEstimate.ts @@ -1,31 +1,44 @@ import { omit, difference, sumBy, mixin } from 'lodash'; -import moment from 'moment'; -import { SaleEstimate, ItemEntry } from '@/models'; +import { Service, Inject } from 'typedi'; import HasItemsEntries from '@/services/Sales/HasItemsEntries'; import { formatDateFields } from '@/utils'; +import TenancyService from '@/services/Tenancy/TenancyService'; +/** + * Sale estimate service. + * @Service + */ +@Service() export default class SaleEstimateService { + @Inject() + tenancy: TenancyService; + + @Inject() + itemsEntriesService: HasItemsEntries; + /** * Creates a new estimate with associated entries. * @async + * @param {number} tenantId - The tenant id. * @param {EstimateDTO} estimate * @return {void} */ - static async createEstimate(estimateDTO: any) { + async createEstimate(tenantId: number, estimateDTO: any) { + const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId); + + const amount = sumBy(estimateDTO.entries, e => ItemEntry.calcAmount(e)); const estimate = { - amount: sumBy(estimateDTO.entries, 'amount'), + amount, ...formatDateFields(estimateDTO, ['estimate_date', 'expiration_date']), }; - const storedEstimate = await SaleEstimate.tenant() - .query() + const storedEstimate = await SaleEstimate.query() .insert({ ...omit(estimate, ['entries']), }); const storeEstimateEntriesOpers: any[] = []; estimate.entries.forEach((entry: any) => { - const oper = ItemEntry.tenant() - .query() + const oper = ItemEntry.query() .insert({ reference_type: 'SaleEstimate', reference_id: storedEstimate.id, @@ -41,27 +54,33 @@ export default class SaleEstimateService { /** * Edit details of the given estimate with associated entries. * @async + * @param {number} tenantId - The tenant id. * @param {Integer} estimateId * @param {EstimateDTO} estimate * @return {void} */ - static async editEstimate(estimateId: number, estimateDTO: any) { + async editEstimate(tenantId: number, estimateId: number, estimateDTO: any) { + const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId); + const amount = sumBy(estimateDTO.entries, e => ItemEntry.calcAmount(e)); + const estimate = { - amount: sumBy(estimateDTO.entries, 'amount'), + amount, ...formatDateFields(estimateDTO, ['estimate_date', 'expiration_date']), }; - const updatedEstimate = await SaleEstimate.tenant() - .query() + const updatedEstimate = await SaleEstimate.query() .update({ ...omit(estimate, ['entries']), }); - const storedEstimateEntries = await ItemEntry.tenant() - .query() + const storedEstimateEntries = await ItemEntry.query() .where('reference_id', estimateId) .where('reference_type', 'SaleEstimate'); - const patchItemsEntries = HasItemsEntries.patchItemsEntries( - estimate.entries, storedEstimateEntries, 'SaleEstimate', estimateId + const patchItemsEntries = this.itemsEntriesService.patchItemsEntries( + tenantId, + estimate.entries, + storedEstimateEntries, + 'SaleEstimate', + estimateId, ); return Promise.all([ patchItemsEntries, @@ -71,32 +90,32 @@ export default class SaleEstimateService { /** * Deletes the given estimate id with associated entries. * @async + * @param {number} tenantId - The tenant id. * @param {IEstimate} estimateId * @return {void} */ - static async deleteEstimate(estimateId: number) { - await ItemEntry.tenant() - .query() + async deleteEstimate(tenantId: number, estimateId: number) { + const { SaleEstimate, ItemEntry } = this.tenancy.models(tenantId); + await ItemEntry.query() .where('reference_id', estimateId) .where('reference_type', 'SaleEstimate') .delete(); - await SaleEstimate.tenant() - .query() + await SaleEstimate.query() .where('id', estimateId) .delete(); } - /** * Validates the given estimate ID exists. * @async + * @param {number} tenantId - The tenant id. * @param {Numeric} estimateId * @return {Boolean} */ - static async isEstimateExists(estimateId: number) { - const foundEstimate = await SaleEstimate.tenant() - .query() + async isEstimateExists(estimateId: number) { + const { SaleEstimate } = this.tenancy.models(tenantId); + const foundEstimate = await SaleEstimate.query() .where('id', estimateId); return foundEstimate.length !== 0; } @@ -104,16 +123,17 @@ export default class SaleEstimateService { /** * Validates the given estimate entries IDs. * @async - * @param {Numeric} estimateId + * @param {number} tenantId - The tenant id. + * @param {Numeric} estimateId - the sale estimate id. * @param {IEstimate} estimate */ - static async isEstimateEntriesIDsExists(estimateId: number, estimate: any) { + async isEstimateEntriesIDsExists(tenantId: number, estimateId: number, estimate: any) { + const { ItemEntry } = this.tenancy.models(tenantId); const estimateEntriesIds = estimate.entries .filter((e: any) => e.id) .map((e: any) => e.id); - const estimateEntries = await ItemEntry.tenant() - .query() + const estimateEntries = await ItemEntry.query() .whereIn('id', estimateEntriesIds) .where('reference_id', estimateId) .where('reference_type', 'SaleEstimate'); @@ -128,12 +148,14 @@ export default class SaleEstimateService { /** * Retrieve the estimate details of the given estimate id. + * @async + * @param {number} tenantId - The tenant id. * @param {Integer} estimateId * @return {IEstimate} */ - static async getEstimate(estimateId: number) { - const estimate = await SaleEstimate.tenant() - .query() + async getEstimate(tenantId: number, estimateId: number) { + const { SaleEstimate } = this.tenancy.models(tenantId); + const estimate = await SaleEstimate.query() .where('id', estimateId) .first(); @@ -142,11 +164,13 @@ export default class SaleEstimateService { /** * Retrieve the estimate details with associated entries. + * @async + * @param {number} tenantId - The tenant id. * @param {Integer} estimateId */ - static async getEstimateWithEntries(estimateId: number) { - const estimate = await SaleEstimate.tenant() - .query() + async getEstimateWithEntries(tenantId: number, estimateId: number) { + const { SaleEstimate } = this.tenancy.models(tenantId); + const estimate = await SaleEstimate.query() .where('id', estimateId) .withGraphFetched('entries') .withGraphFetched('customer') @@ -157,13 +181,15 @@ export default class SaleEstimateService { /** * Detarmines the estimate number uniqness. + * @async + * @param {number} tenantId - The tenant id. * @param {String} estimateNumber * @param {Integer} excludeEstimateId * @return {Boolean} */ - static async isEstimateNumberUnique(estimateNumber: string, excludeEstimateId: number) { - const foundEstimates = await SaleEstimate.tenant() - .query() + async isEstimateNumberUnique(tenantId: number, estimateNumber: string, excludeEstimateId: number) { + const { SaleEstimate } = this.tenancy.models(tenantId); + const foundEstimates = await SaleEstimate.query() .onBuild((query: any) => { query.where('estimate_number', estimateNumber); diff --git a/server/src/services/Sales/SalesInvoices.ts b/server/src/services/Sales/SalesInvoices.ts index 72b4aa941..981c00382 100644 --- a/server/src/services/Sales/SalesInvoices.ts +++ b/server/src/services/Sales/SalesInvoices.ts @@ -1,52 +1,56 @@ +import { Service, Inject } from 'typedi'; import { omit, sumBy, difference, pick, chain } from 'lodash'; -import { - SaleInvoice, - AccountTransaction, - InventoryTransaction, - Account, - ItemEntry, - Customer, - Item, -} from '@/models'; +import { ISaleInvoice, ISaleInvoiceOTD, IItemEntry } from '@/interfaces'; import JournalPoster from '@/services/Accounting/JournalPoster'; import HasItemsEntries from '@/services/Sales/HasItemsEntries'; -import CustomerRepository from '@/repositories/CustomerRepository'; import InventoryService from '@/services/Inventory/Inventory'; import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost'; -import { ISaleInvoice, ISaleInvoiceOTD, IItemEntry } from '@/interfaces'; +import TenancyService from '@/services/Tenancy/TenancyService'; import { formatDateFields } from '@/utils'; /** * Sales invoices service * @service */ +@Service() export default class SaleInvoicesService extends SalesInvoicesCost { + @Inject() + tenancy: TenancyService; + + @Inject() + inventoryService: InventoryService; + + @Inject() + itemsEntriesService: HasItemsEntries; + /** * Creates a new sale invoices and store it to the storage * with associated to entries and journal transactions. * @async - * @param {ISaleInvoice} + * @param {number} tenantId = + * @param {ISaleInvoice} saleInvoiceDTO - * @return {ISaleInvoice} */ - static async createSaleInvoice(saleInvoiceDTO: ISaleInvoiceOTD) { - const balance = sumBy(saleInvoiceDTO.entries, 'amount'); - const invLotNumber = await InventoryService.nextLotNumber(); + async createSaleInvoice(tenantId: number, saleInvoiceDTO: ISaleInvoiceOTD) { + const { SaleInvoice, Customer, ItemEntry } = this.tenancy.models(tenantId); + + const balance = sumBy(saleInvoiceDTO.entries, e => ItemEntry.calcAmount(e)); + const invLotNumber = await this.inventoryService.nextLotNumber(tenantId); + const saleInvoice: ISaleInvoice = { - ...formatDateFields(saleInvoiceDTO, ['invoiceDate', 'dueDate']), + ...formatDateFields(saleInvoiceDTO, ['invoice_date', 'due_date']), balance, paymentAmount: 0, invLotNumber, }; - const storedInvoice = await SaleInvoice.tenant() - .query() + const storedInvoice = await SaleInvoice.query() .insert({ ...omit(saleInvoice, ['entries']), }); const opers: Array = []; saleInvoice.entries.forEach((entry: any) => { - const oper = ItemEntry.tenant() - .query() + const oper = ItemEntry.query() .insertAndFetch({ reference_type: 'SaleInvoice', reference_id: storedInvoice.id, @@ -67,12 +71,11 @@ export default class SaleInvoicesService extends SalesInvoicesCost { ...opers, incrementOper, ]); // Records the inventory transactions for inventory items. - await this.recordInventoryTranscactions( - saleInvoice, storedInvoice.id - ); + await this.recordInventoryTranscactions(tenantId, saleInvoice, storedInvoice.id); + // Schedule sale invoice re-compute based on the item cost // method and starting date. - await this.scheduleComputeInvoiceItemsCost(storedInvoice.id); + await this.scheduleComputeInvoiceItemsCost(tenantId, storedInvoice.id); return storedInvoice; } @@ -80,12 +83,15 @@ export default class SaleInvoicesService extends SalesInvoicesCost { /** * Edit the given sale invoice. * @async + * @param {number} tenantId - * @param {Number} saleInvoiceId - * @param {ISaleInvoice} saleInvoice - */ - static async editSaleInvoice(saleInvoiceId: number, saleInvoiceDTO: any) { - const balance = sumBy(saleInvoiceDTO.entries, 'amount'); - const oldSaleInvoice = await SaleInvoice.tenant().query() + async editSaleInvoice(tenantId: number, saleInvoiceId: number, saleInvoiceDTO: any) { + const { SaleInvoice, ItemEntry, Customer } = this.tenancy.models(tenantId); + + const balance = sumBy(saleInvoiceDTO.entries, e => ItemEntry.calcAmount(e)); + const oldSaleInvoice = await SaleInvoice.query() .where('id', saleInvoiceId) .first(); @@ -94,24 +100,22 @@ export default class SaleInvoicesService extends SalesInvoicesCost { balance, invLotNumber: oldSaleInvoice.invLotNumber, }; - const updatedSaleInvoices: ISaleInvoice = await SaleInvoice.tenant() - .query() + const updatedSaleInvoices: ISaleInvoice = await SaleInvoice.query() .where('id', saleInvoiceId) .update({ ...omit(saleInvoice, ['entries', 'invLotNumber']), }); // Fetches the sale invoice items entries. - const storedEntries = await ItemEntry.tenant() - .query() + const storedEntries = await ItemEntry.query() .where('reference_id', saleInvoiceId) .where('reference_type', 'SaleInvoice'); // Patch update the sale invoice items entries. - const patchItemsEntriesOper = HasItemsEntries.patchItemsEntries( - saleInvoice.entries, storedEntries, 'SaleInvoice', saleInvoiceId, + const patchItemsEntriesOper = this.itemsEntriesService.patchItemsEntries( + tenantId, saleInvoice.entries, storedEntries, 'SaleInvoice', saleInvoiceId, ); // Changes the diff customer balance between old and new amount. - const changeCustomerBalanceOper = CustomerRepository.changeDiffBalance( + const changeCustomerBalanceOper = Customer.changeDiffBalance( saleInvoice.customer_id, oldSaleInvoice.customerId, balance, @@ -119,7 +123,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost { ); // Records the inventory transactions for inventory items. const recordInventoryTransOper = this.recordInventoryTranscactions( - saleInvoice, saleInvoiceId, true, + tenantId, saleInvoice, saleInvoiceId, true, ); await Promise.all([ patchItemsEntriesOper, @@ -128,23 +132,31 @@ export default class SaleInvoicesService extends SalesInvoicesCost { ]); // Schedule sale invoice re-compute based on the item cost // method and starting date. - await this.scheduleComputeInvoiceItemsCost(saleInvoiceId, true); + await this.scheduleComputeInvoiceItemsCost(tenantId, saleInvoiceId, true); } /** * Deletes the given sale invoice with associated entries * and journal transactions. * @async - * @param {Number} saleInvoiceId + * @param {Number} saleInvoiceId - The given sale invoice id. */ - static async deleteSaleInvoice(saleInvoiceId: number) { - const oldSaleInvoice = await SaleInvoice.tenant().query() + async deleteSaleInvoice(tenantId: number, saleInvoiceId: number) { + const { + SaleInvoice, + ItemEntry, + Customer, + Account, + InventoryTransaction, + AccountTransaction, + } = this.tenancy.models(tenantId); + + const oldSaleInvoice = await SaleInvoice.query() .findById(saleInvoiceId) .withGraphFetched('entries'); - await SaleInvoice.tenant().query().where('id', saleInvoiceId).delete(); - await ItemEntry.tenant() - .query() + await SaleInvoice.query().where('id', saleInvoiceId).delete(); + await ItemEntry.query() .where('reference_id', saleInvoiceId) .where('reference_type', 'SaleInvoice') .delete(); @@ -153,26 +165,25 @@ export default class SaleInvoicesService extends SalesInvoicesCost { oldSaleInvoice.customerId, oldSaleInvoice.balance * -1, ); - const invoiceTransactions = await AccountTransaction.tenant() - .query() + const invoiceTransactions = await AccountTransaction.query() .whereIn('reference_type', ['SaleInvoice']) .where('reference_id', saleInvoiceId) .withGraphFetched('account.type'); - const accountsDepGraph = await Account.tenant().depGraph().query(); + const accountsDepGraph = await Account.depGraph().query(); const journal = new JournalPoster(accountsDepGraph); journal.loadEntries(invoiceTransactions); journal.removeEntries(); - const inventoryTransactions = await InventoryTransaction.tenant() - .query() + const inventoryTransactions = await InventoryTransaction.query() .where('transaction_type', 'SaleInvoice') .where('transaction_id', saleInvoiceId); // Revert inventory transactions. const revertInventoryTransactionsOper = this.revertInventoryTransactions( - inventoryTransactions + tenantId, + inventoryTransactions, ); // Await all async operations. await Promise.all([ @@ -183,7 +194,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost { ]); // Schedule sale invoice re-compute based on the item cost // method and starting date. - await this.scheduleComputeItemsCost(oldSaleInvoice) + await this.scheduleComputeItemsCost(tenantId, oldSaleInvoice) } /** @@ -192,7 +203,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost { * @param {number} saleInvoiceId - * @param {boolean} override - */ - static recordInventoryTranscactions(saleInvoice, saleInvoiceId: number, override?: boolean){ + recordInventoryTranscactions(tenantId: number, saleInvoice, saleInvoiceId: number, override?: boolean){ const inventortyTransactions = saleInvoice.entries .map((entry) => ({ ...pick(entry, ['item_id', 'quantity', 'rate',]), @@ -204,8 +215,8 @@ export default class SaleInvoicesService extends SalesInvoicesCost { entryId: entry.id, })); - return InventoryService.recordInventoryTransactions( - inventortyTransactions, override, + return this.inventoryService.recordInventoryTransactions( + tenantId, inventortyTransactions, override, ); } @@ -214,15 +225,15 @@ export default class SaleInvoicesService extends SalesInvoicesCost { * @param {string} transactionType * @param {number} transactionId */ - static async revertInventoryTransactions(inventoryTransactions: array) { + async revertInventoryTransactions(tenantId: number, inventoryTransactions: array) { + const { InventoryTransaction } = this.tenancy.models(tenantId); const opers: Promise<[]>[] = []; inventoryTransactions.forEach((trans: any) => { switch(trans.direction) { case 'OUT': if (trans.inventoryTransactionId) { - const revertRemaining = InventoryTransaction.tenant() - .query() + const revertRemaining = InventoryTransaction.query() .where('id', trans.inventoryTransactionId) .where('direction', 'OUT') .increment('remaining', trans.quanitity); @@ -231,8 +242,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost { } break; case 'IN': - const removeRelationOper = InventoryTransaction.tenant() - .query() + const removeRelationOper = InventoryTransaction.query() .where('inventory_transaction_id', trans.id) .where('direction', 'IN') .update({ @@ -250,8 +260,9 @@ export default class SaleInvoicesService extends SalesInvoicesCost { * @async * @param {Number} saleInvoiceId */ - static async getSaleInvoiceWithEntries(saleInvoiceId: number) { - return SaleInvoice.tenant().query() + async getSaleInvoiceWithEntries(tenantId: number, saleInvoiceId: number) { + const { SaleInvoice } = this.tenancy.models(tenantId); + return SaleInvoice.query() .where('id', saleInvoiceId) .withGraphFetched('entries') .withGraphFetched('customer') @@ -263,10 +274,11 @@ export default class SaleInvoicesService extends SalesInvoicesCost { * @param {Integer} saleInvoiceId * @return {Boolean} */ - static async isSaleInvoiceExists(saleInvoiceId: number) { - const foundSaleInvoice = await SaleInvoice.tenant() - .query() + async isSaleInvoiceExists(tenantId: number, saleInvoiceId: number) { + const { SaleInvoice } = this.tenancy.models(tenantId); + const foundSaleInvoice = await SaleInvoice.query() .where('id', saleInvoiceId); + return foundSaleInvoice.length !== 0; } @@ -277,12 +289,15 @@ export default class SaleInvoicesService extends SalesInvoicesCost { * @param {Number} saleInvoiceId * @return {Boolean} */ - static async isSaleInvoiceNumberExists(saleInvoiceNumber: string|number, saleInvoiceId: number) { - const foundSaleInvoice = await SaleInvoice.tenant() - .query() + async isSaleInvoiceNumberExists( + tenantId: number, + saleInvoiceNumber: string|number, + saleInvoiceId: number + ) { + const { SaleInvoice } = this.tenancy.models(tenantId); + const foundSaleInvoice = await SaleInvoice.query() .onBuild((query: any) => { query.where('invoice_no', saleInvoiceNumber); - if (saleInvoiceId) { query.whereNot('id', saleInvoiceId); } @@ -296,9 +311,9 @@ export default class SaleInvoicesService extends SalesInvoicesCost { * @param {Array} invoicesIds * @return {Array} */ - static async isInvoicesExist(invoicesIds: Array) { - const storedInvoices = await SaleInvoice.tenant() - .query() + async isInvoicesExist(tenantId: number, invoicesIds: Array) { + const { SaleInvoice } = this.tenancy.models(tenantId); + const storedInvoices = await SaleInvoice.query() .onBuild((builder: any) => { builder.whereIn('id', invoicesIds); return builder; @@ -314,9 +329,13 @@ export default class SaleInvoicesService extends SalesInvoicesCost { * @param {ISaleInvoice} saleInvoice * @return {Promise} */ - static async scheduleComputeInvoiceItemsCost(saleInvoiceId: number, override?: boolean) { - const saleInvoice: ISaleInvoice = await SaleInvoice.tenant() - .query() + async scheduleComputeInvoiceItemsCost( + tenantId: number, + saleInvoiceId: number, + override?: boolean + ) { + const { SaleInvoice } = this.tenancy.models(tenantId); + const saleInvoice: ISaleInvoice = await SaleInvoice.query() .findById(saleInvoiceId) .withGraphFetched('entries.item'); @@ -326,9 +345,10 @@ export default class SaleInvoicesService extends SalesInvoicesCost { .uniq().value(); if (inventoryItemsIds.length === 0) { - await this.writeNonInventoryInvoiceJournals(saleInvoice, override); + await this.writeNonInventoryInvoiceJournals(tenantId, saleInvoice, override); } else { await this.scheduleComputeItemsCost( + tenantId, inventoryItemsIds, saleInvoice.invoice_date, ); @@ -339,13 +359,14 @@ export default class SaleInvoicesService extends SalesInvoicesCost { * Writes the sale invoice journal entries. * @param {SaleInvoice} saleInvoice - */ - static async writeNonInventoryInvoiceJournals(saleInvoice: ISaleInvoice, override: boolean) { - const accountsDepGraph = await Account.tenant().depGraph().query(); + async writeNonInventoryInvoiceJournals(tenantId: number, saleInvoice: ISaleInvoice, override: boolean) { + const { Account, AccountTransaction } = this.tenancy.models(tenantId); + + const accountsDepGraph = await Account.depGraph().query(); const journal = new JournalPoster(accountsDepGraph); if (override) { - const oldTransactions = await AccountTransaction.tenant() - .query() + const oldTransactions = await AccountTransaction.query() .where('reference_type', 'SaleInvoice') .where('reference_id', saleInvoice.id) .withGraphFetched('account.type'); diff --git a/server/src/services/Sales/SalesInvoicesCost.ts b/server/src/services/Sales/SalesInvoicesCost.ts index 0aec941dc..49b795632 100644 --- a/server/src/services/Sales/SalesInvoicesCost.ts +++ b/server/src/services/Sales/SalesInvoicesCost.ts @@ -1,17 +1,18 @@ -import { Container } from 'typedi'; -import { - SaleInvoice, - Account, - AccountTransaction, - Item, -} from '@/models'; +import { Container, Service, Inject } from 'typedi'; import JournalPoster from '@/services/Accounting/JournalPoster'; import JournalEntry from '@/services/Accounting/JournalEntry'; import InventoryService from '@/services/Inventory/Inventory'; -import { ISaleInvoice, IItemEntry, IItem } from '@/interfaces'; -import { ISaleInvoice } from '../../interfaces'; +import TenancyService from '@/services/Tenancy/TenancyService'; +import { ISaleInvoice, IItemEntry } from '@/interfaces'; +@Service() export default class SaleInvoicesCost { + @Inject() + inventoryService: InventoryService; + + @Inject() + tenancy: TenancyService; + /** * Schedule sale invoice re-compute based on the item * cost method and starting date. @@ -19,11 +20,16 @@ export default class SaleInvoicesCost { * @param {Date} startingDate - Starting compute cost date. * @return {Promise} */ - static async scheduleComputeItemsCost(inventoryItemsIds: number[], startingDate: Date) { + async scheduleComputeItemsCost( + tenantId: number, + inventoryItemsIds: number[], + startingDate: Date + ) { const asyncOpers: Promise<[]>[] = []; inventoryItemsIds.forEach((inventoryItemId: number) => { - const oper: Promise<[]> = InventoryService.scheduleComputeItemCost( + const oper: Promise<[]> = this.inventoryService.scheduleComputeItemCost( + tenantId, inventoryItemId, startingDate, ); @@ -37,21 +43,22 @@ export default class SaleInvoicesCost { * @param {Date} startingDate * @return {Promise} */ - static scheduleWriteJournalEntries(startingDate?: Date) { + scheduleWriteJournalEntries(tenantId: number, startingDate?: Date) { const agenda = Container.get('agenda'); return agenda.schedule('in 3 seconds', 'rewrite-invoices-journal-entries', { - startingDate, + startingDate, tenantId, }); } /** * Writes journal entries from sales invoices. + * @param {number} tenantId - The tenant id. * @param {Date} startingDate * @param {boolean} override */ - static async writeJournalEntries(startingDate: Date, override: boolean) { - const salesInvoices = await SaleInvoice.tenant() - .query() + async writeJournalEntries(tenantId: number, startingDate: Date, override: boolean) { + const { AccountTransaction, SaleInvoice, Account } = this.tenancy.models(tenantId); + const salesInvoices = await SaleInvoice.query() .onBuild((builder: any) => { builder.modify('filterDateRange', startingDate); builder.orderBy('invoice_date', 'ASC'); @@ -59,12 +66,11 @@ export default class SaleInvoicesCost { builder.withGraphFetched('entries.item') builder.withGraphFetched('costTransactions(groupedEntriesCost)'); }); - const accountsDepGraph = await Account.tenant().depGraph().query(); + const accountsDepGraph = await Account.depGraph().query(); const journal = new JournalPoster(accountsDepGraph); if (override) { - const oldTransactions = await AccountTransaction.tenant() - .query() + const oldTransactions = await AccountTransaction.query() .whereIn('reference_type', ['SaleInvoice']) .onBuild((builder: any) => { builder.modify('filterDateRange', startingDate); @@ -90,7 +96,7 @@ export default class SaleInvoicesCost { * @param {ISaleInvoice} saleInvoice * @param {JournalPoster} journal */ - static saleInvoiceJournal(saleInvoice: ISaleInvoice, journal: JournalPoster) { + saleInvoiceJournal(saleInvoice: ISaleInvoice, journal: JournalPoster) { let inventoryTotal: number = 0; const receivableAccount = { id: 10 }; const commonEntry = { diff --git a/server/src/services/Sales/SalesReceipts.ts b/server/src/services/Sales/SalesReceipts.ts index c50707816..13da6e207 100644 --- a/server/src/services/Sales/SalesReceipts.ts +++ b/server/src/services/Sales/SalesReceipts.ts @@ -1,36 +1,43 @@ import { omit, difference, sumBy } from 'lodash'; -import { - SaleReceipt, - Account, - ItemEntry, -} from '@/models'; -import JournalPoster from '@/services/Accounting/JournalPoster'; +import { Service, Inject } from 'typedi'; import JournalPosterService from '@/services/Sales/JournalPosterService'; import HasItemEntries from '@/services/Sales/HasItemsEntries'; +import TenancyService from '@/services/Tenancy/TenancyService'; import { formatDateFields } from '@/utils'; -export default class SalesReceipt { +@Service() +export default class SalesReceiptService { + @Inject() + tenancy: TenancyService; + + @Inject() + journalService: JournalPosterService; + + @Inject() + itemsEntriesService: HasItemEntries; + /** * Creates a new sale receipt with associated entries. * @async * @param {ISaleReceipt} saleReceipt * @return {Object} */ - static async createSaleReceipt(saleReceiptDTO: any) { + async createSaleReceipt(tenantId: number, saleReceiptDTO: any) { + const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId); + + const amount = sumBy(saleReceiptDTO.entries, e => ItemEntry.calcAmount(e)); const saleReceipt = { - amount: sumBy(saleReceiptDTO.entries, 'amount'); + amount, ...formatDateFields(saleReceiptDTO, ['receipt_date']) }; - const storedSaleReceipt = await SaleReceipt.tenant() - .query() + const storedSaleReceipt = await SaleReceipt.query() .insert({ ...omit(saleReceipt, ['entries']), }); const storeSaleReceiptEntriesOpers: Array = []; saleReceipt.entries.forEach((entry: any) => { - const oper = ItemEntry.tenant() - .query() + const oper = ItemEntry.query() .insert({ reference_type: 'SaleReceipt', reference_id: storedSaleReceipt.id, @@ -48,25 +55,30 @@ export default class SalesReceipt { * @param {ISaleReceipt} saleReceipt * @return {void} */ - static async editSaleReceipt(saleReceiptId: number, saleReceiptDTO: any) { + async editSaleReceipt(tenantId: number, saleReceiptId: number, saleReceiptDTO: any) { + const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId); + + const amount = sumBy(saleReceiptDTO.entries, e => ItemEntry.calcAmount(e)); const saleReceipt = { - amount: sumBy(saleReceiptDTO.entries, 'amount'), + amount, ...formatDateFields(saleReceiptDTO, ['receipt_date']) }; - const updatedSaleReceipt = await SaleReceipt.tenant() - .query() + const updatedSaleReceipt = await SaleReceipt.query() .where('id', saleReceiptId) .update({ ...omit(saleReceipt, ['entries']), }); - const storedSaleReceiptEntries = await ItemEntry.tenant() - .query() + const storedSaleReceiptEntries = await ItemEntry.query() .where('reference_id', saleReceiptId) .where('reference_type', 'SaleReceipt'); // Patch sale receipt items entries. - const patchItemsEntries = HasItemEntries.patchItemsEntries( - saleReceipt.entries, storedSaleReceiptEntries, 'SaleReceipt', saleReceiptId, + const patchItemsEntries = this.itemsEntriesService.patchItemsEntries( + tenantId, + saleReceipt.entries, + storedSaleReceiptEntries, + 'SaleReceipt', + saleReceiptId, ); return Promise.all([patchItemsEntries]); } @@ -76,20 +88,20 @@ export default class SalesReceipt { * @param {Integer} saleReceiptId * @return {void} */ - static async deleteSaleReceipt(saleReceiptId: number) { - const deleteSaleReceiptOper = SaleReceipt.tenant() - .query() + async deleteSaleReceipt(tenantId: number, saleReceiptId: number) { + const { SaleReceipt, ItemEntry } = this.tenancy.models(tenantId); + const deleteSaleReceiptOper = SaleReceipt.query() .where('id', saleReceiptId) .delete(); - const deleteItemsEntriesOper = ItemEntry.tenant() - .query() + const deleteItemsEntriesOper = ItemEntry.query() .where('reference_id', saleReceiptId) .where('reference_type', 'SaleReceipt') .delete(); // Delete all associated journal transactions to payment receive transaction. - const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions( + const deleteTransactionsOper = this.journalService.deleteJournalTransactions( + tenantId, saleReceiptId, 'SaleReceipt' ); @@ -105,9 +117,9 @@ export default class SalesReceipt { * @param {Integer} saleReceiptId * @returns {Boolean} */ - static async isSaleReceiptExists(saleReceiptId: number) { - const foundSaleReceipt = await SaleReceipt.tenant() - .query() + async isSaleReceiptExists(tenantId: number, saleReceiptId: number) { + const { SaleReceipt } = this.tenancy.models(tenantId); + const foundSaleReceipt = await SaleReceipt.query() .where('id', saleReceiptId); return foundSaleReceipt.length !== 0; } @@ -117,13 +129,13 @@ export default class SalesReceipt { * @param {Integer} saleReceiptId * @param {ISaleReceipt} saleReceipt */ - static async isSaleReceiptEntriesIDsExists(saleReceiptId: number, saleReceipt: any) { + async isSaleReceiptEntriesIDsExists(tenantId: number, saleReceiptId: number, saleReceipt: any) { + const { ItemEntry } = this.tenancy.models(tenantId); const entriesIDs = saleReceipt.entries .filter((e) => e.id) .map((e) => e.id); - const storedEntries = await ItemEntry.tenant() - .query() + const storedEntries = await ItemEntry.query() .whereIn('id', entriesIDs) .where('reference_id', saleReceiptId) .where('reference_type', 'SaleReceipt'); @@ -140,21 +152,12 @@ export default class SalesReceipt { * Retrieve sale receipt with associated entries. * @param {Integer} saleReceiptId */ - static async getSaleReceiptWithEntries(saleReceiptId: number) { - const saleReceipt = await SaleReceipt.tenant().query() + async getSaleReceiptWithEntries(tenantId: number, saleReceiptId: number) { + const { SaleReceipt } = this.tenancy.models(tenantId); + const saleReceipt = await SaleReceipt.query() .where('id', saleReceiptId) .withGraphFetched('entries'); return saleReceipt; } - - /** - * Records journal transactions for sale receipt. - * @param {ISaleReceipt} saleReceipt - * @return {Promise} - */ - static async _recordJournalTransactions(saleReceipt: any) { - const accountsDepGraph = await Account.tenant().depGraph().query(); - const journalPoster = new JournalPoster(accountsDepGraph); - } } diff --git a/server/src/services/Tenancy/TenancyService.ts b/server/src/services/Tenancy/TenancyService.ts new file mode 100644 index 000000000..3c4ffd6c7 --- /dev/null +++ b/server/src/services/Tenancy/TenancyService.ts @@ -0,0 +1,21 @@ +import { Container } from 'typedi'; + +export default class HasTenancyService { + /** + * Retrieve the given tenant container. + * @param {number} tenantId + * @return {Container} + */ + tenantContainer(tenantId: number) { + return Container.of(`tenant-${tenantId}`); + } + + /** + * Retrieve models of the givne tenant id. + * @param {number} tenantId - The tenant id. + */ + models(tenantId: number) { + console.log(tenantId); + return this.tenantContainer(tenantId).get('models'); + } +} \ No newline at end of file diff --git a/server/src/system/models/Subscriptions/Voucher.ts b/server/src/system/models/Subscriptions/Voucher.js similarity index 85% rename from server/src/system/models/Subscriptions/Voucher.ts rename to server/src/system/models/Subscriptions/Voucher.js index 1178e362f..c0fa08173 100644 --- a/server/src/system/models/Subscriptions/Voucher.ts +++ b/server/src/system/models/Subscriptions/Voucher.js @@ -3,7 +3,7 @@ import moment from 'moment'; import SystemModel from '@/system/models/SystemModel'; import { IVouchersFilter } from '@/interfaces'; -export default class Voucher extends mixin(SystemModel) { +export default class Voucher extends SystemModel { /** * Table name. */ @@ -40,7 +40,7 @@ export default class Voucher extends mixin(SystemModel) { }, // Filters vouchers list. - filter(builder, vouchersFilter: IVouchersFilter) { + filter(builder, vouchersFilter) { if (vouchersFilter.active) { builder.modify('filterActiveVoucher') } @@ -80,7 +80,7 @@ export default class Voucher extends mixin(SystemModel) { * @param {string} voucherCode * @return {Promise} */ - static deleteVoucher(voucherCode: string, viaAttribute: string = 'voucher_code') { + static deleteVoucher(voucherCode, viaAttribute = 'voucher_code') { return this.query() .where(viaAttribute, voucherCode) .delete(); @@ -91,7 +91,7 @@ export default class Voucher extends mixin(SystemModel) { * @param {string} voucherCode * @return {Promise} */ - static markVoucherAsDisabled(voucherCode: string, viaAttribute: string = 'voucher_code') { + static markVoucherAsDisabled(voucherCode, viaAttribute = 'voucher_code') { return this.query() .where(viaAttribute, voucherCode) .patch({ @@ -104,7 +104,7 @@ export default class Voucher extends mixin(SystemModel) { * Marks the given voucher code as sent on the storage. * @param {string} voucherCode */ - static markVoucherAsSent(voucherCode: string, viaAttribute: string = 'voucher_code') { + static markVoucherAsSent(voucherCode, viaAttribute = 'voucher_code') { return this.query() .where(viaAttribute, voucherCode) .patch({ @@ -118,7 +118,7 @@ export default class Voucher extends mixin(SystemModel) { * @param {string} voucherCode * @return {Promise} */ - static markVoucherAsUsed(voucherCode: string, viaAttribute: string = 'voucher_code') { + static markVoucherAsUsed(voucherCode, viaAttribute = 'voucher_code') { return this.query() .where(viaAttribute, voucherCode) .patch({ diff --git a/server/src/system/models/SystemModel.js b/server/src/system/models/SystemModel.js index 6dfc00449..35b17d61f 100644 --- a/server/src/system/models/SystemModel.js +++ b/server/src/system/models/SystemModel.js @@ -1,4 +1,5 @@ import BaseModel from '@/models/Model'; export default class SystemModel extends BaseModel{ + } \ No newline at end of file diff --git a/server/src/utils/index.js b/server/src/utils/index.js index 0b2067854..b649db46c 100644 --- a/server/src/utils/index.js +++ b/server/src/utils/index.js @@ -144,7 +144,7 @@ function applyMixins(derivedCtor, baseCtors) { }); } -const formatDateFields = (inputDTO, fields, format = 'YYYY-DD-MM') => { +const formatDateFields = (inputDTO, fields, format = 'YYYY-MM-DD') => { const _inputDTO = { ...inputDTO }; fields.forEach((field) => { diff --git a/server/tsconfig.json b/server/tsconfig.json index f4c8d08ab..6f74ca9f2 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -10,5 +10,10 @@ "esModuleInterop": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, + "baseUrl": "./", + "paths": { + "@": ["src/"], + "~": ["tests/"] + } } } \ No newline at end of file