mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +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 NotifyPaymentReceiveViaSMSDialog from '../containers/Dialogs/NotifyPaymentReceiveViaSMSDialog';
|
||||||
import SMSMessageDialog from '../containers/Dialogs/SMSMessageDialog';
|
import SMSMessageDialog from '../containers/Dialogs/SMSMessageDialog';
|
||||||
import TransactionsLockingDialog from '../containers/Dialogs/TransactionsLockingDialog';
|
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.
|
* Dialogs container.
|
||||||
@@ -60,6 +64,10 @@ export default function DialogsContainer() {
|
|||||||
<BadDebtDialog dialogName={'write-off-bad-debt'} />
|
<BadDebtDialog dialogName={'write-off-bad-debt'} />
|
||||||
<SMSMessageDialog dialogName={'sms-message-form'} />
|
<SMSMessageDialog dialogName={'sms-message-form'} />
|
||||||
<TransactionsLockingDialog dialogName={'transactions-locking'} />
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import withAlertActions from 'containers/Alert/withAlertActions';
|
|||||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||||
|
|
||||||
import { useDeleteCreditNote } from 'hooks/query';
|
import { useDeleteCreditNote } from 'hooks/query';
|
||||||
|
import { handleDeleteErrors } from '../../Sales/CreditNotes/CreditNotesLanding/utils';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +49,9 @@ function CreditNoteDeleteAlert({
|
|||||||
response: {
|
response: {
|
||||||
data: { errors },
|
data: { errors },
|
||||||
},
|
},
|
||||||
}) => {},
|
}) => {
|
||||||
|
handleDeleteErrors(errors);
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
closeAlert(name);
|
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 { DrawerMainTabs } from 'components';
|
||||||
|
|
||||||
import CreditNoteDetailPanel from './CreditNoteDetailPanel';
|
import CreditNoteDetailPanel from './CreditNoteDetailPanel';
|
||||||
|
import RefundCreditNoteTransactionsTable from './RefundCreditNoteTransactions/RefundCreditNoteTransactionsTable';
|
||||||
|
import ReconcileCreditNoteTransactionsTable from './ReconcileCreditNoteTransactions/ReconcileCreditNoteTransactionsTable';
|
||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
|
|
||||||
import CreditNoteDetailCls from '../../../style/components/Drawers/CreditNoteDetails.module.scss';
|
import CreditNoteDetailCls from '../../../style/components/Drawers/CreditNoteDetails.module.scss';
|
||||||
@@ -20,6 +22,16 @@ export default function CreditNoteDetail() {
|
|||||||
id={'details'}
|
id={'details'}
|
||||||
panel={<CreditNoteDetailPanel />}
|
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>
|
</DrawerMainTabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,7 +15,13 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
|
|||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
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';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
@@ -32,7 +38,7 @@ function CreditNoteDetailActionsBar({
|
|||||||
// #withDrawerActions
|
// #withDrawerActions
|
||||||
closeDrawer,
|
closeDrawer,
|
||||||
}) {
|
}) {
|
||||||
const { creditNoteId } = useCreditNoteDetailDrawerContext();
|
const { creditNoteId, creditNote } = useCreditNoteDetailDrawerContext();
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@@ -42,6 +48,10 @@ function CreditNoteDetailActionsBar({
|
|||||||
closeDrawer('credit-note-detail-drawer');
|
closeDrawer('credit-note-detail-drawer');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRefundCreditNote = () => {
|
||||||
|
openDialog('refund-credit-note', { creditNoteId });
|
||||||
|
};
|
||||||
|
|
||||||
// Handle delete credit note.
|
// Handle delete credit note.
|
||||||
const handleDeleteCreditNote = () => {
|
const handleDeleteCreditNote = () => {
|
||||||
openAlert('credit-note-delete', { creditNoteId });
|
openAlert('credit-note-delete', { creditNoteId });
|
||||||
@@ -57,6 +67,15 @@ function CreditNoteDetailActionsBar({
|
|||||||
onClick={handleEditCreditNote}
|
onClick={handleEditCreditNote}
|
||||||
/>
|
/>
|
||||||
<NavbarDivider />
|
<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
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { useCreditNote } from 'hooks/query';
|
import {
|
||||||
|
useCreditNote,
|
||||||
|
useRefundCreditNote,
|
||||||
|
useReconcileCreditNotes,
|
||||||
|
} from 'hooks/query';
|
||||||
import { DrawerHeaderContent, DrawerLoading } from 'components';
|
import { DrawerHeaderContent, DrawerLoading } from 'components';
|
||||||
|
|
||||||
const CreditNoteDetailDrawerContext = React.createContext();
|
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 = {
|
const provider = {
|
||||||
creditNote,
|
creditNote,
|
||||||
|
refundCreditNote,
|
||||||
|
reconcileCreditNotes,
|
||||||
|
|
||||||
|
isRefundCreditNoteLoading,
|
||||||
|
isRefundCreditNoteFetching,
|
||||||
creditNoteId,
|
creditNoteId,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DrawerLoading loading={isCreditNoteLoading}>
|
<DrawerLoading
|
||||||
|
loading={
|
||||||
|
isCreditNoteLoading ||
|
||||||
|
isRefundCreditNoteLoading ||
|
||||||
|
isReconcileCreditNoteLoading
|
||||||
|
}
|
||||||
|
>
|
||||||
<DrawerHeaderContent
|
<DrawerHeaderContent
|
||||||
name="credit-note-detail-drawer"
|
name="credit-note-detail-drawer"
|
||||||
title={intl.get('credit_note.drawer_credit_note_detail')}
|
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 { DrawerMainTabs } from 'components';
|
||||||
|
|
||||||
import VendorCreditDetailPanel from './VendorCreditDetailPanel';
|
import VendorCreditDetailPanel from './VendorCreditDetailPanel';
|
||||||
|
import RefundVendorCreditTransactionsTable from './RefundVendorCreditTransactions/RefundVendorCreditTransactionsTable';
|
||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
|
|
||||||
import VendorCreditDetailCls from '../../../style/components/Drawers/VendorCreditDetail.module.scss';
|
import VendorCreditDetailCls from '../../../style/components/Drawers/VendorCreditDetail.module.scss';
|
||||||
@@ -20,6 +21,11 @@ export default function VendorCreditDetail() {
|
|||||||
id={'details'}
|
id={'details'}
|
||||||
panel={<VendorCreditDetailPanel />}
|
panel={<VendorCreditDetailPanel />}
|
||||||
/>
|
/>
|
||||||
|
<Tab
|
||||||
|
title={intl.get('credit_note.drawer.label_refund_transactions')}
|
||||||
|
id={'refund_transactions'}
|
||||||
|
panel={<RefundVendorCreditTransactionsTable />}
|
||||||
|
/>
|
||||||
</DrawerMainTabs>
|
</DrawerMainTabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
|
|||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
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';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ function VendorCreditDetailActionsBar({
|
|||||||
// #withDrawerActions
|
// #withDrawerActions
|
||||||
closeDrawer,
|
closeDrawer,
|
||||||
}) {
|
}) {
|
||||||
const { vendorCreditId } = useVendorCreditDetailDrawerContext();
|
const { vendorCreditId, vendorCredit } = useVendorCreditDetailDrawerContext();
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@@ -47,6 +47,10 @@ function VendorCreditDetailActionsBar({
|
|||||||
openAlert('vendor-credit-delete', { vendorCreditId });
|
openAlert('vendor-credit-delete', { vendorCreditId });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRefundVendorCredit = () => {
|
||||||
|
openDialog('refund-vendor-credit', { vendorCreditId });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DashboardActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
@@ -57,6 +61,15 @@ function VendorCreditDetailActionsBar({
|
|||||||
onClick={handleEditVendorCredit}
|
onClick={handleEditVendorCredit}
|
||||||
/>
|
/>
|
||||||
<NavbarDivider />
|
<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
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { useVendorCredit } from 'hooks/query';
|
import { useVendorCredit, useRefundVendorCredit } from 'hooks/query';
|
||||||
import { DrawerHeaderContent, DrawerLoading } from 'components';
|
import { DrawerHeaderContent, DrawerLoading } from 'components';
|
||||||
|
|
||||||
const VendorCreditDetailDrawerContext = React.createContext();
|
const VendorCreditDetailDrawerContext = React.createContext();
|
||||||
@@ -15,13 +15,27 @@ function VendorCreditDetailDrawerProvider({ vendorCreditId, ...props }) {
|
|||||||
enabled: !!vendorCreditId,
|
enabled: !!vendorCreditId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle fetch refund credit note.
|
||||||
|
const {
|
||||||
|
data: refundVendorCredit,
|
||||||
|
isFetching: isRefundVendorCreditFetching,
|
||||||
|
isLoading: isRefundVendorCreditLoading,
|
||||||
|
} = useRefundVendorCredit(vendorCreditId, {
|
||||||
|
enabled: !!vendorCreditId,
|
||||||
|
});
|
||||||
|
|
||||||
const provider = {
|
const provider = {
|
||||||
vendorCredit,
|
vendorCredit,
|
||||||
|
refundVendorCredit,
|
||||||
|
isRefundVendorCreditLoading,
|
||||||
|
isRefundVendorCreditFetching,
|
||||||
vendorCreditId,
|
vendorCreditId,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DrawerLoading loading={isVendorCreditLoading}>
|
<DrawerLoading
|
||||||
|
loading={isVendorCreditLoading || isRefundVendorCreditLoading}
|
||||||
|
>
|
||||||
<DrawerHeaderContent
|
<DrawerHeaderContent
|
||||||
name="vendor-credit-detail-drawer"
|
name="vendor-credit-detail-drawer"
|
||||||
title={intl.get('vendor_credit.drawer_vendor_credit_detail')}
|
title={intl.get('vendor_credit.drawer_vendor_credit_detail')}
|
||||||
|
|||||||
@@ -26,35 +26,40 @@ export default function VendorCreditNoteFloatingActions() {
|
|||||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
// Credit note form context.
|
// Credit note form context.
|
||||||
const { setSubmitPayload, isNewMode } = useVendorCreditNoteFormContext();
|
const { setSubmitPayload, vendorCredit } = useVendorCreditNoteFormContext();
|
||||||
|
|
||||||
// Handle submit, save and anothe new button click.
|
// Handle submit as open button click.
|
||||||
const handleSubmitAndNewBtnClick = (event) => {
|
const handleSubmitOpenBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, status: true, resetForm: true });
|
setSubmitPayload({ redirect: true, open: true });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit as save & continue editing button click.
|
// Handle submit, open and anothe new button click.
|
||||||
const handleSubmitSaveContinueEditingBtnClick = (event) => {
|
const handleSubmitOpenAndNewBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, status: true });
|
setSubmitPayload({ redirect: false, open: true, resetForm: true });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle submit as open & continue editing button click.
|
||||||
|
const handleSubmitOpenContinueEditingBtnClick = (event) => {
|
||||||
|
setSubmitPayload({ redirect: false, open: true });
|
||||||
|
submitForm();
|
||||||
|
};
|
||||||
// Handle submit as draft button click.
|
// Handle submit as draft button click.
|
||||||
const handleSubmitDraftBtnClick = (event) => {
|
const handleSubmitDraftBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: true, status: false });
|
setSubmitPayload({ redirect: true, open: false });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// handle submit as draft & new button click.
|
// handle submit as draft & new button click.
|
||||||
const handleSubmitDraftAndNewBtnClick = (event) => {
|
const handleSubmitDraftAndNewBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, status: false, resetForm: true });
|
setSubmitPayload({ redirect: false, open: false, resetForm: true });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit as draft & continue editing button click.
|
// Handle submit as draft & continue editing button click.
|
||||||
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, status: false });
|
setSubmitPayload({ redirect: false, open: false });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,89 +68,113 @@ export default function VendorCreditNoteFloatingActions() {
|
|||||||
history.goBack();
|
history.goBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit button click.
|
|
||||||
const handleSubmitBtnClick = (event) => {
|
|
||||||
setSubmitPayload({ redirect: true });
|
|
||||||
submitForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearBtnClick = (event) => {
|
const handleClearBtnClick = (event) => {
|
||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||||
{/* ----------- Save ----------- */}
|
{/* ----------- Save And Open ----------- */}
|
||||||
<ButtonGroup>
|
<If condition={!vendorCredit || !vendorCredit?.is_open}>
|
||||||
<Button
|
<ButtonGroup>
|
||||||
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}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
|
loading={isSubmitting}
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
onClick={handleSubmitOpenBtnClick}
|
||||||
|
text={<T id={'save_open'} />}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
<Popover
|
||||||
</ButtonGroup>
|
content={
|
||||||
{/* ----------- Save As Draft ----------- */}
|
<Menu>
|
||||||
<ButtonGroup>
|
<MenuItem
|
||||||
<Button
|
text={<T id={'open_and_new'} />}
|
||||||
disabled={isSubmitting}
|
onClick={handleSubmitOpenAndNewBtnClick}
|
||||||
className={'ml1'}
|
/>
|
||||||
onClick={handleSubmitDraftBtnClick}
|
<MenuItem
|
||||||
text={<T id={'save_as_draft'} />}
|
text={<T id={'open_continue_editing'} />}
|
||||||
/>
|
onClick={handleSubmitOpenContinueEditingBtnClick}
|
||||||
<Popover
|
/>
|
||||||
content={
|
</Menu>
|
||||||
<Menu>
|
}
|
||||||
<MenuItem
|
minimal={true}
|
||||||
text={<T id={'save_and_new'} />}
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
onClick={handleSubmitDraftAndNewBtnClick}
|
position={Position.BOTTOM_LEFT}
|
||||||
/>
|
>
|
||||||
<MenuItem
|
<Button
|
||||||
text={<T id={'save_continue_editing'} />}
|
disabled={isSubmitting}
|
||||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
intent={Intent.PRIMARY}
|
||||||
/>
|
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||||
</Menu>
|
/>
|
||||||
}
|
</Popover>
|
||||||
minimal={true}
|
</ButtonGroup>
|
||||||
interactionKind={PopoverInteractionKind.CLICK}
|
{/* ----------- Save As Draft ----------- */}
|
||||||
position={Position.BOTTOM_LEFT}
|
<ButtonGroup>
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
className={'ml1'}
|
||||||
|
onClick={handleSubmitDraftBtnClick}
|
||||||
|
text={<T id={'save_as_draft'} />}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
<Popover
|
||||||
</ButtonGroup>
|
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----------- */}
|
{/* ----------- Clear & Reset----------- */}
|
||||||
<Button
|
<Button
|
||||||
className={'ml1'}
|
className={'ml1'}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
onClick={handleClearBtnClick}
|
onClick={handleClearBtnClick}
|
||||||
text={isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
|
text={vendorCredit ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||||
/>
|
/>
|
||||||
{/* ----------- Cancel ----------- */}
|
{/* ----------- Cancel ----------- */}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Button, Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { sumBy, omit, isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import {
|
import {
|
||||||
@@ -95,6 +95,7 @@ function VendorCreditNoteForm({
|
|||||||
}
|
}
|
||||||
const form = {
|
const form = {
|
||||||
...transformFormValuesToRequest(values),
|
...transformFormValuesToRequest(values),
|
||||||
|
open: submitPayload.open,
|
||||||
};
|
};
|
||||||
// Handle the request success.
|
// Handle the request success.
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const getSchema = Yup.object().shape({
|
|||||||
.min(1)
|
.min(1)
|
||||||
.max(DATATYPES_LENGTH.TEXT)
|
.max(DATATYPES_LENGTH.TEXT)
|
||||||
.label(intl.get('note')),
|
.label(intl.get('note')),
|
||||||
|
open: Yup.boolean(),
|
||||||
entries: Yup.array().of(
|
entries: Yup.array().of(
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
quantity: Yup.number()
|
quantity: Yup.number()
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const defaultVendorsCreditNote = {
|
|||||||
vendor_id: '',
|
vendor_id: '',
|
||||||
vendor_credit_number: '',
|
vendor_credit_number: '',
|
||||||
vendor_credit_no_manually: false,
|
vendor_credit_no_manually: false,
|
||||||
|
open: '',
|
||||||
vendor_credit_date: moment(new Date()).format('YYYY-MM-DD'),
|
vendor_credit_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
// reference_no: '',
|
// reference_no: '',
|
||||||
note: '',
|
note: '',
|
||||||
@@ -93,6 +94,7 @@ export const transformFormValuesToRequest = (values) => {
|
|||||||
return {
|
return {
|
||||||
...values,
|
...values,
|
||||||
entries: transformEntriesToSubmit(entries),
|
entries: transformEntriesToSubmit(entries),
|
||||||
|
open: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,7 +121,7 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
|||||||
/**
|
/**
|
||||||
* Syncs invoice no. settings with form.
|
* Syncs invoice no. settings with form.
|
||||||
*/
|
*/
|
||||||
export const useObserveVendorCreditNoSettings = (prefix, nextNumber) => {
|
export const useObserveVendorCreditNoSettings = (prefix, nextNumber) => {
|
||||||
const { setFieldValue } = useFormikContext();
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
|||||||
import withVendorsCreditNotesActions from './withVendorsCreditNotesActions';
|
import withVendorsCreditNotesActions from './withVendorsCreditNotesActions';
|
||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||||
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withSettings from '../../../Settings/withSettings';
|
import withSettings from '../../../Settings/withSettings';
|
||||||
|
|
||||||
import { useVendorsCreditNoteTableColumns, ActionsMenu } from './components';
|
import { useVendorsCreditNoteTableColumns, ActionsMenu } from './components';
|
||||||
@@ -33,6 +34,9 @@ function VendorsCreditNoteDataTable({
|
|||||||
// #withDrawerActions
|
// #withDrawerActions
|
||||||
openDrawer,
|
openDrawer,
|
||||||
|
|
||||||
|
// #withDialogAction
|
||||||
|
openDialog,
|
||||||
|
|
||||||
// #withSettings
|
// #withSettings
|
||||||
creditNoteTableSize,
|
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 (
|
return (
|
||||||
<DashboardContentTable>
|
<DashboardContentTable>
|
||||||
<DataTable
|
<DataTable
|
||||||
@@ -118,6 +136,9 @@ function VendorsCreditNoteDataTable({
|
|||||||
onViewDetails: handleViewDetailVendorCredit,
|
onViewDetails: handleViewDetailVendorCredit,
|
||||||
onDelete: handleDeleteVendorCreditNote,
|
onDelete: handleDeleteVendorCreditNote,
|
||||||
onEdit: hanldeEditVendorCreditNote,
|
onEdit: hanldeEditVendorCreditNote,
|
||||||
|
onRefund: handleRefundCreditVendor,
|
||||||
|
onOpen: handleOpenCreditNote,
|
||||||
|
onReconcile: handleReconcileVendorCredit,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DashboardContentTable>
|
</DashboardContentTable>
|
||||||
@@ -129,6 +150,7 @@ export default compose(
|
|||||||
withVendorsCreditNotesActions,
|
withVendorsCreditNotesActions,
|
||||||
withAlertsActions,
|
withAlertsActions,
|
||||||
withDrawerActions,
|
withDrawerActions,
|
||||||
|
withDialogActions,
|
||||||
withSettings(({ vendorsCreditNoteSetting }) => ({
|
withSettings(({ vendorsCreditNoteSetting }) => ({
|
||||||
creditNoteTableSize: vendorsCreditNoteSetting?.tableSize,
|
creditNoteTableSize: vendorsCreditNoteSetting?.tableSize,
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { Intent, Tag, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||||
Intent,
|
|
||||||
Tag,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
MenuDivider,
|
|
||||||
ProgressBar,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
|
|
||||||
@@ -14,18 +7,17 @@ import { CLASSES } from '../../../../common/classes';
|
|||||||
import {
|
import {
|
||||||
FormatDateCell,
|
FormatDateCell,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
AppToaster,
|
|
||||||
Choose,
|
Choose,
|
||||||
If,
|
If,
|
||||||
Icon,
|
Icon,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { formattedAmount, safeCallback, calculateStatus } from 'utils';
|
import { safeCallback } from 'utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actions menu.
|
* Actions menu.
|
||||||
*/
|
*/
|
||||||
export function ActionsMenu({
|
export function ActionsMenu({
|
||||||
payload: { onEdit, onDelete, onViewDetails },
|
payload: { onEdit, onDelete, onOpen, onRefund, onReconcile, onViewDetails },
|
||||||
row: { original },
|
row: { original },
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@@ -41,6 +33,26 @@ export function ActionsMenu({
|
|||||||
text={intl.get('vendor_credits.action.edit_vendor_credit')}
|
text={intl.get('vendor_credits.action.edit_vendor_credit')}
|
||||||
onClick={safeCallback(onEdit, original)}
|
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
|
<MenuItem
|
||||||
text={intl.get('vendor_credits.action.delete_vendor_credit')}
|
text={intl.get('vendor_credits.action.delete_vendor_credit')}
|
||||||
intent={Intent.DANGER}
|
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.
|
* Retrieve vendors credit note table columns.
|
||||||
*/
|
*/
|
||||||
@@ -98,8 +139,8 @@ export function useVendorsCreditNoteTableColumns() {
|
|||||||
{
|
{
|
||||||
id: 'status',
|
id: 'status',
|
||||||
Header: intl.get('status'),
|
Header: intl.get('status'),
|
||||||
// accessor:
|
accessor: StatusAccessor,
|
||||||
width: 120, // 160
|
width: 160,
|
||||||
className: 'status',
|
className: 'status',
|
||||||
clickable: true,
|
clickable: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ const VendorCreditDeleteAlert = React.lazy(() =>
|
|||||||
import('../../Alerts/VendorCeditNotes/VendorCreditDeleteAlert'),
|
import('../../Alerts/VendorCeditNotes/VendorCreditDeleteAlert'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const RefundVendorCreditDeleteAlert = React.lazy(() =>
|
||||||
|
import('../../Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const OpenVendorCreditAlert = React.lazy(() =>
|
||||||
|
import('../../Alerts/VendorCeditNotes/VendorCreditOpenedAlert'),
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vendor Credit notes alerts.
|
* Vendor Credit notes alerts.
|
||||||
*/
|
*/
|
||||||
@@ -12,4 +20,12 @@ export default [
|
|||||||
name: 'vendor-credit-delete',
|
name: 'vendor-credit-delete',
|
||||||
component: VendorCreditDeleteAlert,
|
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();
|
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
// Credit note form context.
|
// Credit note form context.
|
||||||
const { setSubmitPayload, isNewMode } = useCreditNoteFormContext();
|
const { setSubmitPayload, creditNote } = useCreditNoteFormContext();
|
||||||
|
|
||||||
// Handle submit, save and anothe new button click.
|
// Handle submit as open button click.
|
||||||
const handleSubmitAndNewBtnClick = (event) => {
|
const handleSubmitOpenBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, status: true, resetForm: true });
|
setSubmitPayload({ redirect: true, open: true });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit as save & continue editing button click.
|
// Handle submit, open and anothe new button click.
|
||||||
const handleSubmitSaveContinueEditingBtnClick = (event) => {
|
const handleSubmitOpenAndNewBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, status: true });
|
setSubmitPayload({ redirect: false, open: true, resetForm: true });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle submit as open & continue editing button click.
|
||||||
|
const handleSubmitOpenContinueEditingBtnClick = (event) => {
|
||||||
|
setSubmitPayload({ redirect: false, open: true });
|
||||||
|
submitForm();
|
||||||
|
};
|
||||||
// Handle submit as draft button click.
|
// Handle submit as draft button click.
|
||||||
const handleSubmitDraftBtnClick = (event) => {
|
const handleSubmitDraftBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: true, status: false });
|
setSubmitPayload({ redirect: true, open: false });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// handle submit as draft & new button click.
|
// handle submit as draft & new button click.
|
||||||
const handleSubmitDraftAndNewBtnClick = (event) => {
|
const handleSubmitDraftAndNewBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, status: false, resetForm: true });
|
setSubmitPayload({ redirect: false, open: false, resetForm: true });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit as draft & continue editing button click.
|
// Handle submit as draft & continue editing button click.
|
||||||
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: false, status: false });
|
setSubmitPayload({ redirect: false, open: false });
|
||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,89 +68,114 @@ export default function CreditNoteFloatingActions() {
|
|||||||
history.goBack();
|
history.goBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle submit button click.
|
|
||||||
const handleSubmitBtnClick = (event) => {
|
|
||||||
setSubmitPayload({ redirect: true });
|
|
||||||
submitForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearBtnClick = (event) => {
|
const handleClearBtnClick = (event) => {
|
||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||||
{/* ----------- Save ----------- */}
|
{/* ----------- Save And Open ----------- */}
|
||||||
<ButtonGroup>
|
<If condition={!creditNote || !creditNote?.is_open}>
|
||||||
<Button
|
<ButtonGroup>
|
||||||
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}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
|
loading={isSubmitting}
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
onClick={handleSubmitOpenBtnClick}
|
||||||
|
text={<T id={'save_open'} />}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
<Popover
|
||||||
</ButtonGroup>
|
content={
|
||||||
{/* ----------- Save As Draft ----------- */}
|
<Menu>
|
||||||
<ButtonGroup>
|
<MenuItem
|
||||||
<Button
|
text={<T id={'open_and_new'} />}
|
||||||
disabled={isSubmitting}
|
onClick={handleSubmitOpenAndNewBtnClick}
|
||||||
className={'ml1'}
|
/>
|
||||||
onClick={handleSubmitDraftBtnClick}
|
<MenuItem
|
||||||
text={<T id={'save_as_draft'} />}
|
text={<T id={'open_continue_editing'} />}
|
||||||
/>
|
onClick={handleSubmitOpenContinueEditingBtnClick}
|
||||||
<Popover
|
/>
|
||||||
content={
|
</Menu>
|
||||||
<Menu>
|
}
|
||||||
<MenuItem
|
minimal={true}
|
||||||
text={<T id={'save_and_new'} />}
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
onClick={handleSubmitDraftAndNewBtnClick}
|
position={Position.BOTTOM_LEFT}
|
||||||
/>
|
>
|
||||||
<MenuItem
|
<Button
|
||||||
text={<T id={'save_continue_editing'} />}
|
disabled={isSubmitting}
|
||||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
intent={Intent.PRIMARY}
|
||||||
/>
|
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||||
</Menu>
|
/>
|
||||||
}
|
</Popover>
|
||||||
minimal={true}
|
</ButtonGroup>
|
||||||
interactionKind={PopoverInteractionKind.CLICK}
|
{/* ----------- Save As Draft ----------- */}
|
||||||
position={Position.BOTTOM_LEFT}
|
<ButtonGroup>
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
className={'ml1'}
|
||||||
|
onClick={handleSubmitDraftBtnClick}
|
||||||
|
text={<T id={'save_as_draft'} />}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
<Popover
|
||||||
</ButtonGroup>
|
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----------- */}
|
{/* ----------- Clear & Reset----------- */}
|
||||||
<Button
|
<Button
|
||||||
className={'ml1'}
|
className={'ml1'}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
onClick={handleClearBtnClick}
|
onClick={handleClearBtnClick}
|
||||||
text={isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
|
text={creditNote ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||||
/>
|
/>
|
||||||
{/* ----------- Cancel ----------- */}
|
{/* ----------- Cancel ----------- */}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useHistory } from 'react-router-dom';
|
|||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { sumBy, omit, isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import {
|
import {
|
||||||
@@ -97,6 +97,7 @@ function CreditNoteForm({
|
|||||||
}
|
}
|
||||||
const form = {
|
const form = {
|
||||||
...transformFormValuesToRequest(values),
|
...transformFormValuesToRequest(values),
|
||||||
|
open: submitPayload.open,
|
||||||
};
|
};
|
||||||
// Handle the request success.
|
// Handle the request success.
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const getSchema = () =>
|
|||||||
.min(1)
|
.min(1)
|
||||||
.max(DATATYPES_LENGTH.TEXT)
|
.max(DATATYPES_LENGTH.TEXT)
|
||||||
.label(intl.get('note')),
|
.label(intl.get('note')),
|
||||||
|
open: Yup.boolean(),
|
||||||
terms_conditions: Yup.string()
|
terms_conditions: Yup.string()
|
||||||
.trim()
|
.trim()
|
||||||
.min(1)
|
.min(1)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export const defaultCreditNote = {
|
|||||||
credit_note_date: moment(new Date()).format('YYYY-MM-DD'),
|
credit_note_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
credit_note_number: '',
|
credit_note_number: '',
|
||||||
credit_note_no_manually: false,
|
credit_note_no_manually: false,
|
||||||
|
open: '',
|
||||||
// reference_no: '',
|
// reference_no: '',
|
||||||
note: '',
|
note: '',
|
||||||
terms_conditions: '',
|
terms_conditions: '',
|
||||||
@@ -82,19 +83,20 @@ export const transformEntriesToSubmit = (entries) => {
|
|||||||
/**
|
/**
|
||||||
* Filters the givne non-zero entries.
|
* Filters the givne non-zero entries.
|
||||||
*/
|
*/
|
||||||
export const filterNonZeroEntries = (entries) => {
|
export const filterNonZeroEntries = (entries) => {
|
||||||
return entries.filter((item) => item.item_id && item.quantity);
|
return entries.filter((item) => item.item_id && item.quantity);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes form values to request body.
|
* Transformes form values to request body.
|
||||||
*/
|
*/
|
||||||
export const transformFormValuesToRequest = (values) => {
|
export const transformFormValuesToRequest = (values) => {
|
||||||
const entries = filterNonZeroEntries(values.entries);
|
const entries = filterNonZeroEntries(values.entries);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...values,
|
...values,
|
||||||
entries: transformEntriesToSubmit(entries),
|
entries: transformEntriesToSubmit(entries),
|
||||||
|
open: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -121,7 +123,7 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
|||||||
/**
|
/**
|
||||||
* Syncs invoice no. settings with form.
|
* Syncs invoice no. settings with form.
|
||||||
*/
|
*/
|
||||||
export const useObserveCreditNoSettings = (prefix, nextNumber) => {
|
export const useObserveCreditNoSettings = (prefix, nextNumber) => {
|
||||||
const { setFieldValue } = useFormikContext();
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
@@ -4,6 +4,18 @@ const CreditNoteDeleteAlert = React.lazy(() =>
|
|||||||
import('../../Alerts/CreditNotes/CreditNoteDeleteAlert'),
|
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.
|
* Credit notes alerts.
|
||||||
*/
|
*/
|
||||||
@@ -12,4 +24,16 @@ export default [
|
|||||||
name: 'credit-note-delete',
|
name: 'credit-note-delete',
|
||||||
component: CreditNoteDeleteAlert,
|
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 withCreditNotesActions from './withCreditNotesActions';
|
||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||||
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withSettings from '../../../Settings/withSettings';
|
import withSettings from '../../../Settings/withSettings';
|
||||||
|
|
||||||
import { useCreditNoteTableColumns, ActionsMenu } from './components';
|
import { useCreditNoteTableColumns, ActionsMenu } from './components';
|
||||||
@@ -33,6 +34,9 @@ function CreditNotesDataTable({
|
|||||||
// #withDrawerActions
|
// #withDrawerActions
|
||||||
openDrawer,
|
openDrawer,
|
||||||
|
|
||||||
|
// #withDialogAction
|
||||||
|
openDialog,
|
||||||
|
|
||||||
// #withSettings
|
// #withSettings
|
||||||
creditNoteTableSize,
|
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 (
|
return (
|
||||||
<DashboardContentTable>
|
<DashboardContentTable>
|
||||||
<DataTable
|
<DataTable
|
||||||
@@ -117,6 +135,9 @@ function CreditNotesDataTable({
|
|||||||
onViewDetails: handleViewDetailCreditNote,
|
onViewDetails: handleViewDetailCreditNote,
|
||||||
onDelete: handleDeleteCreditNote,
|
onDelete: handleDeleteCreditNote,
|
||||||
onEdit: hanldeEditCreditNote,
|
onEdit: hanldeEditCreditNote,
|
||||||
|
onRefund: handleRefundCreditNote,
|
||||||
|
onOpen: handleOpenCreditNote,
|
||||||
|
onReconcile: handleReconcileCreditNote,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DashboardContentTable>
|
</DashboardContentTable>
|
||||||
@@ -128,6 +149,7 @@ export default compose(
|
|||||||
withCreditNotesActions,
|
withCreditNotesActions,
|
||||||
withDrawerActions,
|
withDrawerActions,
|
||||||
withAlertsActions,
|
withAlertsActions,
|
||||||
|
withDialogActions,
|
||||||
withSettings(({ creditNoteSettings }) => ({
|
withSettings(({ creditNoteSettings }) => ({
|
||||||
creditNoteTableSize: creditNoteSettings?.tableSize,
|
creditNoteTableSize: creditNoteSettings?.tableSize,
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { Intent, Tag, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||||
Intent,
|
|
||||||
Tag,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
MenuDivider,
|
|
||||||
ProgressBar,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
|
|
||||||
@@ -14,15 +7,14 @@ import { CLASSES } from '../../../../common/classes';
|
|||||||
import {
|
import {
|
||||||
FormatDateCell,
|
FormatDateCell,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
AppToaster,
|
|
||||||
Choose,
|
Choose,
|
||||||
If,
|
If,
|
||||||
Icon,
|
Icon,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { formattedAmount, safeCallback, calculateStatus } from 'utils';
|
import { safeCallback } from 'utils';
|
||||||
|
|
||||||
export function ActionsMenu({
|
export function ActionsMenu({
|
||||||
payload: { onEdit, onDelete, onViewDetails },
|
payload: { onEdit, onDelete, onRefund, onOpen, onReconcile, onViewDetails },
|
||||||
row: { original },
|
row: { original },
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@@ -38,6 +30,26 @@ export function ActionsMenu({
|
|||||||
text={intl.get('credit_note.action.edit_credit_note')}
|
text={intl.get('credit_note.action.edit_credit_note')}
|
||||||
onClick={safeCallback(onEdit, original)}
|
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
|
<MenuItem
|
||||||
text={intl.get('credit_note.action.delete_credit_note')}
|
text={intl.get('credit_note.action.delete_credit_note')}
|
||||||
intent={Intent.DANGER}
|
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.
|
* Retrieve credit note table columns.
|
||||||
*/
|
*/
|
||||||
@@ -95,8 +136,8 @@ export function useCreditNoteTableColumns() {
|
|||||||
{
|
{
|
||||||
id: 'status',
|
id: 'status',
|
||||||
Header: intl.get('status'),
|
Header: intl.get('status'),
|
||||||
// accessor:
|
accessor: StatusAccessor,
|
||||||
width: 120, // 160
|
width: 160, // 160
|
||||||
className: 'status',
|
className: 'status',
|
||||||
clickable: true,
|
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,
|
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({
|
export function ActionsMenu({
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ const commonInvalidateQueries = (queryClient) => {
|
|||||||
// Invalidate settings.
|
// Invalidate settings.
|
||||||
queryClient.invalidateQueries([t.SETTING, t.SETTING_CREDIT_NOTES]);
|
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.
|
// Invalidate financial reports.
|
||||||
queryClient.invalidateQueries(t.FINANCIAL_REPORT);
|
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 = {
|
const CREDIT_NOTES = {
|
||||||
CREDIT_NOTE: 'CREDIT_NOTE',
|
CREDIT_NOTE: 'CREDIT_NOTE',
|
||||||
CREDIT_NOTES: 'CREDIT_NOTES',
|
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 = {
|
const VENDOR_CREDIT_NOTES = {
|
||||||
VENDOR_CREDITS: 'VENDOR_CREDITS',
|
VENDOR_CREDITS: 'VENDOR_CREDITS',
|
||||||
VENDOR_CREDIT: 'VENDOR_CREDIT',
|
VENDOR_CREDIT: 'VENDOR_CREDIT',
|
||||||
|
REFUND_VENDOR_CREDIT: 'REFUND_VENDOR_CREDIT',
|
||||||
|
RECONCILE_VENDOR_CREDIT: 'RECONCILE_VENDOR_CREDIT',
|
||||||
|
RECONCILE_VENDOR_CREDITS: 'RECONCILE_VENDOR_CREDITS',
|
||||||
};
|
};
|
||||||
|
|
||||||
const SETTING = {
|
const SETTING = {
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ const commonInvalidateQueries = (queryClient) => {
|
|||||||
// Invalidate settings.
|
// Invalidate settings.
|
||||||
queryClient.invalidateQueries([t.SETTING, t.SETTING_VENDOR_CREDITS]);
|
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.
|
// Invalidate financial reports.
|
||||||
queryClient.invalidateQueries(t.FINANCIAL_REPORT);
|
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.new_credit_note": "New Credit Note",
|
||||||
"credit_note.label.edit_credit_note": "Edit Credit Note",
|
"credit_note.label.edit_credit_note": "Edit Credit Note",
|
||||||
"credit_note.action.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.action.delete_credit_note": "Delete Credit note",
|
||||||
"credit_note.column.credit_note_no": "Credit Note #",
|
"credit_note.column.credit_note_no": "Credit Note #",
|
||||||
"credit_note.column.credit_date": "Credit Date",
|
"credit_note.column.credit_date": "Credit Date",
|
||||||
@@ -1512,6 +1513,7 @@
|
|||||||
"vendor_credits.column.vendor_credit_no": "Vendor Credit #",
|
"vendor_credits.column.vendor_credit_no": "Vendor Credit #",
|
||||||
"vendor_credits.action.new_vendor_credit": "New Vendor Credit",
|
"vendor_credits.action.new_vendor_credit": "New Vendor Credit",
|
||||||
"vendor_credits.action.edit_vendor_credit": "Edit Vendot 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.action.delete_vendor_credit": "Delete Vendot Credit",
|
||||||
"vendor_credits.success_message": "The vendor credit has been created successfully",
|
"vendor_credits.success_message": "The vendor credit has been created successfully",
|
||||||
"vendor_credits.edit_success_message": "The vendor credit has been edited 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.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?",
|
"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",
|
"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_credit_note_detail": "Credit Note details",
|
||||||
"credit_note.drawer.label_credit_note_no":"Credit Note #",
|
"credit_note.drawer.label_credit_note_no": "Credit Note #",
|
||||||
"credit_note.drawer.label_credit_note_date":"Credit Date",
|
"credit_note.drawer.label_credit_note_date": "Credit Date",
|
||||||
"credit_note.drawer.label_create_at":"Create at",
|
"credit_note.drawer.label_create_at": "Create at",
|
||||||
"credit_note.drawer.label_total": "TOTAL",
|
"credit_note.drawer.label_total": "TOTAL",
|
||||||
"credit_note.drawer.label_subtotal": "Subtotal",
|
"credit_note.drawer.label_subtotal": "Subtotal",
|
||||||
"vendor_credit.drawer_vendor_credit_detail":"Vendor Credit details",
|
"credit_note.drawer.label_refund_transactions": "Refund transactions",
|
||||||
"vendor_credit.drawer.label_vendor_credit_no":"Vendor Credit #",
|
"credit_note.drawer.label_reconcile_transactions": "Reconcile transactions",
|
||||||
"vendor_credit.drawer.label_vendor_credit_date":"Vendor Credit Date",
|
"vendor_credit.drawer_vendor_credit_detail": "Vendor Credit details",
|
||||||
"vendor_credit.drawer.label_create_at":"Create at",
|
"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_total": "TOTAL",
|
||||||
"vendor_credit.drawer.label_subtotal": "Subtotal",
|
"vendor_credit.drawer.label_subtotal": "Subtotal",
|
||||||
"landed_cost.dialog.label_select_transaction":"Select transaction",
|
"landed_cost.dialog.label_select_transaction": "Select transaction",
|
||||||
"landed_cost.dialog.label_select_transaction_entry":"Select transaction entry"
|
"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