diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b0e0d0a0..9f9a76ef1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,20 @@
All notable changes to Bigcapital server-side will be in this file.
+## [1.5.8] - 13-01-2022
+
+### Added
+- Add payment receive PDF print.
+- Add credit note PDF print.
+
+### Fixed
+- fix: Payment receive initial loading state depends on request loading state instead fetching.
+- fix: Balance sheet report alert positioning.
+- fix: Separate customer and vendor inactivate and activate alerts.
+- fix: Hide convert to invoice button if the invoice is already converted.
+- fix: Customer and vendor balance summary percentage of column option.
+- fix: Remove duplicated details in sales and purchases details drawers.
+
## [1.5.3] - 03-01-2020
### Fixed
diff --git a/package.json b/package.json
index 2fda86adc..56532c6c0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bigcapital-client",
- "version": "1.5.3",
+ "version": "1.5.8",
"private": true,
"dependencies": {
"@babel/core": "7.8.4",
diff --git a/src/components/CommercialDoc/index.js b/src/components/CommercialDoc/index.js
index c8e2c593b..d1480c3f5 100644
--- a/src/components/CommercialDoc/index.js
+++ b/src/components/CommercialDoc/index.js
@@ -21,5 +21,5 @@ export const CommercialDocEntriesTable = styled(DataTable)`
`;
export const CommercialDocFooter = styled.div`
- margin-top: 25px;
+ margin-top: 28px;
`;
diff --git a/src/components/DialogsContainer.js b/src/components/DialogsContainer.js
index d9d3609c5..cd8eb745e 100644
--- a/src/components/DialogsContainer.js
+++ b/src/components/DialogsContainer.js
@@ -32,6 +32,8 @@ import ReconcileVendorCreditDialog from '../containers/Dialogs/ReconcileVendorCr
import LockingTransactionsDialog from '../containers/Dialogs/LockingTransactionsDialog';
import UnlockingTransactionsDialog from '../containers/Dialogs/UnlockingTransactionsDialog';
import UnlockingPartialTransactionsDialog from '../containers/Dialogs/UnlockingPartialTransactionsDialog';
+import CreditNotePdfPreviewDialog from '../containers/Dialogs/CreditNotePdfPreviewDialog';
+import PaymentReceivePdfPreviewDialog from '../containers/Dialogs/PaymentReceivePdfPreviewDialog';
/**
* Dialogs container.
@@ -74,6 +76,8 @@ export default function DialogsContainer() {
+
+
);
}
diff --git a/src/containers/Alerts/Customers/CustomerActivateAlert.js b/src/containers/Alerts/Customers/CustomerActivateAlert.js
new file mode 100644
index 000000000..ef86134a3
--- /dev/null
+++ b/src/containers/Alerts/Customers/CustomerActivateAlert.js
@@ -0,0 +1,67 @@
+import React from 'react';
+import { FormattedMessage as T } from 'components';
+import intl from 'react-intl-universal';
+import { Intent, Alert } from '@blueprintjs/core';
+import { AppToaster } from 'components';
+
+import { useActivateContact } from 'hooks/query';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+
+import { compose } from 'utils';
+
+/**
+ * Customer activate alert.
+ */
+function CustomerActivateAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { customerId, service },
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: activateContact, isLoading } = useActivateContact();
+
+ // Handle activate constomer alert cancel.
+ const handleCancelActivateCustomer = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm customer activated.
+ const handleConfirmCustomerActivate = () => {
+ activateContact(customerId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('customer.alert.activated_message'),
+ intent: Intent.SUCCESS,
+ });
+ })
+ .catch((error) => {})
+ .finally(() => {
+ closeAlert(name);
+ });
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ onCancel={handleCancelActivateCustomer}
+ loading={isLoading}
+ onConfirm={handleConfirmCustomerActivate}
+ >
+
{intl.get('customer.alert.are_you_sure_want_to_activate_this_customer')}
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(CustomerActivateAlert);
diff --git a/src/containers/Alerts/Customers/CustomerInactivateAlert.js b/src/containers/Alerts/Customers/CustomerInactivateAlert.js
new file mode 100644
index 000000000..d86675c91
--- /dev/null
+++ b/src/containers/Alerts/Customers/CustomerInactivateAlert.js
@@ -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 { AppToaster } from 'components';
+
+import { useInactivateContact } from 'hooks/query';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+
+import { compose } from 'utils';
+
+/**
+ * customer inactivate alert.
+ */
+function CustomerInactivateAlert({
+ name,
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { customerId, service },
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: inactivateContact, isLoading } = useInactivateContact();
+
+ // Handle cancel inactivate alert.
+ const handleCancelInactivateCustomer = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm contact Inactive.
+ const handleConfirmCustomerInactive = () => {
+ inactivateContact(customerId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('the_contact_has_been_inactivated_successfully'),
+ intent: Intent.SUCCESS,
+ });
+ })
+ .catch((error) => {})
+ .finally(() => {
+ closeAlert(name);
+ });
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ onCancel={handleCancelInactivateCustomer}
+ onConfirm={handleConfirmCustomerInactive}
+ loading={isLoading}
+ >
+
+ {intl.get(
+ 'customer.alert.are_you_sure_want_to_inactivate_this_customer',
+ )}
+
+
+ );
+}
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(CustomerInactivateAlert);
diff --git a/src/containers/Alerts/Vendors/VendorActivateAlert.js b/src/containers/Alerts/Vendors/VendorActivateAlert.js
new file mode 100644
index 000000000..8e48ab34f
--- /dev/null
+++ b/src/containers/Alerts/Vendors/VendorActivateAlert.js
@@ -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 { AppToaster } from 'components';
+
+import { useActivateContact } from 'hooks/query';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+
+import { compose } from 'utils';
+
+/**
+ * Vendor activate alert.
+ */
+function VendorActivateAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { vendorId },
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: activateContact, isLoading } = useActivateContact();
+
+ // Handle activate vendor alert cancel.
+ const handleCancelActivateVendor = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm vendor activated.
+ const handleConfirmVendorActivate = () => {
+ activateContact(vendorId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('vendor.alert.activated_message'),
+ intent: Intent.SUCCESS,
+ });
+ })
+ .catch((error) => {})
+ .finally(() => {
+ closeAlert(name);
+ });
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ onCancel={handleCancelActivateVendor}
+ loading={isLoading}
+ onConfirm={handleConfirmVendorActivate}
+ >
+
+ {intl.get('vendor.alert.are_you_sure_want_to_activate_this_vendor')}
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(VendorActivateAlert);
diff --git a/src/containers/Alerts/Vendors/VendorInactivateAlert.js b/src/containers/Alerts/Vendors/VendorInactivateAlert.js
new file mode 100644
index 000000000..a5d48358d
--- /dev/null
+++ b/src/containers/Alerts/Vendors/VendorInactivateAlert.js
@@ -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 { AppToaster } from 'components';
+
+import { useInactivateContact } from 'hooks/query';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+
+import { compose } from 'utils';
+
+/**
+ * Vendor inactivate alert.
+ */
+function VendorInactivateAlert({
+ name,
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { vendorId },
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { mutateAsync: inactivateContact, isLoading } = useInactivateContact();
+
+ // Handle cancel inactivate alert.
+ const handleCancelInactivateVendor = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm contact Inactive.
+ const handleConfirmVendorInactive = () => {
+ inactivateContact(vendorId)
+ .then(() => {
+ AppToaster.show({
+ message: intl.get('vendor.alert.inactivated_message'),
+ intent: Intent.SUCCESS,
+ });
+ })
+ .catch((error) => {})
+ .finally(() => {
+ closeAlert(name);
+ });
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ onCancel={handleCancelInactivateVendor}
+ onConfirm={handleConfirmVendorInactive}
+ loading={isLoading}
+ >
+
+ {intl.get('vendor.alert.are_you_sure_want_to_inactivate_this_vendor')}
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(VendorInactivateAlert);
diff --git a/src/containers/Customers/CustomersAlerts.js b/src/containers/Customers/CustomersAlerts.js
index a7bab20eb..7cd902d21 100644
--- a/src/containers/Customers/CustomersAlerts.js
+++ b/src/containers/Customers/CustomersAlerts.js
@@ -3,11 +3,11 @@ import React from 'react';
const CustomerDeleteAlert = React.lazy(() =>
import('../Alerts/Customers/CustomerDeleteAlert'),
);
-const ContactActivateAlert = React.lazy(() =>
- import('../Alerts/Contacts/ContactActivateAlert'),
+const CustomerActivateAlert = React.lazy(() =>
+ import('../Alerts/Customers/CustomerActivateAlert'),
);
-const ContactInactivateAlert = React.lazy(() =>
- import('../Alerts/Contacts/ContactInactivateAlert'),
+const CustomerInactivateAlert = React.lazy(() =>
+ import('../Alerts/Customers/CustomerInactivateAlert'),
);
/**
@@ -15,6 +15,6 @@ const ContactInactivateAlert = React.lazy(() =>
*/
export default [
{ name: 'customer-delete', component: CustomerDeleteAlert },
- { name: 'contact-activate', component: ContactActivateAlert },
- { name: 'contact-inactivate', component: ContactInactivateAlert },
+ { name: 'customer-activate', component: CustomerActivateAlert },
+ { name: 'customer-inactivate', component: CustomerInactivateAlert },
];
diff --git a/src/containers/Customers/CustomersLanding/CustomersTable.js b/src/containers/Customers/CustomersLanding/CustomersTable.js
index 7619936ac..9618ba4cf 100644
--- a/src/containers/Customers/CustomersLanding/CustomersTable.js
+++ b/src/containers/Customers/CustomersLanding/CustomersTable.js
@@ -89,15 +89,17 @@ function CustomersTable({
// Handle cancel/confirm inactive.
const handleInactiveCustomer = ({ id, contact_service }) => {
- openAlert('contact-inactivate', {
- contactId: id,
- service: contact_service,
+ openAlert('customer-inactivate', {
+ customerId: id,
});
};
// Handle cancel/confirm activate.
const handleActivateCustomer = ({ id, contact_service }) => {
- openAlert('contact-activate', { contactId: id, service: contact_service });
+ openAlert('customer-activate', {
+ customerId: id,
+ service: contact_service,
+ });
};
// Handle view detail contact.
diff --git a/src/containers/Dialogs/CreditNotePdfPreviewDialog/CreditNotePdfPreviewDialogContent.js b/src/containers/Dialogs/CreditNotePdfPreviewDialog/CreditNotePdfPreviewDialogContent.js
new file mode 100644
index 000000000..3c13480c9
--- /dev/null
+++ b/src/containers/Dialogs/CreditNotePdfPreviewDialog/CreditNotePdfPreviewDialogContent.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import { AnchorButton } from '@blueprintjs/core';
+
+import { DialogContent, PdfDocumentPreview, T } from 'components';
+import { usePdfCreditNote } from 'hooks/query';
+
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose } from 'utils';
+
+function CreditNotePdfPreviewDialogContent({
+ subscriptionForm: { creditNoteId },
+}) {
+ const { isLoading, pdfUrl } = usePdfCreditNote(creditNoteId);
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default compose(withDialogActions)(CreditNotePdfPreviewDialogContent);
diff --git a/src/containers/Dialogs/CreditNotePdfPreviewDialog/index.js b/src/containers/Dialogs/CreditNotePdfPreviewDialog/index.js
new file mode 100644
index 000000000..061cc6b85
--- /dev/null
+++ b/src/containers/Dialogs/CreditNotePdfPreviewDialog/index.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import classNames from 'classnames';
+
+import { T, Dialog, DialogSuspense } from 'components';
+
+import withDialogRedux from 'components/DialogReduxConnect';
+
+import { CLASSES } from 'common/classes';
+import { compose } from 'utils';
+
+const PdfPreviewDialogContent = React.lazy(() =>
+ import('./CreditNotePdfPreviewDialogContent'),
+);
+
+/**
+ * Credit note PDF previwe dialog.
+ */
+function CreditNotePdfPreviewDialog({
+ dialogName,
+ payload = { creditNoteId: null },
+ isOpen,
+}) {
+ return (
+ }
+ className={classNames(CLASSES.DIALOG_PDF_PREVIEW)}
+ autoFocus={true}
+ canEscapeKeyClose={true}
+ isOpen={isOpen}
+ style={{ width: '1000px' }}
+ >
+
+
+
+
+ );
+}
+export default compose(withDialogRedux())(CreditNotePdfPreviewDialog);
diff --git a/src/containers/Dialogs/PaymentReceivePdfPreviewDialog/PaymentReceivePdfPreviewContent.js b/src/containers/Dialogs/PaymentReceivePdfPreviewDialog/PaymentReceivePdfPreviewContent.js
new file mode 100644
index 000000000..c03dc9488
--- /dev/null
+++ b/src/containers/Dialogs/PaymentReceivePdfPreviewDialog/PaymentReceivePdfPreviewContent.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import { AnchorButton } from '@blueprintjs/core';
+
+import { DialogContent, PdfDocumentPreview, T } from 'components';
+import { usePdfPaymentReceive } from 'hooks/query';
+
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose } from 'utils';
+
+function PaymentReceivePdfPreviewDialogContent({
+ subscriptionForm: { paymentReceiveId },
+}) {
+ const { isLoading, pdfUrl } = usePdfPaymentReceive(paymentReceiveId);
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default compose(withDialogActions)(
+ PaymentReceivePdfPreviewDialogContent,
+);
diff --git a/src/containers/Dialogs/PaymentReceivePdfPreviewDialog/index.js b/src/containers/Dialogs/PaymentReceivePdfPreviewDialog/index.js
new file mode 100644
index 000000000..f96b22147
--- /dev/null
+++ b/src/containers/Dialogs/PaymentReceivePdfPreviewDialog/index.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import classNames from 'classnames';
+
+import { T, Dialog, DialogSuspense } from 'components';
+
+import withDialogRedux from 'components/DialogReduxConnect';
+
+import { CLASSES } from 'common/classes';
+import { compose } from 'utils';
+
+// Lazy loading the content.
+const PdfPreviewDialogContent = React.lazy(() =>
+ import('./PaymentReceivePdfPreviewContent'),
+);
+
+/**
+ * Payment receive PDF preview dialog.
+ */
+function PaymentReceivePdfPreviewDialog({
+ dialogName,
+ payload = { paymentReceiveId: null },
+ isOpen,
+}) {
+ return (
+ }
+ className={classNames(CLASSES.DIALOG_PDF_PREVIEW)}
+ autoFocus={true}
+ canEscapeKeyClose={true}
+ isOpen={isOpen}
+ style={{ width: '1000px' }}
+ >
+
+
+
+
+ );
+}
+
+export default compose(withDialogRedux())(PaymentReceivePdfPreviewDialog);
diff --git a/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailActionsBar.js b/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailActionsBar.js
index 5d7810b34..2ab7d47a6 100644
--- a/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailActionsBar.js
+++ b/src/containers/Drawers/CreditNoteDetailDrawer/CreditNoteDetailActionsBar.js
@@ -65,6 +65,11 @@ function CreditNoteDetailActionsBar({
openDialog('reconcile-credit-note', { creditNoteId });
};
+ // Handle print credit note.
+ const handlePrintCreditNote = () => {
+ openDialog('credit-note-pdf-preview', { creditNoteId });
+ };
+
return (
@@ -88,6 +93,14 @@ function CreditNoteDetailActionsBar({
+
+ }
+ text={}
+ onClick={handlePrintCreditNote}
+ />
+
+
+ }
+ text={}
+ onClick={handlePrintPaymentReceive}
+ />
+