diff --git a/packages/server/src/api/controllers/Sales/SalesReceipts.ts b/packages/server/src/api/controllers/Sales/SalesReceipts.ts index 18a9506cc..4ba94fffe 100644 --- a/packages/server/src/api/controllers/Sales/SalesReceipts.ts +++ b/packages/server/src/api/controllers/Sales/SalesReceipts.ts @@ -524,7 +524,7 @@ export default class SalesReceiptsController extends BaseController { const { id: receiptId } = req.params; try { - const data = await this.saleReceiptsApplication.getSaleReceiptMail( + const data = await this.saleReceiptsApplication.getSaleReceiptMailState( tenantId, receiptId ); diff --git a/packages/server/src/services/Sales/Receipts/GetSaleReceiptMailState.ts b/packages/server/src/services/Sales/Receipts/GetSaleReceiptMailState.ts new file mode 100644 index 000000000..bc0e810c7 --- /dev/null +++ b/packages/server/src/services/Sales/Receipts/GetSaleReceiptMailState.ts @@ -0,0 +1,45 @@ +import { Inject, Service } from 'typedi'; +import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; +import { SaleReceiptMailNotification } from './SaleReceiptMailNotification'; +import { GetSaleReceiptMailStateTransformer } from './GetSaleReceiptMailStateTransformer'; + +@Service() +export class GetSaleReceiptMailState { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private transformer: TransformerInjectable; + + @Inject() + private receiptMail: SaleReceiptMailNotification; + + /** + * Retrieves the sale receipt mail state of the given sale receipt. + * @param {number} tenantId + * @param {number} saleReceiptId + */ + public async getMailState(tenantId: number, saleReceiptId: number) { + const { SaleReceipt } = this.tenancy.models(tenantId); + + const saleReceipt = await SaleReceipt.query() + .findById(saleReceiptId) + .withGraphFetched('entries.item') + .withGraphFetched('customer') + .throwIfNotFound(); + + const mailOptions = await this.receiptMail.getMailOptions( + tenantId, + saleReceiptId + ); + return this.transformer.transform( + tenantId, + saleReceipt, + new GetSaleReceiptMailStateTransformer(), + { + mailOptions, + } + ); + } +} diff --git a/packages/server/src/services/Sales/Receipts/GetSaleReceiptMailStateTransformer.ts b/packages/server/src/services/Sales/Receipts/GetSaleReceiptMailStateTransformer.ts new file mode 100644 index 000000000..c127f633a --- /dev/null +++ b/packages/server/src/services/Sales/Receipts/GetSaleReceiptMailStateTransformer.ts @@ -0,0 +1,194 @@ +import { Transformer } from '@/lib/Transformer/Transformer'; +import { ItemEntryTransformer } from '../Invoices/ItemEntryTransformer'; + +export class GetSaleReceiptMailStateTransformer extends Transformer { + /** + * Exclude these attributes from user object. + * @returns {Array} + */ + public excludeAttributes = (): string[] => { + return ['*']; + }; + + /** + * Included attributes. + * @returns {Array} + */ + public includeAttributes = (): string[] => { + return [ + 'companyName', + 'companyLogoUri', + 'primaryColor', + 'customerName', + 'total', + 'totalFormatted', + 'subtotal', + 'subtotalFormatted', + 'receiptDate', + 'receiptDateFormatted', + 'closedAtDate', + 'closedAtDateFormatted', + 'receiptNumber', + 'entries', + ]; + }; + + /** + * Retrieves the customer name of the invoice. + * @returns {string} + */ + protected customerName = (receipt) => { + return receipt.customer.displayName; + }; + + /** + * Retrieves the company name. + * @returns {string} + */ + protected companyName = () => { + return this.context.organization.name; + }; + + /** + * Retrieves the company logo uri. + * @returns {string | null} + */ + protected companyLogoUri = (receipt) => { + return receipt.pdfTemplate?.companyLogoUri; + }; + + /** + * Retrieves the primary color. + * @returns {string} + */ + protected primaryColor = (receipt) => { + return receipt.pdfTemplate?.attributes?.primaryColor; + }; + + /** + * + * @param receipt + * @returns + */ + protected total = (receipt) => { + return receipt.amount; + }; + + /** + * + * @param receipt + * @returns + */ + protected totalFormatted = (receipt) => { + return this.formatMoney(receipt.amount, { + currencyCode: receipt.currencyCode, + }); + }; + + /** + * + * @param receipt + * @returns + */ + protected subtotal = (receipt) => { + return receipt.amount; + }; + + /** + * + * @param receipt + * @returns + */ + protected subtotalFormatted = (receipt) => { + return this.formatMoney(receipt.amount, { + currencyCode: receipt.currencyCode, + }); + }; + + /** + * + * @param receipt + * @returns + */ + protected receiptDate = (receipt): string => { + return receipt.receiptDate; + }; + + /** + * + * @param {ISaleReceipt} invoice + * @returns {string} + */ + protected receiptDateFormatted = (receipt): string => { + return this.formatDate(receipt.receiptDate); + }; + + /** + * + * @param receipt + * @returns + */ + protected closedAtDate = (receipt): string => { + return receipt.closedAt; + }; + + /** + * Retrieve formatted estimate closed at date. + * @param {ISaleReceipt} invoice + * @returns {String} + */ + protected closedAtDateFormatted = (receipt): string => { + return this.formatDate(receipt.closedAt); + }; + + /** + * + * @param invoice + * @returns + */ + protected entries = (receipt) => { + return this.item( + receipt.entries, + new GetSaleReceiptEntryMailStateTransformer(), + { + currencyCode: receipt.currencyCode, + } + ); + }; + + /** + * Merges the mail options with the invoice object. + */ + public transform = (object: any) => { + return { + ...this.options.mailOptions, + ...object, + }; + }; +} + +class GetSaleReceiptEntryMailStateTransformer extends ItemEntryTransformer { + /** + * Exclude these attributes from user object. + * @returns {Array} + */ + public excludeAttributes = (): string[] => { + return ['*']; + }; + + public name = (entry) => { + return entry.item.name; + }; + + public includeAttributes = (): string[] => { + return [ + 'name', + 'quantity', + 'quantityFormatted', + 'rate', + 'rateFormatted', + 'total', + 'totalFormatted', + ]; + }; +} diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptApplication.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptApplication.ts index 5d8a134b8..5fbcd78cc 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptApplication.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptApplication.ts @@ -18,6 +18,7 @@ import { SaleReceiptsPdf } from './SaleReceiptsPdfService'; import { SaleReceiptNotifyBySms } from './SaleReceiptNotifyBySms'; import { SaleReceiptMailNotification } from './SaleReceiptMailNotification'; import { GetSaleReceiptState } from './GetSaleReceiptState'; +import { GetSaleReceiptMailState } from './GetSaleReceiptMailState'; @Service() export class SaleReceiptApplication { @@ -51,6 +52,9 @@ export class SaleReceiptApplication { @Inject() private getSaleReceiptStateService: GetSaleReceiptState; + @Inject() + private getSaleReceiptMailStateService: GetSaleReceiptMailState; + /** * Creates a new sale receipt with associated entries. * @param {number} tenantId @@ -234,4 +238,20 @@ export class SaleReceiptApplication { public getSaleReceiptState(tenantId: number): Promise { return this.getSaleReceiptStateService.getSaleReceiptState(tenantId); } + + /** + * Retrieves the mail state of the given sale receipt. + * @param {number} tenantId + * @param {number} saleReceiptId + * @returns + */ + public getSaleReceiptMailState( + tenantId: number, + saleReceiptId: number + ): Promise { + return this.getSaleReceiptMailStateService.getMailState( + tenantId, + saleReceiptId + ); + } } diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptSendMailDrawer/ReceiptSendMailReceipt.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptSendMailDrawer/ReceiptSendMailReceipt.tsx index 75032a1fe..f44c21b51 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptSendMailDrawer/ReceiptSendMailReceipt.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptSendMailDrawer/ReceiptSendMailReceipt.tsx @@ -27,11 +27,6 @@ export interface ReceiptSendMailReceiptProps extends SendMailReceiptProps { // # Subtotal subtotal: string; subtotalLabel?: string; - - // # View receipt button - showViewReceiptButton?: boolean; - viewReceiptButtonLabel?: string; - viewReceiptButtonOnClick?: () => void; } export function ReceiptSendMailReceipt({ @@ -55,11 +50,6 @@ export function ReceiptSendMailReceipt({ subtotal, subtotalLabel = 'Subtotal', - // # View receipt button - showViewReceiptButton, - viewReceiptButtonLabel, - viewReceiptButtonOnClick, - ...rest }: ReceiptSendMailReceiptProps) { return ( @@ -80,62 +70,57 @@ export function ReceiptSendMailReceipt({ {receiptNumberLabel} {receiptNumber} + - {showViewReceiptButton && ( - - {viewReceiptButtonLabel} - - )} - - - {items?.map((item, key) => ( - - {item.label} - - {item.quantity} x {item.total} - - - ))} + + {message} + + + {items?.map((item, key) => ( - {subtotalLabel} - - {subtotal} + {item.label} + + {item.quantity} x {item.total} - - - {totalLabel} - - {total} - - - + ))} + + + {subtotalLabel} + + {subtotal} + + + + + {totalLabel} + + {total} + + ); diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptSendMailDrawer/withReceiptMailReceiptPreviewProps.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptSendMailDrawer/withReceiptMailReceiptPreviewProps.tsx index dea0114ff..01b6ca634 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptSendMailDrawer/withReceiptMailReceiptPreviewProps.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptSendMailDrawer/withReceiptMailReceiptPreviewProps.tsx @@ -1,4 +1,4 @@ -import { ComponentType } from 'react'; +import { ComponentType, useMemo } from 'react'; import { ReceiptSendMailReceipt, ReceiptSendMailReceiptProps, @@ -19,26 +19,25 @@ export const withReceiptMailReceiptPreviewProps = < const message = useSendReceiptMailMessage(); const { receiptMailState } = useReceiptSendMailBoot(); - // const items = useMemo( - // () => - // invoiceMailState?.entries?.map((entry: any) => ({ - // quantity: entry.quantity, - // total: entry.totalFormatted, - // label: entry.name, - // })), - // [invoiceMailState?.entries], - // ); + const items = useMemo( + () => + receiptMailState?.entries?.map((entry: any) => ({ + quantity: entry.quantity, + total: entry.totalFormatted, + label: entry.name, + })), + [receiptMailState?.entries], + ); const mailReceiptPreviewProps = { ...defaultReceiptMailProps, - // companyName: receiptMailState?.companyName, - // companyLogoUri: receiptMailState?.companyLogoUri, - // primaryColor: receiptMailState?.primaryColor, - // total: receiptMailState?.totalFormatted, - // dueDate: receiptMailState?.dueDateFormatted, - // dueAmount: invoiceMailState?.dueAmountFormatted, - // invoiceNumber: invoiceMailState?.invoiceNo, - // items, + companyName: receiptMailState?.companyName, + companyLogoUri: receiptMailState?.companyLogoUri, + primaryColor: receiptMailState?.primaryColor, + total: receiptMailState?.totalFormatted, + subtotal: receiptMailState?.subtotalFormatted, + receiptNumber: receiptMailState?.receiptNumber, + items, message, }; return ; diff --git a/packages/webapp/src/hooks/query/receipts.tsx b/packages/webapp/src/hooks/query/receipts.tsx index e37fb4962..5275a9fcd 100644 --- a/packages/webapp/src/hooks/query/receipts.tsx +++ b/packages/webapp/src/hooks/query/receipts.tsx @@ -239,13 +239,46 @@ export function useSendSaleReceiptMail(props) { export interface GetSaleReceiptMailStateResponse { attachReceipt: boolean; + + closedAtDate: string; + closedAtDateFormatted: string; + + companyName: string; + customerName: string; + formatArgs: Record; + from: string[]; - fromOptions: Array<{ mail: string; label: string; primary: boolean; }> + fromOptions: Array<{ mail: string; label: string; primary: boolean; }>; message: string; + + receiptDate: string; + receiptDateFormatted: string; + subject: string; + + subtotal: number; + subtotalFormatted: string; + to: string[]; toOptions: Array<{ mail: string; label: string; primary: boolean; }>; + + total: number; + totalFormatted: string; + + companyLogoUri?: string | null; + primaryColor?: string | null; + + entries: Array<{ + name: string; + quantity: number; + quantityFormatted: string; + rate: number; + rateFormatted: string; + total: number; + totalFormatted: string + }>, + receiptNumber: string; } export function useSaleReceiptMailState(