From 6ba54a994a1e6ad5675b163a32cd5e7d576ddf07 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 3 Nov 2024 20:22:59 +0200 Subject: [PATCH 1/4] fix: company logo does not show up in mail receipt preview --- packages/server/src/models/PdfTemplate.ts | 15 ++++++++++++++- .../CreditNotes/CreditNoteBrandingTemplate.ts | 7 +++++-- .../PdfTemplate/GetPdfTemplateTransformer.ts | 17 +++-------------- .../server/src/services/PdfTemplate/types.ts | 1 - ...InvoicePaymentMailAttributesTransformer.ts | 2 +- .../Sales/Invoices/GetSaleInvoiceMailState.ts | 1 - .../GetSaleInvoiceMailStateTransformer.ts | 19 +++++++++++++++++-- .../Sales/Invoices/SaleEstimatePdfTemplate.ts | 6 +++++- .../Sales/Invoices/SaleInvoicePdfTemplate.ts | 6 +++++- .../Sales/Invoices/SaleInvoicesApplication.ts | 7 ++++--- .../PaymentReceivedBrandingTemplate.ts | 6 +++++- .../Receipts/SaleReceiptBrandingTemplate.ts | 6 +++++- .../InvoiceCustomize/InvoiceMailReceipt.tsx | 1 + .../InvoiceMailReceiptPreviewConnected..tsx | 2 +- .../InvoiceSendMailContentBoot.tsx | 1 - packages/webapp/src/hooks/query/invoices.tsx | 1 + 16 files changed, 67 insertions(+), 31 deletions(-) diff --git a/packages/server/src/models/PdfTemplate.ts b/packages/server/src/models/PdfTemplate.ts index 5cf27f358..ccf5f48d2 100644 --- a/packages/server/src/models/PdfTemplate.ts +++ b/packages/server/src/models/PdfTemplate.ts @@ -1,6 +1,9 @@ +import { getUploadedObjectUri } from '@/services/Attachments/utils'; import TenantModel from 'models/TenantModel'; export class PdfTemplate extends TenantModel { + public readonly attributes: Record; + /** * Table name. */ @@ -47,7 +50,17 @@ export class PdfTemplate extends TenantModel { * Virtual attributes. */ static get virtualAttributes() { - return []; + return ['companyLogoUri']; + } + + /** + * Retrieves the company logo uri. + * @returns {string} + */ + get companyLogoUri() { + return this.attributes.companyLogoKey + ? getUploadedObjectUri(this.attributes.companyLogoKey) + : ''; } /** diff --git a/packages/server/src/services/CreditNotes/CreditNoteBrandingTemplate.ts b/packages/server/src/services/CreditNotes/CreditNoteBrandingTemplate.ts index 872f16ef4..ef0150747 100644 --- a/packages/server/src/services/CreditNotes/CreditNoteBrandingTemplate.ts +++ b/packages/server/src/services/CreditNotes/CreditNoteBrandingTemplate.ts @@ -35,9 +35,12 @@ export class CreditNoteBrandingTemplate { ...defaultCreditNoteBrandingAttributes, ...commonOrgBrandingAttrs, }; - + const brandingTemplateAttrs = { + ...template.attributes, + companyLogoUri: template.companyLogoUri, + }; const attributes = mergePdfTemplateWithDefaultAttributes( - template.attributes, + brandingTemplateAttrs, organizationBrandingAttrs ); return { diff --git a/packages/server/src/services/PdfTemplate/GetPdfTemplateTransformer.ts b/packages/server/src/services/PdfTemplate/GetPdfTemplateTransformer.ts index fddc28c59..5d7470018 100644 --- a/packages/server/src/services/PdfTemplate/GetPdfTemplateTransformer.ts +++ b/packages/server/src/services/PdfTemplate/GetPdfTemplateTransformer.ts @@ -1,10 +1,9 @@ import { Transformer } from '@/lib/Transformer/Transformer'; import { getTransactionTypeLabel } from '@/utils/transactions-types'; -import { getUploadedObjectUri } from '../Attachments/utils'; export class GetPdfTemplateTransformer extends Transformer { /** - * Includeded attributes. + * Included attributes. * @returns {string[]} */ public includeAttributes = (): string[] => { @@ -44,20 +43,10 @@ export class GetPdfTemplateTransformer extends Transformer { class GetPdfTemplateAttributesTransformer extends Transformer { /** - * Includeded attributes. + * Included attributes. * @returns {string[]} */ public includeAttributes = (): string[] => { - return ['companyLogoUri']; + return []; }; - - /** - * Retrieves the company logo uri. - * @returns {string} - */ - protected companyLogoUri(template) { - return template.companyLogoKey - ? getUploadedObjectUri(template.companyLogoKey) - : ''; - } } diff --git a/packages/server/src/services/PdfTemplate/types.ts b/packages/server/src/services/PdfTemplate/types.ts index 6fad632ab..de1029afa 100644 --- a/packages/server/src/services/PdfTemplate/types.ts +++ b/packages/server/src/services/PdfTemplate/types.ts @@ -66,7 +66,6 @@ export interface ICreateInvoicePdfTemplateDTO { showStatement?: boolean; } - export interface CommonOrganizationBrandingAttributes { companyName?: string; primaryColor?: string; diff --git a/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMailAttributesTransformer.ts b/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMailAttributesTransformer.ts index 976b5053f..1ac116064 100644 --- a/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMailAttributesTransformer.ts +++ b/packages/server/src/services/Sales/Invoices/GetInvoicePaymentMailAttributesTransformer.ts @@ -41,7 +41,7 @@ export class GetInvoiceMailTemplateAttributesTransformer extends Transformer { }; public companyLogoUri(): string { - return this.options.brandingTemplate?.attributes?.companyLogoUri; + return this.options.brandingTemplate?.companyLogoUri; } public companyName(): string { diff --git a/packages/server/src/services/Sales/Invoices/GetSaleInvoiceMailState.ts b/packages/server/src/services/Sales/Invoices/GetSaleInvoiceMailState.ts index c32fa2430..6f00dd08d 100644 --- a/packages/server/src/services/Sales/Invoices/GetSaleInvoiceMailState.ts +++ b/packages/server/src/services/Sales/Invoices/GetSaleInvoiceMailState.ts @@ -18,7 +18,6 @@ export class GetSaleInvoiceMailState { /** * Retrieves the invoice mail state of the given sale invoice. * Invoice mail state includes the mail options, branding attributes and the invoice details. - * * @param {number} tenantId * @param {number} saleInvoiceId * @returns {Promise} diff --git a/packages/server/src/services/Sales/Invoices/GetSaleInvoiceMailStateTransformer.ts b/packages/server/src/services/Sales/Invoices/GetSaleInvoiceMailStateTransformer.ts index 75d3d1f62..e1676d845 100644 --- a/packages/server/src/services/Sales/Invoices/GetSaleInvoiceMailStateTransformer.ts +++ b/packages/server/src/services/Sales/Invoices/GetSaleInvoiceMailStateTransformer.ts @@ -1,4 +1,3 @@ -import { Transformer } from '@/lib/Transformer/Transformer'; import { SaleInvoiceTransformer } from './SaleInvoiceTransformer'; import { ItemEntryTransformer } from './ItemEntryTransformer'; @@ -11,6 +10,10 @@ export class GetSaleInvoiceMailStateTransformer extends SaleInvoiceTransformer { return ['*']; }; + /** + * Included attributes. + * @returns {Array} + */ public includeAttributes = (): string[] => { return [ 'invoiceDate', @@ -39,14 +42,26 @@ export class GetSaleInvoiceMailStateTransformer extends SaleInvoiceTransformer { ]; }; + /** + * Retrieves the company name. + * @returns {string} + */ protected companyName = () => { return this.context.organization.name; }; + /** + * Retrieves the company logo uri. + * @returns {string | null} + */ protected companyLogoUri = (invoice) => { - return invoice.pdfTemplate?.attributes?.companyLogoUri; + return invoice.pdfTemplate?.companyLogoUri; }; + /** + * Retrieves the primary color. + * @returns {string} + */ protected primaryColor = (invoice) => { return invoice.pdfTemplate?.attributes?.primaryColor; }; diff --git a/packages/server/src/services/Sales/Invoices/SaleEstimatePdfTemplate.ts b/packages/server/src/services/Sales/Invoices/SaleEstimatePdfTemplate.ts index 54048619a..544468b49 100644 --- a/packages/server/src/services/Sales/Invoices/SaleEstimatePdfTemplate.ts +++ b/packages/server/src/services/Sales/Invoices/SaleEstimatePdfTemplate.ts @@ -33,8 +33,12 @@ export class SaleEstimatePdfTemplate { ...defaultEstimatePdfBrandingAttributes, ...commonOrgBrandingAttrs, }; + const brandingTemplateAttrs = { + ...template.attributes, + companyLogoUri: template.companyLogoUri, + }; const attributes = mergePdfTemplateWithDefaultAttributes( - template.attributes, + brandingTemplateAttrs, orgainizationBrandingAttrs ); return { diff --git a/packages/server/src/services/Sales/Invoices/SaleInvoicePdfTemplate.ts b/packages/server/src/services/Sales/Invoices/SaleInvoicePdfTemplate.ts index ca1f8c142..e7cffd69f 100644 --- a/packages/server/src/services/Sales/Invoices/SaleInvoicePdfTemplate.ts +++ b/packages/server/src/services/Sales/Invoices/SaleInvoicePdfTemplate.ts @@ -32,8 +32,12 @@ export class SaleInvoicePdfTemplate { ...defaultInvoicePdfTemplateAttributes, ...commonOrgBrandingAttrs, }; + const brandingTemplateAttrs = { + ...template.attributes, + companyLogoUri: template.companyLogoUri, + }; const attributes = mergePdfTemplateWithDefaultAttributes( - template.attributes, + brandingTemplateAttrs, organizationBrandingAttrs ); return { diff --git a/packages/server/src/services/Sales/Invoices/SaleInvoicesApplication.ts b/packages/server/src/services/Sales/Invoices/SaleInvoicesApplication.ts index e04ce4439..5490bec44 100644 --- a/packages/server/src/services/Sales/Invoices/SaleInvoicesApplication.ts +++ b/packages/server/src/services/Sales/Invoices/SaleInvoicesApplication.ts @@ -28,9 +28,7 @@ import { GetInvoicePaymentsService } from './GetInvoicePaymentsService'; import { SaleInvoiceNotifyBySms } from './SaleInvoiceNotifyBySms'; import { SendInvoiceMailReminder } from './SendSaleInvoiceMailReminder'; import { SendSaleInvoiceMail } from './SendSaleInvoiceMail'; -import { GetSaleInvoiceMailReminder } from './GetSaleInvoiceMailReminder'; import { GetSaleInvoiceState } from './GetSaleInvoiceState'; -import { GetSaleInvoiceBrandTemplate } from './GetSaleInvoiceBrandTemplate'; import { GetSaleInvoiceMailState } from './GetSaleInvoiceMailState'; @Service() @@ -366,7 +364,10 @@ export class SaleInvoiceApplication { * @param {number} saleInvoiceid * @returns {Promise} */ - public getSaleInvoiceMailState(tenantId: number, saleInvoiceid: number) { + public getSaleInvoiceMailState( + tenantId: number, + saleInvoiceid: number + ): Promise { return this.getSaleInvoiceMailStateService.getInvoiceMailState( tenantId, saleInvoiceid diff --git a/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedBrandingTemplate.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedBrandingTemplate.ts index 5e7c89fb7..d7a88bf6b 100644 --- a/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedBrandingTemplate.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedBrandingTemplate.ts @@ -37,8 +37,12 @@ export class PaymentReceivedBrandingTemplate { ...defaultPaymentReceivedPdfTemplateAttributes, ...commonOrgBrandingAttrs, }; + const brandingTemplateAttrs = { + ...template.attributes, + companyLogoUri: template.companyLogoUri, + }; const attributes = mergePdfTemplateWithDefaultAttributes( - template.attributes, + brandingTemplateAttrs, organizationBrandingAttrs ); return { diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptBrandingTemplate.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptBrandingTemplate.ts index 1cef714b9..12689d7cd 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptBrandingTemplate.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptBrandingTemplate.ts @@ -37,8 +37,12 @@ export class SaleReceiptBrandingTemplate { ...defaultSaleReceiptBrandingAttributes, ...commonOrgBrandingAttrs, }; + const brandingTemplateAttrs = { + ...template.attributes, + companyLogoUri: template.companyLogoUri, + }; const attributes = mergePdfTemplateWithDefaultAttributes( - template.attributes, + brandingTemplateAttrs, organizationBrandingAttrs ); return { diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceCustomize/InvoiceMailReceipt.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceCustomize/InvoiceMailReceipt.tsx index 02801698a..7669a1fc9 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceCustomize/InvoiceMailReceipt.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceCustomize/InvoiceMailReceipt.tsx @@ -93,6 +93,7 @@ export function InvoiceMailReceipt({ h="90px" w="90px" mx="auto" + borderRadius="3px" backgroundRepeat="no-repeat" backgroundPosition="center center" backgroundSize="contain" diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceMailReceiptPreviewConnected..tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceMailReceiptPreviewConnected..tsx index b93d26696..fe593a4ca 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceMailReceiptPreviewConnected..tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceMailReceiptPreviewConnected..tsx @@ -25,7 +25,7 @@ export function InvoiceMailReceiptPreviewConneceted() { Date: Sun, 3 Nov 2024 20:36:52 +0200 Subject: [PATCH 2/4] fix: change default invoice mail message --- .../src/services/Sales/Invoices/constants.ts | 25 ++- .../src/lib/InvoicePaymentEmail.tsx | 177 +++++++++--------- 2 files changed, 101 insertions(+), 101 deletions(-) diff --git a/packages/server/src/services/Sales/Invoices/constants.ts b/packages/server/src/services/Sales/Invoices/constants.ts index cff791b3c..0e2b9989c 100644 --- a/packages/server/src/services/Sales/Invoices/constants.ts +++ b/packages/server/src/services/Sales/Invoices/constants.ts @@ -1,20 +1,19 @@ import config from '@/config'; export const DEFAULT_INVOICE_MAIL_SUBJECT = - 'Invoice {Invoice Number} from {Company Name}'; -export const DEFAULT_INVOICE_MAIL_CONTENT = ` -

Dear {Customer Name}

-

Thank you for your business, You can view or print your invoice from attachements.

-

-Invoice #{Invoice Number}
-Due Date : {Invoice Due Date}
-Amount : {Invoice Amount}
-

+ 'Invoice {Invoice Number} from {Company Name} for {Customer Name}'; +export const DEFAULT_INVOICE_MAIL_CONTENT = `Hi {Customer Name}, -

-Regards
-{Company Name} -

+Here's invoice # {Invoice Number} for {Invoice Amount} + +The amount outstanding of {Invoice Due Amount} is due on {Invoice Due Date}. + +From your online payment page you can print a PDF or view your outstanding bills. + +If you have any questions, please let us know. + +Thanks, +{Company Name} `; export const DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT = diff --git a/shared/email-components/src/lib/InvoicePaymentEmail.tsx b/shared/email-components/src/lib/InvoicePaymentEmail.tsx index 905144df9..bbd062b85 100644 --- a/shared/email-components/src/lib/InvoicePaymentEmail.tsx +++ b/shared/email-components/src/lib/InvoicePaymentEmail.tsx @@ -95,109 +95,109 @@ export const InvoicePaymentEmail: React.FC< items, }) => { - return ( - - - {preview} + return ( + + + {preview} - - - -
- {companyLogoUri && ( -
-
- Image -
+ + + +
+ {companyLogoUri && ( +
+
+ Image +
+
+ )} + +
+ + + {companyName} + + + + {invoiceAmount} + + + + {invoiceNumberLabel?.replace( + '{invoiceNumber}', + invoiceNumber + )} + + + + + {dueDateLabel.replace('{dueDate}', dueDate)} + +
- )} -
- - - {companyName} - - - - {invoiceAmount} - - - - {invoiceNumberLabel?.replace( - '{invoiceNumber}', - invoiceNumber - )} - - - - - {dueDateLabel.replace('{dueDate}', dueDate)} - - -
+ {invoiceMessage} + - {invoiceMessage} - +
+ {items.map((item, index) => ( + + + {item.label} + -
- {items.map((item, index) => ( - + + + {item.quantity} x {item.rate} + + + + ))} + + - {item.label} + + {dueAmountLabel} + - - {item.quantity} x {item.rate} + + {dueAmount} - ))} - - - - {dueAmountLabel} - - + + + {totalLabel} + - - - {dueAmount} - - - - - - - {totalLabel} - - - - {total} - - + + {total} + + +
-
-
- -
- - ); -}; + + + + + ); + }; export const renderInvoicePaymentEmail = (props: InvoicePaymentEmailProps) => { return render(); @@ -274,6 +274,7 @@ const invoiceMessageStyle: CSSProperties = { whiteSpace: 'pre-line', color: '#252A31', margin: '0 0 20px 0', + lineHeight: '20px', }; const dueAmounLineRowStyle: CSSProperties = { From 2646ad5bc41c58fd5c515670e050d8e6213ee151 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 4 Nov 2024 14:18:47 +0200 Subject: [PATCH 3/4] fix: typing invoice send mail fields --- packages/webapp/package.json | 2 +- .../InvoiceSendMailFields.tsx | 116 ++-- pnpm-lock.yaml | 520 +----------------- 3 files changed, 86 insertions(+), 552 deletions(-) diff --git a/packages/webapp/package.json b/packages/webapp/package.json index a315192d1..0db7053e3 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@bigcapital/utils": "*", - "@blueprintjs-formik/core": "^0.3.6", + "@blueprintjs-formik/core": "^0.3.7", "@blueprintjs-formik/datetime": "^0.3.7", "@blueprintjs-formik/select": "^0.3.5", "@blueprintjs/colors": "4.1.19", diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx index 17956ff24..1ae32d1d7 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx @@ -18,9 +18,14 @@ import { import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; import { useDrawerActions } from '@/hooks/state'; import { useInvoiceMailItems, useSendInvoiceFormatArgsOptions } from './_hooks'; +import { InvoiceSendMailFormValues } from './_types'; // Create new account renderer. -const createNewItemRenderer = (query, active, handleClick) => { +const createNewItemRenderer = ( + query: string, + active: boolean, + handleClick: React.MouseEventHandler, +) => { return ( { }; // Create new item from the given query string. -const createNewItemFromQuery = (name) => ({ name }); +const createNewItemFromQuery = (text: string): SelectOptionProps => ({ text }); const styleEmailButton = css` &.bp4-button.bp4-small { @@ -62,19 +67,19 @@ export function InvoiceSendMailFields() { const [showCCField, setShowCCField] = useState(false); const [showBccField, setShowBccField] = useState(false); const textareaRef = useRef(null); - - const { values, setFieldValue } = useFormikContext(); + const { values, setFieldValue } = + useFormikContext(); const items = useInvoiceMailItems(); const argsOptions = useSendInvoiceFormatArgsOptions(); - const handleClickCcBtn = (event) => { + const handleClickCcBtn = (event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); setShowCCField(true); }; - const handleClickBccBtn = (event) => { + const handleClickBccBtn = (event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); @@ -82,64 +87,71 @@ export function InvoiceSendMailFields() { }; const handleCreateToItemSelect = (value: SelectOptionProps) => { - setFieldValue('to', [...values?.to, value?.name]); + setFieldValue('to', [...values?.to, value?.text]); }; const handleCreateCcItemSelect = (value: SelectOptionProps) => { - setFieldValue('cc', [...values?.cc, value?.name]); + setFieldValue('cc', [...values?.cc, value?.text]); }; + const handleCreateBccItemSelect = (value: SelectOptionProps) => { - setFieldValue('bcc', [...values?.bcc, value?.name]); + setFieldValue('bcc', [...values?.bcc, value?.text]); }; - const rightElementsToField = useMemo(() => ( - - + - - - ), []); + + + ), + [], + ); - const handleTextareaChange = useCallback((item: SelectOptionProps) => { - const textarea = textareaRef.current; - if (!textarea) return; + const handleTextareaChange = useCallback( + (item: SelectOptionProps) => { + const textarea = textareaRef.current; + if (!textarea) return; - const { selectionStart, selectionEnd, value: text } = textarea; - const insertText = `{${item.value}}`; - const message = - text.substring(0, selectionStart) + - insertText + - text.substring(selectionEnd); + const { selectionStart, selectionEnd, value: text } = textarea; + const insertText = `{${item.value}}`; + const message = + text.substring(0, selectionStart) + + insertText + + text.substring(selectionEnd); - setFieldValue('message', message); + setFieldValue('message', message); - // Move the cursor to the end of the inserted text - setTimeout(() => { - textarea.selectionStart = textarea.selectionEnd = - selectionStart + insertText.length; - textarea.focus(); - }, 0); - }, [setFieldValue]); + // Move the cursor to the end of the inserted text + setTimeout(() => { + textarea.selectionStart = textarea.selectionEnd = + selectionStart + insertText.length; + textarea.focus(); + }, 0); + }, + [setFieldValue], + ); return ( ( + input={() => (