diff --git a/server/src/database/migrations/20200715194514_create_payment_receives_table.js b/server/src/database/migrations/20200715194514_create_payment_receives_table.js index 554bad85c..bf299c4ee 100644 --- a/server/src/database/migrations/20200715194514_create_payment_receives_table.js +++ b/server/src/database/migrations/20200715194514_create_payment_receives_table.js @@ -9,6 +9,8 @@ exports.up = function(knex) { table.string('reference_no'); table.integer('deposit_account_id').unsigned(); table.string('payment_receive_no'); + table.text('description'); + table.integer('user_id').unsigned(); table.timestamps(); }); }; diff --git a/server/src/database/seeds/seed_resources.js b/server/src/database/seeds/seed_resources.js index 481397c6e..fd086ae40 100644 --- a/server/src/database/seeds/seed_resources.js +++ b/server/src/database/seeds/seed_resources.js @@ -20,6 +20,7 @@ exports.seed = (knex) => { { id: 12, name: 'sales_payment_receives' }, { id: 13, name: 'bills' }, { id: 14, name: 'bill_payments' }, + { id: 16, name: 'payment_receives' }, ]); }); }; diff --git a/server/src/http/controllers/Budget.js b/server/src/http/controllers/Budget.js deleted file mode 100644 index b5db9d5d5..000000000 --- a/server/src/http/controllers/Budget.js +++ /dev/null @@ -1,247 +0,0 @@ -import express from 'express'; -import { - check, - query, - param, - validationResult, -} from 'express-validator'; -import { pick, difference, groupBy } from 'lodash'; -import asyncMiddleware from "@/http/middleware/asyncMiddleware"; -import JWTAuth from '@/http/middleware/jwtAuth'; -import Budget from '@/models/Budget'; -import BudgetEntry from '@/models/BudgetEntry'; -import Account from '@/models/Account'; -import moment from '@/services/Moment'; -import BudgetEntriesSet from '@/collection/BudgetEntriesSet'; -import AccountType from '@/models/AccountType'; -import NestedSet from '@/collection/NestedSet'; -import { dateRangeFormat } from '@/utils'; - - -export default { - /** - * Router constructor. - */ - router() { - const router = express.Router(); - - router.use(JWTAuth); - - router.post('/', - this.newBudget.validation, - asyncMiddleware(this.newBudget.handler)); - - router.get('/:id', - this.getBudget.validation, - asyncMiddleware(this.getBudget.handler)); - - router.get('/:id', - this.deleteBudget.validation, - asyncMiddleware(this.deleteBudget.handler)); - - router.get('/', - this.listBudgets.validation, - asyncMiddleware(this.listBudgets.handler)); - - return router; - }, - - /** - * Retrieve budget details of the given id. - */ - getBudget: { - validation: [ - param('id').exists().isNumeric().toInt(), - ], - async handler(req, res) { - const validationErrors = validationResult(req); - - if (!validationErrors.isEmpty()) { - return res.boom.badData(null, { - code: 'validation_error', ...validationErrors, - }); - } - const { id } = req.params; - const budget = await Budget.query().findById(id); - - if (!budget) { - return res.status(404).send({ - errors: [{ type: 'budget.not.found', code: 100 }], - }); - } - const accountTypes = await AccountType.query().where('balance_sheet', true); - - const [budgetEntries, accounts] = await Promise.all([ - BudgetEntry.query().where('budget_id', budget.id), - Account.query().whereIn('account_type_id', accountTypes.map((a) => a.id)), - ]); - - const accountsNestedSet = new NestedSet(accounts); - - const columns = []; - const fromDate = moment(budget.year).startOf('year') - .add(budget.rangeOffset, budget.rangeBy).toDate(); - - const toDate = moment(budget.year).endOf('year').toDate(); - - const dateRange = moment.range(fromDate, toDate); - const dateRangeCollection = Array.from(dateRange.by(budget.rangeBy, { - step: budget.rangeIncrement, excludeEnd: false, excludeStart: false, - })); - - dateRangeCollection.forEach((date) => { - columns.push(date.format(dateRangeFormat(budget.rangeBy))); - }); - const budgetEntriesSet = BudgetEntriesSet.from(budgetEntries, { - orderSize: columns.length, - }); - budgetEntriesSet.setZeroPlaceholder(); - budgetEntriesSet.calcTotalSummary(); - - return res.status(200).send({ - columns, - accounts: budgetEntriesSet.toArray(), - total: budgetEntriesSet.toArrayTotalSummary(), - }); - }, - }, - - /** - * Delete the given budget. - */ - deleteBudget: { - validation: [ - param('id').exists(), - ], - async handler(req, res) { - const validationErrors = validationResult(req); - - if (!validationErrors.isEmpty()) { - return res.boom.badData(null, { - code: 'validation_error', ...validationErrors, - }); - } - - const { id } = req.params; - const budget = await Budget.query().findById(id); - - if (!budget) { - return res.status(404).send({ - errors: [{ type: 'budget.not.found', code: 100 }], - }); - } - await BudgetEntry.query().where('budget_id', budget.id).delete(); - await budget.delete(); - - return res.status(200).send(); - }, - }, - - /** - * Saves the new budget. - */ - newBudget: { - validation: [ - check('name').exists(), - check('fiscal_year').exists(), - check('period').exists().isIn(['year', 'month', 'quarter', 'half-year']), - check('accounts_type').exists().isIn(['balance_sheet', 'profit_loss']), - check('accounts').isArray(), - check('accounts.*.account_id').exists().isNumeric().toInt(), - check('accounts.*.entries').exists().isArray(), - check('accounts.*.entries.*.amount').exists().isNumeric().toFloat(), - check('accounts.*.entries.*.order').exists().isNumeric().toInt(), - ], - async handler(req, res) { - const validationErrors = validationResult(req); - - if (!validationErrors.isEmpty()) { - return res.boom.badData(null, { - code: 'validation_error', ...validationErrors, - }); - } - - const form = { ...req.body }; - const submitAccountsIds = form.accounts.map((a) => a.account_id); - const storedAccounts = await Account.query().whereIn('id', submitAccountsIds); - const storedAccountsIds = storedAccounts.map((a) => a.id); - - const errorReasons = []; - const notFoundAccountsIds = difference(submitAccountsIds, storedAccountsIds); - - if (notFoundAccountsIds.length > 0) { - errorReasons.push({ - type: 'ACCOUNT.NOT.FOUND', code: 200, accounts: notFoundAccountsIds, - }); - } - if (errorReasons.length > 0) { - return res.status(400).send({ errors: errorReasons }); - } - // validation entries order. - const budget = await Budget.query().insert({ - ...pick(form, ['name', 'fiscal_year', 'period', 'accounts_type']), - }); - - const promiseOpers = []; - - form.accounts.forEach((account) => { - account.entries.forEach((entry) => { - const budgetEntry = BudgetEntry.query().insert({ - account_id: account.account_id, - amount: entry.amount, - order: entry.order, - }); - promiseOpers.push(budgetEntry); - }); - }); - await Promise.all(promiseOpers); - - return res.status(200).send({ id: budget.id }); - }, - }, - - /** - * List of paginated budgets items. - */ - listBudgets: { - validation: [ - query('year').optional(), - query('income_statement').optional().isBoolean().toBoolean(), - query('profit_loss').optional().isBoolean().toBoolean(), - query('page').optional().isNumeric().toInt(), - query('page_size').isNumeric().toInt(), - query('custom_view_id').optional().isNumeric().toInt(), - ], - async handler(req, res) { - const validationErrors = validationResult(req); - - if (!validationErrors.isEmpty()) { - return res.boom.badData(null, { - code: 'validation_error', ...validationErrors, - }); - } - - const filter = { - page_size: 10, - page: 1, - ...req.query, - }; - const budgets = await Budget.query().runBefore((result, q) => { - if (filter.profit_loss) { - q.modify('filterByYear', filter.year); - } - if (filter.income_statement) { - q.modify('filterByIncomeStatement', filter.income_statement); - } - if (filter.profit_loss) { - q.modify('filterByProfitLoss', filter.profit_loss); - } - q.page(filter.page, filter.page_size); - return result; - }); - return res.status(200).send({ - items: budgets.items, - }); - }, - }, -}; diff --git a/server/src/http/controllers/BudgetReports.js b/server/src/http/controllers/BudgetReports.js deleted file mode 100644 index a0216dc23..000000000 --- a/server/src/http/controllers/BudgetReports.js +++ /dev/null @@ -1,122 +0,0 @@ -import express from 'express'; -import { query, validationResult } from 'express-validator'; -import moment from 'moment'; -import jwtAuth from '@/http/middleware/jwtAuth'; -import asyncMiddleware from '@/http/middleware/asyncMiddleware'; -import Budget from '@/models/Budget'; -import Account from '@/models/Account'; -import AccountType from '@/models/AccountType'; -import NestedSet from '@/collection/NestedSet'; -import BudgetEntry from '@/models/BudgetEntry'; -import { dateRangeFormat } from '@/utils'; - -export default { - - /** - * Router constructor. - */ - router() { - const router = express.Router(); - - router.use(jwtAuth); - - router.get('/budget_verses_actual/:reportId', - this.budgetVersesActual.validation, - asyncMiddleware(this.budgetVersesActual.handler)); - - return router; - }, - - budgetVersesActual: { - validation: [ - query('basis').optional().isIn(['cash', 'accural']), - query('period').optional(), - query('active_accounts').optional().toBoolean(), - ], - async handler(req, res) { - const validationErrors = validationResult(req); - - if (!validationErrors.isEmpty()) { - return res.boom.badData(null, { - code: 'validation_error', ...validationErrors, - }); - } - const { reportId } = req.params; - const form = { ...req.body }; - const errorReasons = []; - - const budget = await Budget.query().findById(reportId); - - if (!budget) { - errorReasons.push({ type: 'BUDGET_NOT_FOUND', code: 100 }); - } - const budgetEntries = await BudgetEntry.query().where('budget_id', budget.id); - - if (errorReasons.length > 0) { - return res.status(400).send({ errors: errorReasons }); - } - const accountTypes = await AccountType.query() - .where('balance_sheet', budget.accountTypes === 'balance_sheet') - .where('income_sheet', budget.accountTypes === 'profit_losss'); - - const accounts = await Account.query().runBefore((result, q) => { - const accountTypesIds = accountTypes.map((t) => t.id); - - if (accountTypesIds.length > 0) { - q.whereIn('account_type_id', accountTypesIds); - } - q.where('active', form.active_accounts === true); - q.withGraphFetched('transactions'); - }); - - // const accountsNestedSet = NestedSet.from(accounts); - - const fromDate = moment(budget.year).startOf('year') - .add(budget.rangeOffset, budget.rangeBy).toDate(); - - const toDate = moment(budget.year).endOf('year').toDate(); - - const dateRange = moment.range(fromDate, toDate); - const dateRangeCollection = Array.from(dateRange.by(budget.rangeBy, { - step: budget.rangeIncrement, excludeEnd: false, excludeStart: false, - })); - - // // const accounts = { - // // assets: [ - // // { - // // name: '', - // // code: '', - // // totalEntries: [ - // // { - - // // } - // // ], - // // children: [ - // // { - // // name: '', - // // code: '', - // // entries: [ - // // { - - // // } - // // ] - // // } - // // ] - // // } - // // ] - // // } - - return res.status(200).send({ - columns: dateRangeCollection.map(d => d.format(dateRangeFormat(budget.rangeBy))), - // accounts: { - // asset: [], - // liabilities: [], - // equaity: [], - - // income: [], - // expenses: [], - // } - }); - }, - }, -} \ No newline at end of file diff --git a/server/src/http/controllers/CurrencyAdjustment.js b/server/src/http/controllers/CurrencyAdjustment.js deleted file mode 100644 index ce043d5b6..000000000 --- a/server/src/http/controllers/CurrencyAdjustment.js +++ /dev/null @@ -1,17 +0,0 @@ - -export default { - - - router() { - - }, - - addExchangePrice: { - validation: { - - }, - async handler(req, res) { - - }, - }, -} \ No newline at end of file diff --git a/server/src/http/controllers/Invoices.js b/server/src/http/controllers/Invoices.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/src/http/controllers/Purchases/BillsPayments.ts b/server/src/http/controllers/Purchases/BillsPayments.ts index 4ef0d5a2f..28489a237 100644 --- a/server/src/http/controllers/Purchases/BillsPayments.ts +++ b/server/src/http/controllers/Purchases/BillsPayments.ts @@ -2,15 +2,18 @@ import { Router } from 'express'; import { check, param, query, ValidationChain } from 'express-validator'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; +import validateMiddleware from '@/http/middleware/validateMiddleware'; import BaseController from '@/http/controllers/BaseController'; import BillPaymentsService from '@/services/Purchases/BillPayments'; import AccountsService from '@/services/Accounts/AccountsService'; -import ItemsService from '@/services/Items/ItemsService'; -import { IBillPaymentEntry, IBillPayment } from '@/interfaces/BillPayment'; import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder'; import DynamicListing from '@/services/DynamicListing/DynamicListing'; import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing'; +/** + * Bills payments controller. + * @controller + */ export default class BillsPayments extends BaseController { /** * Router constructor. @@ -21,38 +24,43 @@ export default class BillsPayments extends BaseController { router.post('/', [ ...this.billPaymentSchemaValidation, ], + validateMiddleware, asyncMiddleware(this.validateBillPaymentVendorExistance), asyncMiddleware(this.validatePaymentAccount), asyncMiddleware(this.validatePaymentNumber), - asyncMiddleware(this.validateItemsIds), + asyncMiddleware(this.validateEntriesBillsExistance), + asyncMiddleware(this.validateVendorsDueAmount), asyncMiddleware(this.createBillPayment), ); router.post('/:id', [ ...this.billPaymentSchemaValidation, ...this.specificBillPaymentValidateSchema, ], + validateMiddleware, asyncMiddleware(this.validateBillPaymentVendorExistance), asyncMiddleware(this.validatePaymentAccount), asyncMiddleware(this.validatePaymentNumber), - asyncMiddleware(this.validateItemsIds), - asyncMiddleware(this.validateEntriesIds), + asyncMiddleware(this.validateEntriesBillsExistance), + asyncMiddleware(this.validateVendorsDueAmount), asyncMiddleware(this.editBillPayment), ) router.delete('/:id', this.specificBillPaymentValidateSchema, + validateMiddleware, asyncMiddleware(this.validateBillPaymentExistance), asyncMiddleware(this.deleteBillPayment), ); router.get('/:id', this.specificBillPaymentValidateSchema, + validateMiddleware, asyncMiddleware(this.validateBillPaymentExistance), asyncMiddleware(this.getBillPayment), ); router.get('/', this.listingValidationSchema, + validateMiddleware, asyncMiddleware(this.getBillsPayments) ); - return router; } @@ -69,13 +77,8 @@ export default class BillsPayments extends BaseController { check('reference').optional().trim().escape(), check('entries').exists().isArray({ min: 1 }), - check('entries.*.id').optional().isNumeric().toInt(), - check('entries.*.index').exists().isNumeric().toInt(), - check('entries.*.item_id').exists().isNumeric().toInt(), - check('entries.*.rate').exists().isNumeric().toFloat(), - check('entries.*.quantity').exists().isNumeric().toFloat(), - check('entries.*.discount').optional().isNumeric().toFloat(), - check('entries.*.description').optional().trim().escape(), + check('entries.*.bill_id').exists().isNumeric().toInt(), + check('entries.*.payment_amount').exists().isNumeric().toInt(), ]; } @@ -97,7 +100,7 @@ export default class BillsPayments extends BaseController { static async validateBillPaymentVendorExistance(req: Request, res: Response, next: any ) { const billPayment = req.body; const { Vendor } = req.models; - const isVendorExists = await Vendor.query('id', billPayment.vendor_id).first(); + const isVendorExists = await Vendor.query().findById(billPayment.vendor_id); if (!isVendorExists) { return res.status(400).send({ @@ -121,7 +124,7 @@ export default class BillsPayments extends BaseController { errors: [{ type: 'BILL.PAYMENT.NOT.FOUND', code: 100 }], }); } - next(req, res, next); + next(); } /** @@ -132,14 +135,15 @@ export default class BillsPayments extends BaseController { */ static async validatePaymentAccount(req: Request, res: Response, next: any) { const billPayment = { ...req.body }; - const isAccountExists = AccountsService.isAccountExists(billPayment); - + const isAccountExists = await AccountsService.isAccountExists( + billPayment.payment_account_id + ); if (!isAccountExists) { return res.status(400).send({ errors: [{ type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 200 }], }); } - next(req, res, next); + next(); } /** @@ -149,61 +153,75 @@ export default class BillsPayments extends BaseController { * @param {Function} res */ static async validatePaymentNumber(req: Request, res: Response, next: any) { + const { BillPayment } = req.models; const billPayment = { ...req.body }; - const isNumberExists = await BillPaymentsService.isBillNoExists(billPayment); - - if (!isNumberExists) { + const foundBillPayment = await BillPayment.query() + .where('payment_number', billPayment.payment_number) + .first(); + + if (foundBillPayment) { return res.status(400).send({ errors: [{ type: 'PAYMENT.NUMBER.NOT.UNIQUE', code: 300 }], }); } - next(req, res, next); - } - - /** - * validate entries items ids existance on the storage. - * @param {Request} req - * @param {Response} res - * @param {Function} next - */ - static async validateItemsIds(req: Request, res: Response, next: Function) { - const billPayment: any = { ...req.body }; - const itemsIds = billPayment.entries.map((e) => e.item_id); - const notFoundItemsIds = await ItemsService.isItemsIdsExists( - itemsIds - ); - if (notFoundItemsIds.length > 0) { - return res.status(400).send({ - errors: [{ type: 'ITEMS.IDS.NOT.FOUND', code: 400 }], - }); - } next(); } /** - * Validates the entries ids in edit bill payment. + * Validate whether the entries bills ids exist on the storage. * @param {Request} req * @param {Response} res * @param {NextFunction} next */ - static async validateEntriesIds(req: Request, res: Response, next: Function) { - const { BillPaymentEntry } = req.models; - const { id: billPaymentId } = req.params; - const billPayment = { id: billPaymentId, ...req.body }; + static 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); - const entriesIds = billPayment.entries - .filter((entry: IBillPaymentEntry) => entry.id) - .map((entry: IBillPaymentEntry) => entry.id); + const notFoundBillsIds = await Bill.getNotFoundBills(entriesBillsIds); - const storedEntries = await BillPaymentEntry.tenant().query() - .where('bill_payment_id', billPaymentId); - - const storedEntriesIds = storedEntries.map((entry: IBillPaymentEntry) => entry.id); - const notFoundEntriesIds = difference(entriesIds, storedEntriesIds); - - if (notFoundEntriesIds.length > 0) { + if (notFoundBillsIds.length > 0) { return res.status(400).send({ - errors: [{ type: 'ENTEIES.IDS.NOT.FOUND', code: 800 }], + errors: [{ type: 'BILLS.IDS.NOT.EXISTS', code: 600 }], + }); + } + next(); + } + + /** + * Validate wether the payment amount bigger than the payable amount. + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + * @return {void} + */ + static 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() + .whereIn('id', billsIds); + + const storedBillsMap = new Map( + storedBills.map((bill: any) => [bill.id, bill]), + ); + interface invalidPaymentAmountError{ + index: number, + due_amount: number + }; + const hasWrongPaymentAmount: invalidPaymentAmountError[] = []; + const { entries } = req.body; + + entries.forEach((entry: any, index: number) => { + const entryBill = storedBillsMap.get(entry.bill_id); + const { dueAmount } = entryBill; + + if (dueAmount < entry.payment_amount) { + hasWrongPaymentAmount.push({ index, due_amount: dueAmount }); + } + }); + if (hasWrongPaymentAmount.length > 0) { + return res.status(400).send({ + errors: [{ type: 'INVALID.BILL.PAYMENT.AMOUNT', code: 400, indexes: hasWrongPaymentAmount }] }); } next(); @@ -297,7 +315,6 @@ export default class BillsPayments extends BaseController { errors: [{ type: 'BILL.PAYMENTS.RESOURCE.NOT_FOUND', code: 200 }], }); } - const viewMeta = await View.query() .modify('allMetadata') .modify('specificOrFavourite', filter.custom_view_id) diff --git a/server/src/http/controllers/PurchasesReports.js b/server/src/http/controllers/PurchasesReports.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/src/http/controllers/RecurringJournal.js b/server/src/http/controllers/RecurringJournal.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/src/http/controllers/RecurringJournalEntry.js b/server/src/http/controllers/RecurringJournalEntry.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/src/http/controllers/Sales/PaymentReceives.js b/server/src/http/controllers/Sales/PaymentReceives.js index df8cc8d10..19199e834 100644 --- a/server/src/http/controllers/Sales/PaymentReceives.js +++ b/server/src/http/controllers/Sales/PaymentReceives.js @@ -1,15 +1,22 @@ import express from 'express'; import { check, param, query } from 'express-validator'; import { difference } from 'lodash'; +import { PaymentReceiveEntry } from '@/models'; import BaseController from '@/http/controllers/BaseController'; import validateMiddleware from '@/http/middleware/validateMiddleware'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; -import PaymentReceiveService from '@/services/Sales/PaymentReceive'; +import PaymentReceiveService from '@/services/Sales/PaymentsReceives'; import CustomersService from '@/services/Customers/CustomersService'; -import SaleInvoicesService from '@/services/Sales/SaleInvoice'; +import SaleInvoicesService from '@/services/Sales/SalesInvoices'; import AccountsService from '@/services/Accounts/AccountsService'; -import { PaymentReceiveEntry } from '@/models'; +import DynamicListing from '@/services/DynamicListing/DynamicListing'; +import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder'; +import { dynamicListingErrorsToResponse } from '@/services/DynamicListing/hasDynamicListing'; +/** + * Payments receives controller. + * @controller + */ export default class PaymentReceivesController extends BaseController { /** * Router constructor. @@ -27,10 +34,11 @@ export default class PaymentReceivesController extends BaseController { asyncMiddleware(this.validateDepositAccount), asyncMiddleware(this.validateInvoicesIDs), asyncMiddleware(this.validateEntriesIdsExistance), - asyncMiddleware(this.editPaymentReceive) + asyncMiddleware(this.validateInvoicesPaymentsAmount), + asyncMiddleware(this.editPaymentReceive), ); router.post( - '/', + '/', this.newPaymentReceiveValidation, validateMiddleware, asyncMiddleware(this.validatePaymentReceiveNoExistance), @@ -38,7 +46,7 @@ export default class PaymentReceivesController extends BaseController { asyncMiddleware(this.validateDepositAccount), asyncMiddleware(this.validateInvoicesIDs), asyncMiddleware(this.validateInvoicesPaymentsAmount), - asyncMiddleware(this.newPaymentReceive) + asyncMiddleware(this.newPaymentReceive), ); router.get( '/:id', @@ -58,7 +66,7 @@ export default class PaymentReceivesController extends BaseController { this.paymentReceiveValidation, validateMiddleware, asyncMiddleware(this.validatePaymentReceiveExistance), - asyncMiddleware(this.deletePaymentReceive) + asyncMiddleware(this.deletePaymentReceive), ); return router; } @@ -340,7 +348,7 @@ export default class PaymentReceivesController extends BaseController { /** * Payment receive list validation schema. */ - static async validatePaymentReceiveList() { + static get validatePaymentReceiveList() { return [ query('custom_view_id').optional().isNumeric().toInt(), query('stringified_filter_roles').optional().isJSON(), @@ -407,7 +415,8 @@ export default class PaymentReceivesController extends BaseController { const paymentReceives = await PaymentReceive.query().onBuild((builder) => { dynamicListing.buildQuery()(builder); return builder; - }); + }).pagination(filter.page - 1, filter.page_size); + return res.status(200).send({ payment_receives: { ...paymentReceives, diff --git a/server/src/http/controllers/Sales/SalesEstimates.js b/server/src/http/controllers/Sales/SalesEstimates.js index f9ca33e86..3d1d4b38f 100644 --- a/server/src/http/controllers/Sales/SalesEstimates.js +++ b/server/src/http/controllers/Sales/SalesEstimates.js @@ -103,9 +103,10 @@ export default class SalesEstimatesController extends BaseController { 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(), ] } @@ -201,7 +202,7 @@ export default class SalesEstimatesController extends BaseController { .filter(e => e.id) .map((e) => e.id); - const foundEntries = await ItemEntry.query() + const foundEntries = await ItemEntry.tenant().query() .whereIn('id', entriesIds) .where('reference_type', 'SaleInvoice') .where('reference_id', saleInvoiceId); @@ -323,7 +324,7 @@ export default class SalesEstimatesController extends BaseController { return res.status(400).send({ errors: errorReasons }); } - const salesEstimates = await SaleEstimate.query().onBuild((query) => { + const salesEstimates = await SaleEstimate.query().onBuild((builder) => { dynamicListing.buildQuery()(builder); return builder; }).pagination(filter.page - 1, filter.page_size); diff --git a/server/src/http/controllers/Sales/SalesInvoices.js b/server/src/http/controllers/Sales/SalesInvoices.js index 703761e36..1598d3706 100644 --- a/server/src/http/controllers/Sales/SalesInvoices.js +++ b/server/src/http/controllers/Sales/SalesInvoices.js @@ -1,16 +1,15 @@ import express from 'express'; import { check, param, query } from 'express-validator'; +import { difference } from 'lodash'; import { ItemEntry } from '@/models'; import validateMiddleware from '@/http/middleware/validateMiddleware'; import asyncMiddleware from '@/http/middleware/asyncMiddleware'; -import SaleInvoiceService from '@/services/Sales/SaleInvoice'; +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 { SaleInvoice } from '../../../models'; -import { difference } from 'lodash'; export default class SaleInvoicesController { /** @@ -23,6 +22,7 @@ export default class SaleInvoicesController { '/', this.saleInvoiceValidationSchema, validateMiddleware, + asyncMiddleware(this.validateInvoiceCustomerExistance), asyncMiddleware(this.validateInvoiceNumberUnique), asyncMiddleware(this.validateInvoiceItemsIdsExistance), asyncMiddleware(this.newSaleInvoice) @@ -35,6 +35,7 @@ export default class SaleInvoicesController { ], validateMiddleware, asyncMiddleware(this.validateInvoiceExistance), + asyncMiddleware(this.validateInvoiceCustomerExistance), asyncMiddleware(this.validateInvoiceNumberUnique), asyncMiddleware(this.validateInvoiceItemsIdsExistance), asyncMiddleware(this.valdiateInvoiceEntriesIdsExistance), @@ -96,12 +97,17 @@ export default class SaleInvoicesController { return [param('id').exists().isNumeric().toInt()]; } + /** + * Sales invoices list validation schema. + */ static get saleInvoiceListValidationSchema() { 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(), ]; } @@ -145,6 +151,7 @@ export default class SaleInvoicesController { } /** + * * Validate whether sale invoice number unqiue on the storage. * @param {Request} req * @param {Response} res @@ -266,8 +273,13 @@ export default class SaleInvoicesController { */ static async editSaleInvoice(req, res) { const { id: saleInvoiceId } = req.params; - const saleInvoice = { ...req.body }; - + 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); @@ -311,6 +323,8 @@ export default class SaleInvoicesController { const filter = { filter_roles: [], sort_order: 'asc', + page: 1, + page_size: 10, ...req.query, }; if (filter.stringified_filter_roles) { diff --git a/server/src/http/controllers/Sales/SalesReceipt.js b/server/src/http/controllers/Sales/SalesReceipts.js similarity index 95% rename from server/src/http/controllers/Sales/SalesReceipt.js rename to server/src/http/controllers/Sales/SalesReceipts.js index 8162b4532..1fdd3267a 100644 --- a/server/src/http/controllers/Sales/SalesReceipt.js +++ b/server/src/http/controllers/Sales/SalesReceipts.js @@ -6,7 +6,7 @@ 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/SalesReceipt'; +import SaleReceiptService from '@/services/Sales/SalesReceipts'; import DynamicListingBuilder from '@/services/DynamicListing/DynamicListingBuilder'; import DynamicListing from '@/services/DynamicListing/DynamicListing'; import { @@ -51,7 +51,7 @@ export default class SalesReceiptsController { ); router.get( '/', - this.listingSalesReceipts, + this.listSalesReceiptsValidationSchema, validateMiddleware, asyncMiddleware(this.listingSalesReceipts) ); @@ -103,6 +103,8 @@ export default class SalesReceiptsController { 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(), ]; } @@ -272,6 +274,7 @@ export default class SalesReceiptsController { sort_order: 'asc', page: 1, page_size: 10, + ...req.query, }; if (filter.stringified_filter_roles) { filter.filter_roles = JSON.parse(filter.stringified_filter_roles); @@ -312,14 +315,17 @@ export default class SalesReceiptsController { const salesReceipts = await SaleReceipt.query().onBuild((builder) => { builder.withGraphFetched('entries'); dynamicListing.buildQuery()(builder); - return builder; }).pagination(filter.page - 1, filter.page_size); return res.status(200).send({ - sales_receipts: salesReceipts, - ...(viewMeta ? { - customViewId: viewMeta.id, - } : {}), + sales_receipts: { + ...salesReceipts, + ...(viewMeta ? { + view_meta: { + customViewId: viewMeta.id, + } + } : {}), + }, }); } }; diff --git a/server/src/http/controllers/Sales/index.js b/server/src/http/controllers/Sales/index.js index 64f195b25..664391216 100644 --- a/server/src/http/controllers/Sales/index.js +++ b/server/src/http/controllers/Sales/index.js @@ -1,6 +1,6 @@ import express from 'express'; import SalesEstimates from './SalesEstimates'; -import SalesReceipts from './SalesReceipt'; +import SalesReceipts from './SalesReceipts'; import SalesInvoices from './SalesInvoices' import PaymentReceives from './PaymentReceives'; diff --git a/server/src/http/controllers/SalesReports.js b/server/src/http/controllers/SalesReports.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/src/http/controllers/Suppliers.js b/server/src/http/controllers/Suppliers.js deleted file mode 100644 index f7a91021d..000000000 --- a/server/src/http/controllers/Suppliers.js +++ /dev/null @@ -1,10 +0,0 @@ -import express from 'express'; - -export default { - - router() { - const router = express.Router(); - - return router; - }, -}; diff --git a/server/src/http/index.js b/server/src/http/index.js index 5cd949977..6a7c005cd 100644 --- a/server/src/http/index.js +++ b/server/src/http/index.js @@ -3,36 +3,26 @@ import express from 'express'; import Authentication from '@/http/controllers/Authentication'; import InviteUsers from '@/http/controllers/InviteUsers'; import Users from '@/http/controllers/Users'; -// import Roles from '@/http/controllers/Roles'; import Items from '@/http/controllers/Items'; import ItemCategories from '@/http/controllers/ItemCategories'; import Accounts from '@/http/controllers/Accounts'; import AccountTypes from '@/http/controllers/AccountTypes'; -// import AccountOpeningBalance from '@/http/controllers/AccountOpeningBalance'; import Views from '@/http/controllers/Views'; -// import CustomFields from '@/http/controllers/Fields'; import Accounting from '@/http/controllers/Accounting'; import FinancialStatements from '@/http/controllers/FinancialStatements'; import Expenses from '@/http/controllers/Expenses'; import Options from '@/http/controllers/Options'; -// import Budget from '@/http/controllers/Budget'; -// import BudgetReports from '@/http/controllers/BudgetReports'; import Currencies from '@/http/controllers/Currencies'; import Customers from '@/http/controllers/Customers'; import Vendors from '@/http/controllers/Vendors'; import Sales from '@/http/controllers/Sales' -// import Suppliers from '@/http/controllers/Suppliers'; import Purchases from '@/http/controllers/Purchases'; -// import CurrencyAdjustment from './controllers/CurrencyAdjustment'; import Resources from './controllers/Resources'; import ExchangeRates from '@/http/controllers/ExchangeRates'; -// import SalesReports from '@/http/controllers/SalesReports'; -// import PurchasesReports from '@/http/controllers/PurchasesReports'; import Media from '@/http/controllers/Media'; import JWTAuth from '@/http/middleware/jwtAuth'; import TenancyMiddleware from '@/http/middleware/TenancyMiddleware'; - export default (app) => { // app.use('/api/oauth2', OAuth2.router()); app.use('/api/auth', Authentication.router()); @@ -45,31 +35,22 @@ export default (app) => { dashboard.use('/api/currencies', Currencies.router()); dashboard.use('/api/users', Users.router()); - // app.use('/api/roles', Roles.router()); dashboard.use('/api/accounts', Accounts.router()); dashboard.use('/api/account_types', AccountTypes.router()); dashboard.use('/api/accounting', Accounting.router()); - // app.use('/api/accounts_opening_balances', AccountOpeningBalance.router()); dashboard.use('/api/views', Views.router()); - // app.use('/api/fields', CustomFields.router()); dashboard.use('/api/items', Items.router()); dashboard.use('/api/item_categories', ItemCategories.router()); dashboard.use('/api/expenses', Expenses.router()); dashboard.use('/api/financial_statements', FinancialStatements.router()); dashboard.use('/api/options', Options.router()); dashboard.use('/api/sales', Sales.router()); - // app.use('/api/budget_reports', BudgetReports.router()); dashboard.use('/api/customers', Customers.router()); dashboard.use('/api/vendors', Vendors.router()); dashboard.use('/api/purchases', Purchases.router()); - // app.use('/api/suppliers', Suppliers.router()); - // app.use('/api/budget', Budget.router()); dashboard.use('/api/resources', Resources.router()); dashboard.use('/api/exchange_rates', ExchangeRates.router()); dashboard.use('/api/media', Media.router()); - app.use('/', dashboard); - // app.use('/api/currency_adjustment', CurrencyAdjustment.router()); - // app.use('/api/reports/sales', SalesReports.router()); - // app.use('/api/reports/purchases', PurchasesReports.router()); + app.use('/', dashboard); }; diff --git a/server/src/models/Bill.js b/server/src/models/Bill.js index b4a6e6e09..484e2bb9e 100644 --- a/server/src/models/Bill.js +++ b/server/src/models/Bill.js @@ -1,5 +1,6 @@ import { Model, mixin } from 'objection'; import moment from 'moment'; +import { difference } from 'lodash'; import TenantModel from '@/models/TenantModel'; import CachableQueryBuilder from '@/lib/Cachable/CachableQueryBuilder'; import CachableModel from '@/lib/Cachable/CachableModel'; @@ -29,8 +30,25 @@ export default class Bill extends mixin(TenantModel, [CachableModel]) { /** * Due amount of the given. + * @return {number} */ get dueAmount() { - return Math.max(this.balance - this.paymentAmount, 0); + return this.amount - this.paymentAmount; + } + + /** + * Retrieve the not found bills ids as array. + * @param {Array} billsIds + * @return {Array} + */ + static async getNotFoundBills(billsIds) { + const storedBills = await this.tenant().query().whereIn('id', billsIds); + const storedBillsIds = storedBills.map((t) => t.id); + + const notFoundBillsIds = difference( + billsIds, + storedBillsIds, + ); + return notFoundBillsIds; } } diff --git a/server/src/models/BillPayment.js b/server/src/models/BillPayment.js index 920161756..c417fd55a 100644 --- a/server/src/models/BillPayment.js +++ b/server/src/models/BillPayment.js @@ -39,10 +39,11 @@ export default class BillPayment extends mixin(TenantModel, [CachableModel]) { */ static get relationMappings() { const BillPaymentEntry = require('@/models/BillPaymentEntry'); + const Vendor = require('@/models/Vendor'); return { /** - * Account model may belongs to account type. + * */ entries: { relation: Model.BelongsToOneRelation, @@ -52,6 +53,17 @@ export default class BillPayment extends mixin(TenantModel, [CachableModel]) { to: 'bills_payments_entries.billPaymentId', }, }, + /** + * + */ + vendor: { + relation: Model.BelongsToOneRelation, + modelClass: this.relationBindKnex(Vendor.default), + join: { + from: 'bills_payments.vendorId', + to: 'vendors.id', + }, + } }; } } diff --git a/server/src/repositories/CustomerRepository.js b/server/src/repositories/CustomerRepository.js index bb41dee8f..aace6b3b4 100644 --- a/server/src/repositories/CustomerRepository.js +++ b/server/src/repositories/CustomerRepository.js @@ -3,17 +3,17 @@ import { Customer } from '@/models'; export default class CustomerRepository { static changeDiffBalance(customerId, oldCustomerId, amount, oldAmount) { - const diffAmount = (amount - oldAmount) * -1; + const diffAmount = amount - oldAmount; const asyncOpers = []; if (customerId != oldCustomerId) { const oldCustomerOper = Customer.changeBalance( oldCustomerId, - oldAmount + (oldAmount * -1) ); const customerOper = Customer.changeBalance( customerId, - (amount + diffAmount) * -1 + amount, ); asyncOpers.push(customerOper); asyncOpers.push(oldCustomerOper); diff --git a/server/src/repositories/SaleInvoiceRepository.js b/server/src/repositories/SaleInvoiceRepository.js new file mode 100644 index 000000000..2e2c7235d --- /dev/null +++ b/server/src/repositories/SaleInvoiceRepository.js @@ -0,0 +1,7 @@ + + +export default class SaleInvoiceRepository { + + + +} \ No newline at end of file diff --git a/server/src/services/Purchases/BillPayments.js b/server/src/services/Purchases/BillPayments.js index b0c3b8d3d..6f2ff9aae 100644 --- a/server/src/services/Purchases/BillPayments.js +++ b/server/src/services/Purchases/BillPayments.js @@ -1,8 +1,6 @@ import express from 'express'; -import { omit } from 'lodash'; -import { check, query, validationResult, param } from 'express-validator'; +import { omit, sumBy } from 'lodash'; import { BillPayment, BillPaymentEntry, Vendor } from '@/models'; -import asyncMiddleware from '@/http/middleware/asyncMiddleware'; import ServiceItemsEntries from '../Sales/ServiceItemsEntries'; import AccountsService from '../Accounts/AccountsService'; import JournalPoster from '../Accounting/JournalPoster'; diff --git a/server/src/services/Purchases/Bills.js b/server/src/services/Purchases/Bills.js index 335eb760b..a03d8946d 100644 --- a/server/src/services/Purchases/Bills.js +++ b/server/src/services/Purchases/Bills.js @@ -1,19 +1,20 @@ -import { omit, sumBy, difference } from 'lodash'; +import { omit, sumBy } from 'lodash'; import moment from 'moment'; import { + Account, Bill, Vendor, - InventoryTransaction, ItemEntry, Item, - Account, + InventoryTransaction, + AccountTransaction, } from '@/models'; import JournalPoster from '@/services/Accounting/JournalPoster'; import JournalEntry from '@/services/Accounting/JournalEntry'; import AccountsService from '@/services/Accounts/AccountsService'; import JournalPosterService from '@/services/Sales/JournalPosterService'; -import InventoryService from '../Inventory/Inventory'; -import { AccountTransaction } from '../../models'; +import InventoryService from '@/services/Inventory/Inventory'; +import HasItemsEntries from '@/services/Sales/HasItemsEntries'; /** * Vendor bills services. @@ -65,57 +66,7 @@ export default class BillsService { return storedBill; } - /** - * Patch items entries to the storage. - * - * @param {Array} newEntries - * @param {Array} oldEntries - * @param {String} referenceType - * - * @return {Promise} - */ - static async patchItemsEntries(newEntries, oldEntries, referenceType, billId) { - const entriesHasIds = newEntries.filter((entry) => entry.id); - const entriesHasNoIds = newEntries.filter((entry) => !entry.id); - - const entriesIds = entriesHasIds.map(entry => entry.id); - - const oldEntriesIds = oldEntries.map((e) => e.id); - const opers = []; - - const entriesIdsShouldDelete = difference( - oldEntriesIds, - entriesIds, - ); - if (entriesIdsShouldDelete.length > 0) { - const deleteOper = ItemEntry.tenant() - .query() - .whereIn('id', entriesIdsShouldDelete) - .delete(); - opers.push(deleteOper); - } - entriesHasIds.forEach((entry) => { - const updateOper = ItemEntry.tenant() - .query() - .where('id', entry.id) - .update({ - ...omit(entry, ['id']), - }); - opers.push(updateOper); - }); - entriesHasNoIds.forEach((entry) => { - const insertOper = ItemEntry.tenant() - .query() - .insert({ - reference_id: billId, - reference_type: referenceType, - ...omit(entry, ['id', 'amount']), - }); - opers.push(insertOper); - }); - return Promise.all([...opers]); - }; - + /** * Edits details of the given bill id with associated entries. * @@ -150,8 +101,9 @@ export default class BillsService { .where('reference_type', 'Bill'); // Patch the bill entries. - const patchEntriesOper = this.patchItemsEntries(bill.entries, storedEntries, 'Bill', billId); - + const patchEntriesOper = HasItemsEntries.patchItemsEntries( + bill.entries, storedEntries, 'Bill', billId, + ); // Record bill journal transactions. const recordTransactionsOper = this.recordJournalTransactions(bill, billId); diff --git a/server/src/services/Sales/HasItemsEntries.ts b/server/src/services/Sales/HasItemsEntries.ts new file mode 100644 index 000000000..6fb312a82 --- /dev/null +++ b/server/src/services/Sales/HasItemsEntries.ts @@ -0,0 +1,56 @@ +import { difference, omit } from 'lodash'; +import { ItemEntry } from '@/models'; + +export default class HasItemEntries { + /** + * Patch items entries to the storage. + * + * @param {Array} newEntries - + * @param {Array} oldEntries - + * @param {String} referenceType - + * @param {String|Number} referenceId - + * + * @return {Promise} + */ + static async patchItemsEntries(newEntries: Array, oldEntries: Array, referenceType: string, referenceId: string|number) { + const entriesHasIds = newEntries.filter((entry) => entry.id); + const entriesHasNoIds = newEntries.filter((entry) => !entry.id); + const entriesIds = entriesHasIds.map(entry => entry.id); + + const oldEntriesIds = oldEntries.map((e) => e.id); + const excludeAttrs = ['id', 'amount']; + const opers = []; + + const entriesIdsShouldDelete = difference( + oldEntriesIds, + entriesIds, + ); + if (entriesIdsShouldDelete.length > 0) { + const deleteOper = ItemEntry.tenant() + .query() + .whereIn('id', entriesIdsShouldDelete) + .delete(); + opers.push(deleteOper); + } + entriesHasIds.forEach((entry) => { + const updateOper = ItemEntry.tenant() + .query() + .where('id', entry.id) + .update({ + ...omit(entry, excludeAttrs), + }); + opers.push(updateOper); + }); + entriesHasNoIds.forEach((entry) => { + const insertOper = ItemEntry.tenant() + .query() + .insert({ + reference_id: referenceId, + reference_type: referenceType, + ...omit(entry, excludeAttrs), + }); + opers.push(insertOper); + }); + return Promise.all([...opers]); + } +} \ No newline at end of file diff --git a/server/src/services/Sales/PaymentReceive.js b/server/src/services/Sales/PaymentsReceives.ts similarity index 86% rename from server/src/services/Sales/PaymentReceive.js rename to server/src/services/Sales/PaymentsReceives.ts index 2c20128bc..29e4c1cad 100644 --- a/server/src/services/Sales/PaymentReceive.js +++ b/server/src/services/Sales/PaymentsReceives.ts @@ -1,5 +1,5 @@ -import { omit, sumBy, mapValues, groupBy, chain } from 'lodash'; -import moment, { updateLocale } from 'moment'; +import { omit, sumBy, chain } from 'lodash'; +import moment from 'moment'; import { AccountTransaction, PaymentReceive, @@ -16,14 +16,18 @@ import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries'; import PaymentReceiveEntryRepository from '@/repositories/PaymentReceiveEntryRepository'; import CustomerRepository from '@/repositories/CustomerRepository'; -export default class PaymentReceiveService extends JournalPosterService { +/** + * Payment receive service. + * @service + */ +export default class PaymentReceiveService { /** * Creates a new payment receive and store it to the storage * with associated invoices payment and journal transactions. * @async * @param {IPaymentReceive} paymentReceive */ - static async createPaymentReceive(paymentReceive) { + static async createPaymentReceive(paymentReceive: any) { const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount'); const storedPaymentReceive = await PaymentReceive.tenant() .query() @@ -31,9 +35,9 @@ export default class PaymentReceiveService extends JournalPosterService { amount: paymentAmount, ...omit(paymentReceive, ['entries']), }); - const storeOpers = []; + const storeOpers: Array = []; - paymentReceive.entries.forEach((entry) => { + paymentReceive.entries.forEach((entry: any) => { const oper = PaymentReceiveEntry.tenant() .query() .insert({ @@ -51,12 +55,13 @@ export default class PaymentReceiveService extends JournalPosterService { }); const customerIncrementOper = Customer.decrementBalance( paymentReceive.customer_id, - paymentAmount + paymentAmount, ); + // Records the sale invoice journal transactions. const recordJournalTransactions = this.recordPaymentReceiveJournalEntries({ - id: storedPaymentReceive.id, - ...paymentReceive, - }); + id: storedPaymentReceive.id, + ...paymentReceive, + }); await Promise.all([ ...storeOpers, customerIncrementOper, @@ -81,9 +86,9 @@ export default class PaymentReceiveService extends JournalPosterService { * @param {IPaymentReceive} oldPaymentReceive */ static async editPaymentReceive( - paymentReceiveId, - paymentReceive, - oldPaymentReceive + paymentReceiveId: number, + paymentReceive: any, + oldPaymentReceive: any ) { const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount'); // Update the payment receive transaction. @@ -129,14 +134,14 @@ export default class PaymentReceiveService extends JournalPosterService { id: oldPaymentReceive.id, ...paymentReceive, }, - paymentReceiveId + paymentReceiveId, ); // Increment/decrement the customer balance after calc the diff // between old and new value. const changeCustomerBalance = CustomerRepository.changeDiffBalance( paymentReceive.customer_id, oldPaymentReceive.customerId, - paymentAmount, + paymentAmount * -1, oldPaymentReceive.amount, ); // Change the difference between the old and new invoice payment amount. @@ -164,8 +169,9 @@ export default class PaymentReceiveService extends JournalPosterService { * - Revert the payment amount of the associated invoices. * @async * @param {Integer} paymentReceiveId + * @param {IPaymentReceive} paymentReceive */ - static async deletePaymentReceive(paymentReceiveId, paymentReceive) { + static async deletePaymentReceive(paymentReceiveId: number, paymentReceive: any) { // Deletes the payment receive transaction. await PaymentReceive.tenant() .query() @@ -179,7 +185,7 @@ export default class PaymentReceiveService extends JournalPosterService { .delete(); // Delete all associated journal transactions to payment receive transaction. - const deleteTransactionsOper = this.deleteJournalTransactions( + const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions( paymentReceiveId, 'PaymentReceive' ); @@ -190,7 +196,7 @@ export default class PaymentReceiveService extends JournalPosterService { ); // Revert the invoices payments amount. const revertInvoicesPaymentAmount = this.revertInvoicePaymentAmount( - paymentReceive.entries.map((entry) => ({ + paymentReceive.entries.map((entry: any) => ({ invoiceId: entry.invoiceId, revertAmount: entry.paymentAmount, })) @@ -206,7 +212,7 @@ export default class PaymentReceiveService extends JournalPosterService { * Retrieve the payment receive details of the given id. * @param {Integer} paymentReceiveId */ - static async getPaymentReceive(paymentReceiveId) { + static async getPaymentReceive(paymentReceiveId: number) { const paymentReceive = await PaymentReceive.tenant() .query() .where('id', paymentReceiveId) @@ -219,7 +225,7 @@ export default class PaymentReceiveService extends JournalPosterService { * Retrieve the payment receive details with associated invoices. * @param {Integer} paymentReceiveId */ - static async getPaymentReceiveWithInvoices(paymentReceiveId) { + static async getPaymentReceiveWithInvoices(paymentReceiveId: number) { return PaymentReceive.tenant() .query() .where('id', paymentReceiveId) @@ -231,7 +237,7 @@ export default class PaymentReceiveService extends JournalPosterService { * Detarmines whether the payment receive exists on the storage. * @param {Integer} paymentReceiveId */ - static async isPaymentReceiveExists(paymentReceiveId) { + static async isPaymentReceiveExists(paymentReceiveId: number) { const paymentReceives = await PaymentReceive.tenant() .query() .where('id', paymentReceiveId); @@ -245,8 +251,8 @@ export default class PaymentReceiveService extends JournalPosterService { * @param {Integer} paymentReceiveId - Payment receive id. */ static async isPaymentReceiveNoExists( - paymentReceiveNumber, - paymentReceiveId + paymentReceiveNumber: string|number, + paymentReceiveId: number ) { const paymentReceives = await PaymentReceive.tenant() .query() @@ -261,17 +267,22 @@ export default class PaymentReceiveService extends JournalPosterService { /** * Records payment receive journal transactions. + * + * Invoice payment journals. + * -------- + * - Account receivable -> Debit + * - Payment account [current asset] -> Credit + * * @async * @param {IPaymentReceive} paymentReceive + * @param {Number} paymentReceiveId */ static async recordPaymentReceiveJournalEntries( - paymentReceive, - paymentReceiveId + paymentReceive: any, + paymentReceiveId?: number ) { const paymentAmount = sumBy(paymentReceive.entries, 'payment_amount'); - const formattedDate = moment(paymentReceive.payment_date).format( - 'YYYY-MM-DD' - ); + const formattedDate = moment(paymentReceive.payment_date).format('YYYY-MM-DD'); const receivableAccount = await AccountsService.getAccountByType( 'accounts_receivable' ); @@ -320,8 +331,8 @@ export default class PaymentReceiveService extends JournalPosterService { * Revert the payment amount of the given invoices ids. * @param {Array} revertInvoices */ - static async revertInvoicePaymentAmount(revertInvoices) { - const opers = []; + static async revertInvoicePaymentAmount(revertInvoices: any[]) { + const opers: Promise[] = []; revertInvoices.forEach((revertInvoice) => { const { revertAmount, invoiceId } = revertInvoice; @@ -338,13 +349,13 @@ export default class PaymentReceiveService extends JournalPosterService { * Saves difference changing between old and new invoice payment amount. * @param {Array} paymentReceiveEntries * @param {Array} newPaymentReceiveEntries - * @return + * @return */ static async saveChangeInvoicePaymentAmount( - paymentReceiveEntries, - newPaymentReceiveEntries + paymentReceiveEntries: [], + newPaymentReceiveEntries: [], ) { - const opers = []; + const opers: Promise[] = []; const newEntriesTable = chain(newPaymentReceiveEntries) .groupBy('invoice_id') .mapValues((group) => (sumBy(group, 'payment_amount') || 0) * -1) @@ -359,7 +370,7 @@ export default class PaymentReceiveService extends JournalPosterService { .values() .value(); - diffEntries.forEach((diffEntry) => { + diffEntries.forEach((diffEntry: any) => { const oper = SaleInvoice.changePaymentAmount( diffEntry.invoice_id, diffEntry.payment_amount diff --git a/server/src/services/Sales/SalesEstimate.js b/server/src/services/Sales/SalesEstimate.ts similarity index 62% rename from server/src/services/Sales/SalesEstimate.js rename to server/src/services/Sales/SalesEstimate.ts index 4de127d66..d4e2e7739 100644 --- a/server/src/services/Sales/SalesEstimate.js +++ b/server/src/services/Sales/SalesEstimate.ts @@ -1,15 +1,15 @@ -import { omit, difference, sumBy } from 'lodash'; +import { omit, difference, sumBy, mixin } from 'lodash'; import { SaleEstimate, ItemEntry } from '@/models'; -import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries'; +import HasItemsEntries from '@/services/Sales/HasItemsEntries'; -export default class SaleEstimateService extends ServiceItemsEntries { +export default class SaleEstimateService { /** * Creates a new estimate with associated entries. * @async * @param {IEstimate} estimate * @return {void} */ - static async createEstimate(estimate) { + static async createEstimate(estimate: any) { const amount = sumBy(estimate.entries, 'amount'); const storedEstimate = await SaleEstimate.tenant() .query() @@ -17,15 +17,15 @@ export default class SaleEstimateService extends ServiceItemsEntries { amount, ...omit(estimate, ['entries']), }); - const storeEstimateEntriesOpers = []; + const storeEstimateEntriesOpers: any[] = []; - estimate.entries.forEach((entry) => { + estimate.entries.forEach((entry: any) => { const oper = ItemEntry.tenant() .query() .insert({ reference_type: 'SaleEstimate', reference_id: storedEstimate.id, - ...omit(entry, ['total', 'amount']), + ...omit(entry, ['total', 'amount', 'id']), }); storeEstimateEntriesOpers.push(oper); }); @@ -40,7 +40,7 @@ export default class SaleEstimateService extends ServiceItemsEntries { * @param {IEstimate} estimateId * @return {void} */ - static async deleteEstimate(estimateId) { + static async deleteEstimate(estimateId: number) { await ItemEntry.tenant() .query() .where('reference_id', estimateId) @@ -56,7 +56,7 @@ export default class SaleEstimateService extends ServiceItemsEntries { * @param {IEstimate} estimate * @return {void} */ - static async editEstimate(estimateId, estimate) { + static async editEstimate(estimateId: number, estimate: any) { const amount = sumBy(estimate.entries, 'amount'); const updatedEstimate = await SaleEstimate.tenant() .query() @@ -69,45 +69,12 @@ export default class SaleEstimateService extends ServiceItemsEntries { .where('reference_id', estimateId) .where('reference_type', 'SaleEstimate'); - const opers = []; - const entriesHasID = estimate.entries.filter((entry) => entry.id); - const entriesHasNoIDs = estimate.entries.filter((entry) => !entry.id); - - const storedEntriesIds = storedEstimateEntries.map((e) => e.id); - const formEstimateEntriesIds = entriesHasID.map((entry) => entry.id); - - const entriesIdsShouldBeDeleted = difference( - storedEntriesIds, - formEstimateEntriesIds, + const patchItemsEntries = HasItemsEntries.patchItemsEntries( + estimate.entries, storedEstimateEntries, 'SaleEstimate', estimateId ); - // Deletes the given sale estimate entries ids. - if (entriesIdsShouldBeDeleted.length > 0) { - const oper = ItemEntry.tenant() - .query() - .whereIn('id', entriesIdsShouldBeDeleted) - .delete(); - opers.push(oper); - } - // Insert the new sale estimate entries. - entriesHasNoIDs.forEach((entry) => { - const oper = ItemEntry.tenant() - .query() - .insert({ - reference_type: 'SaleEstimate', - reference_id: estimateId, - ...entry, - }); - opers.push(oper); - }); - entriesHasID.forEach((entry) => { - const oper = ItemEntry.tenant() - .query() - .patchAndFetchById(entry.id, { - ...omit(entry, ['id']), - }); - opers.push(oper); - }); - return Promise.all([...opers]); + return Promise.all([ + patchItemsEntries, + ]); } /** @@ -116,7 +83,7 @@ export default class SaleEstimateService extends ServiceItemsEntries { * @param {Numeric} estimateId * @return {Boolean} */ - static async isEstimateExists(estimateId) { + static async isEstimateExists(estimateId: number) { const foundEstimate = await SaleEstimate.tenant() .query() .where('id', estimateId); @@ -129,7 +96,7 @@ export default class SaleEstimateService extends ServiceItemsEntries { * @param {Numeric} estimateId * @param {IEstimate} estimate */ - static async isEstimateEntriesIDsExists(estimateId, estimate) { + static async isEstimateEntriesIDsExists(estimateId: number, estimate: any) { const estimateEntriesIds = estimate.entries .filter((e) => e.id) .map((e) => e.id); @@ -140,7 +107,7 @@ export default class SaleEstimateService extends ServiceItemsEntries { .where('reference_id', estimateId) .where('reference_type', 'SaleEstimate'); - const storedEstimateEntriesIds = estimateEntries.map((e) => e.id); + const storedEstimateEntriesIds = estimateEntries.map((e: any) => e.id); const notFoundEntriesIDs = difference( estimateEntriesIds, storedEstimateEntriesIds @@ -153,7 +120,7 @@ export default class SaleEstimateService extends ServiceItemsEntries { * @param {Integer} estimateId * @return {IEstimate} */ - static async getEstimate(estimateId) { + static async getEstimate(estimateId: number) { const estimate = await SaleEstimate.tenant() .query() .where('id', estimateId) @@ -166,7 +133,7 @@ export default class SaleEstimateService extends ServiceItemsEntries { * Retrieve the estimate details with associated entries. * @param {Integer} estimateId */ - static async getEstimateWithEntries(estimateId) { + static async getEstimateWithEntries(estimateId: number) { const estimate = await SaleEstimate.tenant() .query() .where('id', estimateId) @@ -178,14 +145,14 @@ export default class SaleEstimateService extends ServiceItemsEntries { /** * Detarmines the estimate number uniqness. - * @param {Integer} estimateNumber + * @param {String} estimateNumber * @param {Integer} excludeEstimateId * @return {Boolean} */ - static async isEstimateNumberUnique(estimateNumber, excludeEstimateId) { + static async isEstimateNumberUnique(estimateNumber: string, excludeEstimateId: number) { const foundEstimates = await SaleEstimate.tenant() .query() - .onBuild((query) => { + .onBuild((query: any) => { query.where('estimate_number', estimateNumber); if (excludeEstimateId) { @@ -195,4 +162,4 @@ export default class SaleEstimateService extends ServiceItemsEntries { }); return foundEstimates.length > 0; } -} +} \ No newline at end of file diff --git a/server/src/services/Sales/SaleInvoice.js b/server/src/services/Sales/SalesInvoices.ts similarity index 50% rename from server/src/services/Sales/SaleInvoice.js rename to server/src/services/Sales/SalesInvoices.ts index e62e2c878..ab67dd7a8 100644 --- a/server/src/services/Sales/SaleInvoice.js +++ b/server/src/services/Sales/SalesInvoices.ts @@ -3,21 +3,26 @@ import { SaleInvoice, AccountTransaction, Account, - Item, ItemEntry, Customer, } from '@/models'; import JournalPoster from '@/services/Accounting/JournalPoster'; -import ServiceItemsEntries from '@/services/Sales/ServiceItemsEntries'; +import HasItemsEntries from '@/services/Sales/HasItemsEntries'; +import CustomerRepository from '@/repositories/CustomerRepository'; -export default class SaleInvoicesService extends ServiceItemsEntries { +/** + * Sales invoices service + * @service + */ +export default class SaleInvoicesService { /** * Creates a new sale invoices and store it to the storage * with associated to entries and journal transactions. + * @async * @param {ISaleInvoice} * @return {ISaleInvoice} */ - static async createSaleInvoice(saleInvoice) { + static async createSaleInvoice(saleInvoice: any) { const balance = sumBy(saleInvoice.entries, 'amount'); const storedInvoice = await SaleInvoice.tenant() .query() @@ -26,9 +31,9 @@ export default class SaleInvoicesService extends ServiceItemsEntries { balance, payment_amount: 0, }); - const opers = []; + const opers: Array = []; - saleInvoice.entries.forEach((entry) => { + saleInvoice.entries.forEach((entry: any) => { const oper = ItemEntry.tenant() .query() .insert({ @@ -47,79 +52,56 @@ export default class SaleInvoicesService extends ServiceItemsEntries { } /** - * Records the journal entries of sale invoice. - * @param {ISaleInvoice} saleInvoice - * @return {void} + * Edit the given sale invoice. + * @async + * @param {Number} saleInvoiceId - + * @param {ISaleInvoice} saleInvoice - */ - async recordJournalEntries(saleInvoice) { - const accountsDepGraph = await Account.depGraph().query().remember(); - const journal = new JournalPoster(accountsDepGraph); - const receivableTotal = sumBy(saleInvoice.entries, 'total'); + static async editSaleInvoice(saleInvoiceId: number, saleInvoice: any) { + const balance = sumBy(saleInvoice.entries, 'amount'); + const oldSaleInvoice = await SaleInvoice.tenant().query() + .where('id', saleInvoiceId) + .first(); - const receivableAccount = await Account.tenant().query(); - const formattedDate = moment(saleInvoice.invoice_date).format('YYYY-MM-DD'); - - const saleItemsIds = saleInvoice.entries.map((e) => e.item_id); - const storedInvoiceItems = await Item.tenant() + const updatedSaleInvoices = await SaleInvoice.tenant() .query() - .whereIn('id', saleItemsIds); - - const commonJournalMeta = { - debit: 0, - credit: 0, - referenceId: saleInvoice.id, - referenceType: 'SaleInvoice', - date: formattedDate, - }; - const totalReceivableEntry = new journalEntry({ - ...commonJournalMeta, - debit: receivableTotal, - account: receivableAccount.id, - accountNormal: 'debit', - }); - journal.debit(totalReceivableEntry); - - saleInvoice.entries.forEach((entry) => { - const item = {}; - const incomeEntry = JournalEntry({ - ...commonJournalMeta, - credit: entry.total, - account: item.sellAccountId, - accountNormal: 'credit', - note: '', + .where('id', saleInvoiceId) + .update({ + balance, + ...omit(saleInvoice, ['entries']), }); + // Fetches the sale invoice items entries. + const storedEntries = await ItemEntry.tenant() + .query() + .where('reference_id', saleInvoiceId) + .where('reference_type', 'SaleInvoice'); - if (item.type === 'inventory') { - const inventoryCredit = JournalEntry({ - ...commonJournalMeta, - credit: entry.total, - account: item.inventoryAccountId, - accountNormal: 'credit', - note: '', - }); - const costEntry = JournalEntry({ - ...commonJournalMeta, - debit: entry.total, - account: item.costAccountId, - accountNormal: 'debit', - note: '', - }); - journal.debit(costEntry); - } - journal.credit(incomeEntry); - }); + // Patch update the sale invoice items entries. + const patchItemsEntriesOper = HasItemsEntries.patchItemsEntries( + saleInvoice.entries, storedEntries, 'SaleInvoice', saleInvoiceId, + ); + // Changes the diff customer balance between old and new amount. + const changeCustomerBalanceOper = CustomerRepository.changeDiffBalance( + saleInvoice.customer_id, + oldSaleInvoice.customerId, + balance, + oldSaleInvoice.balance, + ); await Promise.all([ - journalPoster.saveEntries(), - journalPoster.saveBalance(), + patchItemsEntriesOper, + changeCustomerBalanceOper, ]); } /** * Deletes the given sale invoice with associated entries * and journal transactions. - * @param {Integer} saleInvoiceId + * @async + * @param {Number} saleInvoiceId */ - static async deleteSaleInvoice(saleInvoiceId) { + static async deleteSaleInvoice(saleInvoiceId: number) { + const oldSaleInvoice = await SaleInvoice.tenant().query().findById(saleInvoiceId); + await SaleInvoice.tenant().query().where('id', saleInvoiceId).delete(); await ItemEntry.tenant() .query() @@ -127,6 +109,10 @@ export default class SaleInvoicesService extends ServiceItemsEntries { .where('reference_type', 'SaleInvoice') .delete(); + const revertCustomerBalanceOper = Customer.changeBalance( + oldSaleInvoice.customerId, + oldSaleInvoice.balance * -1, + ); const invoiceTransactions = await AccountTransaction.tenant() .query() .whereIn('reference_type', ['SaleInvoice']) @@ -139,68 +125,29 @@ export default class SaleInvoicesService extends ServiceItemsEntries { journal.loadEntries(invoiceTransactions); journal.removeEntries(); - await Promise.all([journal.deleteEntries(), journal.saveBalance()]); + await Promise.all([ + journal.deleteEntries(), + journal.saveBalance(), + revertCustomerBalanceOper, + ]); } /** - * Edit the given sale invoice. - * @param {Integer} saleInvoiceId - - * @param {ISaleInvoice} saleInvoice - + * Records the journal entries of sale invoice. + * @async + * @param {ISaleInvoice} saleInvoice + * @return {void} */ - static async editSaleInvoice(saleInvoiceId, saleInvoice) { - const updatedSaleInvoices = await SaleInvoice.tenant() - .query() - .where('id', saleInvoiceId) - .update({ - ...omit(saleInvoice, ['entries']), - }); - const opers = []; - const entriesIds = saleInvoice.entries.filter((entry) => entry.id); - const entriesNoIds = saleInvoice.entries.filter((entry) => !entry.id); + async recordJournalEntries(saleInvoice: any) { - const storedEntries = await ItemEntry.tenant() - .query() - .where('reference_id', saleInvoiceId) - .where('reference_type', 'SaleInvoice'); - - const entriesIdsShouldDelete = this.entriesShouldDeleted( - storedEntries, - entriesIds - ); - if (entriesIdsShouldDelete.length > 0) { - const updateOper = ItemEntry.tenant() - .query() - .whereIn('id', entriesIdsShouldDelete) - .delete(); - opers.push(updateOper); - } - entriesIds.forEach((entry) => { - const updateOper = ItemEntry.tenant() - .query() - .where('id', entry.id) - .update({ - ...omit(entry, ['id']), - }); - opers.push(updateOper); - }); - entriesNoIds.forEach((entry) => { - const insertOper = ItemEntry.tenant() - .query() - .insert({ - reference_type: 'SaleInvoice', - reference_id: saleInvoiceId, - ...omit(entry, ['id']), - }); - opers.push(insertOper); - }) - await Promise.all([...opers]); } /** * Retrieve sale invoice with associated entries. - * @param {Integer} saleInvoiceId + * @async + * @param {Number} saleInvoiceId */ - static async getSaleInvoiceWithEntries(saleInvoiceId) { + static async getSaleInvoiceWithEntries(saleInvoiceId: number) { return SaleInvoice.tenant().query() .where('id', saleInvoiceId) .withGraphFetched('entries') @@ -212,7 +159,7 @@ export default class SaleInvoicesService extends ServiceItemsEntries { * @param {Integer} saleInvoiceId * @return {Boolean} */ - static async isSaleInvoiceExists(saleInvoiceId) { + static async isSaleInvoiceExists(saleInvoiceId: number) { const foundSaleInvoice = await SaleInvoice.tenant() .query() .where('id', saleInvoiceId); @@ -221,10 +168,12 @@ export default class SaleInvoicesService extends ServiceItemsEntries { /** * Detarmines the sale invoice number exists on the storage. - * @param {Integer} saleInvoiceNumber + * @async + * @param {Number|String} saleInvoiceNumber + * @param {Number} saleInvoiceId * @return {Boolean} */ - static async isSaleInvoiceNumberExists(saleInvoiceNumber, saleInvoiceId) { + static async isSaleInvoiceNumberExists(saleInvoiceNumber: string|number, saleInvoiceId: number) { const foundSaleInvoice = await SaleInvoice.tenant() .query() .onBuild((query) => { @@ -243,7 +192,7 @@ export default class SaleInvoicesService extends ServiceItemsEntries { * @param {Array} invoicesIds * @return {Array} */ - static async isInvoicesExist(invoicesIds) { + static async isInvoicesExist(invoicesIds: Array) { const storedInvoices = await SaleInvoice.tenant() .query() .onBuild((builder) => { diff --git a/server/src/services/Sales/SalesReceipt.js b/server/src/services/Sales/SalesReceipts.ts similarity index 66% rename from server/src/services/Sales/SalesReceipt.js rename to server/src/services/Sales/SalesReceipts.ts index d38c61884..f3ba7ae62 100644 --- a/server/src/services/Sales/SalesReceipt.js +++ b/server/src/services/Sales/SalesReceipts.ts @@ -2,19 +2,20 @@ import { omit, difference, sumBy } from 'lodash'; import { SaleReceipt, Account, + ItemEntry, } from '@/models'; import JournalPoster from '@/services/Accounting/JournalPoster'; -import ItemEntry from '../../models/ItemEntry'; import JournalPosterService from '@/services/Sales/JournalPosterService'; +import HasItemEntries from '@/services/Sales/HasItemsEntries'; -export default class SalesReceipt extends JournalPosterService { +export default class SalesReceipt { /** * Creates a new sale receipt with associated entries. * @async * @param {ISaleReceipt} saleReceipt * @return {Object} */ - static async createSaleReceipt(saleReceipt) { + static async createSaleReceipt(saleReceipt: any) { const amount = sumBy(saleReceipt.entries, 'amount'); const storedSaleReceipt = await SaleReceipt.tenant() .query() @@ -22,9 +23,9 @@ export default class SalesReceipt extends JournalPosterService { amount, ...omit(saleReceipt, ['entries']), }); - const storeSaleReceiptEntriesOpers = []; + const storeSaleReceiptEntriesOpers: Array = []; - saleReceipt.entries.forEach((entry) => { + saleReceipt.entries.forEach((entry: any) => { const oper = ItemEntry.tenant() .query() .insert({ @@ -43,7 +44,7 @@ export default class SalesReceipt extends JournalPosterService { * @param {ISaleReceipt} saleReceipt * @return {Promise} */ - static async _recordJournalTransactions(saleReceipt) { + static async _recordJournalTransactions(saleReceipt: any) { const accountsDepGraph = await Account.tenant().depGraph().query(); const journalPoster = new JournalPoster(accountsDepGraph); } @@ -54,7 +55,7 @@ export default class SalesReceipt extends JournalPosterService { * @param {ISaleReceipt} saleReceipt * @return {void} */ - static async editSaleReceipt(saleReceiptId, saleReceipt) { + static async editSaleReceipt(saleReceiptId: number, saleReceipt: any) { const amount = sumBy(saleReceipt.entries, 'amount'); const updatedSaleReceipt = await SaleReceipt.tenant() .query() @@ -68,32 +69,11 @@ export default class SalesReceipt extends JournalPosterService { .where('reference_id', saleReceiptId) .where('reference_type', 'SaleReceipt'); - const storedSaleReceiptsIds = storedSaleReceiptEntries.map((e) => e.id); - const entriesHasID = saleReceipt.entries.filter((entry) => entry.id); - const entriesIds = entriesHasID.map((e) => e.id); - - const opers = []; - const entriesIdsShouldBeDeleted = difference( - storedSaleReceiptsIds, - entriesIds + // Patch sale receipt items entries. + const patchItemsEntries = HasItemEntries.patchItemsEntries( + saleReceipt.entries, storedSaleReceiptEntries, 'SaleReceipt', saleReceiptId, ); - if (entriesIdsShouldBeDeleted.length > 0) { - const deleteOper = ItemEntry.tenant() - .query() - .whereIn('id', entriesIdsShouldBeDeleted) - .where('reference_type', 'SaleReceipt') - .delete(); - opers.push(deleteOper); - } - entriesHasID.forEach((entry) => { - const updateOper = ItemEntry.tenant() - .query() - .patchAndFetchById(entry.id, { - ...omit(entry, ['id']), - }); - opers.push(updateOper); - }); - return Promise.all([...opers]); + return Promise.all([patchItemsEntries]); } /** @@ -101,20 +81,22 @@ export default class SalesReceipt extends JournalPosterService { * @param {Integer} saleReceiptId * @return {void} */ - static async deleteSaleReceipt(saleReceiptId) { - await SaleReceipt.tenant().query().where('id', saleReceiptId).delete(); - await ItemEntry.tenant() + static async deleteSaleReceipt(saleReceiptId: number) { + const deleteSaleReceiptOper = SaleReceipt.tenant().query().where('id', saleReceiptId).delete(); + const deleteItemsEntriesOper = ItemEntry.tenant() .query() .where('reference_id', saleReceiptId) .where('reference_type', 'SaleReceipt') .delete(); // Delete all associated journal transactions to payment receive transaction. - const deleteTransactionsOper = this.deleteJournalTransactions( + const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions( saleReceiptId, 'SaleReceipt' ); return Promise.all([ + deleteItemsEntriesOper, + deleteSaleReceiptOper, deleteTransactionsOper, ]); } @@ -124,7 +106,7 @@ export default class SalesReceipt extends JournalPosterService { * @param {Integer} saleReceiptId * @returns {Boolean} */ - static async isSaleReceiptExists(saleReceiptId) { + static async isSaleReceiptExists(saleReceiptId: number) { const foundSaleReceipt = await SaleReceipt.tenant() .query() .where('id', saleReceiptId); @@ -136,7 +118,7 @@ export default class SalesReceipt extends JournalPosterService { * @param {Integer} saleReceiptId * @param {ISaleReceipt} saleReceipt */ - static async isSaleReceiptEntriesIDsExists(saleReceiptId, saleReceipt) { + static async isSaleReceiptEntriesIDsExists(saleReceiptId: number, saleReceipt: any) { const entriesIDs = saleReceipt.entries .filter((e) => e.id) .map((e) => e.id); @@ -147,7 +129,7 @@ export default class SalesReceipt extends JournalPosterService { .where('reference_id', saleReceiptId) .where('reference_type', 'SaleReceipt'); - const storedEntriesIDs = storedEntries.map((e) => e.id); + const storedEntriesIDs = storedEntries.map((e: any) => e.id); const notFoundEntriesIDs = difference( entriesIDs, storedEntriesIDs @@ -159,7 +141,7 @@ export default class SalesReceipt extends JournalPosterService { * Retrieve sale receipt with associated entries. * @param {Integer} saleReceiptId */ - static async getSaleReceiptWithEntries(saleReceiptId) { + static async getSaleReceiptWithEntries(saleReceiptId: number) { const saleReceipt = await SaleReceipt.tenant().query() .where('id', saleReceiptId) .withGraphFetched('entries'); diff --git a/server/src/utils/index.js b/server/src/utils/index.js index 9c51451fb..5d6959e86 100644 --- a/server/src/utils/index.js +++ b/server/src/utils/index.js @@ -132,6 +132,18 @@ const getTotalDeep = (items, deepProp, totalProp) => return _.sumBy(item, totalProp) + total + acc; }, 0); +function applyMixins(derivedCtor, baseCtors) { + baseCtors.forEach((baseCtor) => { + Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => { + Object.defineProperty( + derivedCtor.prototype, + name, + Object.getOwnPropertyDescriptor(baseCtor.prototype, name) + ); + }); + }); +} + export { hashPassword, origin, @@ -143,4 +155,5 @@ export { flatToNestedArray, itemsStartWith, getTotalDeep, + applyMixins, }; diff --git a/server/tsconfig.json b/server/tsconfig.json index 1753c9ea7..f2280a902 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -6,6 +6,7 @@ "module": "commonjs", "target": "es5", "jsx": "react", - "allowJs": true + "allowJs": true, + "esModuleInterop": true, } } \ No newline at end of file