mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +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 RefundVendorCreditDialog from '../containers/Dialogs/RefundVendorCreditDialog';
|
||||
import ReconcileCreditNoteDialog from '../containers/Dialogs/ReconcileCreditNoteDialog';
|
||||
import ReconcileVendorCreditDialog from '../containers/Dialogs/ReconcileVendorCreditDialog';
|
||||
|
||||
/**
|
||||
* Dialogs container.
|
||||
@@ -66,6 +67,7 @@ export default function DialogsContainer() {
|
||||
<RefundCreditNoteDialog dialogName={'refund-credit-note'} />
|
||||
<RefundVendorCreditDialog dialogName={'refund-vendor-credit'} />
|
||||
<ReconcileCreditNoteDialog dialogName={'reconcile-credit-note'} />
|
||||
<ReconcileVendorCreditDialog dialogName={'reconcile-vendor-credit'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ function ReconcileCreditNoteForm({
|
||||
|
||||
// Filters the 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]));
|
||||
|
||||
const form = {
|
||||
|
||||
@@ -32,7 +32,7 @@ function ReconcileCreditNoteFormFloatingActions({
|
||||
</Button>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
style={{ minWidth: '85px' }}
|
||||
style={{ minWidth: '95px' }}
|
||||
type="submit"
|
||||
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 }) => {
|
||||
openAlert('vendor-credit-open', { vendorCreditId: id });
|
||||
};
|
||||
|
||||
// Handle reconcile credit note.
|
||||
const handleReconcileVendorCredit = ({ id }) => {
|
||||
openDialog('reconcile-vendor-credit', { vendorCreditId: id });
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardContentTable>
|
||||
<DataTable
|
||||
@@ -132,6 +138,7 @@ function VendorsCreditNoteDataTable({
|
||||
onEdit: hanldeEditVendorCreditNote,
|
||||
onRefund: handleRefundCreditVendor,
|
||||
onOpen: handleOpenCreditNote,
|
||||
onReconcile: handleReconcileVendorCredit,
|
||||
}}
|
||||
/>
|
||||
</DashboardContentTable>
|
||||
|
||||
@@ -17,7 +17,7 @@ import { safeCallback } from 'utils';
|
||||
* Actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
payload: { onEdit, onDelete, onOpen, onRefund, onViewDetails },
|
||||
payload: { onEdit, onDelete, onOpen, onRefund, onReconcile, onViewDetails },
|
||||
row: { original },
|
||||
}) {
|
||||
return (
|
||||
@@ -47,6 +47,12 @@ export function ActionsMenu({
|
||||
onClick={safeCallback(onOpen, original)}
|
||||
/>
|
||||
</If>
|
||||
<MenuItem
|
||||
text={'Reconcile Credit Note With bills'}
|
||||
// icon={<Icon icon="quick-payment-16" />}
|
||||
// text={intl.get('credit_note.action.refund_credit_note')}
|
||||
onClick={safeCallback(onReconcile, original)}
|
||||
/>
|
||||
<MenuItem
|
||||
text={intl.get('vendor_credits.action.delete_vendor_credit')}
|
||||
intent={Intent.DANGER}
|
||||
|
||||
@@ -28,6 +28,7 @@ const commonInvalidateQueries = (queryClient) => {
|
||||
queryClient.invalidateQueries(t.REFUND_CREDIT_NOTE);
|
||||
|
||||
// Invalidate reconcile.
|
||||
queryClient.invalidateQueries(t.RECONCILE_CREDIT_NOTE);
|
||||
queryClient.invalidateQueries(t.RECONCILE_CREDIT_NOTES);
|
||||
|
||||
// Invalidate financial reports.
|
||||
|
||||
@@ -27,6 +27,10 @@ const commonInvalidateQueries = (queryClient) => {
|
||||
// Invalidate refund vendor credit
|
||||
queryClient.invalidateQueries(t.REFUND_VENDOR_CREDIT);
|
||||
|
||||
// Invalidate reconcile vendor credit.
|
||||
queryClient.invalidateQueries(t.RECONCILE_VENDOR_CREDIT);
|
||||
queryClient.invalidateQueries(t.RECONCILE_VENDOR_CREDITS);
|
||||
|
||||
// Invalidate financial reports.
|
||||
queryClient.invalidateQueries(t.FINANCIAL_REPORT);
|
||||
};
|
||||
@@ -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?",
|
||||
"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."
|
||||
"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