mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
feat: add reconcile vendor credit.
This commit is contained in:
@@ -29,6 +29,7 @@ import TransactionsLockingDialog from '../containers/Dialogs/TransactionsLocking
|
|||||||
import RefundCreditNoteDialog from '../containers/Dialogs/RefundCreditNoteDialog';
|
import RefundCreditNoteDialog from '../containers/Dialogs/RefundCreditNoteDialog';
|
||||||
import RefundVendorCreditDialog from '../containers/Dialogs/RefundVendorCreditDialog';
|
import RefundVendorCreditDialog from '../containers/Dialogs/RefundVendorCreditDialog';
|
||||||
import ReconcileCreditNoteDialog from '../containers/Dialogs/ReconcileCreditNoteDialog';
|
import ReconcileCreditNoteDialog from '../containers/Dialogs/ReconcileCreditNoteDialog';
|
||||||
|
import ReconcileVendorCreditDialog from '../containers/Dialogs/ReconcileVendorCreditDialog';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialogs container.
|
* Dialogs container.
|
||||||
@@ -66,6 +67,7 @@ export default function DialogsContainer() {
|
|||||||
<RefundCreditNoteDialog dialogName={'refund-credit-note'} />
|
<RefundCreditNoteDialog dialogName={'refund-credit-note'} />
|
||||||
<RefundVendorCreditDialog dialogName={'refund-vendor-credit'} />
|
<RefundVendorCreditDialog dialogName={'refund-vendor-credit'} />
|
||||||
<ReconcileCreditNoteDialog dialogName={'reconcile-credit-note'} />
|
<ReconcileCreditNoteDialog dialogName={'reconcile-credit-note'} />
|
||||||
|
<ReconcileVendorCreditDialog dialogName={'reconcile-vendor-credit'} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ function ReconcileCreditNoteForm({
|
|||||||
|
|
||||||
// Filters the entries.
|
// Filters the entries.
|
||||||
const entries = values.entries
|
const entries = values.entries
|
||||||
.filter((entry) => entry.id && entry.amount)
|
.filter((entry) => entry.invoice_id && entry.amount)
|
||||||
.map((entry) => transformToForm(entry, defaultInitialValues.entries[0]));
|
.map((entry) => transformToForm(entry, defaultInitialValues.entries[0]));
|
||||||
|
|
||||||
const form = {
|
const form = {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function ReconcileCreditNoteFormFloatingActions({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
style={{ minWidth: '85px' }}
|
style={{ minWidth: '95px' }}
|
||||||
type="submit"
|
type="submit"
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -104,6 +104,12 @@ function VendorsCreditNoteDataTable({
|
|||||||
const handleOpenCreditNote = ({ id }) => {
|
const handleOpenCreditNote = ({ id }) => {
|
||||||
openAlert('vendor-credit-open', { vendorCreditId: 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
|
||||||
@@ -132,6 +138,7 @@ function VendorsCreditNoteDataTable({
|
|||||||
onEdit: hanldeEditVendorCreditNote,
|
onEdit: hanldeEditVendorCreditNote,
|
||||||
onRefund: handleRefundCreditVendor,
|
onRefund: handleRefundCreditVendor,
|
||||||
onOpen: handleOpenCreditNote,
|
onOpen: handleOpenCreditNote,
|
||||||
|
onReconcile: handleReconcileVendorCredit,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DashboardContentTable>
|
</DashboardContentTable>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { safeCallback } from 'utils';
|
|||||||
* Actions menu.
|
* Actions menu.
|
||||||
*/
|
*/
|
||||||
export function ActionsMenu({
|
export function ActionsMenu({
|
||||||
payload: { onEdit, onDelete, onOpen, onRefund, onViewDetails },
|
payload: { onEdit, onDelete, onOpen, onRefund, onReconcile, onViewDetails },
|
||||||
row: { original },
|
row: { original },
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@@ -47,6 +47,12 @@ export function ActionsMenu({
|
|||||||
onClick={safeCallback(onOpen, original)}
|
onClick={safeCallback(onOpen, original)}
|
||||||
/>
|
/>
|
||||||
</If>
|
</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}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const commonInvalidateQueries = (queryClient) => {
|
|||||||
queryClient.invalidateQueries(t.REFUND_CREDIT_NOTE);
|
queryClient.invalidateQueries(t.REFUND_CREDIT_NOTE);
|
||||||
|
|
||||||
// Invalidate reconcile.
|
// Invalidate reconcile.
|
||||||
|
queryClient.invalidateQueries(t.RECONCILE_CREDIT_NOTE);
|
||||||
queryClient.invalidateQueries(t.RECONCILE_CREDIT_NOTES);
|
queryClient.invalidateQueries(t.RECONCILE_CREDIT_NOTES);
|
||||||
|
|
||||||
// Invalidate financial reports.
|
// Invalidate financial reports.
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ const commonInvalidateQueries = (queryClient) => {
|
|||||||
// Invalidate refund vendor credit
|
// Invalidate refund vendor credit
|
||||||
queryClient.invalidateQueries(t.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);
|
||||||
};
|
};
|
||||||
@@ -241,3 +245,87 @@ export function useOpenVendorCredit(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,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1579,5 +1579,13 @@
|
|||||||
"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?",
|
"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_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.",
|
"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."
|
"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,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user