feat: add status & opened alert in credit & vendor.

This commit is contained in:
elforjani13
2021-12-06 16:28:42 +02:00
parent 2a48d9be51
commit ac99a6ca75
25 changed files with 567 additions and 222 deletions

View 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);

View File

@@ -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);

View File

@@ -41,7 +41,7 @@ function RefundCreditNoteForm({
// Handles the form submit. // Handles the form submit.
const handleFormSubmit = (values, { setSubmitting, setFieldError }) => { const handleFormSubmit = (values, { setSubmitting, setFieldError }) => {
const form = { const form = {
...omit(values, ['currency_code', 'formatted_amount']), ...omit(values, ['currency_code', 'credits_remaining']),
}; };
// Handle request response success. // Handle request response success.

View File

@@ -31,8 +31,8 @@ function RefundCreditNoteFormProvider({ creditNoteId, dialogName, ...props }) {
// State provider. // State provider.
const provider = { const provider = {
creditNote: { creditNote: {
...pick(creditNote, ['id', 'formatted_amount', 'currency_code']), ...pick(creditNote, ['id', 'credits_remaining', 'currency_code']),
amount: creditNote.formatted_amount, amount: creditNote.credits_remaining,
}, },
accounts, accounts,
dialogName, dialogName,

View File

@@ -41,7 +41,7 @@ function RefundVendorCreditForm({
// Handles the form submit. // Handles the form submit.
const handleFormSubmit = (values, { setSubmitting, setFieldError }) => { const handleFormSubmit = (values, { setSubmitting, setFieldError }) => {
const form = { const form = {
...omit(values, ['currency_code', 'formatted_amount']), ...omit(values, ['currency_code', 'credits_remaining']),
}; };
// Handle request response success. // Handle request response success.

View File

@@ -31,8 +31,8 @@ function RefundVendorCreditFormProvider({
// State provider. // State provider.
const provider = { const provider = {
vendorCredit: { vendorCredit: {
...pick(vendorCredit, ['id', 'formatted_amount', 'currency_code']), ...pick(vendorCredit, ['id', 'credits_remaining', 'currency_code']),
amount: vendorCredit.formatted_amount, amount: vendorCredit.credits_remaining,
}, },
accounts, accounts,
dialogName, dialogName,

View File

@@ -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();
@@ -61,15 +67,15 @@ function CreditNoteDetailActionsBar({
onClick={handleEditCreditNote} onClick={handleEditCreditNote}
/> />
<NavbarDivider /> <NavbarDivider />
<Button <If condition={!creditNote.is_closed && !creditNote.is_draft}>
className={Classes.MINIMAL} <Button
icon={<Icon icon="quick-payment-16" iconSize={16} />} className={Classes.MINIMAL}
text={'Refund'} icon={<Icon icon="quick-payment-16" iconSize={16} />}
// text={<T id={'add_payment'} />} text={<T id={'refund'} />}
onClick={handleRefundCreditNote} onClick={handleRefundCreditNote}
/> />
<NavbarDivider /> <NavbarDivider />
</If>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon={'trash-16'} iconSize={16} />} icon={<Icon icon={'trash-16'} iconSize={16} />}

View File

@@ -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();
@@ -42,14 +42,15 @@ function VendorCreditDetailActionsBar({
closeDrawer('vendor-credit-detail-drawer'); closeDrawer('vendor-credit-detail-drawer');
}; };
const handleRefundVendorCredit = () => {
openDialog('refund-vendor-credit', { vendorCreditId });
};
// Handle delete credit note. // Handle delete credit note.
const handleDeleteVendorCredit = () => { const handleDeleteVendorCredit = () => {
openAlert('vendor-credit-delete', { vendorCreditId }); openAlert('vendor-credit-delete', { vendorCreditId });
}; };
const handleRefundVendorCredit = () => {
openDialog('refund-vendor-credit', { vendorCreditId });
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
<NavbarGroup> <NavbarGroup>
@@ -60,14 +61,15 @@ function VendorCreditDetailActionsBar({
onClick={handleEditVendorCredit} onClick={handleEditVendorCredit}
/> />
<NavbarDivider /> <NavbarDivider />
<Button <If condition={!vendorCredit.is_closed && !vendorCredit.is_draft}>
className={Classes.MINIMAL} <Button
icon={<Icon icon="quick-payment-16" iconSize={16} />} className={Classes.MINIMAL}
text={'Refund'} icon={<Icon icon="quick-payment-16" iconSize={16} />}
// text={<T id={'add_payment'} />} text={<T id={'refund'} />}
onClick={handleRefundVendorCredit} onClick={handleRefundVendorCredit}
/> />
<NavbarDivider /> <NavbarDivider />
</If>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon={'trash-16'} iconSize={16} />} icon={<Icon icon={'trash-16'} iconSize={16} />}

View File

@@ -26,35 +26,39 @@ 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 as open button click.
// Handle submit, save and anothe new button click. const handleSubmitOpenBtnClick = (event) => {
const handleSubmitAndNewBtnClick = (event) => { setSubmitPayload({ redirect: true, open: true });
setSubmitPayload({ redirect: false, status: true, resetForm: 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 +67,114 @@ 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

View File

@@ -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) => {

View File

@@ -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()

View File

@@ -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(() => {

View File

@@ -100,6 +100,10 @@ function VendorsCreditNoteDataTable({
openDialog('refund-vendor-credit', { vendorCreditId: id }); openDialog('refund-vendor-credit', { vendorCreditId: id });
}; };
// Handle cancel/confirm vendor credit open.
const handleOpenCreditNote = ({ id }) => {
openAlert('vendor-credit-open', { vendorCreditId: id });
};
return ( return (
<DashboardContentTable> <DashboardContentTable>
<DataTable <DataTable
@@ -127,6 +131,7 @@ function VendorsCreditNoteDataTable({
onDelete: handleDeleteVendorCreditNote, onDelete: handleDeleteVendorCreditNote,
onEdit: hanldeEditVendorCreditNote, onEdit: hanldeEditVendorCreditNote,
onRefund: handleRefundCreditVendor, onRefund: handleRefundCreditVendor,
onOpen: handleOpenCreditNote,
}} }}
/> />
</DashboardContentTable> </DashboardContentTable>

View File

@@ -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, onRefund, onViewDetails }, payload: { onEdit, onDelete, onOpen, onRefund, onViewDetails },
row: { original }, row: { original },
}) { }) {
return ( return (
@@ -41,11 +33,20 @@ 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)}
/> />
<MenuItem <If condition={!original.is_closed && !original.is_draft}>
icon={<Icon icon="quick-payment-16" />} <MenuItem
text={intl.get('vendor_credits.action.refund_vendor_credit')} icon={<Icon icon="quick-payment-16" />}
onClick={safeCallback(onRefund, original)} 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 <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}
@@ -56,6 +57,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.
*/ */
@@ -103,8 +133,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,
}, },

View File

@@ -8,6 +8,10 @@ const RefundVendorCreditDeleteAlert = React.lazy(() =>
import('../../Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert'), import('../../Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert'),
); );
const OpenVendorCreditAlert = React.lazy(() =>
import('../../Alerts/VendorCeditNotes/VendorCreditOpenedAlert'),
);
/** /**
* Vendor Credit notes alerts. * Vendor Credit notes alerts.
*/ */
@@ -16,6 +20,10 @@ export default [
name: 'vendor-credit-delete', name: 'vendor-credit-delete',
component: VendorCreditDeleteAlert, component: VendorCreditDeleteAlert,
}, },
{
name: 'vendor-credit-open',
component: OpenVendorCreditAlert,
},
{ {
name: 'refund-vendor-delete', name: 'refund-vendor-delete',
component: RefundVendorCreditDeleteAlert, component: RefundVendorCreditDeleteAlert,

View File

@@ -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

View File

@@ -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) => {

View File

@@ -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)

View File

@@ -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(() => {

View File

@@ -8,6 +8,10 @@ const RefundCreditNoteDeleteAlert = React.lazy(() =>
import('../../Alerts/CreditNotes/RefundCreditNoteDeleteAlert'), import('../../Alerts/CreditNotes/RefundCreditNoteDeleteAlert'),
); );
const OpenCreditNoteAlert = React.lazy(() =>
import('../../Alerts/CreditNotes/CreditNoteOpenedAlert'),
);
/** /**
* Credit notes alerts. * Credit notes alerts.
*/ */
@@ -16,6 +20,10 @@ export default [
name: 'credit-note-delete', name: 'credit-note-delete',
component: CreditNoteDeleteAlert, component: CreditNoteDeleteAlert,
}, },
{
name: 'credit-note-open',
component: OpenCreditNoteAlert,
},
{ {
name: 'refund-credit-delete', name: 'refund-credit-delete',
component: RefundCreditNoteDeleteAlert, component: RefundCreditNoteDeleteAlert,

View File

@@ -100,6 +100,11 @@ function CreditNotesDataTable({
openDialog('refund-credit-note', { creditNoteId: id }); openDialog('refund-credit-note', { creditNoteId: id });
}; };
// Handle cancel/confirm crdit note open.
const handleOpenCreditNote = ({ id }) => {
openAlert('credit-note-open', { creditNoteId: id });
};
return ( return (
<DashboardContentTable> <DashboardContentTable>
<DataTable <DataTable
@@ -126,6 +131,7 @@ function CreditNotesDataTable({
onDelete: handleDeleteCreditNote, onDelete: handleDeleteCreditNote,
onEdit: hanldeEditCreditNote, onEdit: hanldeEditCreditNote,
onRefund: handleRefundCreditNote, onRefund: handleRefundCreditNote,
onOpen: handleOpenCreditNote,
}} }}
/> />
</DashboardContentTable> </DashboardContentTable>

View File

@@ -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, onRefund, onViewDetails }, payload: { onEdit, onDelete, onRefund, onOpen, onViewDetails },
row: { original }, row: { original },
}) { }) {
return ( return (
@@ -38,11 +30,21 @@ 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)}
/> />
<MenuItem <If condition={!original.is_closed && !original.is_draft}>
icon={<Icon icon="quick-payment-16" />} <MenuItem
text={intl.get('credit_note.action.refund_credit_note')} icon={<Icon icon="quick-payment-16" />}
onClick={safeCallback(onRefund, original)} 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 <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}
@@ -53,6 +55,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.
*/ */
@@ -100,8 +131,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,
}, },

View File

@@ -208,3 +208,22 @@ export function useRefundCreditNote(id, props, requestProps) {
}, },
); );
} }
/**
* 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,
});
}

View File

@@ -219,3 +219,25 @@ export function useRefundVendorCredit(id, props, requestProps) {
}, },
); );
} }
/**
* 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,
},
);
}

View File

@@ -1562,6 +1562,10 @@
"refund_vendor_credit.column.withdrawal_account": "Withdrawal account", "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.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_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" "refund": "Refund",
"credit_note_opened.alert.success_message":"The credit note has been opened successfully",
"credit_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?"
} }