diff --git a/src/components/DialogsContainer.js b/src/components/DialogsContainer.js
index 871e38a58..656c7d9d8 100644
--- a/src/components/DialogsContainer.js
+++ b/src/components/DialogsContainer.js
@@ -26,6 +26,10 @@ import NotifyEstimateViaSMSDialog from '../containers/Dialogs/NotifyEstimateViaS
import NotifyPaymentReceiveViaSMSDialog from '../containers/Dialogs/NotifyPaymentReceiveViaSMSDialog';
import SMSMessageDialog from '../containers/Dialogs/SMSMessageDialog';
import TransactionsLockingDialog from '../containers/Dialogs/TransactionsLockingDialog';
+import RefundCreditNoteDialog from '../containers/Dialogs/RefundCreditNoteDialog';
+import RefundVendorCreditDialog from '../containers/Dialogs/RefundVendorCreditDialog';
+import ReconcileCreditNoteDialog from '../containers/Dialogs/ReconcileCreditNoteDialog';
+import ReconcileVendorCreditDialog from '../containers/Dialogs/ReconcileVendorCreditDialog';
/**
* Dialogs container.
@@ -60,6 +64,10 @@ export default function DialogsContainer() {
+
+
+
+
);
}
diff --git a/src/containers/Alerts/CreditNotes/CreditNoteDeleteAlert.js b/src/containers/Alerts/CreditNotes/CreditNoteDeleteAlert.js
index be664372a..5c70206cd 100644
--- a/src/containers/Alerts/CreditNotes/CreditNoteDeleteAlert.js
+++ b/src/containers/Alerts/CreditNotes/CreditNoteDeleteAlert.js
@@ -9,6 +9,7 @@ import withAlertActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { useDeleteCreditNote } from 'hooks/query';
+import { handleDeleteErrors } from '../../Sales/CreditNotes/CreditNotesLanding/utils';
import { compose } from 'utils';
/**
@@ -48,7 +49,9 @@ function CreditNoteDeleteAlert({
response: {
data: { errors },
},
- }) => {},
+ }) => {
+ handleDeleteErrors(errors);
+ },
)
.finally(() => {
closeAlert(name);
diff --git a/src/containers/Alerts/CreditNotes/CreditNoteOpenedAlert.js b/src/containers/Alerts/CreditNotes/CreditNoteOpenedAlert.js
new file mode 100644
index 000000000..58b9e8554
--- /dev/null
+++ b/src/containers/Alerts/CreditNotes/CreditNoteOpenedAlert.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import { FormattedMessage as T } from 'components';
+import intl from 'react-intl-universal';
+import { Intent, Alert } from '@blueprintjs/core';
+
+import { useOpenCreditNote } from 'hooks/query';
+import { AppToaster } from 'components';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+
+import { compose } from 'utils';
+
+/**
+ * Credit note opened alert.
+ */
+function CreditNoteOpenedAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { creditNoteId },
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: openCreditNoteMutate, isLoading } = useOpenCreditNote();
+
+ // Handle cancel opened credit note alert.
+ const handleAlertCancel = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm credit note opened.
+ const handleAlertConfirm = () => {
+ openCreditNoteMutate(creditNoteId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('credit_note_opened.alert.success_message'),
+ intent: Intent.SUCCESS,
+ });
+ })
+ .catch((error) => {})
+ .finally(() => {
+ closeAlert(name);
+ });
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ onCancel={handleAlertCancel}
+ onConfirm={handleAlertConfirm}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(CreditNoteOpenedAlert);
diff --git a/src/containers/Alerts/CreditNotes/ReconcileCreditNoteDeleteAlert.js b/src/containers/Alerts/CreditNotes/ReconcileCreditNoteDeleteAlert.js
new file mode 100644
index 000000000..1d84cdbb6
--- /dev/null
+++ b/src/containers/Alerts/CreditNotes/ReconcileCreditNoteDeleteAlert.js
@@ -0,0 +1,88 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
+import { Intent, Alert } from '@blueprintjs/core';
+import { AppToaster } from 'components';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withDrawerActions from 'containers/Drawer/withDrawerActions';
+
+import { useDeleteReconcileCredit } from 'hooks/query';
+import { handleDeleteErrors } from '../../Sales/CreditNotes/CreditNotesLanding/utils';
+import { compose } from 'utils';
+
+/**
+ * Reconcile credit note delete alert.
+ */
+function ReconcileCreditNoteDeleteAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { creditNoteId },
+
+ // #withAlertActions
+ closeAlert,
+
+ // #withDrawerActions
+ closeDrawer,
+}) {
+ const { isLoading, mutateAsync: deleteReconcileCreditMutate } =
+ useDeleteReconcileCredit();
+
+ // handle cancel delete credit note alert.
+ const handleCancelDeleteAlert = () => {
+ closeAlert(name);
+ };
+
+ const handleConfirmVendorCreditDelete = () => {
+ deleteReconcileCreditMutate(creditNoteId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('reconcile_credit_note.alert.success_message'),
+ intent: Intent.SUCCESS,
+ });
+ closeDrawer('credit-note-detail-drawer');
+ })
+ .catch(
+ ({
+ response: {
+ data: { errors },
+ },
+ }) => {
+ // handleDeleteErrors(errors);
+ },
+ )
+ .finally(() => {
+ closeAlert(name);
+ });
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancelDeleteAlert}
+ onConfirm={handleConfirmVendorCreditDelete}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+ withDrawerActions,
+)(ReconcileCreditNoteDeleteAlert);
diff --git a/src/containers/Alerts/CreditNotes/RefundCreditNoteDeleteAlert.js b/src/containers/Alerts/CreditNotes/RefundCreditNoteDeleteAlert.js
new file mode 100644
index 000000000..9fe3774ad
--- /dev/null
+++ b/src/containers/Alerts/CreditNotes/RefundCreditNoteDeleteAlert.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
+import { Intent, Alert } from '@blueprintjs/core';
+import { AppToaster } from 'components';
+import { useDeleteRefundCreditNote } from 'hooks/query';
+
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+
+import { compose } from 'utils';
+
+/**
+ * Refund credit transactions delete alert
+ */
+function RefundCreditNoteDeleteAlert({
+ name,
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { creditNoteId },
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: deleteRefundCreditMutate, isLoading } =
+ useDeleteRefundCreditNote();
+
+ // Handle cancel delete.
+ const handleCancelAlert = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm delete .
+ const handleConfirmRefundCreditDelete = () => {
+ deleteRefundCreditMutate(creditNoteId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('refund_credit_transactions.alert.delete_message'),
+ intent: Intent.SUCCESS,
+ });
+ closeAlert(name);
+ })
+ .catch(() => {});
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancelAlert}
+ onConfirm={handleConfirmRefundCreditDelete}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(RefundCreditNoteDeleteAlert);
diff --git a/src/containers/Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert.js b/src/containers/Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert.js
new file mode 100644
index 000000000..526d92bfb
--- /dev/null
+++ b/src/containers/Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
+import { Intent, Alert } from '@blueprintjs/core';
+import { AppToaster } from 'components';
+import { useDeleteRefundVendorCredit } from 'hooks/query';
+
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+
+import { compose } from 'utils';
+
+/**
+ * Refund Vendor transactions delete alert.
+ */
+function RefundVendorCreditDeleteAlert({
+ name,
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { vendorCreditId },
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: deleteRefundVendorCreditMutate, isLoading } =
+ useDeleteRefundVendorCredit();
+
+ // Handle cancel delete.
+ const handleCancelAlert = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm delete .
+ const handleConfirmRefundVendorCreditDelete = () => {
+ deleteRefundVendorCreditMutate(vendorCreditId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get(
+ 'refund_vendor_credit_transactions.alert.delete_message',
+ ),
+ intent: Intent.SUCCESS,
+ });
+ closeAlert(name);
+ })
+ .catch(() => {});
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancelAlert}
+ onConfirm={handleConfirmRefundVendorCreditDelete}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(RefundVendorCreditDeleteAlert);
diff --git a/src/containers/Alerts/VendorCeditNotes/VendorCreditOpenedAlert.js b/src/containers/Alerts/VendorCeditNotes/VendorCreditOpenedAlert.js
new file mode 100644
index 000000000..56c0fe5bf
--- /dev/null
+++ b/src/containers/Alerts/VendorCeditNotes/VendorCreditOpenedAlert.js
@@ -0,0 +1,69 @@
+import React from 'react';
+import { FormattedMessage as T } from 'components';
+import intl from 'react-intl-universal';
+import { Intent, Alert } from '@blueprintjs/core';
+
+import { useOpenVendorCredit } from 'hooks/query';
+import { AppToaster } from 'components';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+
+import { compose } from 'utils';
+
+/**
+ * Vendor credit opened alert.
+ */
+function VendorCreditOpenedAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { vendorCreditId },
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: openVendorCreditMutate, isLoading } =
+ useOpenVendorCredit();
+
+ // Handle cancel opened credit note alert.
+ const handleAlertCancel = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm vendor credit as opened.
+ const handleAlertConfirm = () => {
+ openVendorCreditMutate(vendorCreditId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('vendor_credit_opened.alert.success_message'),
+ intent: Intent.SUCCESS,
+ });
+ })
+ .catch((error) => {})
+ .finally(() => {
+ closeAlert(name);
+ });
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ onCancel={handleAlertCancel}
+ onConfirm={handleAlertConfirm}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(VendorCreditOpenedAlert);
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteDialogContent.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteDialogContent.js
new file mode 100644
index 000000000..62b18d718
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteDialogContent.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import { ReconcileCreditNoteFormProvider } from './ReconcileCreditNoteFormProvider';
+import ReconcileCreditNoteForm from './ReconcileCreditNoteForm';
+
+/**
+ * Reconcile credit note dialog content.
+ */
+export default function ReconcileCreditNoteDialogContent({
+ // #ownProps
+ dialogName,
+ creditNoteId,
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteEntriesTable.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteEntriesTable.js
new file mode 100644
index 000000000..c268c7e00
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteEntriesTable.js
@@ -0,0 +1,75 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { MoneyFieldCell, DataTableEditable, FormatDateCell } from 'components';
+import { compose, updateTableCell } from 'utils';
+
+/**
+ * Reconcile credit note entries table.
+ */
+export default function ReconcileCreditNoteEntriesTable({
+ onUpdateData,
+ entries,
+ errors,
+}) {
+ const columns = React.useMemo(
+ () => [
+ {
+ Header: intl.get('invoice_date'),
+ accessor: 'formatted_invoice_date',
+ Cell: FormatDateCell,
+ disableSortBy: true,
+ width: '120',
+ },
+ {
+ Header: intl.get('invoice_no'),
+ accessor: 'invoice_no',
+ disableSortBy: true,
+ width: '100',
+ },
+ {
+ Header: intl.get('amount'),
+ accessor: 'formatted_amount',
+ disableSortBy: true,
+ align: 'right',
+ width: '100',
+ },
+ {
+ Header: intl.get('reconcile_credit_note.column.remaining_amount'),
+ accessor: 'formatted_due_amount',
+ disableSortBy: true,
+ align: 'right',
+ width: '150',
+ },
+ {
+ Header: intl.get('reconcile_credit_note.column.amount_to_credit'),
+ accessor: 'amount',
+ Cell: MoneyFieldCell,
+ disableSortBy: true,
+ width: '150',
+ },
+ ],
+ [],
+ );
+
+ // Handle update data.
+ const handleUpdateData = React.useCallback(
+ (rowIndex, columnId, value) => {
+ const newRows = compose(updateTableCell(rowIndex, columnId, value))(
+ entries,
+ );
+ onUpdateData(newRows);
+ },
+ [onUpdateData, entries],
+ );
+
+ return (
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.js
new file mode 100644
index 000000000..6811e2170
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.js
@@ -0,0 +1,99 @@
+import React from 'react';
+import { Formik } from 'formik';
+import { Intent } from '@blueprintjs/core';
+import intl from 'react-intl-universal';
+
+import '../../../style/pages/ReconcileCreditNote/ReconcileCreditNoteForm.scss';
+import { AppToaster } from 'components';
+import { CreateReconcileCreditNoteFormSchema } from './ReconcileCreditNoteForm.schema';
+import { useReconcileCreditNoteContext } from './ReconcileCreditNoteFormProvider';
+import ReconcileCreditNoteFormContent from './ReconcileCreditNoteFormContent';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose, transformToForm } from 'utils';
+import { transformErrors } from './utils';
+
+// Default form initial values.
+const defaultInitialValues = {
+ entries: [
+ {
+ invoice_id: '',
+ amount: '',
+ },
+ ],
+};
+
+/**
+ * Reconcile credit note form.
+ */
+function ReconcileCreditNoteForm({
+ // #withDialogActions
+ closeDialog,
+}) {
+ const {
+ dialogName,
+ creditNoteId,
+ reconcileCreditNotes,
+ createReconcileCreditNoteMutate,
+ } = useReconcileCreditNoteContext();
+
+ // Initial form values.
+ const initialValues = {
+ entries: reconcileCreditNotes.map((entry) => ({
+ ...entry,
+ invoice_id: entry.id,
+ amount: '',
+ })),
+ };
+
+ // Handle form submit.
+ const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
+ setSubmitting(false);
+
+ // Filters the entries.
+ const entries = values.entries
+ .filter((entry) => entry.invoice_id && entry.amount)
+ .map((entry) => transformToForm(entry, defaultInitialValues.entries[0]));
+
+ const form = {
+ ...values,
+ entries: entries,
+ };
+
+ // Handle the request success.
+ const onSuccess = (response) => {
+ AppToaster.show({
+ message: intl.get('reconcile_credit_note.success_message'),
+ intent: Intent.SUCCESS,
+ });
+ setSubmitting(false);
+ closeDialog(dialogName);
+ };
+
+ // Handle the request error.
+ const onError = ({
+ response: {
+ data: { errors },
+ },
+ }) => {
+ if (errors) {
+ transformErrors(errors, { setErrors });
+ }
+ setSubmitting(false);
+ };
+
+ createReconcileCreditNoteMutate([creditNoteId, form])
+ .then(onSuccess)
+ .catch(onError);
+ };
+
+ return (
+
+ );
+}
+
+export default compose(withDialogActions)(ReconcileCreditNoteForm);
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.schema.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.schema.js
new file mode 100644
index 000000000..a6cd9dc09
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.schema.js
@@ -0,0 +1,12 @@
+import * as Yup from 'yup';
+
+const Schema = Yup.object().shape({
+ entries: Yup.array().of(
+ Yup.object().shape({
+ invoice_id: Yup.number().required(),
+ amount: Yup.number().nullable(),
+ }),
+ ),
+});
+
+export const CreateReconcileCreditNoteFormSchema = Schema;
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormContent.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormContent.js
new file mode 100644
index 000000000..1c1e12d21
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormContent.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import { Form } from 'formik';
+import { Choose } from 'components';
+
+import ReconcileCreditNoteFormFields from './ReconcileCreditNoteFormFields';
+import ReconcileCreditNoteFormFloatingActions from './ReconcileCreditNoteFormFloatingActions';
+import { EmptyStatuCallout } from './utils';
+import { useReconcileCreditNoteContext } from './ReconcileCreditNoteFormProvider';
+
+/**
+ * Reconcile credit note form content.
+ */
+export default function ReconcileCreditNoteFormContent() {
+ const { isEmptyStatus } = useReconcileCreditNoteContext();
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFields.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFields.js
new file mode 100644
index 000000000..a035fd95d
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFields.js
@@ -0,0 +1,60 @@
+import React from 'react';
+import { FastField, useFormikContext } from 'formik';
+import { Classes } from '@blueprintjs/core';
+import { T, TotalLines, TotalLine } from 'components';
+import { getEntriesTotal } from 'containers/Entries/utils';
+import ReconcileCreditNoteEntriesTable from './ReconcileCreditNoteEntriesTable';
+import { useReconcileCreditNoteContext } from './ReconcileCreditNoteFormProvider';
+import { formattedAmount } from 'utils';
+
+/**
+ * Reconcile credit note form fields.
+ */
+export default function ReconcileCreditNoteFormFields() {
+ const {
+ creditNote: { formatted_credits_remaining, currency_code },
+ } = useReconcileCreditNoteContext();
+
+ const { values } = useFormikContext();
+
+ // Calculate the total amount.
+ const totalAmount = React.useMemo(
+ () => getEntriesTotal(values.entries),
+ [values.entries],
+ );
+
+ return (
+
+ {/*------------ Reconcile credit entries table -----------*/}
+
+ {({
+ form: { setFieldValue, values },
+ field: { value },
+ meta: { error, touched },
+ }) => (
+ {
+ setFieldValue('entries', newEntries);
+ }}
+ />
+ )}
+
+
+
+
+ }
+ value={formattedAmount(totalAmount, currency_code)}
+ />
+ }
+ value={formatted_credits_remaining}
+ />
+
+
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFloatingActions.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFloatingActions.js
new file mode 100644
index 000000000..9fb415500
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFloatingActions.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import { useFormikContext } from 'formik';
+import { Intent, Button, Classes } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'components';
+
+import { useReconcileCreditNoteContext } from './ReconcileCreditNoteFormProvider';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose } from 'utils';
+
+/**
+ * Reconcile credit note floating actions.
+ */
+function ReconcileCreditNoteFormFloatingActions({
+ // #withDialogActions
+ closeDialog,
+}) {
+ // Formik context.
+ const { isSubmitting } = useFormikContext();
+
+ const { dialogName } = useReconcileCreditNoteContext();
+
+ // Handle cancel button click.
+ const handleCancelBtnClick = (event) => {
+ closeDialog(dialogName);
+ };
+
+ return (
+
+ );
+}
+export default compose(withDialogActions)(
+ ReconcileCreditNoteFormFloatingActions,
+);
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormProvider.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormProvider.js
new file mode 100644
index 000000000..92cc90bd1
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormProvider.js
@@ -0,0 +1,64 @@
+import React from 'react';
+import { DialogContent } from 'components';
+import {
+ useCreditNote,
+ useReconcileCreditNote,
+ useCreateReconcileCreditNote,
+} from 'hooks/query';
+import { isEmpty } from 'lodash';
+
+const ReconcileCreditNoteDialogContext = React.createContext();
+
+/**
+ * Reconcile credit note provider.
+ */
+function ReconcileCreditNoteFormProvider({
+ creditNoteId,
+ dialogName,
+ ...props
+}) {
+ // Handle fetch reconcile credit note details.
+ const { isLoading: isReconcileCreditLoading, data: reconcileCreditNotes } =
+ useReconcileCreditNote(creditNoteId, {
+ enabled: !!creditNoteId,
+ });
+
+ // Handle fetch vendor credit details.
+ const { data: creditNote, isLoading: isCreditNoteLoading } = useCreditNote(
+ creditNoteId,
+ {
+ enabled: !!creditNoteId,
+ },
+ );
+
+ // Create reconcile credit note mutations.
+ const { mutateAsync: createReconcileCreditNoteMutate } =
+ useCreateReconcileCreditNote();
+
+ // Detarmines the datatable empty status.
+ const isEmptyStatus = isEmpty(reconcileCreditNotes);
+
+ // provider payload.
+ const provider = {
+ dialogName,
+ reconcileCreditNotes,
+ createReconcileCreditNoteMutate,
+ isEmptyStatus,
+ creditNote,
+ creditNoteId,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useReconcileCreditNoteContext = () =>
+ React.useContext(ReconcileCreditNoteDialogContext);
+
+export { ReconcileCreditNoteFormProvider, useReconcileCreditNoteContext };
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/index.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/index.js
new file mode 100644
index 000000000..217d93b32
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/index.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { FormattedMessage as T, Dialog, DialogSuspense } from 'components';
+import withDialogRedux from 'components/DialogReduxConnect';
+import { compose } from 'utils';
+
+const ReconcileCreditNoteDialogContent = React.lazy(() =>
+ import('./ReconcileCreditNoteDialogContent'),
+);
+
+/**
+ * Reconcile credit note dialog.
+ */
+function ReconcileCreditNoteDialog({
+ dialogName,
+ payload: { creditNoteId },
+ isOpen,
+}) {
+ return (
+ }
+ canEscapeKeyClose={true}
+ isOpen={isOpen}
+ className="dialog--reconcile-credit-form"
+ >
+
+
+
+
+ );
+}
+
+export default compose(withDialogRedux())(ReconcileCreditNoteDialog);
diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/utils.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/utils.js
new file mode 100644
index 000000000..4f9b6a8f1
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/utils.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { Callout, Intent, Classes } from '@blueprintjs/core';
+
+import { AppToaster, T } from 'components';
+
+export const transformErrors = (errors, { setErrors }) => {
+ if (errors.some((e) => e.type === 'INVOICES_HAS_NO_REMAINING_AMOUNT')) {
+ AppToaster.show({
+ message: 'INVOICES_HAS_NO_REMAINING_AMOUNT',
+ intent: Intent.DANGER,
+ });
+ }
+
+ if (
+ errors.find((error) => error.type === 'CREDIT_NOTE_HAS_NO_REMAINING_AMOUNT')
+ ) {
+ AppToaster.show({
+ message: 'CREDIT_NOTE_HAS_NO_REMAINING_AMOUNT',
+ intent: Intent.DANGER,
+ });
+ }
+};
+
+export function EmptyStatuCallout() {
+ return (
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditDialogContent.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditDialogContent.js
new file mode 100644
index 000000000..651b33756
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditDialogContent.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import { ReconcileVendorCreditFormProvider } from './ReconcileVendorCreditFormProvider';
+import ReconcileVendorCreditForm from './ReconcileVendorCreditForm';
+
+export default function ReconcileVendorCreditDialogContent({
+ // #ownProps
+ dialogName,
+ vendorCreditId,
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditEntriesTable.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditEntriesTable.js
new file mode 100644
index 000000000..ee6e23eb7
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditEntriesTable.js
@@ -0,0 +1,72 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { MoneyFieldCell, DataTableEditable, FormatDateCell } from 'components';
+import { compose, updateTableCell } from 'utils';
+
+export default function ReconcileVendorCreditEntriesTable({
+ onUpdateData,
+ entries,
+ errors,
+}) {
+ const columns = React.useMemo(
+ () => [
+ {
+ Header: intl.get('bill_date'),
+ accessor: 'formatted_bill_date',
+ Cell: FormatDateCell,
+ disableSortBy: true,
+ width: '120',
+ },
+ {
+ Header: intl.get('reconcile_vendor_credit.column.bill_number'),
+ accessor: 'bill_number',
+ disableSortBy: true,
+ width: '100',
+ },
+ {
+ Header: intl.get('amount'),
+ accessor: 'formatted_amount',
+ disableSortBy: true,
+ align: 'right',
+ width: '100',
+ },
+ {
+ Header: intl.get('reconcile_vendor_credit.column.remaining_amount'),
+ accessor: 'formatted_due_amount',
+ disableSortBy: true,
+ align: 'right',
+ width: '150',
+ },
+ {
+ Header: intl.get('reconcile_vendor_credit.column.amount_to_credit'),
+ accessor: 'amount',
+ Cell: MoneyFieldCell,
+ disableSortBy: true,
+ width: '150',
+ },
+ ],
+ [],
+ );
+
+ // Handle update data.
+ const handleUpdateData = React.useCallback(
+ (rowIndex, columnId, value) => {
+ const newRows = compose(updateTableCell(rowIndex, columnId, value))(
+ entries,
+ );
+ onUpdateData(newRows);
+ },
+ [onUpdateData, entries],
+ );
+
+ return (
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFloatingActions.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFloatingActions.js
new file mode 100644
index 000000000..95dc83c65
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFloatingActions.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import { useFormikContext } from 'formik';
+import { Intent, Button, Classes } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'components';
+
+import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose } from 'utils';
+
+function ReconcileVendorCreditFloatingActions({
+ // #withDialogActions
+ closeDialog,
+}) {
+ // Formik context.
+ const { isSubmitting } = useFormikContext();
+
+ const { dialogName } = useReconcileVendorCreditContext();
+
+ // Handle cancel button click.
+ const handleCancelBtnClick = (event) => {
+ closeDialog(dialogName);
+ };
+ return (
+
+ );
+}
+export default compose(withDialogActions)(ReconcileVendorCreditFloatingActions);
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.js
new file mode 100644
index 000000000..175e0ccb3
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.js
@@ -0,0 +1,98 @@
+import React from 'react';
+import { Formik } from 'formik';
+import { Intent } from '@blueprintjs/core';
+import intl from 'react-intl-universal';
+
+import '../../../style/pages/ReconcileVendorCredit/ReconcileVendorCreditForm.scss';
+
+import { AppToaster } from 'components';
+import { CreateReconcileVendorCreditFormSchema } from './ReconcileVendorCreditForm.schema';
+import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider';
+import ReconcileVendorCreditFormContent from './ReconcileVendorCreditFormContent';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose, transformToForm } from 'utils';
+
+// Default form initial values.
+const defaultInitialValues = {
+ entries: [
+ {
+ bill_id: '',
+ amount: '',
+ },
+ ],
+};
+
+/**
+ * Reconcile vendor credit form.
+ */
+function ReconcileVendorCreditForm({
+ // #withDialogActions
+ closeDialog,
+}) {
+ const {
+ dialogName,
+ reconcileVendorCredits,
+ createReconcileVendorCreditMutate,
+ vendorCredit,
+ } = useReconcileVendorCreditContext();
+
+ // Initial form values.
+ const initialValues = {
+ entries: reconcileVendorCredits.map((entry) => ({
+ ...entry,
+ bill_id: entry.id,
+ amount: '',
+ })),
+ };
+
+ // Handle form submit.
+ const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
+ setSubmitting(false);
+
+ // Filters the entries.
+ const entries = values.entries
+ .filter((entry) => entry.bill_id && entry.amount)
+ .map((entry) => transformToForm(entry, defaultInitialValues.entries[0]));
+
+ const form = {
+ ...values,
+ entries: entries,
+ };
+
+ // Handle the request success.
+ const onSuccess = (response) => {
+ AppToaster.show({
+ message: intl.get('reconcile_vendor_credit.dialog.success_message'),
+ intent: Intent.SUCCESS,
+ });
+ setSubmitting(false);
+ closeDialog(dialogName);
+ };
+
+ // Handle the request error.
+ const onError = ({
+ response: {
+ data: { errors },
+ },
+ }) => {
+ // if (errors) {
+ // transformErrors(errors, { setErrors });
+ // }
+ setSubmitting(false);
+ };
+
+ createReconcileVendorCreditMutate([vendorCredit.id, form])
+ .then(onSuccess)
+ .catch(onError);
+ };
+
+ return (
+
+ );
+}
+export default compose(withDialogActions)(ReconcileVendorCreditForm);
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.schema.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.schema.js
new file mode 100644
index 000000000..d15b472d9
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.schema.js
@@ -0,0 +1,12 @@
+import * as Yup from 'yup';
+
+const Schema = Yup.object().shape({
+ entries: Yup.array().of(
+ Yup.object().shape({
+ bill_id: Yup.number().required(),
+ amount: Yup.number().nullable(),
+ }),
+ ),
+});
+
+export const CreateReconcileVendorCreditFormSchema = Schema;
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormContent.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormContent.js
new file mode 100644
index 000000000..c88b1de20
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormContent.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import { Form } from 'formik';
+import { Choose } from 'components';
+
+import { EmptyStatuCallout } from './utils';
+import ReconcileVendorCreditFormFields from './ReconcileVendorCreditFormFields';
+import ReconcileVendorCreditFloatingActions from './ReconcileVendorCreditFloatingActions';
+import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider';
+
+export default function ReconcileVendorCreditFormContent() {
+ const { isEmptyStatus } = useReconcileVendorCreditContext();
+
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormFields.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormFields.js
new file mode 100644
index 000000000..531e18b82
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormFields.js
@@ -0,0 +1,56 @@
+import React from 'react';
+import { FastField, useFormikContext } from 'formik';
+import { Classes } from '@blueprintjs/core';
+import { T, TotalLines, TotalLine } from 'components';
+import { getEntriesTotal } from 'containers/Entries/utils';
+import ReconcileVendorCreditEntriesTable from './ReconcileVendorCreditEntriesTable';
+import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider';
+import { formattedAmount } from 'utils';
+
+export default function ReconcileVendorCreditFormFields() {
+ const { vendorCredit } = useReconcileVendorCreditContext();
+
+ const { values } = useFormikContext();
+
+ // Calculate the total amount.
+ const totalAmount = React.useMemo(
+ () => getEntriesTotal(values.entries),
+ [values.entries],
+ );
+
+ return (
+
+
+ {({
+ form: { setFieldValue, values },
+ field: { value },
+ meta: { error, touched },
+ }) => (
+ {
+ setFieldValue('entries', newEntries);
+ }}
+ />
+ )}
+
+
+
+
+ }
+ value={formattedAmount(totalAmount, vendorCredit.currency_code)}
+ />
+
+ }
+ value={vendorCredit.formatted_credits_remaining}
+ />
+
+
+
+ );
+}
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormProvider.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormProvider.js
new file mode 100644
index 000000000..0b85497f7
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormProvider.js
@@ -0,0 +1,64 @@
+import React from 'react';
+import { DialogContent } from 'components';
+import {
+ useVendorCredit,
+ useReconcileVendorCredit,
+ useCreateReconcileVendorCredit,
+} from 'hooks/query';
+import { isEmpty } from 'lodash';
+
+const ReconcileVendorCreditFormContext = React.createContext();
+
+/**
+ * Reconcile vendor credit provider.
+ */
+function ReconcileVendorCreditFormProvider({
+ vendorCreditId,
+ dialogName,
+ ...props
+}) {
+
+ // Handle fetch reconcile
+ const {
+ isLoading: isReconcileVendorCreditLoading,
+ data: reconcileVendorCredits,
+ } = useReconcileVendorCredit(vendorCreditId, {
+ enabled: !!vendorCreditId,
+ });
+
+ // Handle fetch vendor credit details.
+ const { data: vendorCredit, isLoading: isVendorCreditLoading } =
+ useVendorCredit(vendorCreditId, {
+ enabled: !!vendorCreditId,
+ });
+
+ // Create reconcile vendor credit mutations.
+ const { mutateAsync: createReconcileVendorCreditMutate } =
+ useCreateReconcileVendorCredit();
+
+ // Detarmines the datatable empty status.
+ const isEmptyStatus = isEmpty(reconcileVendorCredits);
+
+ // provider.
+ const provider = {
+ dialogName,
+ reconcileVendorCredits,
+ createReconcileVendorCreditMutate,
+ isEmptyStatus,
+ vendorCredit,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useReconcileVendorCreditContext = () =>
+ React.useContext(ReconcileVendorCreditFormContext);
+
+export { ReconcileVendorCreditFormProvider, useReconcileVendorCreditContext };
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/index.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/index.js
new file mode 100644
index 000000000..8dc713917
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/index.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { FormattedMessage as T, Dialog, DialogSuspense } from 'components';
+import withDialogRedux from 'components/DialogReduxConnect';
+import { compose } from 'utils';
+
+const ReconcileVendorCreditDialogContent = React.lazy(() =>
+ import('./ReconcileVendorCreditDialogContent'),
+);
+
+/**
+ * Reconcile vendor credit dialog.
+ */
+function ReconcileVendorCreditDialog({
+ dialogName,
+ payload: { vendorCreditId },
+ isOpen,
+}) {
+ return (
+ }
+ canEscapeKeyClose={true}
+ isOpen={isOpen}
+ className="dialog--reconcile-vendor-credit-form"
+ >
+
+
+
+
+ );
+}
+
+export default compose(withDialogRedux())(ReconcileVendorCreditDialog);
diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/utils.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/utils.js
new file mode 100644
index 000000000..8439dd634
--- /dev/null
+++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/utils.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import { Callout, Intent, Classes } from '@blueprintjs/core';
+import { AppToaster, T } from 'components';
+
+export const transformErrors = (errors, { setErrors }) => {};
+
+export function EmptyStatuCallout() {
+ return (
+
+ );
+}
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteDialogContent.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteDialogContent.js
new file mode 100644
index 000000000..4cc99ae85
--- /dev/null
+++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteDialogContent.js
@@ -0,0 +1,23 @@
+import React from 'react';
+
+import 'style/pages/RefundCreditNote/RefundCreditNote.scss';
+import { RefundCreditNoteFormProvider } from './RefundCreditNoteFormProvider';
+import RefundCreditNoteForm from './RefundCreditNoteForm';
+
+/**
+ * Refund credit note dialog content.
+ */
+export default function RefundCreditNoteDialogContent({
+ // #ownProps
+ dialogName,
+ creditNoteId,
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFloatingActions.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFloatingActions.js
new file mode 100644
index 000000000..32cb154aa
--- /dev/null
+++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFloatingActions.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import { Intent, Button, Classes } from '@blueprintjs/core';
+import { useFormikContext } from 'formik';
+import { FormattedMessage as T } from 'components';
+
+import { useRefundCreditNoteContext } from './RefundCreditNoteFormProvider';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose } from 'utils';
+
+/**
+ * Refund credit note floating actions.
+ */
+function RefundCreditNoteFloatingActions({
+ // #withDialogActions
+ closeDialog,
+}) {
+ // Formik context.
+ const { isSubmitting, values, errors } = useFormikContext();
+
+ // refund credit note dialog context.
+ const { dialogName } = useRefundCreditNoteContext();
+
+ // Handle close button click.
+ const handleCancelBtnClick = () => {
+ closeDialog(dialogName);
+ };
+
+ return (
+
+ );
+}
+export default compose(withDialogActions)(RefundCreditNoteFloatingActions);
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.js
new file mode 100644
index 000000000..05600768f
--- /dev/null
+++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.js
@@ -0,0 +1,77 @@
+import React from 'react';
+import { Formik } from 'formik';
+import { Intent } from '@blueprintjs/core';
+import intl from 'react-intl-universal';
+import moment from 'moment';
+import { omit, defaultTo } from 'lodash';
+
+import { AppToaster } from 'components';
+import { useRefundCreditNoteContext } from './RefundCreditNoteFormProvider';
+import { CreateRefundCreditNoteFormSchema } from './RefundCreditNoteForm.schema';
+import RefundCreditNoteFormContent from './RefundCreditNoteFormContent';
+
+import withSettings from 'containers/Settings/withSettings';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose, transactionNumber } from 'utils';
+
+const defaultInitialValues = {
+ from_account_id: '',
+ date: moment(new Date()).format('YYYY-MM-DD'),
+ reference_no: '',
+ description: '',
+ amount: '',
+};
+
+/**
+ * Refund credit note form.
+ */
+function RefundCreditNoteForm({
+ // #withDialogActions
+ closeDialog,
+}) {
+ const { dialogName, creditNote, createRefundCreditNoteMutate } =
+ useRefundCreditNoteContext();
+
+ // Initial form values
+ const initialValues = {
+ ...defaultInitialValues,
+ ...creditNote,
+ };
+
+ // Handles the form submit.
+ const handleFormSubmit = (values, { setSubmitting, setFieldError }) => {
+ const form = {
+ ...omit(values, ['currency_code', 'credits_remaining']),
+ };
+
+ // Handle request response success.
+ const onSaved = (response) => {
+ AppToaster.show({
+ message: intl.get('refund_credit_note.dialog.success_message'),
+ intent: Intent.SUCCESS,
+ });
+ closeDialog(dialogName);
+ };
+ // Handle request response errors.
+ const onError = ({
+ response: {
+ data: { errors },
+ },
+ }) => {
+ setSubmitting(false);
+ };
+ createRefundCreditNoteMutate([creditNote.id, form])
+ .then(onSaved)
+ .catch(onError);
+ };
+
+ return (
+
+ );
+}
+export default compose(withDialogActions)(RefundCreditNoteForm);
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.schema.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.schema.js
new file mode 100644
index 000000000..8ada6b557
--- /dev/null
+++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.schema.js
@@ -0,0 +1,12 @@
+import * as Yup from 'yup';
+import intl from 'react-intl-universal';
+import { DATATYPES_LENGTH } from 'common/dataTypes';
+
+const Schema = Yup.object().shape({
+ date: Yup.date().required().label(intl.get('date')),
+ amount: Yup.number().required(),
+ reference_no: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
+ from_account_id: Yup.number().required().label(intl.get('deposit_account_')),
+ description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
+});
+export const CreateRefundCreditNoteFormSchema = Schema;
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormContent.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormContent.js
new file mode 100644
index 000000000..d763729bb
--- /dev/null
+++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormContent.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Form } from 'formik';
+import RefundCreditNoteFormFields from './RefundCreditNoteFormFields';
+import RefundCreditNoteFloatingActions from './RefundCreditNoteFloatingActions';
+
+/**
+ * Refund credit note form content.
+ */
+export default function RefundCreditNoteFormContent() {
+ return (
+
+ );
+}
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormFields.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormFields.js
new file mode 100644
index 000000000..a8e9eb100
--- /dev/null
+++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormFields.js
@@ -0,0 +1,166 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { FastField, ErrorMessage } from 'formik';
+import {
+ Classes,
+ FormGroup,
+ InputGroup,
+ TextArea,
+ Position,
+ ControlGroup,
+} from '@blueprintjs/core';
+import classNames from 'classnames';
+import { CLASSES } from 'common/classes';
+import { DateInput } from '@blueprintjs/datetime';
+import {
+ Icon,
+ FieldRequiredHint,
+ AccountsSuggestField,
+ InputPrependText,
+ MoneyInputGroup,
+ FormattedMessage as T,
+} from 'components';
+import {
+ inputIntent,
+ momentFormatter,
+ tansformDateValue,
+ handleDateChange,
+ compose,
+} from 'utils';
+import { useAutofocus } from 'hooks';
+import { ACCOUNT_TYPE } from 'common/accountTypes';
+import { useRefundCreditNoteContext } from './RefundCreditNoteFormProvider';
+import withSettings from 'containers/Settings/withSettings';
+
+/**
+ * Refund credit note form fields.
+ */
+function RefundCreditNoteFormFields() {
+ const { accounts } = useRefundCreditNoteContext();
+ const amountFieldRef = useAutofocus();
+ return (
+
+ {/* ------------- Refund date ------------- */}
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ className={classNames('form-group--select-list', CLASSES.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+ {
+ form.setFieldValue('date', formattedDate);
+ })}
+ popoverProps={{ position: Position.BOTTOM, minimal: true }}
+ inputProps={{
+ leftIcon: ,
+ }}
+ />
+
+ )}
+
+ {/* ------------- Amount ------------- */}
+
+ {({
+ form: { values, setFieldValue },
+ field: { value },
+ meta: { error, touched },
+ }) => (
+ }
+ labelInfo={}
+ className={classNames('form-group--amount', CLASSES.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
+ {
+ setFieldValue('amount', amount);
+ }}
+ intent={inputIntent({ error, touched })}
+ inputRef={(ref) => (amountFieldRef.current = ref)}
+ />
+
+
+ )}
+
+ {/* ------------ Reference No. ------------ */}
+
+ {({ form, field, meta: { error, touched } }) => (
+ }
+ className={classNames('form-group--reference', CLASSES.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
+ )}
+
+
+ {/* ------------ Form account ------------ */}
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ className={classNames(
+ 'form-group--from_account_id',
+ 'form-group--select-list',
+ CLASSES.FILL,
+ )}
+ labelInfo={}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+ form.setFieldValue('from_account_id', id)
+ }
+ inputProps={{
+ placeholder: intl.get('select_account'),
+ }}
+ filterByTypes={[
+ ACCOUNT_TYPE.BANK,
+ ACCOUNT_TYPE.CASH,
+ ACCOUNT_TYPE.FIXED_ASSET,
+ ]}
+ />
+
+ )}
+
+ {/* --------- Statement --------- */}
+
+ {({ form, field, meta: { error, touched } }) => (
+ }
+ className={'form-group--description'}
+ inline={true}
+ >
+
+
+ )}
+
+
+ );
+}
+
+export default RefundCreditNoteFormFields;
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormProvider.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormProvider.js
new file mode 100644
index 000000000..978421272
--- /dev/null
+++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormProvider.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { DialogContent } from 'components';
+import { pick } from 'lodash';
+
+import {
+ useAccounts,
+ useCreditNote,
+ useCreateRefundCreditNote,
+} from 'hooks/query';
+
+const RefundCreditNoteContext = React.createContext();
+
+/**
+ * Refund credit note form provider.
+ */
+function RefundCreditNoteFormProvider({ creditNoteId, dialogName, ...props }) {
+ // Handle fetch accounts data.
+ const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
+
+ // Handle fetch credit note data.
+ const { data: creditNote, isLoading: isCreditNoteLoading } = useCreditNote(
+ creditNoteId,
+ {
+ enabled: !!creditNoteId,
+ },
+ );
+ // Create and edit credit note mutations.
+ const { mutateAsync: createRefundCreditNoteMutate } =
+ useCreateRefundCreditNote();
+
+ // State provider.
+ const provider = {
+ creditNote: {
+ ...pick(creditNote, ['id', 'credits_remaining', 'currency_code']),
+ amount: creditNote.credits_remaining,
+ },
+ accounts,
+ dialogName,
+ createRefundCreditNoteMutate,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useRefundCreditNoteContext = () =>
+ React.useContext(RefundCreditNoteContext);
+
+export { RefundCreditNoteFormProvider, useRefundCreditNoteContext };
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/index.js b/src/containers/Dialogs/RefundCreditNoteDialog/index.js
new file mode 100644
index 000000000..2f9d52d0f
--- /dev/null
+++ b/src/containers/Dialogs/RefundCreditNoteDialog/index.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import { Dialog, DialogSuspense, FormattedMessage as T } from 'components';
+
+import withDialogRedux from 'components/DialogReduxConnect';
+import { compose } from 'redux';
+
+const RefundCreditNoteDialogContent = React.lazy(() =>
+ import('./RefundCreditNoteDialogContent'),
+);
+
+/**
+ * Refund credit note dialog.
+ */
+function RefundCreditNoteDialog({
+ dialogName,
+ payload: { creditNoteId },
+ isOpen,
+}) {
+ return (
+ }
+ isOpen={isOpen}
+ canEscapeJeyClose={true}
+ autoFocus={true}
+ className={'dialog--refund-credit-note'}
+ >
+
+
+
+
+ );
+}
+export default compose(withDialogRedux())(RefundCreditNoteDialog);
diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/utils.js b/src/containers/Dialogs/RefundCreditNoteDialog/utils.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditDialogContent.js b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditDialogContent.js
new file mode 100644
index 000000000..a454c1388
--- /dev/null
+++ b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditDialogContent.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+import 'style/pages/RefundVendorCredit/RefundVendorCredit.scss';
+
+import { RefundVendorCreditFormProvider } from './RefundVendorCreditFormProvider';
+import RefundVendorCreditForm from './RefundVendorCreditForm';
+
+export default function RefundVendorCreditDialogContent({
+ // #ownProps
+ dialogName,
+ vendorCreditId,
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFloatingActions.js b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFloatingActions.js
new file mode 100644
index 000000000..7c43dabcc
--- /dev/null
+++ b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFloatingActions.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import { Intent, Button, Classes } from '@blueprintjs/core';
+import { useFormikContext } from 'formik';
+import { FormattedMessage as T } from 'components';
+
+import { useRefundVendorCreditContext } from './RefundVendorCreditFormProvider';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose } from 'utils';
+
+/**
+ * Refund vendor flaoting actions.
+ */
+function RefundVendorCreditFloatingActions({
+ // #withDialogActions
+ closeDialog,
+}) {
+ // Formik context.
+ const { isSubmitting } = useFormikContext();
+ // refund vendor credit dialog context.
+ const { dialogName } = useRefundVendorCreditContext();
+
+ // Handle close button click.
+ const handleCancelBtnClick = () => {
+ closeDialog(dialogName);
+ };
+
+ return (
+
+ );
+}
+
+export default compose(withDialogActions)(RefundVendorCreditFloatingActions);
diff --git a/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditForm.js b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditForm.js
new file mode 100644
index 000000000..0882a4dd8
--- /dev/null
+++ b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditForm.js
@@ -0,0 +1,79 @@
+import React from 'react';
+import { Formik } from 'formik';
+import { Intent } from '@blueprintjs/core';
+import intl from 'react-intl-universal';
+import moment from 'moment';
+import { omit, defaultTo } from 'lodash';
+
+import { AppToaster } from 'components';
+import { useRefundVendorCreditContext } from './RefundVendorCreditFormProvider';
+import { CreateVendorRefundCreditFormSchema } from './RefundVendorCreditForm.schema';
+import RefundVendorCreditFormContent from './RefundVendorCreditFormContent';
+
+import withSettings from 'containers/Settings/withSettings';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose, transactionNumber } from 'utils';
+
+const defaultInitialValues = {
+ deposit_account_id: '',
+ date: moment(new Date()).format('YYYY-MM-DD'),
+ reference_no: '',
+ description: '',
+ amount: '',
+};
+
+/**
+ * Refund Vendor credit form.
+ */
+function RefundVendorCreditForm({
+ // #withDialogActions
+ closeDialog,
+}) {
+ const { vendorCredit, dialogName, createRefundVendorCreditMutate } =
+ useRefundVendorCreditContext();
+
+ // Initial form values
+ const initialValues = {
+ ...defaultInitialValues,
+ ...vendorCredit,
+ };
+
+ // Handles the form submit.
+ const handleFormSubmit = (values, { setSubmitting, setFieldError }) => {
+ const form = {
+ ...omit(values, ['currency_code', 'credits_remaining']),
+ };
+
+ // Handle request response success.
+ const onSaved = (response) => {
+ AppToaster.show({
+ message: intl.get('refund_vendor_credit.dialog.success_message'),
+ intent: Intent.SUCCESS,
+ });
+ closeDialog(dialogName);
+ };
+ // Handle request response errors.
+ const onError = ({
+ response: {
+ data: { errors },
+ },
+ }) => {
+ setSubmitting(false);
+ };
+
+ createRefundVendorCreditMutate([vendorCredit.id, form])
+ .then(onSaved)
+ .catch(onError);
+ };
+
+ return (
+
+ );
+}
+
+export default compose(withDialogActions)(RefundVendorCreditForm);
diff --git a/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditForm.schema.js b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditForm.schema.js
new file mode 100644
index 000000000..57c0b9f2d
--- /dev/null
+++ b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditForm.schema.js
@@ -0,0 +1,14 @@
+import * as Yup from 'yup';
+import intl from 'react-intl-universal';
+import { DATATYPES_LENGTH } from 'common/dataTypes';
+
+const Schema = Yup.object().shape({
+ date: Yup.date().required().label(intl.get('date')),
+ amount: Yup.number().required(),
+ reference_no: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
+ deposit_account_id: Yup.number()
+ .required()
+ .label(intl.get('deposit_account_')),
+ description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
+});
+export const CreateVendorRefundCreditFormSchema = Schema;
diff --git a/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormContent.js b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormContent.js
new file mode 100644
index 000000000..dfc391028
--- /dev/null
+++ b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormContent.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import { Form } from 'formik';
+import RefundVendorCreditFormFields from './RefundVendorCreditFormFields';
+import RefundVendorCreditFloatingActions from './RefundVendorCreditFloatingActions';
+
+export default function RefundVendorCreditFormContent() {
+ return (
+
+ );
+}
diff --git a/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormFields.js b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormFields.js
new file mode 100644
index 000000000..8f59eb472
--- /dev/null
+++ b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormFields.js
@@ -0,0 +1,167 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { FastField, ErrorMessage } from 'formik';
+import {
+ Classes,
+ FormGroup,
+ InputGroup,
+ TextArea,
+ Position,
+ ControlGroup,
+} from '@blueprintjs/core';
+import classNames from 'classnames';
+import { CLASSES } from 'common/classes';
+import { DateInput } from '@blueprintjs/datetime';
+import {
+ Icon,
+ FieldRequiredHint,
+ AccountsSuggestField,
+ InputPrependText,
+ MoneyInputGroup,
+ FormattedMessage as T,
+} from 'components';
+import {
+ inputIntent,
+ momentFormatter,
+ tansformDateValue,
+ handleDateChange,
+ compose,
+} from 'utils';
+import { useAutofocus } from 'hooks';
+import { ACCOUNT_TYPE } from 'common/accountTypes';
+import { useRefundVendorCreditContext } from './RefundVendorCreditFormProvider';
+import withSettings from 'containers/Settings/withSettings';
+
+/**
+ * Refund Vendor credit form fields.
+ */
+function RefundVendorCreditFormFields() {
+ const { accounts } = useRefundVendorCreditContext();
+ const amountFieldRef = useAutofocus();
+
+ return (
+
+ {/* ------------- Refund date ------------- */}
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ className={classNames('form-group--select-list', CLASSES.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+ {
+ form.setFieldValue('refund_date', formattedDate);
+ })}
+ popoverProps={{ position: Position.BOTTOM, minimal: true }}
+ inputProps={{
+ leftIcon: ,
+ }}
+ />
+
+ )}
+
+ {/* ------------- Amount ------------- */}
+
+ {({
+ form: { values, setFieldValue },
+ field: { value },
+ meta: { error, touched },
+ }) => (
+ }
+ labelInfo={}
+ className={classNames('form-group--amount', CLASSES.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
+ {
+ setFieldValue('amount', amount);
+ }}
+ intent={inputIntent({ error, touched })}
+ inputRef={(ref) => (amountFieldRef.current = ref)}
+ />
+
+
+ )}
+
+ {/* ------------ Reference No. ------------ */}
+
+ {({ form, field, meta: { error, touched } }) => (
+ }
+ className={classNames('form-group--reference', CLASSES.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
+ )}
+
+
+ {/* ------------ Form account ------------ */}
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ className={classNames(
+ 'form-group--deposit_account_id',
+ 'form-group--select-list',
+ CLASSES.FILL,
+ )}
+ labelInfo={}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+ form.setFieldValue('deposit_account_id', id)
+ }
+ inputProps={{
+ placeholder: intl.get('select_account'),
+ }}
+ filterByTypes={[
+ ACCOUNT_TYPE.BANK,
+ ACCOUNT_TYPE.CASH,
+ ACCOUNT_TYPE.FIXED_ASSET,
+ ]}
+ />
+
+ )}
+
+ {/* --------- Statement --------- */}
+
+ {({ form, field, meta: { error, touched } }) => (
+ }
+ className={'form-group--description'}
+ inline={true}
+ >
+
+
+ )}
+
+
+ );
+}
+
+export default RefundVendorCreditFormFields;
diff --git a/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormProvider.js b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormProvider.js
new file mode 100644
index 000000000..0f97aa814
--- /dev/null
+++ b/src/containers/Dialogs/RefundVendorCreditDialog/RefundVendorCreditFormProvider.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { DialogContent } from 'components';
+import { pick } from 'lodash';
+
+import {
+ useAccounts,
+ useVendorCredit,
+ useCreateRefundVendorCredit,
+} from 'hooks/query';
+
+const RefundVendorCreditContext = React.createContext();
+
+function RefundVendorCreditFormProvider({
+ vendorCreditId,
+ dialogName,
+ ...props
+}) {
+ // Handle fetch accounts data.
+ const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
+
+ // Handle fetch vendor credit details.
+ const { data: vendorCredit, isLoading: isVendorCreditLoading } =
+ useVendorCredit(vendorCreditId, {
+ enabled: !!vendorCreditId,
+ });
+
+ // Create refund vendor credit mutations.
+ const { mutateAsync: createRefundVendorCreditMutate } =
+ useCreateRefundVendorCredit();
+
+ // State provider.
+ const provider = {
+ vendorCredit: {
+ ...pick(vendorCredit, ['id', 'credits_remaining', 'currency_code']),
+ amount: vendorCredit.credits_remaining,
+ },
+ accounts,
+ dialogName,
+ createRefundVendorCreditMutate,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useRefundVendorCreditContext = () =>
+ React.useContext(RefundVendorCreditContext);
+
+export { RefundVendorCreditFormProvider, useRefundVendorCreditContext };
diff --git a/src/containers/Dialogs/RefundVendorCreditDialog/index.js b/src/containers/Dialogs/RefundVendorCreditDialog/index.js
new file mode 100644
index 000000000..006dfe69f
--- /dev/null
+++ b/src/containers/Dialogs/RefundVendorCreditDialog/index.js
@@ -0,0 +1,38 @@
+import React from 'react';
+import { Dialog, DialogSuspense, FormattedMessage as T } from 'components';
+
+import withDialogRedux from 'components/DialogReduxConnect';
+import { compose } from 'redux';
+
+const RefundVendorCreditDialogContent = React.lazy(() =>
+ import('./RefundVendorCreditDialogContent'),
+);
+
+/**
+ * Refund vendor credit dialog.
+ */
+function RefundVendorCreditDialog({
+ dialogName,
+ payload: { vendorCreditId },
+ isOpen,
+}) {
+ return (
+ }
+ isOpen={isOpen}
+ canEscapeJeyClose={true}
+ autoFocus={true}
+ className={'dialog--refund-vendor-credit'}
+ >
+
+
+
+
+ );
+}
+
+export default compose(withDialogRedux())(RefundVendorCreditDialog);
diff --git a/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetail.js b/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetail.js
index 74f7e760b..2ca7747de 100644
--- a/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetail.js
+++ b/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetail.js
@@ -4,6 +4,8 @@ import intl from 'react-intl-universal';
import { DrawerMainTabs } from 'components';
import CreditNoteDetailPanel from './CreditNoteDetailPanel';
+import RefundCreditNoteTransactionsTable from './RefundCreditNoteTransactions/RefundCreditNoteTransactionsTable';
+import ReconcileCreditNoteTransactionsTable from './ReconcileCreditNoteTransactions/ReconcileCreditNoteTransactionsTable';
import clsx from 'classnames';
import CreditNoteDetailCls from '../../../style/components/Drawers/CreditNoteDetails.module.scss';
@@ -20,6 +22,16 @@ export default function CreditNoteDetail() {
id={'details'}
panel={}
/>
+ }
+ />
+ }
+ />
);
diff --git a/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailActionsBar.js b/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailActionsBar.js
index a7b92ae21..dbe27e0c1 100644
--- a/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailActionsBar.js
+++ b/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailActionsBar.js
@@ -15,7 +15,13 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions';
-import { Icon, FormattedMessage as T, MoreMenuItems, Can } from 'components';
+import {
+ Icon,
+ FormattedMessage as T,
+ If,
+ MoreMenuItems,
+ Can,
+} from 'components';
import { compose } from 'utils';
@@ -32,7 +38,7 @@ function CreditNoteDetailActionsBar({
// #withDrawerActions
closeDrawer,
}) {
- const { creditNoteId } = useCreditNoteDetailDrawerContext();
+ const { creditNoteId, creditNote } = useCreditNoteDetailDrawerContext();
const history = useHistory();
@@ -42,6 +48,10 @@ function CreditNoteDetailActionsBar({
closeDrawer('credit-note-detail-drawer');
};
+ const handleRefundCreditNote = () => {
+ openDialog('refund-credit-note', { creditNoteId });
+ };
+
// Handle delete credit note.
const handleDeleteCreditNote = () => {
openAlert('credit-note-delete', { creditNoteId });
@@ -57,6 +67,15 @@ function CreditNoteDetailActionsBar({
onClick={handleEditCreditNote}
/>
+
+ }
+ text={}
+ onClick={handleRefundCreditNote}
+ />
+
+
}
diff --git a/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailDrawerProvider.js b/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailDrawerProvider.js
index 1b0673d8c..8f3bd7326 100644
--- a/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailDrawerProvider.js
+++ b/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailDrawerProvider.js
@@ -1,6 +1,10 @@
import React from 'react';
import intl from 'react-intl-universal';
-import { useCreditNote } from 'hooks/query';
+import {
+ useCreditNote,
+ useRefundCreditNote,
+ useReconcileCreditNotes,
+} from 'hooks/query';
import { DrawerHeaderContent, DrawerLoading } from 'components';
const CreditNoteDetailDrawerContext = React.createContext();
@@ -17,13 +21,42 @@ function CreditNoteDetailDrawerProvider({ creditNoteId, ...props }) {
},
);
+ // Handle fetch refund credit note.
+ const {
+ data: refundCreditNote,
+ isFetching: isRefundCreditNoteFetching,
+ isLoading: isRefundCreditNoteLoading,
+ } = useRefundCreditNote(creditNoteId, {
+ enabled: !!creditNoteId,
+ });
+
+ // Handle fetch refund credit note.
+ const {
+ data: reconcileCreditNotes,
+ isFetching: isReconcileCreditNoteFetching,
+ isLoading: isReconcileCreditNoteLoading,
+ } = useReconcileCreditNotes(creditNoteId, {
+ enabled: !!creditNoteId,
+ });
+
const provider = {
creditNote,
+ refundCreditNote,
+ reconcileCreditNotes,
+
+ isRefundCreditNoteLoading,
+ isRefundCreditNoteFetching,
creditNoteId,
};
return (
-
+
{
+ openAlert('reconcile-credit-delete', { creditNoteId: id });
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default compose(withAlertsActions)(RefundCreditNoteTransactionsTable);
diff --git a/src/containers/Drawers/CreditNoteDetailDrawer/ReconcileCreditNoteTransactions/components.js b/src/containers/Drawers/CreditNoteDetailDrawer/ReconcileCreditNoteTransactions/components.js
new file mode 100644
index 000000000..e3bc36fe2
--- /dev/null
+++ b/src/containers/Drawers/CreditNoteDetailDrawer/ReconcileCreditNoteTransactions/components.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import { Intent, MenuItem, Menu } from '@blueprintjs/core';
+import intl from 'react-intl-universal';
+import { FormatDateCell, Icon } from 'components';
+import { safeCallback } from 'utils';
+
+/**
+ * Actions menu.
+ */
+export function ActionsMenu({ payload: { onDelete }, row: { original } }) {
+ return (
+
+ );
+}
+
+export function useReconcileCreditTransactionsTableColumns() {
+ return React.useMemo(
+ () => [
+ {
+ Header: intl.get('date'),
+ accessor: 'formatted_credit_note_date',
+ Cell: FormatDateCell,
+ width: 100,
+ className: 'date',
+ },
+ {
+ Header: intl.get('invoice_no'),
+ accessor: 'invoice_number',
+ width: 100,
+ className: 'invoice_number',
+ },
+ {
+ Header: intl.get('amount'),
+ accessor: 'formtted_amount',
+ width: 100,
+ className: 'amount',
+ align: 'right',
+ },
+ ],
+ [],
+ );
+}
diff --git a/src/containers/Drawers/CreditNoteDetailDrawer/RefundCreditNoteTransactions/RefundCreditNoteTransactionsTable.js b/src/containers/Drawers/CreditNoteDetailDrawer/RefundCreditNoteTransactions/RefundCreditNoteTransactionsTable.js
new file mode 100644
index 000000000..b192330f2
--- /dev/null
+++ b/src/containers/Drawers/CreditNoteDetailDrawer/RefundCreditNoteTransactions/RefundCreditNoteTransactionsTable.js
@@ -0,0 +1,48 @@
+import React from 'react';
+import { DataTable, Card } from 'components';
+
+import '../../../../style/pages/RefundCreditNote/List.scss';
+
+import withAlertsActions from 'containers/Alert/withAlertActions';
+
+import { useCreditNoteDetailDrawerContext } from '../CreditNoteDetailDrawerProvider';
+import {
+ useRefundCreditTransactionsTableColumns,
+ ActionsMenu,
+} from './components';
+
+import { compose } from 'utils';
+
+/**
+ * Refund credit note transactions table.
+ */
+function RefundCreditNoteTransactionsTable({
+ // #withAlertsActions
+ openAlert,
+}) {
+ const { refundCreditNote } = useCreditNoteDetailDrawerContext();
+
+ const columns = useRefundCreditTransactionsTableColumns();
+
+ // Handle delete refund credit.
+
+ const handleDeleteRefundCreditNote = ({ id }) => {
+ openAlert('refund-credit-delete', { creditNoteId: id });
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default compose(withAlertsActions)(RefundCreditNoteTransactionsTable);
diff --git a/src/containers/Drawers/CreditNoteDetailDrawer/RefundCreditNoteTransactions/components.js b/src/containers/Drawers/CreditNoteDetailDrawer/RefundCreditNoteTransactions/components.js
new file mode 100644
index 000000000..1d3c12a93
--- /dev/null
+++ b/src/containers/Drawers/CreditNoteDetailDrawer/RefundCreditNoteTransactions/components.js
@@ -0,0 +1,59 @@
+import React from 'react';
+import { Intent, MenuItem, Menu } from '@blueprintjs/core';
+import intl from 'react-intl-universal';
+import { FormatDateCell, Icon } from 'components';
+import { safeCallback } from 'utils';
+
+/**
+ * Actions menu.
+ */
+export function ActionsMenu({ payload: { onDelete }, row: { original } }) {
+ return (
+
+ );
+}
+
+export function useRefundCreditTransactionsTableColumns() {
+ return React.useMemo(
+ () => [
+ {
+ Header: intl.get('date'),
+ accessor: 'date',
+ Cell: FormatDateCell,
+ width: 100,
+ className: 'date',
+ },
+ {
+ Header: intl.get('refund_credit_transactions.column.amount_refunded'),
+ accessor: 'amount',
+ width: 100,
+ className: 'amount',
+ align: 'right',
+ },
+ {
+ Header: intl.get(
+ 'refund_credit_transactions.column.withdrawal_account',
+ ),
+ accessor: ({ from_account }) => from_account.name,
+ width: 100,
+ className: 'from_account',
+ },
+ {
+ id: 'reference_no',
+ Header: intl.get('reference_no'),
+ accessor: 'reference_no',
+ width: 100,
+ className: 'reference_no',
+ textOverview: true,
+ },
+ ],
+ [],
+ );
+}
diff --git a/src/containers/Drawers/VendorCreditDetailDrawer/RefundVendorCreditTransactions/RefundVendorCreditTransactionsTable.js b/src/containers/Drawers/VendorCreditDetailDrawer/RefundVendorCreditTransactions/RefundVendorCreditTransactionsTable.js
new file mode 100644
index 000000000..4c924f531
--- /dev/null
+++ b/src/containers/Drawers/VendorCreditDetailDrawer/RefundVendorCreditTransactions/RefundVendorCreditTransactionsTable.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import { DataTable, Card } from 'components';
+
+import 'style/pages/RefundVendorCredit/List.scss';
+
+import withAlertsActions from 'containers/Alert/withAlertActions';
+import { useVendorCreditDetailDrawerContext } from '../VendorCreditDetailDrawerProvider';
+import {
+ useRefundCreditTransactionsTableColumns,
+ ActionsMenu,
+} from './components';
+
+import { compose } from 'utils';
+
+/**
+ * Refund vendor transactions table.
+ */
+function RefundVendorCreditTransactionsTable({
+ // #withAlertsActions
+ openAlert,
+}) {
+ const { refundVendorCredit } = useVendorCreditDetailDrawerContext();
+
+ const columns = useRefundCreditTransactionsTableColumns();
+
+ // Handle delete refund vendor credit.
+ const handleDeleteRefundVendorCredit = ({ id }) => {
+ openAlert('refund-vendor-delete', { vendorCreditId: id });
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default compose(withAlertsActions)(RefundVendorCreditTransactionsTable);
diff --git a/src/containers/Drawers/VendorCreditDetailDrawer/RefundVendorCreditTransactions/components.js b/src/containers/Drawers/VendorCreditDetailDrawer/RefundVendorCreditTransactions/components.js
new file mode 100644
index 000000000..2eb0dc9d2
--- /dev/null
+++ b/src/containers/Drawers/VendorCreditDetailDrawer/RefundVendorCreditTransactions/components.js
@@ -0,0 +1,57 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { Intent, MenuItem, Menu } from '@blueprintjs/core';
+import { FormatDateCell, Icon } from 'components';
+import { safeCallback } from 'utils';
+
+/**
+ * Actions menu.
+ */
+export function ActionsMenu({ payload: { onDelete }, row: { original } }) {
+ return (
+
+ );
+}
+
+export function useRefundCreditTransactionsTableColumns() {
+ return React.useMemo(
+ () => [
+ {
+ Header: intl.get('date'),
+ accessor: 'date',
+ Cell: FormatDateCell,
+ width: 100,
+ className: 'date',
+ },
+ {
+ Header: intl.get('refund_vendor_credit.column.amount'),
+ accessor: 'amount',
+ width: 100,
+ className: 'amount',
+ align: 'right',
+ },
+ {
+ Header: intl.get('refund_vendor_credit.column.withdrawal_account'),
+ accessor: ({ from_account }) => from_account.name,
+ width: 100,
+ className: 'from_account',
+ },
+ {
+ id: 'reference_no',
+ Header: intl.get('reference_no'),
+ accessor: 'reference_no',
+ width: 100,
+ className: 'reference_no',
+ textOverview: true,
+ },
+ ],
+ [],
+ );
+}
diff --git a/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetail.js b/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetail.js
index e603c667f..cbc825676 100644
--- a/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetail.js
+++ b/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetail.js
@@ -4,6 +4,7 @@ import intl from 'react-intl-universal';
import { DrawerMainTabs } from 'components';
import VendorCreditDetailPanel from './VendorCreditDetailPanel';
+import RefundVendorCreditTransactionsTable from './RefundVendorCreditTransactions/RefundVendorCreditTransactionsTable';
import clsx from 'classnames';
import VendorCreditDetailCls from '../../../style/components/Drawers/VendorCreditDetail.module.scss';
@@ -20,6 +21,11 @@ export default function VendorCreditDetail() {
id={'details'}
panel={}
/>
+ }
+ />
);
diff --git a/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailActionsBar.js b/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailActionsBar.js
index 9c720ea52..d51ab8a92 100644
--- a/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailActionsBar.js
+++ b/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailActionsBar.js
@@ -15,7 +15,7 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions';
-import { Icon, FormattedMessage as T, Can } from 'components';
+import { If, Icon, FormattedMessage as T, Can } from 'components';
import { compose } from 'utils';
@@ -32,7 +32,7 @@ function VendorCreditDetailActionsBar({
// #withDrawerActions
closeDrawer,
}) {
- const { vendorCreditId } = useVendorCreditDetailDrawerContext();
+ const { vendorCreditId, vendorCredit } = useVendorCreditDetailDrawerContext();
const history = useHistory();
@@ -47,6 +47,10 @@ function VendorCreditDetailActionsBar({
openAlert('vendor-credit-delete', { vendorCreditId });
};
+ const handleRefundVendorCredit = () => {
+ openDialog('refund-vendor-credit', { vendorCreditId });
+ };
+
return (
@@ -57,6 +61,15 @@ function VendorCreditDetailActionsBar({
onClick={handleEditVendorCredit}
/>
+
+ }
+ text={}
+ onClick={handleRefundVendorCredit}
+ />
+
+
}
diff --git a/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailDrawerProvider.js b/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailDrawerProvider.js
index 74236d87c..eb15c6d20 100644
--- a/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailDrawerProvider.js
+++ b/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailDrawerProvider.js
@@ -1,6 +1,6 @@
import React from 'react';
import intl from 'react-intl-universal';
-import { useVendorCredit } from 'hooks/query';
+import { useVendorCredit, useRefundVendorCredit } from 'hooks/query';
import { DrawerHeaderContent, DrawerLoading } from 'components';
const VendorCreditDetailDrawerContext = React.createContext();
@@ -15,13 +15,27 @@ function VendorCreditDetailDrawerProvider({ vendorCreditId, ...props }) {
enabled: !!vendorCreditId,
});
+ // Handle fetch refund credit note.
+ const {
+ data: refundVendorCredit,
+ isFetching: isRefundVendorCreditFetching,
+ isLoading: isRefundVendorCreditLoading,
+ } = useRefundVendorCredit(vendorCreditId, {
+ enabled: !!vendorCreditId,
+ });
+
const provider = {
vendorCredit,
+ refundVendorCredit,
+ isRefundVendorCreditLoading,
+ isRefundVendorCreditFetching,
vendorCreditId,
};
return (
-
+
{
- setSubmitPayload({ redirect: false, status: true, resetForm: true });
+ // Handle submit as open button click.
+ const handleSubmitOpenBtnClick = (event) => {
+ setSubmitPayload({ redirect: true, open: true });
submitForm();
};
- // Handle submit as save & continue editing button click.
- const handleSubmitSaveContinueEditingBtnClick = (event) => {
- setSubmitPayload({ redirect: false, status: true });
+ // Handle submit, open and anothe new button click.
+ const handleSubmitOpenAndNewBtnClick = (event) => {
+ setSubmitPayload({ redirect: false, open: true, resetForm: true });
submitForm();
};
+ // Handle submit as open & continue editing button click.
+ const handleSubmitOpenContinueEditingBtnClick = (event) => {
+ setSubmitPayload({ redirect: false, open: true });
+ submitForm();
+ };
// Handle submit as draft button click.
const handleSubmitDraftBtnClick = (event) => {
- setSubmitPayload({ redirect: true, status: false });
+ setSubmitPayload({ redirect: true, open: false });
submitForm();
};
// handle submit as draft & new button click.
const handleSubmitDraftAndNewBtnClick = (event) => {
- setSubmitPayload({ redirect: false, status: false, resetForm: true });
+ setSubmitPayload({ redirect: false, open: false, resetForm: true });
submitForm();
};
// Handle submit as draft & continue editing button click.
const handleSubmitDraftContinueEditingBtnClick = (event) => {
- setSubmitPayload({ redirect: false, status: false });
+ setSubmitPayload({ redirect: false, open: false });
submitForm();
};
@@ -63,89 +68,113 @@ export default function VendorCreditNoteFloatingActions() {
history.goBack();
};
- // Handle submit button click.
- const handleSubmitBtnClick = (event) => {
- setSubmitPayload({ redirect: true });
- submitForm();
- };
-
const handleClearBtnClick = (event) => {
resetForm();
};
-
return (
- {/* ----------- Save ----------- */}
-
- }
- />
-
-
- }
- onClick={handleSubmitAndNewBtnClick}
- />
- }
- onClick={handleSubmitSaveContinueEditingBtnClick}
- />
-
- }
- minimal={true}
- interactionKind={PopoverInteractionKind.CLICK}
- position={Position.BOTTOM_LEFT}
- >
+ {/* ----------- Save And Open ----------- */}
+
+
}
+ onClick={handleSubmitOpenBtnClick}
+ text={}
/>
-
-
- {/* ----------- Save As Draft ----------- */}
-
- }
- />
-
- }
- onClick={handleSubmitDraftAndNewBtnClick}
- />
- }
- onClick={handleSubmitDraftContinueEditingBtnClick}
- />
-
- }
- minimal={true}
- interactionKind={PopoverInteractionKind.CLICK}
- position={Position.BOTTOM_LEFT}
- >
+
+ }
+ onClick={handleSubmitOpenAndNewBtnClick}
+ />
+ }
+ onClick={handleSubmitOpenContinueEditingBtnClick}
+ />
+
+ }
+ minimal={true}
+ interactionKind={PopoverInteractionKind.CLICK}
+ position={Position.BOTTOM_LEFT}
+ >
+ }
+ />
+
+
+ {/* ----------- Save As Draft ----------- */}
+
}
+ className={'ml1'}
+ onClick={handleSubmitDraftBtnClick}
+ text={}
/>
-
-
+
+ }
+ onClick={handleSubmitDraftAndNewBtnClick}
+ />
+ }
+ onClick={handleSubmitDraftContinueEditingBtnClick}
+ />
+
+ }
+ minimal={true}
+ interactionKind={PopoverInteractionKind.CLICK}
+ position={Position.BOTTOM_LEFT}
+ >
+ }
+ />
+
+
+
+ {/* ----------- Save and New ----------- */}
+
+
+ }
+ />
+
+ }
+ onClick={handleSubmitOpenAndNewBtnClick}
+ />
+
+ }
+ minimal={true}
+ interactionKind={PopoverInteractionKind.CLICK}
+ position={Position.BOTTOM_LEFT}
+ >
+ }
+ />
+
+
+
{/* ----------- Clear & Reset----------- */}
:
}
+ text={vendorCredit ?
:
}
/>
{/* ----------- Cancel ----------- */}
+ );
+}
+
/**
* Retrieve vendors credit note table columns.
*/
@@ -98,8 +139,8 @@ export function useVendorsCreditNoteTableColumns() {
{
id: 'status',
Header: intl.get('status'),
- // accessor:
- width: 120, // 160
+ accessor: StatusAccessor,
+ width: 160,
className: 'status',
clickable: true,
},
diff --git a/src/containers/Purchases/CreditNotes/VendorCreditNotesAlerts.js b/src/containers/Purchases/CreditNotes/VendorCreditNotesAlerts.js
index fb0b1b3ff..9b4ecceba 100644
--- a/src/containers/Purchases/CreditNotes/VendorCreditNotesAlerts.js
+++ b/src/containers/Purchases/CreditNotes/VendorCreditNotesAlerts.js
@@ -4,6 +4,14 @@ const VendorCreditDeleteAlert = React.lazy(() =>
import('../../Alerts/VendorCeditNotes/VendorCreditDeleteAlert'),
);
+const RefundVendorCreditDeleteAlert = React.lazy(() =>
+ import('../../Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert'),
+);
+
+const OpenVendorCreditAlert = React.lazy(() =>
+ import('../../Alerts/VendorCeditNotes/VendorCreditOpenedAlert'),
+);
+
/**
* Vendor Credit notes alerts.
*/
@@ -12,4 +20,12 @@ export default [
name: 'vendor-credit-delete',
component: VendorCreditDeleteAlert,
},
+ {
+ name: 'vendor-credit-open',
+ component: OpenVendorCreditAlert,
+ },
+ {
+ name: 'refund-vendor-delete',
+ component: RefundVendorCreditDeleteAlert,
+ },
];
diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFloatingActions.js b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFloatingActions.js
index 727643a7b..db59adfe5 100644
--- a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFloatingActions.js
+++ b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFloatingActions.js
@@ -26,35 +26,40 @@ export default function CreditNoteFloatingActions() {
const { resetForm, submitForm, isSubmitting } = useFormikContext();
// Credit note form context.
- const { setSubmitPayload, isNewMode } = useCreditNoteFormContext();
+ const { setSubmitPayload, creditNote } = useCreditNoteFormContext();
- // Handle submit, save and anothe new button click.
- const handleSubmitAndNewBtnClick = (event) => {
- setSubmitPayload({ redirect: false, status: true, resetForm: true });
+ // Handle submit as open button click.
+ const handleSubmitOpenBtnClick = (event) => {
+ setSubmitPayload({ redirect: true, open: true });
submitForm();
};
- // Handle submit as save & continue editing button click.
- const handleSubmitSaveContinueEditingBtnClick = (event) => {
- setSubmitPayload({ redirect: false, status: true });
+ // Handle submit, open and anothe new button click.
+ const handleSubmitOpenAndNewBtnClick = (event) => {
+ setSubmitPayload({ redirect: false, open: true, resetForm: true });
submitForm();
};
+ // Handle submit as open & continue editing button click.
+ const handleSubmitOpenContinueEditingBtnClick = (event) => {
+ setSubmitPayload({ redirect: false, open: true });
+ submitForm();
+ };
// Handle submit as draft button click.
const handleSubmitDraftBtnClick = (event) => {
- setSubmitPayload({ redirect: true, status: false });
+ setSubmitPayload({ redirect: true, open: false });
submitForm();
};
// handle submit as draft & new button click.
const handleSubmitDraftAndNewBtnClick = (event) => {
- setSubmitPayload({ redirect: false, status: false, resetForm: true });
+ setSubmitPayload({ redirect: false, open: false, resetForm: true });
submitForm();
};
// Handle submit as draft & continue editing button click.
const handleSubmitDraftContinueEditingBtnClick = (event) => {
- setSubmitPayload({ redirect: false, status: false });
+ setSubmitPayload({ redirect: false, open: false });
submitForm();
};
@@ -63,89 +68,114 @@ export default function CreditNoteFloatingActions() {
history.goBack();
};
- // Handle submit button click.
- const handleSubmitBtnClick = (event) => {
- setSubmitPayload({ redirect: true });
- submitForm();
- };
-
const handleClearBtnClick = (event) => {
resetForm();
};
return (
- {/* ----------- Save ----------- */}
-
- }
- />
-
-
- }
- onClick={handleSubmitAndNewBtnClick}
- />
- }
- onClick={handleSubmitSaveContinueEditingBtnClick}
- />
-
- }
- minimal={true}
- interactionKind={PopoverInteractionKind.CLICK}
- position={Position.BOTTOM_LEFT}
- >
+ {/* ----------- Save And Open ----------- */}
+
+
}
+ onClick={handleSubmitOpenBtnClick}
+ text={}
/>
-
-
- {/* ----------- Save As Draft ----------- */}
-
- }
- />
-
- }
- onClick={handleSubmitDraftAndNewBtnClick}
- />
- }
- onClick={handleSubmitDraftContinueEditingBtnClick}
- />
-
- }
- minimal={true}
- interactionKind={PopoverInteractionKind.CLICK}
- position={Position.BOTTOM_LEFT}
- >
+
+ }
+ onClick={handleSubmitOpenAndNewBtnClick}
+ />
+ }
+ onClick={handleSubmitOpenContinueEditingBtnClick}
+ />
+
+ }
+ minimal={true}
+ interactionKind={PopoverInteractionKind.CLICK}
+ position={Position.BOTTOM_LEFT}
+ >
+ }
+ />
+
+
+ {/* ----------- Save As Draft ----------- */}
+
}
+ className={'ml1'}
+ onClick={handleSubmitDraftBtnClick}
+ text={}
/>
-
-
+
+ }
+ onClick={handleSubmitDraftAndNewBtnClick}
+ />
+ }
+ onClick={handleSubmitDraftContinueEditingBtnClick}
+ />
+
+ }
+ minimal={true}
+ interactionKind={PopoverInteractionKind.CLICK}
+ position={Position.BOTTOM_LEFT}
+ >
+ }
+ />
+
+
+
+ {/* ----------- Save and New ----------- */}
+
+
+ }
+ />
+
+ }
+ onClick={handleSubmitOpenAndNewBtnClick}
+ />
+
+ }
+ minimal={true}
+ interactionKind={PopoverInteractionKind.CLICK}
+ position={Position.BOTTOM_LEFT}
+ >
+ }
+ />
+
+
+
{/* ----------- Clear & Reset----------- */}
:
}
+ text={creditNote ?
:
}
/>
{/* ----------- Cancel ----------- */}
+ );
+}
+
/**
* Retrieve credit note table columns.
*/
@@ -95,8 +136,8 @@ export function useCreditNoteTableColumns() {
{
id: 'status',
Header: intl.get('status'),
- // accessor:
- width: 120, // 160
+ accessor: StatusAccessor,
+ width: 160, // 160
className: 'status',
clickable: true,
},
diff --git a/src/containers/Sales/CreditNotes/CreditNotesLanding/utils.js b/src/containers/Sales/CreditNotes/CreditNotesLanding/utils.js
new file mode 100644
index 000000000..9d94edad5
--- /dev/null
+++ b/src/containers/Sales/CreditNotes/CreditNotesLanding/utils.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import intl from 'react-intl-universal';
+import { Intent } from '@blueprintjs/core';
+import { AppToaster } from 'components';
+
+export const handleDeleteErrors = (errors) => {
+ if (
+ errors.find((error) => error.type === 'CREDIT_NOTE_HAS_APPLIED_INVOICES')
+ ) {
+ AppToaster.show({
+ message: intl.get(
+ 'credit_note.error.you_couldn_t_delete_credit_note_that_has_associated_invoice',
+ ),
+ intent: Intent.DANGER,
+ });
+ }
+ if (
+ errors.find(
+ (error) => error.type === 'CREDIT_NOTE_HAS_REFUNDS_TRANSACTIONS',
+ )
+ ) {
+ AppToaster.show({
+ message: intl.get(
+ 'credit_note.error.you_couldn_t_delete_credit_note_that_has_associated_refund',
+ ),
+ intent: Intent.DANGER,
+ });
+ }
+};
diff --git a/src/containers/Sales/Invoices/InvoicesLanding/components.js b/src/containers/Sales/Invoices/InvoicesLanding/components.js
index 68e68ae89..35d373136 100644
--- a/src/containers/Sales/Invoices/InvoicesLanding/components.js
+++ b/src/containers/Sales/Invoices/InvoicesLanding/components.js
@@ -99,6 +99,18 @@ export const handleDeleteErrors = (errors) => {
intent: Intent.DANGER,
});
}
+ if (
+ errors.find(
+ (error) => error.type === 'SALE_INVOICE_HAS_APPLIED_TO_CREDIT_NOTES',
+ )
+ ) {
+ AppToaster.show({
+ message: intl.get(
+ 'invoices.error.you_couldn_t_delete_sale_invoice_that_has_reconciled',
+ ),
+ intent: Intent.DANGER,
+ });
+ }
};
export function ActionsMenu({
diff --git a/src/hooks/query/creditNote.js b/src/hooks/query/creditNote.js
index ee3f8f61e..9b05391c9 100644
--- a/src/hooks/query/creditNote.js
+++ b/src/hooks/query/creditNote.js
@@ -24,6 +24,13 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate settings.
queryClient.invalidateQueries([t.SETTING, t.SETTING_CREDIT_NOTES]);
+ // Invalidate refund credit
+ queryClient.invalidateQueries(t.REFUND_CREDIT_NOTE);
+
+ // Invalidate reconcile.
+ queryClient.invalidateQueries(t.RECONCILE_CREDIT_NOTE);
+ queryClient.invalidateQueries(t.RECONCILE_CREDIT_NOTES);
+
// Invalidate financial reports.
queryClient.invalidateQueries(t.FINANCIAL_REPORT);
};
@@ -143,3 +150,169 @@ export function useRefreshCreditNotes() {
},
};
}
+
+/**
+ * Create Round creidt note
+ */
+export function useCreateRefundCreditNote(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ ([id, values]) =>
+ apiRequest.post(`sales/credit_notes/${id}/refund`, values),
+ {
+ onSuccess: (res, [id, values]) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate credit note query.
+ queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Delete the given refund credit note.
+ */
+export function useDeleteRefundCreditNote(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ (id) => apiRequest.delete(`sales/credit_notes/refunds/${id}`),
+ {
+ onSuccess: (res, id) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate vendor credit query.
+ queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve refund credit note detail of the given id.
+ * @param {number} id
+ *
+ */
+export function useRefundCreditNote(id, props, requestProps) {
+ return useRequestQuery(
+ [t.REFUND_CREDIT_NOTE, id],
+ { method: 'get', url: `sales/credit_notes/${id}/refund`, ...requestProps },
+ {
+ select: (res) => res.data.data,
+ defaultData: {},
+ ...props,
+ },
+ );
+}
+
+/**
+ * Mark the given credit note as opened.
+ */
+export function useOpenCreditNote(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation((id) => apiRequest.post(`sales/credit_notes/${id}/open`), {
+ onSuccess: (res, id) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate specific
+ queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
+ },
+ ...props,
+ });
+}
+
+/**
+ * Retrieve reconcile credit note of the given id.
+ * @param {number} id
+ *
+ */
+export function useReconcileCreditNote(id, props, requestProps) {
+ return useRequestQuery(
+ [t.RECONCILE_CREDIT_NOTE, id],
+ {
+ method: 'get',
+ url: `sales/credit_notes/${id}/apply-to-invoices`,
+ ...requestProps,
+ },
+ {
+ select: (res) => res.data.data,
+ defaultData: [],
+ ...props,
+ },
+ );
+}
+
+/**
+ * Create Reconcile credit note.
+ */
+export function useCreateReconcileCreditNote(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ ([id, values]) =>
+ apiRequest.post(`sales/credit_notes/${id}/apply-to-invoices`, values),
+ {
+ onSuccess: (res, [id, values]) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate credit note query.
+ queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve reconcile credit notes.
+ */
+export function useReconcileCreditNotes(id, props, requestProps) {
+ return useRequestQuery(
+ [t.RECONCILE_CREDIT_NOTES, id],
+ {
+ method: 'get',
+ url: `sales/credit_notes/${id}/applied-invoices`,
+ ...requestProps,
+ },
+ {
+ select: (res) => res.data.data,
+ defaultData: {},
+ ...props,
+ },
+ );
+}
+
+/**
+ * Delete the given reconcile credit note.
+ */
+export function useDeleteReconcileCredit(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ (id) => apiRequest.delete(`sales/credit_notes/applied-to-invoices/${id}`),
+ {
+ onSuccess: (res, id) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate vendor credit query.
+ queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
+ },
+ ...props,
+ },
+ );
+}
diff --git a/src/hooks/query/types.js b/src/hooks/query/types.js
index 374c6540c..23e921237 100644
--- a/src/hooks/query/types.js
+++ b/src/hooks/query/types.js
@@ -111,11 +111,17 @@ const ROLES = {
const CREDIT_NOTES = {
CREDIT_NOTE: 'CREDIT_NOTE',
CREDIT_NOTES: 'CREDIT_NOTES',
+ REFUND_CREDIT_NOTE: 'REFUND_CREDIT_NOTE',
+ RECONCILE_CREDIT_NOTE: 'RECONCILE_CREDIT_NOTE',
+ RECONCILE_CREDIT_NOTES: 'RECONCILE_CREDIT_NOTES',
};
const VENDOR_CREDIT_NOTES = {
VENDOR_CREDITS: 'VENDOR_CREDITS',
VENDOR_CREDIT: 'VENDOR_CREDIT',
+ REFUND_VENDOR_CREDIT: 'REFUND_VENDOR_CREDIT',
+ RECONCILE_VENDOR_CREDIT: 'RECONCILE_VENDOR_CREDIT',
+ RECONCILE_VENDOR_CREDITS: 'RECONCILE_VENDOR_CREDITS',
};
const SETTING = {
diff --git a/src/hooks/query/vendorCredit.js b/src/hooks/query/vendorCredit.js
index d4b5206f6..45fddee27 100644
--- a/src/hooks/query/vendorCredit.js
+++ b/src/hooks/query/vendorCredit.js
@@ -24,6 +24,13 @@ const commonInvalidateQueries = (queryClient) => {
// Invalidate settings.
queryClient.invalidateQueries([t.SETTING, t.SETTING_VENDOR_CREDITS]);
+ // Invalidate refund vendor credit
+ queryClient.invalidateQueries(t.REFUND_VENDOR_CREDIT);
+
+ // Invalidate reconcile vendor credit.
+ queryClient.invalidateQueries(t.RECONCILE_VENDOR_CREDIT);
+ queryClient.invalidateQueries(t.RECONCILE_VENDOR_CREDITS);
+
// Invalidate financial reports.
queryClient.invalidateQueries(t.FINANCIAL_REPORT);
};
@@ -150,3 +157,175 @@ export function useRefreshVendorCredits() {
},
};
}
+
+/**
+ * Create Round vendor creidt
+ */
+export function useCreateRefundVendorCredit(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ ([id, values]) =>
+ apiRequest.post(`purchases/vendor-credit/${id}/refund`, values),
+ {
+ onSuccess: (res, [id, values]) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate credit note query.
+ queryClient.invalidateQueries([t.VENDOR_CREDIT, id]);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Delete the given refund vendor credit.
+ */
+export function useDeleteRefundVendorCredit(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ (id) => apiRequest.delete(`purchases/vendor-credit/refunds/${id}`),
+ {
+ onSuccess: (res, id) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate vendor credit query.
+ queryClient.invalidateQueries([t.CREDIT_NOTE, id]);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve refund credit note detail of the given id.
+ * @param {number} id
+ *
+ */
+export function useRefundVendorCredit(id, props, requestProps) {
+ return useRequestQuery(
+ [t.REFUND_VENDOR_CREDIT, id],
+ {
+ method: 'get',
+ url: `purchases/vendor-credit/${id}/refund`,
+ ...requestProps,
+ },
+ {
+ select: (res) => res.data.data,
+ defaultData: {},
+ ...props,
+ },
+ );
+}
+
+/**
+ * Mark the given vendor credit as opened.
+ */
+export function useOpenVendorCredit(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ (id) => apiRequest.post(`purchases/vendor-credit/${id}/open`),
+ {
+ onSuccess: (res, id) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate specific.
+ queryClient.invalidateQueries([t.VENDOR_CREDIT, id]);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Create Reconcile vendor credit.
+ */
+export function useCreateReconcileVendorCredit(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ ([id, values]) =>
+ apiRequest.post(`purchases/vendor-credit/${id}/apply-to-bills`, values),
+ {
+ onSuccess: (res, [id, values]) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate credit note query.
+ queryClient.invalidateQueries([t.VENDOR_CREDIT, id]);
+ },
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve reconcile vendor credit of the given id.
+ * @param {number} id
+ *
+ */
+export function useReconcileVendorCredit(id, props, requestProps) {
+ return useRequestQuery(
+ [t.RECONCILE_VENDOR_CREDIT, id],
+ {
+ method: 'get',
+ url: `purchases/vendor-credit/${id}/apply-to-bills`,
+ ...requestProps,
+ },
+ {
+ select: (res) => res.data.data,
+ defaultData: [],
+ ...props,
+ },
+ );
+}
+
+/**
+ * Retrieve reconcile credit notes.
+ */
+export function useReconcileVendorCredits(id, props, requestProps) {
+ return useRequestQuery(
+ [t.RECONCILE_VENDOR_CREDITS, id],
+ {
+ method: 'get',
+ url: `purchases/vendor-credit/${id}/applied-bills`,
+ ...requestProps,
+ },
+ {
+ select: (res) => res.data.data,
+ defaultData: {},
+ ...props,
+ },
+ );
+}
+/**
+ * Delete the given reconcile vendor credit.
+ */
+export function useDeleteReconcileVendorCredit(props) {
+ const queryClient = useQueryClient();
+ const apiRequest = useApiRequest();
+
+ return useMutation(
+ (id) => apiRequest.delete(`purchases/vendor-credit/applied-to-bills/${id}`),
+ {
+ onSuccess: (res, id) => {
+ // Common invalidate queries.
+ commonInvalidateQueries(queryClient);
+
+ // Invalidate vendor credit query.
+ queryClient.invalidateQueries([t.VENDOR_CREDIT, id]);
+ },
+ ...props,
+ },
+ );
+}
diff --git a/src/lang/en/index.json b/src/lang/en/index.json
index 3c7434950..f556c159e 100644
--- a/src/lang/en/index.json
+++ b/src/lang/en/index.json
@@ -1493,6 +1493,7 @@
"credit_note.label.new_credit_note": "New Credit Note",
"credit_note.label.edit_credit_note": "Edit Credit Note",
"credit_note.action.edit_credit_note": "Edit Credit note",
+ "credit_note.action.refund_credit_note": "Refund Credit note",
"credit_note.action.delete_credit_note": "Delete Credit note",
"credit_note.column.credit_note_no": "Credit Note #",
"credit_note.column.credit_date": "Credit Date",
@@ -1512,6 +1513,7 @@
"vendor_credits.column.vendor_credit_no": "Vendor Credit #",
"vendor_credits.action.new_vendor_credit": "New Vendor Credit",
"vendor_credits.action.edit_vendor_credit": "Edit Vendot Credit",
+ "vendor_credits.action.refund_vendor_credit": "Refund Vendot Credit",
"vendor_credits.action.delete_vendor_credit": "Delete Vendot Credit",
"vendor_credits.success_message": "The vendor credit has been created successfully",
"vendor_credits.edit_success_message": "The vendor credit has been edited successfully.",
@@ -1525,18 +1527,65 @@
"vendor_credit.auto_increment.auto": "Your vendor credit numbers are set on auto-increment mode. Are you sure changing this setting?",
"vendor_credit.auto_increment.manually": "Your vendor credit numbers are set on manual mode. Are you sure chaning this settings?",
"setting_your_auto_generated_vendor_credit_number": "Setting your auto-generated vendor credit number",
- "credit_note.drawer_credit_note_detail":"Credit Note details",
- "credit_note.drawer.label_credit_note_no":"Credit Note #",
- "credit_note.drawer.label_credit_note_date":"Credit Date",
- "credit_note.drawer.label_create_at":"Create at",
+ "credit_note.drawer_credit_note_detail": "Credit Note details",
+ "credit_note.drawer.label_credit_note_no": "Credit Note #",
+ "credit_note.drawer.label_credit_note_date": "Credit Date",
+ "credit_note.drawer.label_create_at": "Create at",
"credit_note.drawer.label_total": "TOTAL",
"credit_note.drawer.label_subtotal": "Subtotal",
- "vendor_credit.drawer_vendor_credit_detail":"Vendor Credit details",
- "vendor_credit.drawer.label_vendor_credit_no":"Vendor Credit #",
- "vendor_credit.drawer.label_vendor_credit_date":"Vendor Credit Date",
- "vendor_credit.drawer.label_create_at":"Create at",
+ "credit_note.drawer.label_refund_transactions": "Refund transactions",
+ "credit_note.drawer.label_reconcile_transactions": "Reconcile transactions",
+ "vendor_credit.drawer_vendor_credit_detail": "Vendor Credit details",
+ "vendor_credit.drawer.label_vendor_credit_no": "Vendor Credit #",
+ "vendor_credit.drawer.label_vendor_credit_date": "Vendor Credit Date",
+ "vendor_credit.drawer.label_create_at": "Create at",
"vendor_credit.drawer.label_total": "TOTAL",
"vendor_credit.drawer.label_subtotal": "Subtotal",
- "landed_cost.dialog.label_select_transaction":"Select transaction",
- "landed_cost.dialog.label_select_transaction_entry":"Select transaction entry"
+ "landed_cost.dialog.label_select_transaction": "Select transaction",
+ "landed_cost.dialog.label_select_transaction_entry": "Select transaction entry",
+ "refund_credit_note.dialog.label": "Refund Credit Note",
+ "refund_credit_note.dialog.success_message": "The customer credit note refund has been created successfully.",
+ "refund_credit_note.dialog.refund_date": "Refund date",
+ "refund_credit_note.dialog.amount": "Amount",
+ "refund_credit_note.dialog.description": "Description",
+ "refund_credit_note.dialog.form_account": "Form account",
+ "refund_vendor_credit.dialog.label": "Refund Vendor Credit",
+ "refund_vendor_credit.dialog.success_message": "The vendor credit refund has been created successfully.",
+ "refund_vendor_credit.dialog.refund_date": "Refund date",
+ "refund_vendor_credit.dialog.amount": "Amount",
+ "refund_vendor_credit.dialog.description": "Description",
+ "refund_vendor_credit.dialog.deposit_to_account": "Deposit to account",
+ "refund_credit_transactions.column.amount_refunded": "Amount refunded",
+ "refund_credit_transactions.column.withdrawal_account": "Withdrawal account",
+ "refund_credit_transactions.alert.delete_message": "The credit note refund has been deleted successfully.",
+ "refund_credit_transactions.once_your_delete_this_refund_credit_note": "Once your delete this refund credit note, you won't be able to restore it later, Are your sure you want to delete this transaction?",
+ "refund_vendor_credit.column.amount": "Amount refunded",
+ "refund_vendor_credit.column.withdrawal_account": "Withdrawal account",
+ "refund_vendor_credit_transactions.alert.delete_message": "The vendor credit refund has been deleted successfully.",
+ "refund_vendor_credit_transactions.once_your_delete_this_refund_vendor_credit": "Once your delete this refund vendor credit note, you won't be able to restore it later, Are your sure you want to delete this transaction?",
+ "refund": "Refund",
+ "credit_note_opened.alert.success_message": "The credit note has been opened successfully",
+ "credit_note_opened.are_sure_to_open_this_credit": "Are you sure you want to open this credit note?",
+ "vendor_credit_opened.alert.success_message": "The vendor credit has been opened successfully",
+ "vendor_credit_opened.are_sure_to_open_this_credit": "Are you sure you want to open this vendor credit?",
+ "reconcile_credit_note.label": "Reconcile Credit Note With Invoices",
+ "reconcile_credit_note.dialog.total_amount_to_credit": "Total amount to credit",
+ "reconcile_credit_note.dialog.remaining_credits": "Remaining credits",
+ "reconcile_credit_note.column.remaining_amount": "Remaining amount",
+ "reconcile_credit_note.column.amount_to_credit": "Amount to credit",
+ "reconcile_credit_note.success_message": "The credit note has been applied the given invoices successfully.",
+ "reconcile_credit_note.alert.there_is_no_open_sale_invoices": "There is no open sale invoices associated to credit note customer.",
+ "reconcile_credit_note.alert.success_message": "The applied credit to invoices has been deleted successfully.",
+ "reconcile_credit_note.once_you_delete_this_reconcile_credit_note": "Once you delete this reconcile credit note, you won't be able to restore it later. Are you sure you want to delete this reconcile credit note?",
+ "credit_note.error.you_couldn_t_delete_credit_note_that_has_associated_refund": "You couldn't delete credit note that has associated refund transactions.",
+ "credit_note.error.you_couldn_t_delete_credit_note_that_has_associated_invoice": "You couldn't delete credit note that has associated invoice reconcile transactions.",
+ "invoices.error.you_couldn_t_delete_sale_invoice_that_has_reconciled": "You couldn't delete sale invoice that has reconciled with credit note transaction.",
+ "reconcile_vendor_credit.dialog.label": "Reconcile Credit Note with Bills",
+ "reconcile_vendor_credit.dialog.success_message": "The vendor credit has been applied to the given bills successfully",
+ "reconcile_vendor_credit.alert.there_is_no_open_bills":"There is no open bills associated to credit note vendor.",
+ "reconcile_vendor_credit.dialog.total_amount_to_credit":"Total amount to credit",
+ "reconcile_vendor_credit.dialog.remaining_credits":"Remaining amount",
+ "reconcile_vendor_credit.column.bill_number":"Bill #",
+ "reconcile_vendor_credit.column.remaining_amount":"Remaining amount",
+ "reconcile_vendor_credit.column.amount_to_credit":"Amount to credit"
}
\ No newline at end of file
diff --git a/src/style/pages/ReconcileCreditNote/ReconcileCreditNoteForm.scss b/src/style/pages/ReconcileCreditNote/ReconcileCreditNoteForm.scss
new file mode 100644
index 000000000..a87f6fa0b
--- /dev/null
+++ b/src/style/pages/ReconcileCreditNote/ReconcileCreditNoteForm.scss
@@ -0,0 +1,71 @@
+.dialog--reconcile-credit-form {
+ width: 800px;
+
+ .bp3-dialog-body {
+ .footer {
+ display: flex;
+ margin-top: 40px;
+
+ .total_lines {
+ margin-left: auto;
+
+ &_line {
+ border-bottom: none;
+ .title {
+ font-weight: 600;
+ }
+ .amount,
+ .title {
+ padding: 8px 0px;
+ width: 165px;
+ }
+ .amount {
+ text-align: right;
+ }
+ }
+ }
+ }
+ }
+ .bigcapital-datatable {
+ .table {
+ border: 1px solid #d1dee2;
+ min-width: auto;
+
+ .tbody,
+ .tbody-inner {
+ height: auto;
+ scrollbar-width: none;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
+ .tbody {
+ .tr .td {
+ padding: 0.4rem;
+ margin-left: -1px;
+ border-left: 1px solid #ececec;
+ }
+
+ .bp3-form-group {
+ margin-bottom: 0;
+
+ &:not(.bp3-intent-danger) .bp3-input {
+ border: 1px solid #d0dfe2;
+
+ &:focus {
+ box-shadow: 0 0 0 1px #116cd0;
+ border-color: #116cd0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .bp3-callout {
+ font-size: 14px;
+ }
+ .bp3-dialog-footer {
+ padding-top: 10px;
+ }
+}
diff --git a/src/style/pages/ReconcileVendorCredit/ReconcileVendorCreditForm.scss b/src/style/pages/ReconcileVendorCredit/ReconcileVendorCreditForm.scss
new file mode 100644
index 000000000..c04f7bc27
--- /dev/null
+++ b/src/style/pages/ReconcileVendorCredit/ReconcileVendorCreditForm.scss
@@ -0,0 +1,72 @@
+.dialog--reconcile-vendor-credit-form {
+ width: 800px;
+
+ .bp3-dialog-body {
+ .footer {
+ display: flex;
+ margin-top: 40px;
+
+ .total_lines {
+ margin-left: auto;
+
+ &_line {
+ border-bottom: none;
+ .title {
+ font-weight: 600;
+ }
+ .amount,
+ .title {
+ padding: 8px 0px;
+ width: 165px;
+ }
+ .amount {
+ text-align: right;
+ }
+ }
+ }
+ }
+ }
+
+ .bigcapital-datatable {
+ .table {
+ border: 1px solid #d1dee2;
+ min-width: auto;
+
+ .tbody,
+ .tbody-inner {
+ height: auto;
+ scrollbar-width: none;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ }
+ .tbody {
+ .tr .td {
+ padding: 0.4rem;
+ margin-left: -1px;
+ border-left: 1px solid #ececec;
+ }
+
+ .bp3-form-group {
+ margin-bottom: 0;
+
+ &:not(.bp3-intent-danger) .bp3-input {
+ border: 1px solid #d0dfe2;
+
+ &:focus {
+ box-shadow: 0 0 0 1px #116cd0;
+ border-color: #116cd0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .bp3-callout {
+ font-size: 14px;
+ }
+ .bp3-dialog-footer {
+ padding-top: 10px;
+ }
+}
diff --git a/src/style/pages/RefundCreditNote/List.scss b/src/style/pages/RefundCreditNote/List.scss
new file mode 100644
index 000000000..05ebf36d1
--- /dev/null
+++ b/src/style/pages/RefundCreditNote/List.scss
@@ -0,0 +1,27 @@
+.datatable--refund-transactions {
+ padding: 12px;
+ .table {
+ .tbody,
+ .thead {
+ .tr .th {
+ padding: 8px 8px;
+ background-color: #fff;
+ font-size: 14px;
+ border-bottom: 1px solid #000;
+ border-top: 1px solid #000;
+ }
+ }
+ .tbody {
+ .tr .td {
+ border-bottom: 0;
+ padding-top: 0.4rem;
+ padding-bottom: 0.4rem;
+
+ &.credit,
+ &.debit {
+ font-weight: 600;
+ }
+ }
+ }
+ }
+}
diff --git a/src/style/pages/RefundCreditNote/RefundCreditNote.scss b/src/style/pages/RefundCreditNote/RefundCreditNote.scss
new file mode 100644
index 000000000..74c1fffa3
--- /dev/null
+++ b/src/style/pages/RefundCreditNote/RefundCreditNote.scss
@@ -0,0 +1,28 @@
+.dialog--refund-credit-note {
+ .bp3-dialog-body {
+ .bp3-form-group {
+ label.bp3-label {
+ min-width: 140px;
+ font-size: 13px;
+ }
+ .bp3-form-content {
+ width: 250px;
+ }
+ }
+
+ .form-group {
+ &--description {
+ .bp3-form-content {
+ textarea {
+ width: 100%;
+ min-width: 100%;
+ font-size: 14px;
+ }
+ }
+ }
+ }
+ }
+ .bp3-dialog-footer {
+ padding-top: 10px;
+ }
+}
diff --git a/src/style/pages/RefundVendorCredit/List.scss b/src/style/pages/RefundVendorCredit/List.scss
new file mode 100644
index 000000000..05ebf36d1
--- /dev/null
+++ b/src/style/pages/RefundVendorCredit/List.scss
@@ -0,0 +1,27 @@
+.datatable--refund-transactions {
+ padding: 12px;
+ .table {
+ .tbody,
+ .thead {
+ .tr .th {
+ padding: 8px 8px;
+ background-color: #fff;
+ font-size: 14px;
+ border-bottom: 1px solid #000;
+ border-top: 1px solid #000;
+ }
+ }
+ .tbody {
+ .tr .td {
+ border-bottom: 0;
+ padding-top: 0.4rem;
+ padding-bottom: 0.4rem;
+
+ &.credit,
+ &.debit {
+ font-weight: 600;
+ }
+ }
+ }
+ }
+}
diff --git a/src/style/pages/RefundVendorCredit/RefundVendorCredit.scss b/src/style/pages/RefundVendorCredit/RefundVendorCredit.scss
new file mode 100644
index 000000000..ac74dfa56
--- /dev/null
+++ b/src/style/pages/RefundVendorCredit/RefundVendorCredit.scss
@@ -0,0 +1,28 @@
+.dialog--refund-vendor-credit {
+ .bp3-dialog-body {
+ .bp3-form-group {
+ label.bp3-label {
+ min-width: 140px;
+ font-size: 13px;
+ }
+ .bp3-form-content {
+ width: 250px;
+ }
+ }
+
+ .form-group {
+ &--description {
+ .bp3-form-content {
+ textarea {
+ width: 100%;
+ min-width: 100%;
+ font-size: 14px;
+ }
+ }
+ }
+ }
+ }
+ .bp3-dialog-footer {
+ padding-top: 10px;
+ }
+}