mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
Merge branch 'feature/credit-memo' into develop
This commit is contained in:
@@ -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() {
|
||||
<BadDebtDialog dialogName={'write-off-bad-debt'} />
|
||||
<SMSMessageDialog dialogName={'sms-message-form'} />
|
||||
<TransactionsLockingDialog dialogName={'transactions-locking'} />
|
||||
<RefundCreditNoteDialog dialogName={'refund-credit-note'} />
|
||||
<RefundVendorCreditDialog dialogName={'refund-vendor-credit'} />
|
||||
<ReconcileCreditNoteDialog dialogName={'reconcile-credit-note'} />
|
||||
<ReconcileVendorCreditDialog dialogName={'reconcile-vendor-credit'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
68
src/containers/Alerts/CreditNotes/CreditNoteOpenedAlert.js
Normal file
68
src/containers/Alerts/CreditNotes/CreditNoteOpenedAlert.js
Normal file
@@ -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 (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'open'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleAlertCancel}
|
||||
onConfirm={handleAlertConfirm}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'credit_note_opened.are_sure_to_open_this_credit'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(CreditNoteOpenedAlert);
|
||||
@@ -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 (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmVendorCreditDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={
|
||||
'reconcile_credit_note.once_you_delete_this_reconcile_credit_note'
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(ReconcileCreditNoteDeleteAlert);
|
||||
@@ -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 (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelAlert}
|
||||
onConfirm={handleConfirmRefundCreditDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T
|
||||
id={`refund_credit_transactions.once_your_delete_this_refund_credit_note`}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(RefundCreditNoteDeleteAlert);
|
||||
@@ -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 (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelAlert}
|
||||
onConfirm={handleConfirmRefundVendorCreditDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T
|
||||
id={`refund_vendor_credit_transactions.once_your_delete_this_refund_vendor_credit`}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(RefundVendorCreditDeleteAlert);
|
||||
@@ -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 (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'open'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleAlertCancel}
|
||||
onConfirm={handleAlertConfirm}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'vendor_credit_opened.are_sure_to_open_this_credit'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(VendorCreditOpenedAlert);
|
||||
@@ -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 (
|
||||
<ReconcileCreditNoteFormProvider
|
||||
creditNoteId={creditNoteId}
|
||||
dialogName={dialogName}
|
||||
>
|
||||
<ReconcileCreditNoteForm />
|
||||
</ReconcileCreditNoteFormProvider>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<DataTableEditable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
payload={{
|
||||
errors: errors || [],
|
||||
updateData: handleUpdateData,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<Formik
|
||||
validationSchema={CreateReconcileCreditNoteFormSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={ReconcileCreditNoteFormContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogActions)(ReconcileCreditNoteForm);
|
||||
@@ -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;
|
||||
@@ -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 (
|
||||
<Choose>
|
||||
<Choose.When condition={isEmptyStatus}>
|
||||
<EmptyStatuCallout />
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<Form>
|
||||
<ReconcileCreditNoteFormFields />
|
||||
<ReconcileCreditNoteFormFloatingActions />
|
||||
</Form>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/*------------ Reconcile credit entries table -----------*/}
|
||||
<FastField name={'entries'}>
|
||||
{({
|
||||
form: { setFieldValue, values },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<ReconcileCreditNoteEntriesTable
|
||||
entries={value}
|
||||
errors={error}
|
||||
onUpdateData={(newEntries) => {
|
||||
setFieldValue('entries', newEntries);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
<div className="footer">
|
||||
<TotalLines className="total_lines">
|
||||
<TotalLine
|
||||
title={
|
||||
<T id={'reconcile_credit_note.dialog.total_amount_to_credit'} />
|
||||
}
|
||||
value={formattedAmount(totalAmount, currency_code)}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'reconcile_credit_note.dialog.remaining_credits'}/>}
|
||||
value={formatted_credits_remaining}
|
||||
/>
|
||||
</TotalLines>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={handleCancelBtnClick} style={{ minWidth: '85px' }}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
style={{ minWidth: '95px' }}
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{<T id={'save'} />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(
|
||||
ReconcileCreditNoteFormFloatingActions,
|
||||
);
|
||||
@@ -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 (
|
||||
<DialogContent
|
||||
isLoading={isReconcileCreditLoading || isCreditNoteLoading}
|
||||
name={'reconcile-credit-note'}
|
||||
>
|
||||
<ReconcileCreditNoteDialogContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
|
||||
const useReconcileCreditNoteContext = () =>
|
||||
React.useContext(ReconcileCreditNoteDialogContext);
|
||||
|
||||
export { ReconcileCreditNoteFormProvider, useReconcileCreditNoteContext };
|
||||
36
src/containers/Dialogs/ReconcileCreditNoteDialog/index.js
Normal file
36
src/containers/Dialogs/ReconcileCreditNoteDialog/index.js
Normal file
@@ -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 (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={<T id={'reconcile_credit_note.label'} />}
|
||||
canEscapeKeyClose={true}
|
||||
isOpen={isOpen}
|
||||
className="dialog--reconcile-credit-form"
|
||||
>
|
||||
<DialogSuspense>
|
||||
<ReconcileCreditNoteDialogContent
|
||||
creditNoteId={creditNoteId}
|
||||
dialogName={dialogName}
|
||||
/>
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogRedux())(ReconcileCreditNoteDialog);
|
||||
35
src/containers/Dialogs/ReconcileCreditNoteDialog/utils.js
Normal file
35
src/containers/Dialogs/ReconcileCreditNoteDialog/utils.js
Normal file
@@ -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 (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Callout intent={Intent.PRIMARY}>
|
||||
<p>
|
||||
<T id={'reconcile_credit_note.alert.there_is_no_open_sale_invoices'} />
|
||||
</p>
|
||||
</Callout>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { ReconcileVendorCreditFormProvider } from './ReconcileVendorCreditFormProvider';
|
||||
import ReconcileVendorCreditForm from './ReconcileVendorCreditForm';
|
||||
|
||||
export default function ReconcileVendorCreditDialogContent({
|
||||
// #ownProps
|
||||
dialogName,
|
||||
vendorCreditId,
|
||||
}) {
|
||||
return (
|
||||
<ReconcileVendorCreditFormProvider
|
||||
vendorCreditId={vendorCreditId}
|
||||
dialogName={dialogName}
|
||||
>
|
||||
<ReconcileVendorCreditForm />
|
||||
</ReconcileVendorCreditFormProvider>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<DataTableEditable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
payload={{
|
||||
errors: errors || [],
|
||||
updateData: handleUpdateData,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={handleCancelBtnClick} style={{ minWidth: '85px' }}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
style={{ minWidth: '95px' }}
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{<T id={'save'} />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(ReconcileVendorCreditFloatingActions);
|
||||
@@ -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 (
|
||||
<Formik
|
||||
validationSchema={CreateReconcileVendorCreditFormSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={ReconcileVendorCreditFormContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(ReconcileVendorCreditForm);
|
||||
@@ -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;
|
||||
@@ -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 (
|
||||
<Choose>
|
||||
<Choose.When condition={isEmptyStatus}>
|
||||
<EmptyStatuCallout />
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<Form>
|
||||
<ReconcileVendorCreditFormFields />
|
||||
<ReconcileVendorCreditFloatingActions />
|
||||
</Form>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<FastField name={'entries'}>
|
||||
{({
|
||||
form: { setFieldValue, values },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<ReconcileVendorCreditEntriesTable
|
||||
entries={value}
|
||||
errors={error}
|
||||
onUpdateData={(newEntries) => {
|
||||
setFieldValue('entries', newEntries);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
<div className="footer">
|
||||
<TotalLines className="total_lines">
|
||||
<TotalLine
|
||||
title={
|
||||
<T id={'reconcile_vendor_credit.dialog.total_amount_to_credit'} />
|
||||
}
|
||||
value={formattedAmount(totalAmount, vendorCredit.currency_code)}
|
||||
/>
|
||||
<TotalLine
|
||||
title={
|
||||
<T id={'reconcile_vendor_credit.dialog.remaining_credits'} />
|
||||
}
|
||||
value={vendorCredit.formatted_credits_remaining}
|
||||
/>
|
||||
</TotalLines>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<DialogContent
|
||||
isLoading={isVendorCreditLoading || isReconcileVendorCreditLoading}
|
||||
name={'reconcile-vendor-credit'}
|
||||
>
|
||||
<ReconcileVendorCreditFormContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
|
||||
const useReconcileVendorCreditContext = () =>
|
||||
React.useContext(ReconcileVendorCreditFormContext);
|
||||
|
||||
export { ReconcileVendorCreditFormProvider, useReconcileVendorCreditContext };
|
||||
36
src/containers/Dialogs/ReconcileVendorCreditDialog/index.js
Normal file
36
src/containers/Dialogs/ReconcileVendorCreditDialog/index.js
Normal file
@@ -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 (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={<T id={'reconcile_vendor_credit.dialog.label'} />}
|
||||
canEscapeKeyClose={true}
|
||||
isOpen={isOpen}
|
||||
className="dialog--reconcile-vendor-credit-form"
|
||||
>
|
||||
<DialogSuspense>
|
||||
<ReconcileVendorCreditDialogContent
|
||||
vendorCreditId={vendorCreditId}
|
||||
dialogName={dialogName}
|
||||
/>
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogRedux())(ReconcileVendorCreditDialog);
|
||||
17
src/containers/Dialogs/ReconcileVendorCreditDialog/utils.js
Normal file
17
src/containers/Dialogs/ReconcileVendorCreditDialog/utils.js
Normal file
@@ -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 (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Callout intent={Intent.PRIMARY}>
|
||||
<p>
|
||||
<T id={'reconcile_vendor_credit.alert.there_is_no_open_bills'} />
|
||||
</p>
|
||||
</Callout>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<RefundCreditNoteFormProvider
|
||||
creditNoteId={creditNoteId}
|
||||
dialogName={dialogName}
|
||||
>
|
||||
<RefundCreditNoteForm />
|
||||
</RefundCreditNoteFormProvider>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={handleCancelBtnClick} style={{ minWidth: '75px' }}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
loading={isSubmitting}
|
||||
style={{ minWidth: '85px' }}
|
||||
type="submit"
|
||||
text={<T id={'refund'} />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(RefundCreditNoteFloatingActions);
|
||||
@@ -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 (
|
||||
<Formik
|
||||
validationSchema={CreateRefundCreditNoteFormSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={RefundCreditNoteFormContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogActions)(RefundCreditNoteForm);
|
||||
@@ -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;
|
||||
@@ -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 (
|
||||
<Form>
|
||||
<RefundCreditNoteFormFields />
|
||||
<RefundCreditNoteFloatingActions />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/* ------------- Refund date ------------- */}
|
||||
<FastField name={'date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.refund_date'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="date" />}
|
||||
inline={true}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(value)}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('date', formattedDate);
|
||||
})}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* ------------- Amount ------------- */}
|
||||
<FastField name={'amount'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.amount'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--amount', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="amount" />}
|
||||
inline={true}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
minimal={true}
|
||||
onChange={(amount) => {
|
||||
setFieldValue('amount', amount);
|
||||
}}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inputRef={(ref) => (amountFieldRef.current = ref)}
|
||||
/>
|
||||
</ControlGroup>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* ------------ Reference No. ------------ */}
|
||||
<FastField name={'reference_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference_no'} />}
|
||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference" />}
|
||||
inline={true}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Form account ------------ */}
|
||||
<FastField name={'from_account_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.form_account'} />}
|
||||
className={classNames(
|
||||
'form-group--from_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'from_account_id'} />}
|
||||
inline={true}
|
||||
>
|
||||
<AccountsSuggestField
|
||||
selectedAccountId={value}
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) =>
|
||||
form.setFieldValue('from_account_id', id)
|
||||
}
|
||||
inputProps={{
|
||||
placeholder: intl.get('select_account'),
|
||||
}}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.FIXED_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'description'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_credit_note.dialog.description'} />}
|
||||
className={'form-group--description'}
|
||||
inline={true}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RefundCreditNoteFormFields;
|
||||
@@ -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 (
|
||||
<DialogContent isLoading={isAccountsLoading || isCreditNoteLoading}>
|
||||
<RefundCreditNoteContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
|
||||
const useRefundCreditNoteContext = () =>
|
||||
React.useContext(RefundCreditNoteContext);
|
||||
|
||||
export { RefundCreditNoteFormProvider, useRefundCreditNoteContext };
|
||||
37
src/containers/Dialogs/RefundCreditNoteDialog/index.js
Normal file
37
src/containers/Dialogs/RefundCreditNoteDialog/index.js
Normal file
@@ -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 (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={<T id={'refund_credit_note.dialog.label'} />}
|
||||
isOpen={isOpen}
|
||||
canEscapeJeyClose={true}
|
||||
autoFocus={true}
|
||||
className={'dialog--refund-credit-note'}
|
||||
>
|
||||
<DialogSuspense>
|
||||
<RefundCreditNoteDialogContent
|
||||
dialogName={dialogName}
|
||||
creditNoteId={creditNoteId}
|
||||
/>
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
export default compose(withDialogRedux())(RefundCreditNoteDialog);
|
||||
@@ -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 (
|
||||
<RefundVendorCreditFormProvider
|
||||
vendorCreditId={vendorCreditId}
|
||||
dialogName={dialogName}
|
||||
>
|
||||
<RefundVendorCreditForm />
|
||||
</RefundVendorCreditFormProvider>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={handleCancelBtnClick} style={{ minWidth: '75px' }}>
|
||||
<T id={'cancel'} />
|
||||
</Button>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
loading={isSubmitting}
|
||||
style={{ minWidth: '85px' }}
|
||||
type="submit"
|
||||
text="Refund"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogActions)(RefundVendorCreditFloatingActions);
|
||||
@@ -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 (
|
||||
<Formik
|
||||
validationSchema={CreateVendorRefundCreditFormSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
component={RefundVendorCreditFormContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogActions)(RefundVendorCreditForm);
|
||||
@@ -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;
|
||||
@@ -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 (
|
||||
<Form>
|
||||
<RefundVendorCreditFormFields />
|
||||
<RefundVendorCreditFloatingActions />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/* ------------- Refund date ------------- */}
|
||||
<FastField name={'refund_date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_vendor_credit.dialog.refund_date'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="refund_date" />}
|
||||
inline={true}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(value)}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('refund_date', formattedDate);
|
||||
})}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* ------------- Amount ------------- */}
|
||||
<FastField name={'amount'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_vendor_credit.dialog.amount'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--amount', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="amount" />}
|
||||
inline={true}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={values.currency_code} />
|
||||
<MoneyInputGroup
|
||||
value={value}
|
||||
minimal={true}
|
||||
onChange={(amount) => {
|
||||
setFieldValue('amount', amount);
|
||||
}}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inputRef={(ref) => (amountFieldRef.current = ref)}
|
||||
/>
|
||||
</ControlGroup>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* ------------ Reference No. ------------ */}
|
||||
<FastField name={'reference_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference_no'} />}
|
||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference" />}
|
||||
inline={true}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Form account ------------ */}
|
||||
<FastField name={'deposit_account_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_vendor_credit.dialog.deposit_to_account'} />}
|
||||
className={classNames(
|
||||
'form-group--deposit_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'deposit_account_id'} />}
|
||||
inline={true}
|
||||
>
|
||||
<AccountsSuggestField
|
||||
selectedAccountId={value}
|
||||
accounts={accounts}
|
||||
onAccountSelected={({ id }) =>
|
||||
form.setFieldValue('deposit_account_id', id)
|
||||
}
|
||||
inputProps={{
|
||||
placeholder: intl.get('select_account'),
|
||||
}}
|
||||
filterByTypes={[
|
||||
ACCOUNT_TYPE.BANK,
|
||||
ACCOUNT_TYPE.CASH,
|
||||
ACCOUNT_TYPE.FIXED_ASSET,
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'description'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'refund_vendor_credit.dialog.description'} />}
|
||||
className={'form-group--description'}
|
||||
inline={true}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RefundVendorCreditFormFields;
|
||||
@@ -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 (
|
||||
<DialogContent isLoading={isAccountsLoading || isVendorCreditLoading}>
|
||||
<RefundVendorCreditContext.Provider value={provider} {...props} />
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
|
||||
const useRefundVendorCreditContext = () =>
|
||||
React.useContext(RefundVendorCreditContext);
|
||||
|
||||
export { RefundVendorCreditFormProvider, useRefundVendorCreditContext };
|
||||
38
src/containers/Dialogs/RefundVendorCreditDialog/index.js
Normal file
38
src/containers/Dialogs/RefundVendorCreditDialog/index.js
Normal file
@@ -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 (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={<T id={'refund_vendor_credit.dialog.label'} />}
|
||||
isOpen={isOpen}
|
||||
canEscapeJeyClose={true}
|
||||
autoFocus={true}
|
||||
className={'dialog--refund-vendor-credit'}
|
||||
>
|
||||
<DialogSuspense>
|
||||
<RefundVendorCreditDialogContent
|
||||
dialogName={dialogName}
|
||||
vendorCreditId={vendorCreditId}
|
||||
/>
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogRedux())(RefundVendorCreditDialog);
|
||||
@@ -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={<CreditNoteDetailPanel />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('credit_note.drawer.label_refund_transactions')}
|
||||
id={'refund_transactions'}
|
||||
panel={<RefundCreditNoteTransactionsTable />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('credit_note.drawer.label_reconcile_transactions')}
|
||||
id={'reconcile_transactions'}
|
||||
panel={<ReconcileCreditNoteTransactionsTable />}
|
||||
/>
|
||||
</DrawerMainTabs>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<If condition={!creditNote.is_closed && !creditNote.is_draft}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="quick-payment-16" iconSize={16} />}
|
||||
text={<T id={'refund'} />}
|
||||
onClick={handleRefundCreditNote}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</If>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
|
||||
@@ -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 (
|
||||
<DrawerLoading loading={isCreditNoteLoading}>
|
||||
<DrawerLoading
|
||||
loading={
|
||||
isCreditNoteLoading ||
|
||||
isRefundCreditNoteLoading ||
|
||||
isReconcileCreditNoteLoading
|
||||
}
|
||||
>
|
||||
<DrawerHeaderContent
|
||||
name="credit-note-detail-drawer"
|
||||
title={intl.get('credit_note.drawer_credit_note_detail')}
|
||||
|
||||
@@ -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 {
|
||||
useReconcileCreditTransactionsTableColumns,
|
||||
ActionsMenu,
|
||||
} from './components';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Reconcile credit transactions table.
|
||||
*/
|
||||
function RefundCreditNoteTransactionsTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
}) {
|
||||
const { reconcileCreditNotes } = useCreditNoteDetailDrawerContext();
|
||||
|
||||
const columns = useReconcileCreditTransactionsTableColumns();
|
||||
|
||||
// Handle delete reconile credit.
|
||||
const handleDeleteReconcileCreditNote = ({ id }) => {
|
||||
openAlert('reconcile-credit-delete', { creditNoteId: id });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={reconcileCreditNotes}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeleteReconcileCreditNote,
|
||||
}}
|
||||
className={'datatable--refund-transactions'}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertsActions)(RefundCreditNoteTransactionsTable);
|
||||
@@ -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 (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={intl.get('delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
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',
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={refundCreditNote}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeleteRefundCreditNote,
|
||||
}}
|
||||
className={'datatable--refund-transactions'}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertsActions)(RefundCreditNoteTransactionsTable);
|
||||
@@ -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 (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={intl.get('delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={refundVendorCredit}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeleteRefundVendorCredit,
|
||||
}}
|
||||
className={'datatable--refund-transactions'}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertsActions)(RefundVendorCreditTransactionsTable);
|
||||
@@ -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 (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={intl.get('delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
@@ -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={<VendorCreditDetailPanel />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('credit_note.drawer.label_refund_transactions')}
|
||||
id={'refund_transactions'}
|
||||
panel={<RefundVendorCreditTransactionsTable />}
|
||||
/>
|
||||
</DrawerMainTabs>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -57,6 +61,15 @@ function VendorCreditDetailActionsBar({
|
||||
onClick={handleEditVendorCredit}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<If condition={!vendorCredit.is_closed && !vendorCredit.is_draft}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="quick-payment-16" iconSize={16} />}
|
||||
text={<T id={'refund'} />}
|
||||
onClick={handleRefundVendorCredit}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</If>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
|
||||
@@ -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 (
|
||||
<DrawerLoading loading={isVendorCreditLoading}>
|
||||
<DrawerLoading
|
||||
loading={isVendorCreditLoading || isRefundVendorCreditLoading}
|
||||
>
|
||||
<DrawerHeaderContent
|
||||
name="vendor-credit-detail-drawer"
|
||||
title={intl.get('vendor_credit.drawer_vendor_credit_detail')}
|
||||
|
||||
@@ -26,35 +26,40 @@ export default function VendorCreditNoteFloatingActions() {
|
||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||
|
||||
// Credit note form context.
|
||||
const { setSubmitPayload, isNewMode } = useVendorCreditNoteFormContext();
|
||||
const { setSubmitPayload, vendorCredit } = useVendorCreditNoteFormContext();
|
||||
|
||||
// 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,113 @@ export default function VendorCreditNoteFloatingActions() {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
// Handle submit button click.
|
||||
const handleSubmitBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: true });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
const handleClearBtnClick = (event) => {
|
||||
resetForm();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||
{/* ----------- Save ----------- */}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={handleSubmitBtnClick}
|
||||
text={<T id={'save'} />}
|
||||
/>
|
||||
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'save_continue_editing'} />}
|
||||
onClick={handleSubmitSaveContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
{/* ----------- Save And Open ----------- */}
|
||||
<If condition={!vendorCredit || !vendorCredit?.is_open}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
onClick={handleSubmitOpenBtnClick}
|
||||
text={<T id={'save_open'} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
{/* ----------- Save As Draft ----------- */}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
className={'ml1'}
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
text={<T id={'save_as_draft'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitDraftAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'save_continue_editing'} />}
|
||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'open_and_new'} />}
|
||||
onClick={handleSubmitOpenAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'open_continue_editing'} />}
|
||||
onClick={handleSubmitOpenContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
{/* ----------- Save As Draft ----------- */}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
className={'ml1'}
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
text={<T id={'save_as_draft'} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitDraftAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'save_continue_editing'} />}
|
||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
</If>
|
||||
{/* ----------- Save and New ----------- */}
|
||||
<If condition={vendorCredit && vendorCredit?.is_open}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={handleSubmitOpenBtnClick}
|
||||
text={<T id={'save'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitOpenAndNewBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
</If>
|
||||
{/* ----------- Clear & Reset----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
disabled={isSubmitting}
|
||||
onClick={handleClearBtnClick}
|
||||
text={isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
text={vendorCredit ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
/>
|
||||
{/* ----------- Cancel ----------- */}
|
||||
<Button
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Button, Intent } from '@blueprintjs/core';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import { sumBy, omit, isEmpty } from 'lodash';
|
||||
import { isEmpty } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import {
|
||||
@@ -95,6 +95,7 @@ function VendorCreditNoteForm({
|
||||
}
|
||||
const form = {
|
||||
...transformFormValuesToRequest(values),
|
||||
open: submitPayload.open,
|
||||
};
|
||||
// Handle the request success.
|
||||
const onSuccess = (response) => {
|
||||
|
||||
@@ -15,6 +15,7 @@ const getSchema = Yup.object().shape({
|
||||
.min(1)
|
||||
.max(DATATYPES_LENGTH.TEXT)
|
||||
.label(intl.get('note')),
|
||||
open: Yup.boolean(),
|
||||
entries: Yup.array().of(
|
||||
Yup.object().shape({
|
||||
quantity: Yup.number()
|
||||
|
||||
@@ -33,6 +33,7 @@ export const defaultVendorsCreditNote = {
|
||||
vendor_id: '',
|
||||
vendor_credit_number: '',
|
||||
vendor_credit_no_manually: false,
|
||||
open: '',
|
||||
vendor_credit_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
// reference_no: '',
|
||||
note: '',
|
||||
@@ -93,6 +94,7 @@ export const transformFormValuesToRequest = (values) => {
|
||||
return {
|
||||
...values,
|
||||
entries: transformEntriesToSubmit(entries),
|
||||
open: false,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -119,7 +121,7 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
/**
|
||||
* Syncs invoice no. settings with form.
|
||||
*/
|
||||
export const useObserveVendorCreditNoSettings = (prefix, nextNumber) => {
|
||||
export const useObserveVendorCreditNoSettings = (prefix, nextNumber) => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withVendorsCreditNotesActions from './withVendorsCreditNotesActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withSettings from '../../../Settings/withSettings';
|
||||
|
||||
import { useVendorsCreditNoteTableColumns, ActionsMenu } from './components';
|
||||
@@ -33,6 +34,9 @@ function VendorsCreditNoteDataTable({
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
|
||||
// #withDialogAction
|
||||
openDialog,
|
||||
|
||||
// #withSettings
|
||||
creditNoteTableSize,
|
||||
}) {
|
||||
@@ -92,6 +96,20 @@ function VendorsCreditNoteDataTable({
|
||||
});
|
||||
};
|
||||
|
||||
const handleRefundCreditVendor = ({ id }) => {
|
||||
openDialog('refund-vendor-credit', { vendorCreditId: id });
|
||||
};
|
||||
|
||||
// Handle cancel/confirm vendor credit open.
|
||||
const handleOpenCreditNote = ({ id }) => {
|
||||
openAlert('vendor-credit-open', { vendorCreditId: id });
|
||||
};
|
||||
|
||||
// Handle reconcile credit note.
|
||||
const handleReconcileVendorCredit = ({ id }) => {
|
||||
openDialog('reconcile-vendor-credit', { vendorCreditId: id });
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardContentTable>
|
||||
<DataTable
|
||||
@@ -118,6 +136,9 @@ function VendorsCreditNoteDataTable({
|
||||
onViewDetails: handleViewDetailVendorCredit,
|
||||
onDelete: handleDeleteVendorCreditNote,
|
||||
onEdit: hanldeEditVendorCreditNote,
|
||||
onRefund: handleRefundCreditVendor,
|
||||
onOpen: handleOpenCreditNote,
|
||||
onReconcile: handleReconcileVendorCredit,
|
||||
}}
|
||||
/>
|
||||
</DashboardContentTable>
|
||||
@@ -129,6 +150,7 @@ export default compose(
|
||||
withVendorsCreditNotesActions,
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
withDialogActions,
|
||||
withSettings(({ vendorsCreditNoteSetting }) => ({
|
||||
creditNoteTableSize: vendorsCreditNoteSetting?.tableSize,
|
||||
})),
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Tag,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
ProgressBar,
|
||||
} from '@blueprintjs/core';
|
||||
import { Intent, Tag, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
|
||||
@@ -14,18 +7,17 @@ import { CLASSES } from '../../../../common/classes';
|
||||
import {
|
||||
FormatDateCell,
|
||||
FormattedMessage as T,
|
||||
AppToaster,
|
||||
Choose,
|
||||
If,
|
||||
Icon,
|
||||
} from 'components';
|
||||
import { formattedAmount, safeCallback, calculateStatus } from 'utils';
|
||||
import { safeCallback } from 'utils';
|
||||
|
||||
/**
|
||||
* Actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
payload: { onEdit, onDelete, onViewDetails },
|
||||
payload: { onEdit, onDelete, onOpen, onRefund, onReconcile, onViewDetails },
|
||||
row: { original },
|
||||
}) {
|
||||
return (
|
||||
@@ -41,6 +33,26 @@ export function ActionsMenu({
|
||||
text={intl.get('vendor_credits.action.edit_vendor_credit')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
<If condition={!original.is_closed && !original.is_draft}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="quick-payment-16" />}
|
||||
text={intl.get('vendor_credits.action.refund_vendor_credit')}
|
||||
onClick={safeCallback(onRefund, original)}
|
||||
/>
|
||||
</If>
|
||||
<If condition={original.is_draft}>
|
||||
<MenuItem
|
||||
icon={<Icon icon={'check'} iconSize={18} />}
|
||||
text={intl.get('mark_as_opened')}
|
||||
onClick={safeCallback(onOpen, original)}
|
||||
/>
|
||||
</If>
|
||||
<MenuItem
|
||||
text={'Reconcile Credit Note With bills'}
|
||||
// icon={<Icon icon="quick-payment-16" />}
|
||||
// text={intl.get('credit_note.action.refund_credit_note')}
|
||||
onClick={safeCallback(onReconcile, original)}
|
||||
/>
|
||||
<MenuItem
|
||||
text={intl.get('vendor_credits.action.delete_vendor_credit')}
|
||||
intent={Intent.DANGER}
|
||||
@@ -51,6 +63,35 @@ export function ActionsMenu({
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status accessor.
|
||||
*/
|
||||
export function StatusAccessor(creditNote) {
|
||||
return (
|
||||
<div>
|
||||
<Choose>
|
||||
<Choose.When condition={creditNote.is_open}>
|
||||
<Tag minimal={true} intent={Intent.WARNING}>
|
||||
<T id={'open'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={creditNote.is_closed}>
|
||||
<Tag minimal={true} intent={Intent.SUCCESS}>
|
||||
<T id={'closed'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={creditNote.is_draft}>
|
||||
<Tag minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
</Choose>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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 (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||
{/* ----------- Save ----------- */}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={handleSubmitBtnClick}
|
||||
text={<T id={'save'} />}
|
||||
/>
|
||||
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'save_continue_editing'} />}
|
||||
onClick={handleSubmitSaveContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
{/* ----------- Save And Open ----------- */}
|
||||
<If condition={!creditNote || !creditNote?.is_open}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
onClick={handleSubmitOpenBtnClick}
|
||||
text={<T id={'save_open'} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
{/* ----------- Save As Draft ----------- */}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
className={'ml1'}
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
text={<T id={'save_as_draft'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitDraftAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'save_continue_editing'} />}
|
||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'open_and_new'} />}
|
||||
onClick={handleSubmitOpenAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'open_continue_editing'} />}
|
||||
onClick={handleSubmitOpenContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
{/* ----------- Save As Draft ----------- */}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
className={'ml1'}
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
text={<T id={'save_as_draft'} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitDraftAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'save_continue_editing'} />}
|
||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
</If>
|
||||
{/* ----------- Save and New ----------- */}
|
||||
<If condition={creditNote && creditNote?.is_open}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={handleSubmitOpenBtnClick}
|
||||
text={<T id={'save'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitOpenAndNewBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
</If>
|
||||
{/* ----------- Clear & Reset----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
disabled={isSubmitting}
|
||||
onClick={handleClearBtnClick}
|
||||
text={isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
text={creditNote ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
/>
|
||||
{/* ----------- Cancel ----------- */}
|
||||
<Button
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useHistory } from 'react-router-dom';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import { sumBy, omit, isEmpty } from 'lodash';
|
||||
import { isEmpty } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import {
|
||||
@@ -97,6 +97,7 @@ function CreditNoteForm({
|
||||
}
|
||||
const form = {
|
||||
...transformFormValuesToRequest(values),
|
||||
open: submitPayload.open,
|
||||
};
|
||||
// Handle the request success.
|
||||
const onSuccess = (response) => {
|
||||
|
||||
@@ -16,6 +16,7 @@ const getSchema = () =>
|
||||
.min(1)
|
||||
.max(DATATYPES_LENGTH.TEXT)
|
||||
.label(intl.get('note')),
|
||||
open: Yup.boolean(),
|
||||
terms_conditions: Yup.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
|
||||
@@ -35,6 +35,7 @@ export const defaultCreditNote = {
|
||||
credit_note_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
credit_note_number: '',
|
||||
credit_note_no_manually: false,
|
||||
open: '',
|
||||
// reference_no: '',
|
||||
note: '',
|
||||
terms_conditions: '',
|
||||
@@ -82,19 +83,20 @@ export const transformEntriesToSubmit = (entries) => {
|
||||
/**
|
||||
* Filters the givne non-zero entries.
|
||||
*/
|
||||
export const filterNonZeroEntries = (entries) => {
|
||||
export const filterNonZeroEntries = (entries) => {
|
||||
return entries.filter((item) => item.item_id && item.quantity);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transformes form values to request body.
|
||||
*/
|
||||
export const transformFormValuesToRequest = (values) => {
|
||||
export const transformFormValuesToRequest = (values) => {
|
||||
const entries = filterNonZeroEntries(values.entries);
|
||||
|
||||
return {
|
||||
...values,
|
||||
entries: transformEntriesToSubmit(entries),
|
||||
open: false,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -121,7 +123,7 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
/**
|
||||
* Syncs invoice no. settings with form.
|
||||
*/
|
||||
export const useObserveCreditNoSettings = (prefix, nextNumber) => {
|
||||
export const useObserveCreditNoSettings = (prefix, nextNumber) => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -4,6 +4,18 @@ const CreditNoteDeleteAlert = React.lazy(() =>
|
||||
import('../../Alerts/CreditNotes/CreditNoteDeleteAlert'),
|
||||
);
|
||||
|
||||
const RefundCreditNoteDeleteAlert = React.lazy(() =>
|
||||
import('../../Alerts/CreditNotes/RefundCreditNoteDeleteAlert'),
|
||||
);
|
||||
|
||||
const OpenCreditNoteAlert = React.lazy(() =>
|
||||
import('../../Alerts/CreditNotes/CreditNoteOpenedAlert'),
|
||||
);
|
||||
|
||||
const ReconcileCreditDeleteAlert = React.lazy(() =>
|
||||
import('../../Alerts/CreditNotes/ReconcileCreditNoteDeleteAlert'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Credit notes alerts.
|
||||
*/
|
||||
@@ -12,4 +24,16 @@ export default [
|
||||
name: 'credit-note-delete',
|
||||
component: CreditNoteDeleteAlert,
|
||||
},
|
||||
{
|
||||
name: 'credit-note-open',
|
||||
component: OpenCreditNoteAlert,
|
||||
},
|
||||
{
|
||||
name: 'refund-credit-delete',
|
||||
component: RefundCreditNoteDeleteAlert,
|
||||
},
|
||||
{
|
||||
name: 'reconcile-credit-delete',
|
||||
component: ReconcileCreditDeleteAlert,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -13,6 +13,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withCreditNotesActions from './withCreditNotesActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withSettings from '../../../Settings/withSettings';
|
||||
|
||||
import { useCreditNoteTableColumns, ActionsMenu } from './components';
|
||||
@@ -33,6 +34,9 @@ function CreditNotesDataTable({
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
|
||||
// #withDialogAction
|
||||
openDialog,
|
||||
|
||||
// #withSettings
|
||||
creditNoteTableSize,
|
||||
}) {
|
||||
@@ -92,6 +96,20 @@ function CreditNotesDataTable({
|
||||
});
|
||||
};
|
||||
|
||||
const handleRefundCreditNote = ({ id }) => {
|
||||
openDialog('refund-credit-note', { creditNoteId: id });
|
||||
};
|
||||
|
||||
// Handle cancel/confirm crdit note open.
|
||||
const handleOpenCreditNote = ({ id }) => {
|
||||
openAlert('credit-note-open', { creditNoteId: id });
|
||||
};
|
||||
|
||||
// Handle reconcile credit note.
|
||||
const handleReconcileCreditNote = ({ id }) => {
|
||||
openDialog('reconcile-credit-note', { creditNoteId: id });
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardContentTable>
|
||||
<DataTable
|
||||
@@ -117,6 +135,9 @@ function CreditNotesDataTable({
|
||||
onViewDetails: handleViewDetailCreditNote,
|
||||
onDelete: handleDeleteCreditNote,
|
||||
onEdit: hanldeEditCreditNote,
|
||||
onRefund: handleRefundCreditNote,
|
||||
onOpen: handleOpenCreditNote,
|
||||
onReconcile: handleReconcileCreditNote,
|
||||
}}
|
||||
/>
|
||||
</DashboardContentTable>
|
||||
@@ -128,6 +149,7 @@ export default compose(
|
||||
withCreditNotesActions,
|
||||
withDrawerActions,
|
||||
withAlertsActions,
|
||||
withDialogActions,
|
||||
withSettings(({ creditNoteSettings }) => ({
|
||||
creditNoteTableSize: creditNoteSettings?.tableSize,
|
||||
})),
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Tag,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
ProgressBar,
|
||||
} from '@blueprintjs/core';
|
||||
import { Intent, Tag, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
|
||||
@@ -14,15 +7,14 @@ import { CLASSES } from '../../../../common/classes';
|
||||
import {
|
||||
FormatDateCell,
|
||||
FormattedMessage as T,
|
||||
AppToaster,
|
||||
Choose,
|
||||
If,
|
||||
Icon,
|
||||
} from 'components';
|
||||
import { formattedAmount, safeCallback, calculateStatus } from 'utils';
|
||||
import { safeCallback } from 'utils';
|
||||
|
||||
export function ActionsMenu({
|
||||
payload: { onEdit, onDelete, onViewDetails },
|
||||
payload: { onEdit, onDelete, onRefund, onOpen, onReconcile, onViewDetails },
|
||||
row: { original },
|
||||
}) {
|
||||
return (
|
||||
@@ -38,6 +30,26 @@ export function ActionsMenu({
|
||||
text={intl.get('credit_note.action.edit_credit_note')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
<If condition={!original.is_closed && !original.is_draft}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="quick-payment-16" />}
|
||||
text={intl.get('credit_note.action.refund_credit_note')}
|
||||
onClick={safeCallback(onRefund, original)}
|
||||
/>
|
||||
</If>
|
||||
<If condition={original.is_draft}>
|
||||
<MenuItem
|
||||
icon={<Icon icon={'check'} iconSize={18} />}
|
||||
text={intl.get('mark_as_opened')}
|
||||
onClick={safeCallback(onOpen, original)}
|
||||
/>
|
||||
</If>
|
||||
<MenuItem
|
||||
text={'Reconcile Credit Note With Invoice'}
|
||||
// icon={<Icon icon="quick-payment-16" />}
|
||||
// text={intl.get('credit_note.action.refund_credit_note')}
|
||||
onClick={safeCallback(onReconcile, original)}
|
||||
/>
|
||||
<MenuItem
|
||||
text={intl.get('credit_note.action.delete_credit_note')}
|
||||
intent={Intent.DANGER}
|
||||
@@ -48,6 +60,35 @@ export function ActionsMenu({
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status accessor.
|
||||
*/
|
||||
export function StatusAccessor(creditNote) {
|
||||
return (
|
||||
<div>
|
||||
<Choose>
|
||||
<Choose.When condition={creditNote.is_open}>
|
||||
<Tag minimal={true} intent={Intent.WARNING}>
|
||||
<T id={'open'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={creditNote.is_closed}>
|
||||
<Tag minimal={true} intent={Intent.SUCCESS}>
|
||||
<T id={'closed'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={creditNote.is_draft}>
|
||||
<Tag minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
</Choose>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
},
|
||||
|
||||
29
src/containers/Sales/CreditNotes/CreditNotesLanding/utils.js
Normal file
29
src/containers/Sales/CreditNotes/CreditNotesLanding/utils.js
Normal file
@@ -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,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
27
src/style/pages/RefundCreditNote/List.scss
Normal file
27
src/style/pages/RefundCreditNote/List.scss
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/style/pages/RefundCreditNote/RefundCreditNote.scss
Normal file
28
src/style/pages/RefundCreditNote/RefundCreditNote.scss
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
27
src/style/pages/RefundVendorCredit/List.scss
Normal file
27
src/style/pages/RefundVendorCredit/List.scss
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/style/pages/RefundVendorCredit/RefundVendorCredit.scss
Normal file
28
src/style/pages/RefundVendorCredit/RefundVendorCredit.scss
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user