diff --git a/client/src/containers/Authentication/components.js b/client/src/containers/Authentication/components.js
index afa369ea4..2c3740f8e 100644
--- a/client/src/containers/Authentication/components.js
+++ b/client/src/containers/Authentication/components.js
@@ -2,10 +2,18 @@ import React from 'react';
import { FormattedMessage as T } from 'react-intl';
import ContentLoader from 'react-content-loader';
import { If, Icon } from 'components';
+import { saveInvoke } from 'utils';
+
+export function PasswordRevealer({ defaultShown = false, onChange }) {
+ const [shown, setShown] = React.useState(defaultShown);
+
+ const handleClick = () => {
+ setShown(!shown);
+ saveInvoke(onChange, !shown);
+ };
-export function PasswordRevealer({ shown, onClick }) {
return (
-
+
{' '}
diff --git a/client/src/containers/Entries/ItemsEntriesTable.js b/client/src/containers/Entries/ItemsEntriesTable.js
index 24319c773..3167afbdb 100644
--- a/client/src/containers/Entries/ItemsEntriesTable.js
+++ b/client/src/containers/Entries/ItemsEntriesTable.js
@@ -1,40 +1,25 @@
import React, { useEffect, useCallback } from 'react';
-import { Button } from '@blueprintjs/core';
-import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
import { useItem } from 'hooks/query';
-import ItemsEntriesDeleteAlert from 'containers/Alerts/ItemsEntries/ItemsEntriesDeleteAlert';
-import withAlertActions from 'containers/Alert/withAlertActions';
-
import { CLASSES } from 'common/classes';
import { DataTableEditable } from 'components';
import { useEditableItemsEntriesColumns } from './components';
import {
saveInvoke,
- updateTableRow,
- repeatValue,
- removeRowsByIndex,
compose,
+ updateTableRow,
+ updateMinEntriesLines,
+ updateAutoAddNewLine,
+ updateRemoveLineByIndex,
} from 'utils';
import { updateItemsEntriesTotal, ITEM_TYPE } from './utils';
-import { last } from 'lodash';
-
-const updateAutoAddNewLine = (defaultEntry) => (entries) => {
- const newEntries = [...entries];
- const lastEntry = last(newEntries);
-
- return (lastEntry.item_id) ? [...entries, defaultEntry] : [...entries];
-};
/**
* Items entries table.
*/
function ItemsEntriesTable({
- // #withAlertActions
- openAlert,
-
// #ownProps
items,
entries,
@@ -116,7 +101,7 @@ function ItemsEntriesTable({
setRowItem({ rowIndex, columnId, itemId: value });
}
const newRows = compose(
- updateAutoAddNewLine(defaultEntry),
+ updateAutoAddNewLine(defaultEntry, ['item_id']),
updateItemsEntriesTotal,
updateTableRow(rowIndex, columnId, value),
)(rows);
@@ -129,55 +114,35 @@ function ItemsEntriesTable({
// Handle table rows removing by index.
const handleRemoveRow = (rowIndex) => {
- const newRows = removeRowsByIndex(rows, rowIndex);
- setRows(newRows);
- saveInvoke(onUpdateData, newRows);
- };
+ const newRows = compose(
+ // Ensure minimum lines count.
+ updateMinEntriesLines(4, defaultEntry),
+ // Remove the line by the given index.
+ updateRemoveLineByIndex(rowIndex),
+ )(rows);
- // Handle table rows adding a new row.
- const onClickNewRow = (event) => {
- const newRows = [...rows, defaultEntry];
- setRows(newRows);
- saveInvoke(onUpdateData, newRows);
- };
-
- // Handle table clearing all rows.
- const handleClickClearAllLines = (event) => {
- openAlert('items-entries-clear-lines');
- };
-
- // Handle alert confirm of clear all lines.
- const handleClearLinesAlertConfirm = () => {
- const newRows = repeatValue(defaultEntry, linesNumber);
setRows(newRows);
saveInvoke(onUpdateData, newRows);
};
return (
- <>
-
-
- >
+
);
}
@@ -186,7 +151,7 @@ ItemsEntriesTable.defaultProps = {
index: 0,
item_id: '',
description: '',
- quantity: 1,
+ quantity: '',
rate: '',
discount: '',
},
@@ -194,4 +159,4 @@ ItemsEntriesTable.defaultProps = {
linesNumber: 4,
};
-export default compose(withAlertActions)(ItemsEntriesTable);
+export default ItemsEntriesTable;
diff --git a/client/src/containers/Entries/utils.js b/client/src/containers/Entries/utils.js
index 082038efc..e51b59c5f 100644
--- a/client/src/containers/Entries/utils.js
+++ b/client/src/containers/Entries/utils.js
@@ -1,10 +1,11 @@
+import { repeat } from 'lodash';
import { toSafeNumber } from 'utils';
/**
* Retrieve item entry total from the given rate, quantity and discount.
- * @param {number} rate
- * @param {number} quantity
- * @param {number} discount
+ * @param {number} rate
+ * @param {number} quantity
+ * @param {number} discount
* @return {number}
*/
export const calcItemEntryTotal = (discount, quantity, rate) => {
@@ -21,9 +22,9 @@ export const calcItemEntryTotal = (discount, quantity, rate) => {
export function updateItemsEntriesTotal(rows) {
return rows.map((row) => ({
...row,
- total: calcItemEntryTotal(row.discount, row.quantity, row.rate)
+ total: calcItemEntryTotal(row.discount, row.quantity, row.rate),
}));
-};
+}
export const ITEM_TYPE = {
SELLABLE: 'SELLABLE',
diff --git a/client/src/containers/Expenses/ExpenseForm/ExpenseFormBody.js b/client/src/containers/Expenses/ExpenseForm/ExpenseFormBody.js
index 2e6f1dd6b..f2cf39ace 100644
--- a/client/src/containers/Expenses/ExpenseForm/ExpenseFormBody.js
+++ b/client/src/containers/Expenses/ExpenseForm/ExpenseFormBody.js
@@ -3,12 +3,10 @@ import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import ExpenseFormEntriesField from './ExpenseFormEntriesField';
-export default function ExpenseFormBody({
-
-}) {
+export default function ExpenseFormBody() {
return (
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesField.js b/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesField.js
index 36c8794b9..4d579c5df 100644
--- a/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesField.js
+++ b/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesField.js
@@ -1,7 +1,7 @@
import { FastField } from 'formik';
import React from 'react';
import ExpenseFormEntriesTable from './ExpenseFormEntriesTable';
-import { useExpenseFormContext } from './ExpenseFormPageProvider';
+import { defaultExpenseEntry } from './utils';
/**
* Expense form entries field.
@@ -9,8 +9,6 @@ import { useExpenseFormContext } from './ExpenseFormPageProvider';
export default function ExpenseFormEntriesField({
linesNumber = 4,
}) {
- const { defaultCategoryEntry } = useExpenseFormContext();
-
return (
{({ form, field: { value }, meta: { error, touched } }) => (
@@ -20,7 +18,7 @@ export default function ExpenseFormEntriesField({
onChange={(entries) => {
form.setFieldValue('categories', entries);
}}
- defaultEntry={defaultCategoryEntry}
+ defaultEntry={defaultExpenseEntry}
linesNumber={linesNumber}
/>
)}
diff --git a/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesTable.js b/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesTable.js
index d795e305d..2cce9d4c3 100644
--- a/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesTable.js
+++ b/client/src/containers/Expenses/ExpenseForm/ExpenseFormEntriesTable.js
@@ -1,23 +1,21 @@
import React, { useCallback } from 'react';
-import { Button } from '@blueprintjs/core';
-import { FormattedMessage as T } from 'react-intl';
import { DataTableEditable } from 'components';
-import ExpenseDeleteEntriesAlert from 'containers/Alerts/Expenses/ExpenseDeleteEntriesAlert';
import { useExpenseFormContext } from './ExpenseFormPageProvider';
import { useExpenseFormTableColumns } from './components';
-
-import withAlertActions from 'containers/Alert/withAlertActions';
-
-import { transformUpdatedRows, compose, saveInvoke, repeatValue } from 'utils';
+import {
+ saveInvoke,
+ compose,
+ updateTableRow,
+ updateMinEntriesLines,
+ updateAutoAddNewLine,
+ updateRemoveLineByIndex,
+} from 'utils';
/**
* Expenses form entries.
*/
-function ExpenseFormEntriesTable({
- // #withAlertActions
- openAlert,
-
+export default function ExpenseFormEntriesTable({
// #ownPorps
entries,
defaultEntry,
@@ -32,29 +30,30 @@ function ExpenseFormEntriesTable({
// Handles update datatable data.
const handleUpdateData = useCallback(
- (rowIndex, columnIdOrObj, value) => {
- const newRows = transformUpdatedRows(
- entries,
- rowIndex,
- columnIdOrObj,
- value,
- );
+ (rowIndex, columnId, value) => {
+ const newRows = compose(
+ updateAutoAddNewLine(defaultEntry, ['expense_account_id']),
+ updateTableRow(rowIndex, columnId, value),
+ )(entries);
+
saveInvoke(onChange, newRows);
},
- [entries, onChange],
+ [entries, defaultEntry, onChange],
);
// Handles click remove datatable row.
const handleRemoveRow = useCallback(
(rowIndex) => {
- // Can't continue if there is just one row line or less.
- if (entries.length <= 1) {
- return;
- }
- const newRows = entries.filter((row, index) => index !== rowIndex);
+ const newRows = compose(
+ // Ensure minimum lines count.
+ updateMinEntriesLines(4, defaultEntry),
+ // Remove the line by the given index.
+ updateRemoveLineByIndex(rowIndex),
+ )(entries);
+
saveInvoke(onChange, newRows);
},
- [entries, onChange],
+ [entries, defaultEntry, onChange],
);
return (
@@ -72,6 +71,4 @@ function ExpenseFormEntriesTable({
footer={true}
/>
);
-}
-
-export default compose(withAlertActions)(ExpenseFormEntriesTable);
+}
\ No newline at end of file
diff --git a/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceItemsEntriesEditorField.js b/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceItemsEntriesEditorField.js
index b09755137..6502f736a 100644
--- a/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceItemsEntriesEditorField.js
+++ b/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceItemsEntriesEditorField.js
@@ -1,4 +1,4 @@
-import React, { useCallback } from 'react';
+import React from 'react';
import { FastField } from 'formik';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
diff --git a/client/src/utils.js b/client/src/utils.js
index 197a515d1..0d57c3e59 100644
--- a/client/src/utils.js
+++ b/client/src/utils.js
@@ -93,17 +93,6 @@ export const compose = (...funcs) =>
);
-export const updateTableRow = (rowIndex, columnId, value) => (old) => {
- return old.map((row, index) => {
- if (index === rowIndex) {
- return {
- ...old[rowIndex],
- [columnId]: value,
- }
- }
- return row
- })
-}
export const getObjectDiff = (a, b) => {
return _.reduce(
a,
@@ -600,4 +589,41 @@ export const amountPaymentEntries = (amount, entries) => {
payment_amount: diff,
};
});
+};
+
+export const updateAutoAddNewLine = (defaultEntry, props) => (entries) => {
+ const newEntries = [...entries];
+ const lastEntry = _.last(newEntries);
+ const newLine = props.filter((entryKey) => !isBlank(lastEntry[entryKey]));
+
+ return newLine.length > 0 ? [...entries, defaultEntry] : [...entries];
+};
+
+/**
+ * Ensure min entries lines.
+ * @param {number} min
+ * @param {any} defaultEntry
+ */
+export const updateMinEntriesLines = (min, defaultEntry) => (lines) => {
+ if (lines.length < min) {
+ const diffLines = Math.max(min - lines.length, 0);
+ return [...lines, ...repeatValue(defaultEntry, diffLines)];
+ }
+};
+
+export const updateRemoveLineByIndex = (rowIndex) => (entries) => {
+ const removeIndex = parseInt(rowIndex, 10);
+ return entries.filter((row, index) => index !== removeIndex);
+};
+
+export const updateTableRow = (rowIndex, columnId, value) => (old) => {
+ return old.map((row, index) => {
+ if (index === rowIndex) {
+ return {
+ ...old[rowIndex],
+ [columnId]: value,
+ }
+ }
+ return row
+ })
};
\ No newline at end of file
diff --git a/server/src/services/Sales/SalesReceipts.ts b/server/src/services/Sales/SalesReceipts.ts
index bf5210239..25360d6ca 100644
--- a/server/src/services/Sales/SalesReceipts.ts
+++ b/server/src/services/Sales/SalesReceipts.ts
@@ -172,26 +172,6 @@ export default class SalesReceiptService {
);
}
- /**
- * Retrieve estimate number to object model.
- * @param {number} tenantId
- * @param {ISaleReceiptDTO} saleReceiptDTO - Sale receipt DTO.
- * @param {ISaleReceipt} oldSaleReceipt - Old receipt model object.
- */
- transformReceiptNumberToModel(
- tenantId: number,
- saleReceiptDTO: ISaleReceiptDTO,
- oldSaleReceipt?: ISaleReceipt
- ): string {
- // Retreive the next invoice number.
- const autoNextNumber = this.getNextReceiptNumber(tenantId);
-
- if (saleReceiptDTO.receiptNumber) {
- return saleReceiptDTO.receiptNumber;
- }
- return oldSaleReceipt ? oldSaleReceipt.receiptNumber : autoNextNumber;
- }
-
/**
* Transform create DTO object to model object.
* @param {ISaleReceiptDTO} saleReceiptDTO -
@@ -233,7 +213,7 @@ export default class SalesReceiptService {
receiptNumber,
// Avoid rewrite the deliver date in edit mode when already published.
...(saleReceiptDTO.closed &&
- !oldSaleReceipt.closedAt && {
+ !oldSaleReceipt?.closedAt && {
closedAt: moment().toMySqlDateTime(),
}),
entries: saleReceiptDTO.entries.map((entry) => ({