From 0aca6d9af73ad0424570915d12c7ea808186df83 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Tue, 28 Sep 2021 18:37:10 +0200 Subject: [PATCH] BIG-125: prevent items entries from send amount computed attribute. --- .../Expenses/ExpenseForm/ExpenseForm.js | 17 +++--- src/containers/Expenses/ExpenseForm/utils.js | 23 ++++++++ .../Purchases/Bills/BillForm/BillForm.js | 34 ++---------- .../Purchases/Bills/BillForm/utils.js | 53 ++++++++++++++++++- .../Estimates/EstimateForm/EstimateForm.js | 38 ++++--------- .../Sales/Estimates/EstimateForm/utils.js | 49 ++++++++++++++++- .../Sales/Invoices/InvoiceForm/InvoiceForm.js | 17 +++--- .../Sales/Invoices/InvoiceForm/utils.js | 20 ++++++- .../Sales/Receipts/ReceiptForm/ReceiptForm.js | 31 +++-------- .../Sales/Receipts/ReceiptForm/utils.js | 43 +++++++++++++++ 10 files changed, 225 insertions(+), 100 deletions(-) diff --git a/src/containers/Expenses/ExpenseForm/ExpenseForm.js b/src/containers/Expenses/ExpenseForm/ExpenseForm.js index 86924ec84..33511dc6e 100644 --- a/src/containers/Expenses/ExpenseForm/ExpenseForm.js +++ b/src/containers/Expenses/ExpenseForm/ExpenseForm.js @@ -6,7 +6,6 @@ import { Formik, Form } from 'formik'; import classNames from 'classnames'; import { useHistory } from 'react-router-dom'; import { CLASSES } from 'common/classes'; -import * as R from 'ramda'; import ExpenseFormHeader from './ExpenseFormHeader'; import ExpenseFormBody from './ExpenseFormBody'; @@ -25,8 +24,13 @@ import { CreateExpenseFormSchema, EditExpenseFormSchema, } from './ExpenseForm.schema'; -import { transformErrors, defaultExpense, transformToEditForm } from './utils'; -import { compose, orderingLinesIndexes } from 'utils'; +import { + transformErrors, + defaultExpense, + transformToEditForm, + transformFormValuesToRequest, +} from './utils'; +import { compose } from 'utils'; /** * Expense form. @@ -79,15 +83,10 @@ function ExpenseForm({ }); return; } - // Filter expense entries that has no amount or expense account. - const categories = values.categories.filter( - (category) => category.amount && category.expense_account_id, - ); const form = { - ...values, + ...transformFormValuesToRequest(values), publish: submitPayload.publish, - categories: R.compose(orderingLinesIndexes)(categories), }; // Handle request success. const handleSuccess = (response) => { diff --git a/src/containers/Expenses/ExpenseForm/utils.js b/src/containers/Expenses/ExpenseForm/utils.js index d197b69b3..2c4594c2a 100644 --- a/src/containers/Expenses/ExpenseForm/utils.js +++ b/src/containers/Expenses/ExpenseForm/utils.js @@ -8,6 +8,7 @@ import { transformToForm, repeatValue, ensureEntriesHasEmptyLine, + orderingLinesIndexes } from 'utils'; const ERROR = { @@ -104,3 +105,25 @@ export const accountsFieldShouldUpdate = (newProps, oldProps) => { defaultFastFieldShouldUpdate(newProps, oldProps) ); }; + + +/** + * Filter expense entries that has no amount or expense account. + */ +export const filterNonZeroEntries = (categories) => { + return categories.filter( + (category) => category.amount && category.expense_account_id, + ); +} + +/** + * Transformes the form values to request body. + */ +export const transformFormValuesToRequest = (values) => { + const categories = filterNonZeroEntries(values.categories); + + return { + ...values, + categories: R.compose(orderingLinesIndexes)(categories), + }; +}; diff --git a/src/containers/Purchases/Bills/BillForm/BillForm.js b/src/containers/Purchases/Bills/BillForm/BillForm.js index a63e20c54..c9ec20a01 100644 --- a/src/containers/Purchases/Bills/BillForm/BillForm.js +++ b/src/containers/Purchases/Bills/BillForm/BillForm.js @@ -2,7 +2,6 @@ import React, { useMemo } from 'react'; import { Formik, Form } from 'formik'; import { Intent } from '@blueprintjs/core'; import classNames from 'classnames'; -import * as R from 'ramda'; import intl from 'react-intl-universal'; import { useHistory } from 'react-router-dom'; import { isEmpty } from 'lodash'; @@ -16,13 +15,14 @@ import BillItemsEntriesEditor from './BillItemsEntriesEditor'; import { AppToaster } from 'components'; -import { ERROR } from 'common/errors'; import { useBillFormContext } from './BillFormProvider'; import { compose, safeSumBy } from 'utils'; import { defaultBill, + filterNonZeroEntries, transformToEditForm, - transformEntriesToSubmit, + transformFormValuesToRequest, + handleErrors, } from './utils'; import withCurrentOrganization from 'containers/Organization/withCurrentOrganization'; @@ -55,35 +55,12 @@ function BillForm({ [bill, base_currency], ); - // Transform response error to fields. - const handleErrors = (errors, { setErrors }) => { - if (errors.some((e) => e.type === ERROR.BILL_NUMBER_EXISTS)) { - setErrors({ - bill_number: intl.get('bill_number_exists'), - }); - } - if ( - errors.some( - (e) => e.type === ERROR.ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED, - ) - ) { - setErrors( - AppToaster.show({ - intent: Intent.DANGER, - message: 'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED', - }), - ); - } - }; - // Handles form submit. const handleFormSubmit = ( values, { setSubmitting, setErrors, resetForm }, ) => { - const entries = values.entries.filter( - (item) => item.item_id && item.quantity, - ); + const entries = filterNonZeroEntries(values.entries); const totalQuantity = safeSumBy(entries, 'quantity'); if (totalQuantity === 0) { @@ -95,9 +72,8 @@ function BillForm({ return; } const form = { - ...values, + ...transformFormValuesToRequest(values), open: submitPayload.status, - entries: transformEntriesToSubmit(entries), }; // Handle the request success. const onSuccess = (response) => { diff --git a/src/containers/Purchases/Bills/BillForm/utils.js b/src/containers/Purchases/Bills/BillForm/utils.js index 3408e1e38..457dc1324 100644 --- a/src/containers/Purchases/Bills/BillForm/utils.js +++ b/src/containers/Purchases/Bills/BillForm/utils.js @@ -41,6 +41,12 @@ export const defaultBill = { entries: [...repeatValue(defaultBillEntry, MIN_LINES_NUMBER)], }; +export const ERRORS = { + // Bills + BILL_NUMBER_EXISTS: 'BILL.NUMBER.EXISTS', + ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED: + 'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED', +}; /** * Transformes the bill to initial values of edit form. */ @@ -70,11 +76,33 @@ export const transformToEditForm = (bill) => { * Transformes bill entries to submit request. */ export const transformEntriesToSubmit = (entries) => { - const transformBillEntry = R.curry(transformToForm)(R.__, defaultBillEntry); - + const transformBillEntry = R.compose( + R.omit(['amount']), + R.curry(transformToForm)(R.__, defaultBillEntry), + ); return R.compose(orderingLinesIndexes, R.map(transformBillEntry))(entries); }; +/** + * Filters the givne non-zero entries. + */ +export const filterNonZeroEntries = (entries) => { + return entries.filter((item) => item.item_id && item.quantity); +}; + +/** + * Transformes form values to request body. + */ +export const transformFormValuesToRequest = (values) => { + const entries = filterNonZeroEntries(values.entries); + + return { + ...values, + entries: transformEntriesToSubmit(entries), + open: false, + }; +}; + /** * Handle delete errors. */ @@ -118,3 +146,24 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => { defaultFastFieldShouldUpdate(newProps, oldProps) ); }; + +// Transform response error to fields. +export const handleErrors = (errors, { setErrors }) => { + if (errors.some((e) => e.type === ERRORS.BILL_NUMBER_EXISTS)) { + setErrors({ + bill_number: intl.get('bill_number_exists'), + }); + } + if ( + errors.some( + (e) => e.type === ERRORS.ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED, + ) + ) { + setErrors( + AppToaster.show({ + intent: Intent.DANGER, + message: 'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED', + }), + ); + } +}; diff --git a/src/containers/Sales/Estimates/EstimateForm/EstimateForm.js b/src/containers/Sales/Estimates/EstimateForm/EstimateForm.js index 0ee6b7979..2745fbd8d 100644 --- a/src/containers/Sales/Estimates/EstimateForm/EstimateForm.js +++ b/src/containers/Sales/Estimates/EstimateForm/EstimateForm.js @@ -1,9 +1,8 @@ import React, { useMemo } from 'react'; import { Formik, Form } from 'formik'; import { Intent } from '@blueprintjs/core'; -import { FormattedMessage as T } from 'components'; import intl from 'react-intl-universal'; -import { omit, sumBy, isEmpty } from 'lodash'; +import { sumBy, isEmpty } from 'lodash'; import classNames from 'classnames'; import { useHistory } from 'react-router-dom'; @@ -23,10 +22,14 @@ import withSettings from 'containers/Settings/withSettings'; import withCurrentOrganization from 'containers/Organization/withCurrentOrganization'; import { AppToaster } from 'components'; -import { ERROR } from 'common/errors'; import { compose, transactionNumber, orderingLinesIndexes } from 'utils'; import { useEstimateFormContext } from './EstimateFormProvider'; -import { transformToEditForm, defaultEstimate } from './utils'; +import { + transformToEditForm, + defaultEstimate, + transfromsFormValuesToRequest, + handleErrors +} from './utils'; /** * Estimate form. @@ -68,25 +71,9 @@ function EstimateForm({ currency_code: base_currency, }), }), - [estimate, estimateNumber, estimateIncrementMode], + [estimate, estimateNumber, estimateIncrementMode, base_currency], ); - // Transform response errors to fields. - const handleErrors = (errors, { setErrors }) => { - if (errors.some((e) => e.type === ERROR.ESTIMATE_NUMBER_IS_NOT_UNQIUE)) { - setErrors({ - estimate_number: intl.get('estimate_number_is_not_unqiue'), - }); - } - if ( - errors.some((error) => error.type === ERROR.SALE_ESTIMATE_NO_IS_REQUIRED) - ) { - setErrors({ - estimate_number: intl.get('estimate.field.error.estimate_number_required'), - }); - } - }; - // Handles form submit. const handleFormSubmit = ( values, @@ -109,13 +96,10 @@ function EstimateForm({ return; } const form = { - ...omit(values, ['estimate_number_manually', 'estimate_number']), - ...(values.estimate_number_manually && { - estimate_number: values.estimate_number, - }), + ...transfromsFormValuesToRequest(values), delivered: submitPayload.deliver, - entries: entries.map((entry) => ({ ...omit(entry, ['total']) })), }; + // Handle the request success. const onSuccess = (response) => { AppToaster.show({ message: intl.get( @@ -135,7 +119,7 @@ function EstimateForm({ resetForm(); } }; - + // Handle the request error. const onError = ({ response: { data: { errors }, diff --git a/src/containers/Sales/Estimates/EstimateForm/utils.js b/src/containers/Sales/Estimates/EstimateForm/utils.js index cbb3b826d..1ad12c5e5 100644 --- a/src/containers/Sales/Estimates/EstimateForm/utils.js +++ b/src/containers/Sales/Estimates/EstimateForm/utils.js @@ -2,6 +2,8 @@ import React from 'react'; import { useFormikContext } from 'formik'; import moment from 'moment'; import * as R from 'ramda'; +import { omit } from 'lodash'; +import intl from 'react-intl-universal'; import { defaultFastFieldShouldUpdate, transactionNumber, @@ -37,6 +39,11 @@ export const defaultEstimate = { entries: [...repeatValue(defaultEstimateEntry, MIN_LINES_NUMBER)], }; +const ERRORS = { + ESTIMATE_NUMBER_IS_NOT_UNQIUE: 'ESTIMATE.NUMBER.IS.NOT.UNQIUE', + SALE_ESTIMATE_NO_IS_REQUIRED: 'SALE_ESTIMATE_NO_IS_REQUIRED', +}; + export const transformToEditForm = (estimate) => { const initialEntries = [ ...estimate.entries.map((estimate) => ({ @@ -54,8 +61,8 @@ export const transformToEditForm = (estimate) => { return { ...transformToForm(estimate, defaultEstimate), - entries - } + entries, + }; }; /** @@ -106,3 +113,41 @@ export const ITEMS_FILTER_ROLES = JSON.stringify([ comparator: 'equals', }, ]); + +/** + * Transform response errors to fields. + * @param {*} errors + * @param {*} param1 + */ +export const handleErrors = (errors, { setErrors }) => { + if (errors.some((e) => e.type === ERRORS.ESTIMATE_NUMBER_IS_NOT_UNQIUE)) { + setErrors({ + estimate_number: intl.get('estimate_number_is_not_unqiue'), + }); + } + if ( + errors.some((error) => error.type === ERRORS.SALE_ESTIMATE_NO_IS_REQUIRED) + ) { + setErrors({ + estimate_number: intl.get( + 'estimate.field.error.estimate_number_required', + ), + }); + } +}; + +/** + * Transform the form values to request body. + */ +export const transfromsFormValuesToRequest = (values) => { + const entries = values.entries.filter( + (item) => item.item_id && item.quantity, + ); + return { + ...omit(values, ['estimate_number_manually', 'estimate_number']), + ...(values.estimate_number_manually && { + estimate_number: values.estimate_number, + }), + entries: entries.map((entry) => ({ ...omit(entry, ['amount']) })), + }; +}; diff --git a/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.js b/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.js index f67a7dc52..e0bddd7fe 100644 --- a/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.js +++ b/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.js @@ -25,7 +25,12 @@ import withCurrentOrganization from 'containers/Organization/withCurrentOrganiza import { AppToaster } from 'components'; import { compose, orderingLinesIndexes, transactionNumber } from 'utils'; import { useInvoiceFormContext } from './InvoiceFormProvider'; -import { transformToEditForm, defaultInvoice, transformErrors } from './utils'; +import { + transformToEditForm, + defaultInvoice, + transformErrors, + transformValueToRequest, +} from './utils'; /** * Invoice form. @@ -72,7 +77,7 @@ function InvoiceForm({ currency_code: base_currency, }), }), - [invoice, newInvoice, invoiceNumber, invoiceIncrementMode], + [invoice, newInvoice, invoiceNumber, invoiceIncrementMode, base_currency], ); // Handles form submit. @@ -93,15 +98,13 @@ function InvoiceForm({ setSubmitting(false); return; } + // Transformes the values of the form to request. const form = { - ...omit(values, ['invoice_no', 'invoice_no_manually']), - ...(values.invoice_no_manually && { - invoice_no: values.invoice_no, - }), + ...transformValueToRequest(values), delivered: submitPayload.deliver, from_estimate_id: estimateId, - entries: entries.map((entry) => ({ ...omit(entry, ['total']) })), }; + // Handle the request success. const onSuccess = () => { AppToaster.show({ diff --git a/src/containers/Sales/Invoices/InvoiceForm/utils.js b/src/containers/Sales/Invoices/InvoiceForm/utils.js index 6874b6b0a..7116ba19d 100644 --- a/src/containers/Sales/Invoices/InvoiceForm/utils.js +++ b/src/containers/Sales/Invoices/InvoiceForm/utils.js @@ -1,5 +1,7 @@ import React from 'react'; import moment from 'moment'; +import { Intent } from '@blueprintjs/core'; +import { omit } from 'lodash'; import { compose, transformToForm, @@ -7,7 +9,6 @@ import { transactionNumber, } from 'utils'; import { useFormikContext } from 'formik'; -import { Intent } from '@blueprintjs/core'; import { defaultFastFieldShouldUpdate } from 'utils'; import intl from 'react-intl-universal'; @@ -146,3 +147,20 @@ export const ITEMS_FILTER_ROLES_QUERY = JSON.stringify([ comparator: 'equals', }, ]); + +/** + * Transformes the form values to request body values. + */ +export function transformValueToRequest(values) { + const entries = values.entries.filter( + (item) => item.item_id && item.quantity, + ); + return { + ...omit(values, ['invoice_no', 'invoice_no_manually']), + ...(values.invoice_no_manually && { + invoice_no: values.invoice_no, + }), + entries: entries.map((entry) => ({ ...omit(entry, ['amount']) })), + delivered: false, + }; +} diff --git a/src/containers/Sales/Receipts/ReceiptForm/ReceiptForm.js b/src/containers/Sales/Receipts/ReceiptForm/ReceiptForm.js index 99d42638a..5bd72558b 100644 --- a/src/containers/Sales/Receipts/ReceiptForm/ReceiptForm.js +++ b/src/containers/Sales/Receipts/ReceiptForm/ReceiptForm.js @@ -2,12 +2,11 @@ import React, { useMemo } from 'react'; import { Formik, Form } from 'formik'; import { Intent } from '@blueprintjs/core'; import intl from 'react-intl-universal'; -import { omit, sumBy, isEmpty } from 'lodash'; +import { sumBy, isEmpty } from 'lodash'; import classNames from 'classnames'; import { useHistory } from 'react-router-dom'; import { CLASSES } from 'common/classes'; -import { ERROR } from 'common/errors'; import { EditReceiptFormSchema, CreateReceiptFormSchema, @@ -27,7 +26,12 @@ import withCurrentOrganization from 'containers/Organization/withCurrentOrganiza import { AppToaster } from 'components'; import { compose, orderingLinesIndexes, transactionNumber } from 'utils'; -import { transformToEditForm, defaultReceipt } from './utils'; +import { + transformToEditForm, + defaultReceipt, + handleErrors, + transformFormValuesToRequest, +} from './utils'; /** * Receipt form. @@ -76,20 +80,6 @@ function ReceiptForm({ [receipt, preferredDepositAccount, nextReceiptNumber, receiptAutoIncrement], ); - // Transform response error to fields. - const handleErrors = (errors, { setErrors }) => { - if (errors.some((e) => e.type === ERROR.SALE_RECEIPT_NUMBER_NOT_UNIQUE)) { - setErrors({ - receipt_number: intl.get('sale_receipt_number_not_unique'), - }); - } - if (errors.some((e) => e.type === ERROR.SALE_RECEIPT_NO_IS_REQUIRED)) { - setErrors({ - receipt_number: intl.get('receipt.field.error.receipt_number_required'), - }); - } - }; - // Handle the form submit. const handleFormSubmit = ( values, @@ -109,13 +99,8 @@ function ReceiptForm({ return; } const form = { - ...omit(values, ['receipt_number_manually', 'receipt_number']), - ...(values.receipt_number_manually && { - receipt_number: values.receipt_number, - currency_code: base_currency, - }), + ...transformFormValuesToRequest(values), closed: submitPayload.status, - entries: entries.map((entry) => ({ ...omit(entry, ['total']) })), }; // Handle the request success. const onSuccess = (response) => { diff --git a/src/containers/Sales/Receipts/ReceiptForm/utils.js b/src/containers/Sales/Receipts/ReceiptForm/utils.js index d2462b9b7..482d0d8a9 100644 --- a/src/containers/Sales/Receipts/ReceiptForm/utils.js +++ b/src/containers/Sales/Receipts/ReceiptForm/utils.js @@ -2,6 +2,8 @@ import React from 'react'; import { useFormikContext } from 'formik'; import moment from 'moment'; import * as R from 'ramda'; +import intl from 'react-intl-universal'; +import { omit } from 'lodash'; import { defaultFastFieldShouldUpdate, transactionNumber, @@ -37,6 +39,11 @@ export const defaultReceipt = { entries: [...repeatValue(defaultReceiptEntry, MIN_LINES_NUMBER)], }; +const ERRORS = { + SALE_RECEIPT_NUMBER_NOT_UNIQUE: 'SALE_RECEIPT_NUMBER_NOT_UNIQUE', + SALE_RECEIPT_NO_IS_REQUIRED: 'SALE_RECEIPT_NO_IS_REQUIRED', +}; + /** * Transform to form in edit mode. */ @@ -99,3 +106,39 @@ export const customersFieldShouldUpdate = (newProps, oldProps) => { defaultFastFieldShouldUpdate(newProps, oldProps) ); }; + +/** + * Transform response error to fields. + */ +export const handleErrors = (errors, { setErrors }) => { + if (errors.some((e) => e.type === ERRORS.SALE_RECEIPT_NUMBER_NOT_UNIQUE)) { + setErrors({ + receipt_number: intl.get('sale_receipt_number_not_unique'), + }); + } + if (errors.some((e) => e.type === ERRORS.SALE_RECEIPT_NO_IS_REQUIRED)) { + setErrors({ + receipt_number: intl.get('receipt.field.error.receipt_number_required'), + }); + } +}; + +/** + * Transformes the form values to request body. + * @param {*} values + * @returns + */ +export const transformFormValuesToRequest = (values) => { + const entries = values.entries.filter( + (item) => item.item_id && item.quantity, + ); + + return { + ...omit(values, ['receipt_number_manually', 'receipt_number']), + ...(values.receipt_number_manually && { + receipt_number: values.receipt_number, + }), + entries: entries.map((entry) => ({ ...omit(entry, ['amount']) })), + closed: false, + }; +};