From 9779591029dd1cad745a5e30d1e2f27e8ccbc3fb Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Tue, 24 Nov 2020 11:33:29 +0200 Subject: [PATCH] fix: subtitle issue with sales transaction number. --- .../Accounting/MakeJournalEntriesForm.js | 24 +++++++-- .../AccountFormDialogContent.js | 1 + .../PaymentMades/PaymentMadeActionsBar.js | 2 +- .../PaymentMades/PaymentMadeForm.schema.js | 1 + .../PaymentMades/PaymentMadeFormHeader.js | 1 + .../containers/Sales/Estimate/EstimateForm.js | 43 +++++++++++----- .../Sales/Estimate/EstimateFormHeader.js | 1 + .../containers/Sales/Invoice/InvoiceForm.js | 23 +++++++-- .../Sales/Invoice/InvoiceFormHeader.js | 1 + .../PaymentReceiveActionsBar.js | 2 +- .../PaymentReceive/PaymentReceiveForm.js | 49 ++++++++++++------- .../PaymentReceiveForm.schema.js | 1 + .../PaymentReceiveFormHeader.js | 1 + .../PaymentReceivesEmptyStatus.js | 2 +- .../containers/Sales/Receipt/ReceiptForm.js | 23 +++++++-- .../Sales/Receipt/ReceiptFormHeader.js | 2 + client/src/lang/en/index.js | 11 +++-- client/src/style/views/Sidebar.scss | 44 ++++++++++++++++- .../src/api/controllers/Contacts/Contacts.ts | 5 +- .../src/api/controllers/Contacts/Customers.ts | 3 -- .../src/api/controllers/Contacts/Vendors.ts | 41 ++++++++++++---- .../api/controllers/Sales/PaymentReceives.ts | 2 +- .../src/services/Contacts/CustomersService.ts | 5 +- .../src/services/Contacts/VendorsService.ts | 6 +-- 24 files changed, 218 insertions(+), 76 deletions(-) diff --git a/client/src/containers/Accounting/MakeJournalEntriesForm.js b/client/src/containers/Accounting/MakeJournalEntriesForm.js index fb8025875..0eded5f18 100644 --- a/client/src/containers/Accounting/MakeJournalEntriesForm.js +++ b/client/src/containers/Accounting/MakeJournalEntriesForm.js @@ -27,7 +27,12 @@ import Dragzone from 'components/Dragzone'; import withMediaActions from 'containers/Media/withMediaActions'; import useMedia from 'hooks/useMedia'; -import { compose, repeatValue, orderingLinesIndexes } from 'utils'; +import { + compose, + repeatValue, + orderingLinesIndexes, + defaultToTransform, +} from 'utils'; import withManualJournalsActions from './withManualJournalsActions'; import withManualJournals from './withManualJournals'; @@ -97,13 +102,18 @@ function MakeJournalEntriesForm({ : journalNextNumber; useEffect(() => { + const transactionNumber = manualJournal + ? manualJournal.journal_number + : journalNumber; + if (manualJournal && manualJournal.id) { changePageTitle(formatMessage({ id: 'edit_journal' })); - changePageSubtitle(`No. ${manualJournal.journal_number}`); } else { - changePageSubtitle(`No. ${journalNumber}`); changePageTitle(formatMessage({ id: 'new_journal' })); } + changePageSubtitle( + defaultToTransform(transactionNumber, `No. ${transactionNumber}`, ''), + ); }, [ changePageTitle, changePageSubtitle, @@ -383,7 +393,9 @@ function MakeJournalEntriesForm({ useEffect(() => { if (journalNumberChanged) { setFieldValue('journal_number', journalNumber); - changePageSubtitle(`No. ${journalNumber}`); + changePageSubtitle( + defaultToTransform(journalNumber, `No. ${journalNumber}`, ''), + ); setJournalNumberChanged(false); } }, [ @@ -440,7 +452,9 @@ function MakeJournalEntriesForm({ // Handle journal number field change. const handleJournalNumberChanged = useCallback( (journalNumber) => { - changePageSubtitle(`No. ${journalNumber}`); + changePageSubtitle( + defaultToTransform(journalNumber, `No. ${journalNumber}`, '') + ); }, [changePageSubtitle], ); diff --git a/client/src/containers/Dialogs/AccountFormDialog/AccountFormDialogContent.js b/client/src/containers/Dialogs/AccountFormDialog/AccountFormDialogContent.js index 5880c8f8f..a0ea4f5cc 100644 --- a/client/src/containers/Dialogs/AccountFormDialog/AccountFormDialogContent.js +++ b/client/src/containers/Dialogs/AccountFormDialog/AccountFormDialogContent.js @@ -345,6 +345,7 @@ function AccountFormDialogContent({ onAccountSelected={onChangeSubaccount} defaultSelectText={} selectedAccountId={values.parent_account_id} + popoverFill={true} /> diff --git a/client/src/containers/Purchases/PaymentMades/PaymentMadeActionsBar.js b/client/src/containers/Purchases/PaymentMades/PaymentMadeActionsBar.js index c7afc012f..0ea2b5c74 100644 --- a/client/src/containers/Purchases/PaymentMades/PaymentMadeActionsBar.js +++ b/client/src/containers/Purchases/PaymentMades/PaymentMadeActionsBar.js @@ -49,7 +49,7 @@ function PaymentMadeActionsBar({ const { formatMessage } = useIntl(); const handleClickNewPaymentMade = useCallback(() => { - history.push('/payment-made/new'); + history.push('/payment-mades/new'); }, [history]); // const filterDropdown = FilterDropdown({ diff --git a/client/src/containers/Purchases/PaymentMades/PaymentMadeForm.schema.js b/client/src/containers/Purchases/PaymentMades/PaymentMadeForm.schema.js index d0d22b619..8f818d943 100644 --- a/client/src/containers/Purchases/PaymentMades/PaymentMadeForm.schema.js +++ b/client/src/containers/Purchases/PaymentMades/PaymentMadeForm.schema.js @@ -13,6 +13,7 @@ const Schema = Yup.object().shape({ .required() .label(formatMessage({ id: 'payment_account_' })), payment_number: Yup.string() + .nullable() .max(DATATYPES_LENGTH.STRING) .label(formatMessage({ id: 'payment_no_' })), reference: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(), diff --git a/client/src/containers/Purchases/PaymentMades/PaymentMadeFormHeader.js b/client/src/containers/Purchases/PaymentMades/PaymentMadeFormHeader.js index c7c2e26de..262106929 100644 --- a/client/src/containers/Purchases/PaymentMades/PaymentMadeFormHeader.js +++ b/client/src/containers/Purchases/PaymentMades/PaymentMadeFormHeader.js @@ -107,6 +107,7 @@ function PaymentMadeFormHeader({ defaultSelectText={ } onContactSelected={onChangeSelect('vendor_id')} disabled={!isNewMode} + popoverFill={true} /> diff --git a/client/src/containers/Sales/Estimate/EstimateForm.js b/client/src/containers/Sales/Estimate/EstimateForm.js index 5beaaeb8c..71dd1e9be 100644 --- a/client/src/containers/Sales/Estimate/EstimateForm.js +++ b/client/src/containers/Sales/Estimate/EstimateForm.js @@ -31,7 +31,12 @@ import Dragzone from 'components/Dragzone'; import useMedia from 'hooks/useMedia'; import { ERROR } from 'common/errors'; -import { compose, repeatValue, orderingLinesIndexes } from 'utils'; +import { + compose, + repeatValue, + defaultToTransform, + orderingLinesIndexes, +} from 'utils'; const MIN_LINES_NUMBER = 4; @@ -98,16 +103,20 @@ const EstimateForm = ({ : estimateNextNumber; useEffect(() => { - if (estimate && estimate.id) { + const transNumber = !isNewMode ? estimate.estimate_number : estimateNumber; + + if (isNewMode) { changePageTitle(formatMessage({ id: 'edit_estimate' })); - changePageSubtitle(`No. ${estimate.estimate_number}`); } else { - changePageSubtitle(`No. ${estimateNumber}`); changePageTitle(formatMessage({ id: 'new_estimate' })); } + changePageSubtitle( + defaultToTransform(estimateNumber, `No. ${transNumber}`, ''), + ); }, [ estimate, estimateNumber, + isNewMode, formatMessage, changePageTitle, changePageSubtitle, @@ -211,18 +220,26 @@ const EstimateForm = ({ const handleEstimateNumberChange = useCallback( (estimateNumber) => { - changePageSubtitle(`No. ${estimateNumber}`); + changePageSubtitle( + defaultToTransform(estimateNumber, `No. ${estimateNumber}`, ''), + ); }, [changePageSubtitle], ); - const handleSubmitClick = useCallback((event) => { - setSubmitPayload({ redirect: true }); - }, [setSubmitPayload]); + const handleSubmitClick = useCallback( + (event) => { + setSubmitPayload({ redirect: true }); + }, + [setSubmitPayload], + ); - const handleCancelClick = useCallback((event) => { - history.goBack(); - }, [history]); + const handleCancelClick = useCallback( + (event) => { + history.goBack(); + }, + [history], + ); return (
@@ -238,8 +255,8 @@ const EstimateForm = ({ - - + + { form.setFieldValue('customer_id', customer.id); }} + popoverFill={true} /> )} diff --git a/client/src/containers/Sales/Invoice/InvoiceForm.js b/client/src/containers/Sales/Invoice/InvoiceForm.js index dbafc74ac..dc5336b98 100644 --- a/client/src/containers/Sales/Invoice/InvoiceForm.js +++ b/client/src/containers/Sales/Invoice/InvoiceForm.js @@ -30,7 +30,12 @@ import { AppToaster } from 'components'; import useMedia from 'hooks/useMedia'; import { ERROR } from 'common/errors'; -import { compose, repeatValue, saveInvoke, orderingLinesIndexes } from 'utils'; +import { + compose, + repeatValue, + defaultToTransform, + orderingLinesIndexes, +} from 'utils'; import { useHistory } from 'react-router-dom'; const MIN_LINES_NUMBER = 4; @@ -94,13 +99,16 @@ function InvoiceForm({ : invoiceNextNumber; useEffect(() => { + const transactionNumber = invoice ? invoice.invoice_no : invoiceNumber; + if (invoice && invoice.id) { changePageTitle(formatMessage({ id: 'edit_invoice' })); - changePageSubtitle(`No. ${invoice.invoice_no}`); } else { - changePageSubtitle(`No. ${invoiceNumber}`); changePageTitle(formatMessage({ id: 'new_invoice' })); } + changePageSubtitle( + defaultToTransform(transactionNumber, `No. ${transactionNumber}`, ''), + ); }, [ changePageTitle, changePageSubtitle, @@ -216,7 +224,9 @@ function InvoiceForm({ const handleInvoiceNumberChanged = useCallback( (invoiceNumber) => { - changePageSubtitle(`No. ${invoiceNumber}`); + changePageSubtitle( + defaultToTransform(invoiceNumber, `No. ${invoiceNumber}`, ''), + ); }, [changePageSubtitle], ); @@ -236,7 +246,10 @@ function InvoiceForm({ onInvoiceNumberChanged={handleInvoiceNumberChanged} /> - + { form.setFieldValue('customer_id', customer.id); }} + popoverFill={true} /> )} diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceiveActionsBar.js b/client/src/containers/Sales/PaymentReceive/PaymentReceiveActionsBar.js index ca8b78412..33c45c3cb 100644 --- a/client/src/containers/Sales/PaymentReceive/PaymentReceiveActionsBar.js +++ b/client/src/containers/Sales/PaymentReceive/PaymentReceiveActionsBar.js @@ -49,7 +49,7 @@ function PaymentReceiveActionsBar({ const { formatMessage } = useIntl(); const handleClickNewPaymentReceive = useCallback(() => { - history.push('/payment-receive/new'); + history.push('/payment-receives/new'); }, [history]); // const filterDropdown = FilterDropdown({ diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.js b/client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.js index bd9e3e71f..a33136c25 100644 --- a/client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.js +++ b/client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.js @@ -1,18 +1,11 @@ -import React, { - useMemo, - useCallback, - useEffect, - useState, - useRef, -} from 'react'; - -import * as Yup from 'yup'; +import React, { useMemo, useCallback, useEffect, useState } from 'react'; import { useFormik } from 'formik'; import moment from 'moment'; import { FormattedMessage as T, useIntl } from 'react-intl'; import { pick, sumBy, omit } from 'lodash'; import { Intent, Alert } from '@blueprintjs/core'; import classNames from 'classnames'; +import { useHistory } from 'react-router-dom'; import { CLASSES } from 'common/classes'; import PaymentReceiveHeader from './PaymentReceiveFormHeader'; @@ -32,7 +25,7 @@ import { } from './PaymentReceiveForm.schema'; import { AppToaster } from 'components'; -import { compose } from 'utils'; +import { compose, defaultToTransform } from 'utils'; /** * Payment Receive form. @@ -62,35 +55,45 @@ function PaymentReceiveForm({ changePageTitle, changePageSubtitle, }) { + const history = useHistory(); + const [amountChangeAlert, setAmountChangeAlert] = useState(false); const [clearLinesAlert, setClearLinesAlert] = useState(false); const [fullAmount, setFullAmount] = useState(null); const [clearFormAlert, setClearFormAlert] = useState(false); const { formatMessage } = useIntl(); - const isNewMode = !paymentReceiveId; const [localPaymentEntries, setLocalPaymentEntries] = useState( paymentReceiveEntries, ); + const isNewMode = !paymentReceiveId; const paymentReceiveNumber = paymentReceiveNumberPrefix ? `${paymentReceiveNumberPrefix}-${paymentReceiveNextNumber}` : paymentReceiveNextNumber; useEffect(() => { + const transactionNumber = !isNewMode + ? paymentReceive.payment_receive_no + : paymentReceiveNumber; + if (paymentReceive && paymentReceiveId) { changePageTitle(formatMessage({ id: 'edit_payment_receive' })); changePageSubtitle(`No. ${paymentReceive.payment_receive_no}`); } else { - changePageSubtitle(`No. ${paymentReceiveNumber}`); changePageTitle(formatMessage({ id: 'payment_receive' })); } + changePageSubtitle( + defaultToTransform(transactionNumber, `No. ${transactionNumber}`, ''), + ); }, [ + isNewMode, changePageTitle, changePageSubtitle, paymentReceive, paymentReceiveId, formatMessage, + paymentReceiveNumber, ]); useEffect(() => { @@ -103,7 +106,7 @@ function PaymentReceiveForm({ const validationSchema = isNewMode ? CreatePaymentReceiveFormSchema : EditPaymentReceiveFormSchema; - + // Default payment receive entry. const defaultPaymentReceiveEntry = { id: null, @@ -167,8 +170,8 @@ function PaymentReceiveForm({ AppToaster.show({ message: formatMessage({ id: 'you_cannot_make_payment_with_zero_total_amount', - intent: Intent.WARNING, }), + intent: Intent.DANGER, }); setSubmitting(false); return; @@ -180,13 +183,14 @@ function PaymentReceiveForm({ AppToaster.show({ message: formatMessage({ id: paymentReceiveId - ? 'the_payment_has_been_received_successfully_edited' - : 'the_payment_has_been_received_successfully_created', + ? 'the_payment_receive_transaction_has_been_edited' + : 'the_payment_receive_transaction_has_been_created', }), intent: Intent.SUCCESS, }); setSubmitting(false); resetForm(); + history.push('/payment-receives'); }; // Handle request response errors. const onError = (errors) => { @@ -321,7 +325,13 @@ function PaymentReceiveForm({ useEffect(() => { if (paymentReceiveNumberChanged) { setFieldValue('payment_receive_no', paymentReceiveNumber); - changePageSubtitle(`No. ${paymentReceiveNumber}`); + changePageSubtitle( + defaultToTransform( + paymentReceiveNumber, + `No. ${paymentReceiveNumber}`, + '', + ), + ); setPaymentReceiveNumberChanged(false); } }, [ @@ -329,11 +339,14 @@ function PaymentReceiveForm({ paymentReceiveNumberChanged, setFieldValue, changePageSubtitle, + setPaymentReceiveNumberChanged, ]); const handlePaymentReceiveNumberChanged = useCallback( (payment_receive_no) => { - changePageSubtitle(`No.${payment_receive_no}`); + changePageSubtitle( + defaultToTransform(payment_receive_no, `No.${payment_receive_no}`, ''), + ); }, [changePageSubtitle], ); diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.schema.js b/client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.schema.js index 39982e0f2..de9e40581 100644 --- a/client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.schema.js +++ b/client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.schema.js @@ -14,6 +14,7 @@ const Schema = Yup.object().shape({ .label(formatMessage({ id: 'deposit_account_' })), full_amount: Yup.number().nullable(), payment_receive_no: Yup.string() + .nullable() .max(DATATYPES_LENGTH.STRING) .label(formatMessage({ id: 'payment_receive_no_' })), reference_no: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(), diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceiveFormHeader.js b/client/src/containers/Sales/PaymentReceive/PaymentReceiveFormHeader.js index cd6d5ec7d..7144fa3df 100644 --- a/client/src/containers/Sales/PaymentReceive/PaymentReceiveFormHeader.js +++ b/client/src/containers/Sales/PaymentReceive/PaymentReceiveFormHeader.js @@ -114,6 +114,7 @@ function PaymentReceiveFormHeader({ selectedContactId={values.customer_id} defaultSelectText={} onContactSelected={onChangeSelect('customer_id')} + popoverFill={true} /> diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceivesEmptyStatus.js b/client/src/containers/Sales/PaymentReceive/PaymentReceivesEmptyStatus.js index 1a6f0caaf..f307dc64a 100644 --- a/client/src/containers/Sales/PaymentReceive/PaymentReceivesEmptyStatus.js +++ b/client/src/containers/Sales/PaymentReceive/PaymentReceivesEmptyStatus.js @@ -21,7 +21,7 @@ export default function PaymentReceivesEmptyStatus() { intent={Intent.PRIMARY} large={true} onClick={() => { - history.push('/payment-receive/new'); + history.push('/payment-receives/new'); }} > New payment receive diff --git a/client/src/containers/Sales/Receipt/ReceiptForm.js b/client/src/containers/Sales/Receipt/ReceiptForm.js index 23462955f..7b49c6bbe 100644 --- a/client/src/containers/Sales/Receipt/ReceiptForm.js +++ b/client/src/containers/Sales/Receipt/ReceiptForm.js @@ -32,7 +32,12 @@ import { AppToaster } from 'components'; import Dragzone from 'components/Dragzone'; import useMedia from 'hooks/useMedia'; -import { compose, repeatValue, orderingLinesIndexes } from 'utils'; +import { + compose, + repeatValue, + orderingLinesIndexes, + defaultToTransform, +} from 'utils'; const MIN_LINES_NUMBER = 4; @@ -97,14 +102,20 @@ function ReceiptForm({ : receiptNextNumber; useEffect(() => { + const transactionNumber = !isNewMode + ? receipt.receipt_number + : receiptNumber; + if (receipt && receipt.id) { changePageTitle(formatMessage({ id: 'edit_receipt' })); - changePageSubtitle(`No. ${receipt.receipt_number}`); } else { - changePageSubtitle(`No. ${receiptNumber}`); changePageTitle(formatMessage({ id: 'new_receipt' })); } + changePageSubtitle( + defaultToTransform(transactionNumber, `No. ${transactionNumber}`, ''), + ); }, [ + isNewMode, changePageTitle, changePageSubtitle, receipt, @@ -213,7 +224,9 @@ function ReceiptForm({ const handleReceiptNumberChanged = useCallback( (receiptNumber) => { - changePageSubtitle(`No. ${receiptNumber}`); + changePageSubtitle( + defaultToTransform(receiptNumber, `No. ${receiptNumber}`, ''), + ); }, [changePageSubtitle], ); @@ -244,7 +257,7 @@ function ReceiptForm({ onReceiptNumberChanged={handleReceiptNumberChanged} /> - + { form.setFieldValue('customer_id', contact.id); }} + popoverFill={true} /> )} @@ -103,6 +104,7 @@ function ReceiptFormHeader({ defaultSelectText={} selectedAccountId={value} filterByTypes={['current_asset']} + popoverFill={true} /> )} diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js index 8076e4b5f..8cf64e6e2 100644 --- a/client/src/lang/en/index.js +++ b/client/src/lang/en/index.js @@ -91,7 +91,6 @@ export default { count: 'Count', item_type: 'Item Type', item_name: 'Item Name', - sku: 'SKU', category: 'Category', account: 'Account', sales_information: 'Sales Information', @@ -689,12 +688,12 @@ export default { payment_receive_no_: 'Payment receive no', receive_amount: 'Receive Amount', receive_amount_: 'Receive amount', - the_payment_has_been_received_successfully_created: - 'The payment has been received successfully created.', + the_payment_receive_transaction_has_been_created: + 'The payment receive transaction has been created successfully.', the_payment_receive_has_been_successfully_deleted: 'The payment receive has been successfully deleted.', - the_payment_has_been_received_successfully_edited: - 'The payment has been received successfully edited.', + the_payment_receive_transaction_has_been_edited: + 'The payment receive transaction has been edited successfully.', once_delete_this_payment_receive_you_will_able_to_restore_it: `Once you delete this payment receive, you won\'t be able to restore it later. Are you sure you want to delete this payment receive?`, select_invoice: 'Select Invoice', payment_mades: 'Payment Mades', @@ -841,4 +840,6 @@ export default { once_delete_these_vendors_you_will_not_able_restore_them: "Once you delete these vendors, you won't be able to retrieve them later. Are you sure you want to delete them?", vendor_has_bills: 'Vendor has bills', + the_item_has_been_edited_successfully: 'The item has been edited successfully.', + you_cannot_make_payment_with_zero_total_amount: 'You cannot record payment transaction with zero total amount', }; diff --git a/client/src/style/views/Sidebar.scss b/client/src/style/views/Sidebar.scss index 01e730b9c..83c345042 100644 --- a/client/src/style/views/Sidebar.scss +++ b/client/src/style/views/Sidebar.scss @@ -89,13 +89,17 @@ $sidebar-submenu-item-bg-color: #01287d; margin-top: 3px; color: rgba(255, 255, 255, 0.25); } - &-label{ + &-labeler{ display: block; color: $sidebar-menu-label-color; font-size: 12px; padding: 6px 16px; margin-top: 4px; } + + &:hover .bp3-button.menu-item__add-btn{ + display: inline-block; + } } .#{$ns}-submenu { @@ -188,4 +192,42 @@ $sidebar-submenu-item-bg-color: #01287d; } } } + + .bp3-button.menu-item__add-btn{ + width: auto; + + padding: 2px; + margin-right: 0px; + position: relative; + top: 1px; + border-radius: 3px; + display: none; + vertical-align: top; + + &:not([class*="bp3-intent-"]):not(.bp3-minimal):not(:disabled){ + + .bp3-icon{ + color: rgba(255, 255, 255, 0.4); + } + &, + &:hover{ + min-height: auto; + min-width: auto; + outline: 0; + background-color: transparent; + } + + &:hover{ + background-color: rgba(255, 255, 255, 0.12); + + .bp3-icon{ + color: rgba(255, 255, 255, 0.6); + } + } + } + .bp3-icon{ + margin: 0; + display: block; + } + } } diff --git a/server/src/api/controllers/Contacts/Contacts.ts b/server/src/api/controllers/Contacts/Contacts.ts index 91a0c5f38..312938242 100644 --- a/server/src/api/controllers/Contacts/Contacts.ts +++ b/server/src/api/controllers/Contacts/Contacts.ts @@ -1,4 +1,4 @@ -import { check, param, query, ValidationChain } from 'express-validator'; +import { check, param, query, body, ValidationChain } from 'express-validator'; import BaseController from "api/controllers/BaseController"; export default class ContactsController extends BaseController { @@ -48,7 +48,8 @@ export default class ContactsController extends BaseController { */ get contactNewDTOSchema(): ValidationChain[] { return [ - check('balance').optional().isNumeric().toInt(), + check('opening_balance').optional({ nullable: true }).isNumeric().toInt(), + body('opening_balance_at').if(body('opening_balance').exists()).exists(), ]; } diff --git a/server/src/api/controllers/Contacts/Customers.ts b/server/src/api/controllers/Contacts/Customers.ts index 0b14944c0..433f74b51 100644 --- a/server/src/api/controllers/Contacts/Customers.ts +++ b/server/src/api/controllers/Contacts/Customers.ts @@ -90,9 +90,6 @@ export default class CustomersController extends ContactsController { */ get createCustomerDTOSchema() { return [ - check('opening_balance').optional({ nullable: true }).isNumeric().toInt(), - check('opening_balance_at').optional({ nullable: true }).isISO8601(), - check('currency_code').optional().trim().escape(), ]; } diff --git a/server/src/api/controllers/Contacts/Vendors.ts b/server/src/api/controllers/Contacts/Vendors.ts index 50e3e5b05..5a7041823 100644 --- a/server/src/api/controllers/Contacts/Vendors.ts +++ b/server/src/api/controllers/Contacts/Vendors.ts @@ -1,6 +1,7 @@ import { Request, Response, Router, NextFunction } from 'express'; import { Service, Inject } from 'typedi'; -import { check, query, ValidationChain } from 'express-validator'; +import { body, query, ValidationChain, check } from 'express-validator'; + import ContactsController from 'api/controllers/Contacts/Contacts'; import VendorsService from 'services/Contacts/VendorsService'; import { ServiceError } from 'exceptions'; @@ -72,7 +73,7 @@ export default class VendorsController extends ContactsController { */ get vendorDTOSchema(): ValidationChain[] { return [ - check('opening_balance').optional().isNumeric().toInt(), + check('currency_code').optional().trim().escape(), ]; } @@ -105,7 +106,11 @@ export default class VendorsController extends ContactsController { try { const vendor = await this.vendorsService.newVendor(tenantId, contactDTO); - return res.status(200).send({ id: vendor.id }); + + return res.status(200).send({ + id: vendor.id, + message: 'The vendor has been created successfully.', + }); } catch (error) { next(error); } @@ -124,7 +129,11 @@ export default class VendorsController extends ContactsController { try { await this.vendorsService.editVendor(tenantId, contactId, contactDTO); - return res.status(200).send({ id: contactId }); + + return res.status(200).send({ + id: contactId, + message: 'The vendor has been edited successfully.', + }); } catch (error) { next(error); } @@ -142,7 +151,11 @@ export default class VendorsController extends ContactsController { try { await this.vendorsService.deleteVendor(tenantId, contactId) - return res.status(200).send({ id: contactId }); + + return res.status(200).send({ + id: contactId, + message: 'The vendor has been deleted successfully.', + }); } catch (error) { next(error); } @@ -198,7 +211,11 @@ export default class VendorsController extends ContactsController { }; try { - const { vendors, pagination, filterMeta } = await this.vendorsService.getVendorsList(tenantId, vendorsFilter); + const { + vendors, + pagination, + filterMeta, + } = await this.vendorsService.getVendorsList(tenantId, vendorsFilter); return res.status(200).send({ vendors, @@ -219,21 +236,27 @@ export default class VendorsController extends ContactsController { */ handlerServiceErrors(error, req: Request, res: Response, next: NextFunction) { if (error instanceof ServiceError) { + if (error.errorType === 'contact_not_found') { + return res.boom.badRequest(null, { + errors: [{ type: 'VENDOR.NOT.FOUND', code: 100 }], + }); + } if (error.errorType === 'contacts_not_found') { return res.boom.badRequest(null, { - errors: [{ type: 'VENDORS.NOT.FOUND', code: 100 }], + errors: [{ type: 'VENDORS.NOT.FOUND', code: 200 }], }); } if (error.errorType === 'some_vendors_have_bills') { return res.boom.badRequest(null, { - errors: [{ type: 'SOME.VENDORS.HAVE.BILLS', code: 200 }], + errors: [{ type: 'SOME.VENDORS.HAVE.BILLS', code: 300 }], }); } if (error.errorType === 'vendor_has_bills') { return res.status(400).send({ - errors: [{ type: 'VENDOR.HAS.BILLS', code: 200 }], + errors: [{ type: 'VENDOR.HAS.BILLS', code: 400 }], }); } } + next(error); } } \ No newline at end of file diff --git a/server/src/api/controllers/Sales/PaymentReceives.ts b/server/src/api/controllers/Sales/PaymentReceives.ts index 965515d9b..ea78563c5 100644 --- a/server/src/api/controllers/Sales/PaymentReceives.ts +++ b/server/src/api/controllers/Sales/PaymentReceives.ts @@ -84,7 +84,7 @@ export default class PaymentReceivesController extends BaseController { check('payment_date').exists(), check('reference_no').optional(), check('deposit_account_id').exists().isNumeric().toInt(), - check('payment_receive_no').exists().trim().escape(), + check('payment_receive_no').optional({ nullable: true }).trim().escape(), check('statement').optional().trim().escape(), check('entries').isArray({ min: 1 }), diff --git a/server/src/services/Contacts/CustomersService.ts b/server/src/services/Contacts/CustomersService.ts index e26faf14f..2f801d0e2 100644 --- a/server/src/services/Contacts/CustomersService.ts +++ b/server/src/services/Contacts/CustomersService.ts @@ -1,5 +1,5 @@ import { Inject, Service } from 'typedi'; -import { omit, difference } from 'lodash'; +import { omit, difference, defaultTo } from 'lodash'; import { EventDispatcher, EventDispatcherInterface, @@ -51,8 +51,7 @@ export default class CustomersService { return { ...omit(customerDTO, ['customerType']), contactType: customerDTO.customerType, - active: (typeof customerDTO.active === 'undefined') ? - true : customerDTO.active, + active: defaultTo(customerDTO.active, true), }; } diff --git a/server/src/services/Contacts/VendorsService.ts b/server/src/services/Contacts/VendorsService.ts index 8b5bb4862..a803aca8f 100644 --- a/server/src/services/Contacts/VendorsService.ts +++ b/server/src/services/Contacts/VendorsService.ts @@ -1,5 +1,5 @@ import { Inject, Service } from 'typedi'; -import { difference, rest } from 'lodash'; +import { difference, defaultTo } from 'lodash'; import { EventDispatcher, EventDispatcherInterface, @@ -45,8 +45,7 @@ export default class VendorsService { private vendorToContactDTO(vendorDTO: IVendorNewDTO|IVendorEditDTO) { return { ...vendorDTO, - active: (typeof vendorDTO.active === 'undefined') ? - true : vendorDTO.active, + active: defaultTo(vendorDTO.active, true), }; } @@ -62,6 +61,7 @@ export default class VendorsService { const contactDTO = this.vendorToContactDTO(vendorDTO); const vendor = await this.contactService.newContact(tenantId, contactDTO, 'vendor'); + // Triggers `onVendorCreated` event. await this.eventDispatcher.dispatch(events.vendors.onCreated, { tenantId, vendorId: vendor.id, vendor, });