From b23112bc92461666d82490c0d4845890c3da3d09 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Wed, 2 Oct 2024 18:18:57 +0200 Subject: [PATCH] feat: Assign default PDF template automatically --- .../api/controllers/Sales/PaymentReceives.ts | 29 +++++++++++++ .../api/controllers/Sales/SalesEstimates.ts | 23 ++++++++++ .../api/controllers/Sales/SalesInvoices.ts | 25 +++++++++++ .../api/controllers/Sales/SalesReceipts.ts | 30 +++++++++++++ .../server/src/interfaces/PaymentReceive.ts | 5 +++ .../server/src/interfaces/SaleEstimate.ts | 3 ++ packages/server/src/interfaces/SaleInvoice.ts | 10 +++-- packages/server/src/interfaces/SaleReceipt.ts | 5 +++ .../Sales/Estimates/GetSaleEstimateState.ts | 20 +++++++++ .../Estimates/SaleEstimatesApplication.ts | 13 ++++++ .../Sales/Invoices/GetSaleInvoiceState.ts | 18 ++++++++ .../Sales/Invoices/SaleInvoicesApplication.ts | 14 +++++++ .../GetPaymentReceivedState.ts | 18 ++++++++ .../PaymentReceivedApplication.ts | 10 +++++ .../Sales/Receipts/GetSaleReceiptState.ts | 18 ++++++++ .../Sales/Receipts/SaleReceiptApplication.ts | 14 +++++++ .../CreditNoteForm/CreditNoteFormProvider.tsx | 22 +++++++++- .../Estimates/EstimateForm/EstimateForm.tsx | 2 + .../EstimateForm/EstimateFormProvider.tsx | 24 +++++++++-- .../Invoices/InvoiceForm/InvoiceForm.tsx | 2 + .../InvoiceForm/InvoiceFormProvider.tsx | 21 +++++++++- .../PaymentReceiveForm/PaymentReceiveForm.tsx | 37 +++++++--------- .../PaymentReceiveFormProvider.tsx | 24 ++++++++++- .../Receipts/ReceiptForm/ReceiptForm.tsx | 2 + .../ReceiptForm/ReceiptFormProvider.tsx | 25 +++++++++-- .../webapp/src/hooks/query/creditNote.tsx | 20 ++++++++- packages/webapp/src/hooks/query/estimates.tsx | 21 +++++++++- packages/webapp/src/hooks/query/invoices.tsx | 23 +++++++++- .../src/hooks/query/paymentReceives.tsx | 42 +++++++++++++++++-- packages/webapp/src/hooks/query/receipts.tsx | 27 +++++++++++- 30 files changed, 501 insertions(+), 46 deletions(-) create mode 100644 packages/server/src/services/Sales/Estimates/GetSaleEstimateState.ts create mode 100644 packages/server/src/services/Sales/Invoices/GetSaleInvoiceState.ts create mode 100644 packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedState.ts create mode 100644 packages/server/src/services/Sales/Receipts/GetSaleReceiptState.ts diff --git a/packages/server/src/api/controllers/Sales/PaymentReceives.ts b/packages/server/src/api/controllers/Sales/PaymentReceives.ts index 2adeb1a84..f54bf5f15 100644 --- a/packages/server/src/api/controllers/Sales/PaymentReceives.ts +++ b/packages/server/src/api/controllers/Sales/PaymentReceives.ts @@ -95,6 +95,12 @@ export default class PaymentReceivesController extends BaseController { asyncMiddleware(this.getPaymentReceiveInvoices.bind(this)), this.handleServiceErrors ); + router.get( + '/state', + CheckPolicies(PaymentReceiveAction.View, AbilitySubject.PaymentReceive), + this.getPaymentReceivedState.bind(this), + this.handleServiceErrors + ); router.get( '/:id', CheckPolicies(PaymentReceiveAction.View, AbilitySubject.PaymentReceive), @@ -391,6 +397,29 @@ export default class PaymentReceivesController extends BaseController { } } + /** + * + * @async + * @param {Request} req - + * @param {Response} res - + */ + private async getPaymentReceivedState( + req: Request, + res: Response, + next: NextFunction + ) { + const { tenantId } = req; + + try { + const data = await this.paymentReceiveApplication.getPaymentReceivedState( + tenantId + ); + return res.status(200).send({ data }); + } catch (error) { + next(error); + } + } + /** * Retrieve the given payment receive details. * @async diff --git a/packages/server/src/api/controllers/Sales/SalesEstimates.ts b/packages/server/src/api/controllers/Sales/SalesEstimates.ts index c19632ce1..552389f64 100644 --- a/packages/server/src/api/controllers/Sales/SalesEstimates.ts +++ b/packages/server/src/api/controllers/Sales/SalesEstimates.ts @@ -105,6 +105,12 @@ export default class SalesEstimatesController extends BaseController { asyncMiddleware(this.deleteEstimate.bind(this)), this.handleServiceErrors ); + router.get( + '/state', + CheckPolicies(SaleEstimateAction.View, AbilitySubject.SaleEstimate), + this.getSaleEstimateState.bind(this), + this.handleServiceErrors + ); router.get( '/:id', CheckPolicies(SaleEstimateAction.View, AbilitySubject.SaleEstimate), @@ -546,6 +552,23 @@ export default class SalesEstimatesController extends BaseController { } }; + private getSaleEstimateState = async ( + req: Request, + res: Response, + next: NextFunction + ) => { + const { tenantId } = req; + + try { + const data = await this.saleEstimatesApplication.getSaleEstimateState( + tenantId + ); + return res.status(200).send({ data }); + } catch (error) { + next(error); + } + }; + /** * Handles service errors. * @param {Error} error diff --git a/packages/server/src/api/controllers/Sales/SalesInvoices.ts b/packages/server/src/api/controllers/Sales/SalesInvoices.ts index 2cc2fbfa0..a8b2fb5a7 100644 --- a/packages/server/src/api/controllers/Sales/SalesInvoices.ts +++ b/packages/server/src/api/controllers/Sales/SalesInvoices.ts @@ -130,6 +130,12 @@ export default class SaleInvoicesController extends BaseController { this.asyncMiddleware(this.getInvoicePaymentTransactions), this.handleServiceErrors ); + router.get( + '/state', + CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice), + asyncMiddleware(this.getSaleInvoiceState.bind(this)), + this.handleServiceErrors + ); router.get( '/:id', CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice), @@ -138,6 +144,7 @@ export default class SaleInvoicesController extends BaseController { asyncMiddleware(this.getSaleInvoice.bind(this)), this.handleServiceErrors ); + router.get( '/', CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice), @@ -453,6 +460,24 @@ export default class SaleInvoicesController extends BaseController { return res.status(200).send({ saleInvoice }); } } + + private async getSaleInvoiceState( + req: Request, + res: Response, + next: NextFunction + ) { + const { tenantId } = req; + + try { + const data = await this.saleInvoiceApplication.getSaleInvoiceState( + tenantId + ); + return res.status(200).send({ data }); + } catch (error) { + next(error); + } + } + /** * Retrieve paginated sales invoices with custom view metadata. * @param {Request} req diff --git a/packages/server/src/api/controllers/Sales/SalesReceipts.ts b/packages/server/src/api/controllers/Sales/SalesReceipts.ts index 5330c5d26..a21392bb1 100644 --- a/packages/server/src/api/controllers/Sales/SalesReceipts.ts +++ b/packages/server/src/api/controllers/Sales/SalesReceipts.ts @@ -108,6 +108,12 @@ export default class SalesReceiptsController extends BaseController { this.handleServiceErrors, this.dynamicListService.handlerErrorsToResponse ); + router.get( + '/state', + CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt), + asyncMiddleware(this.getSaleReceiptState.bind(this)), + this.handleServiceErrors + ); router.get( '/:id', CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt), @@ -369,6 +375,30 @@ export default class SalesReceiptsController extends BaseController { } } + /** + * + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + */ + public async getSaleReceiptState( + req: Request, + res: Response, + next: NextFunction + ) { + const { tenantId } = req; + + // Retrieves receipt in pdf format. + try { + const data = await this.saleReceiptsApplication.getSaleReceiptState( + tenantId + ); + return res.status(200).send({ data }); + } catch (error) { + next(error); + } + } + /** * Sale receipt notification via SMS. * @param {Request} req diff --git a/packages/server/src/interfaces/PaymentReceive.ts b/packages/server/src/interfaces/PaymentReceive.ts index f09323a55..ccba86b59 100644 --- a/packages/server/src/interfaces/PaymentReceive.ts +++ b/packages/server/src/interfaces/PaymentReceive.ts @@ -238,3 +238,8 @@ export interface PaymentReceivedPdfTemplateAttributes { showPaymentReceivedDate: boolean; paymentReceivedDateLabel: string; } + + +export interface IPaymentReceivedState { + defaultTemplateId: number; +} \ No newline at end of file diff --git a/packages/server/src/interfaces/SaleEstimate.ts b/packages/server/src/interfaces/SaleEstimate.ts index f53693b52..72dd4cd09 100644 --- a/packages/server/src/interfaces/SaleEstimate.ts +++ b/packages/server/src/interfaces/SaleEstimate.ts @@ -144,3 +144,6 @@ export interface ISaleEstimateMailPresendEvent { messageOptions: SaleEstimateMailOptionsDTO; } +export interface ISaleEstimateState { + defaultTemplateId: number; +} diff --git a/packages/server/src/interfaces/SaleInvoice.ts b/packages/server/src/interfaces/SaleInvoice.ts index 6e3f94088..59404b549 100644 --- a/packages/server/src/interfaces/SaleInvoice.ts +++ b/packages/server/src/interfaces/SaleInvoice.ts @@ -20,7 +20,7 @@ export interface PaymentIntegrationTransactionLinkEventPayload { referenceType: string; referenceId: number; saleInvoiceId: number; - trx?: Knex.Transaction + trx?: Knex.Transaction; } export interface PaymentIntegrationTransactionLinkDeleteEventPayload { @@ -30,7 +30,7 @@ export interface PaymentIntegrationTransactionLinkDeleteEventPayload { referenceType: string; referenceId: number; oldSaleInvoiceId: number; - trx?: Knex.Transaction + trx?: Knex.Transaction; } export interface ISaleInvoice { @@ -174,7 +174,7 @@ export interface ISaleInvoiceDeletingPayload { tenantId: number; oldSaleInvoice: ISaleInvoice; saleInvoiceId: number; - trx: Knex.Transaction; + trx: Knex.Transaction; } export interface ISaleInvoiceDeletedPayload { @@ -339,3 +339,7 @@ export interface InvoicePdfTemplateAttributes { showStatement: boolean; statement: string; } + +export interface ISaleInvocieState { + defaultTemplateId: number; +} diff --git a/packages/server/src/interfaces/SaleReceipt.ts b/packages/server/src/interfaces/SaleReceipt.ts index 6966722ff..f44e995d3 100644 --- a/packages/server/src/interfaces/SaleReceipt.ts +++ b/packages/server/src/interfaces/SaleReceipt.ts @@ -211,3 +211,8 @@ export interface ISaleReceiptBrandingTemplateAttributes { showReceiptDate: boolean; receiptDateLabel: string; } + + +export interface ISaleReceiptState { + defaultTemplateId: number; +} \ No newline at end of file diff --git a/packages/server/src/services/Sales/Estimates/GetSaleEstimateState.ts b/packages/server/src/services/Sales/Estimates/GetSaleEstimateState.ts new file mode 100644 index 000000000..d2f440023 --- /dev/null +++ b/packages/server/src/services/Sales/Estimates/GetSaleEstimateState.ts @@ -0,0 +1,20 @@ + + +import { Service } from 'typedi'; +import { ISaleEstimateState } from '@/interfaces'; + +@Service() +export class GetSaleEstimateState { + /** + * + * @param {Number} saleEstimateId - + * @return {Promise} + */ + public async getSaleEstimateState( + tenantId: number + ): Promise { + return { + defaultTemplateId: 1, + }; + } +} diff --git a/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts b/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts index cce664b0e..5fc0b2db4 100644 --- a/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts +++ b/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts @@ -20,6 +20,7 @@ import { RejectSaleEstimate } from './RejectSaleEstimate'; import { SaleEstimateNotifyBySms } from './SaleEstimateSmsNotify'; import { SaleEstimatesPdf } from './SaleEstimatesPdf'; import { SendSaleEstimateMail } from './SendSaleEstimateMail'; +import { GetSaleEstimateState } from './GetSaleEstimateState'; @Service() export class SaleEstimatesApplication { @@ -56,6 +57,9 @@ export class SaleEstimatesApplication { @Inject() private sendEstimateMailService: SendSaleEstimateMail; + @Inject() + private getSaleEstimateStateService: GetSaleEstimateState; + /** * Create a sale estimate. * @param {number} tenantId - The tenant id. @@ -249,4 +253,13 @@ export class SaleEstimatesApplication { saleEstimateId ); } + + /** + * Retrieves the current state of the sale estimate. + * @param {number} tenantId - The ID of the tenant. + * @returns {Promise} - A promise resolving to the sale estimate state. + */ + public getSaleEstimateStat(tenantId: number) { + return this.getSaleEstimateStateService.getSaleEstimateState(tenantId); + } } diff --git a/packages/server/src/services/Sales/Invoices/GetSaleInvoiceState.ts b/packages/server/src/services/Sales/Invoices/GetSaleInvoiceState.ts new file mode 100644 index 000000000..e3c021212 --- /dev/null +++ b/packages/server/src/services/Sales/Invoices/GetSaleInvoiceState.ts @@ -0,0 +1,18 @@ +import { Service } from 'typedi'; +import { ISaleInvocieState } from '@/interfaces'; + +@Service() +export class GetSaleInvoiceState { + /** + * + * @param {Number} saleInvoiceId - + * @return {Promise} + */ + public async getSaleInvoiceState( + tenantId: number + ): Promise { + return { + defaultTemplateId: 1, + }; + } +} diff --git a/packages/server/src/services/Sales/Invoices/SaleInvoicesApplication.ts b/packages/server/src/services/Sales/Invoices/SaleInvoicesApplication.ts index bc3f8c24b..e647d8ef6 100644 --- a/packages/server/src/services/Sales/Invoices/SaleInvoicesApplication.ts +++ b/packages/server/src/services/Sales/Invoices/SaleInvoicesApplication.ts @@ -28,6 +28,7 @@ import { SaleInvoiceNotifyBySms } from './SaleInvoiceNotifyBySms'; import { SendInvoiceMailReminder } from './SendSaleInvoiceMailReminder'; import { SendSaleInvoiceMail } from './SendSaleInvoiceMail'; import { GetSaleInvoiceMailReminder } from './GetSaleInvoiceMailReminder'; +import { GetSaleInvoiceState } from './GetSaleInvoiceState'; @Service() export class SaleInvoiceApplication { @@ -73,6 +74,9 @@ export class SaleInvoiceApplication { @Inject() private getSaleInvoiceReminderService: GetSaleInvoiceMailReminder; + @Inject() + private getSaleInvoiceStateService: GetSaleInvoiceState; + /** * Creates a new sale invoice with associated GL entries. * @param {number} tenantId @@ -169,6 +173,16 @@ export class SaleInvoiceApplication { ); } + /** + * Retrieves the sale invoice state. + * @param {number} tenantId + * @param {number} saleInvoiceId + * @returns + */ + public getSaleInvoiceState(tenantId: number) { + return this.getSaleInvoiceStateService.getSaleInvoiceState(tenantId); + } + /** * Mark the given sale invoice as delivered. * @param {number} tenantId diff --git a/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedState.ts b/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedState.ts new file mode 100644 index 000000000..e6accef36 --- /dev/null +++ b/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedState.ts @@ -0,0 +1,18 @@ +import { Service } from 'typedi'; +import { IPaymentReceivedState } from '@/interfaces'; + +@Service() +export class GetPaymentReceivedState { + /** + * Retrieves the current state of the payment received. + * @param {number} tenantId - The ID of the tenant. + * @returns {Promise} - A promise resolving to the payment received state. + */ + public async getPaymentReceivedState( + tenantId: number + ): Promise { + return { + defaultTemplateId: 1, + }; + } +} diff --git a/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedApplication.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedApplication.ts index 27e92f69e..e9725c712 100644 --- a/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedApplication.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedApplication.ts @@ -19,6 +19,7 @@ import { GetPaymentReceivedInvoices } from './GetPaymentReceivedInvoices'; import { PaymentReceiveNotifyBySms } from './PaymentReceivedSmsNotify'; import GetPaymentReceivedPdf from './GetPaymentReceivedPdf'; import { SendPaymentReceiveMailNotification } from './PaymentReceivedMailNotification'; +import { GetPaymentReceivedState } from './GetPaymentReceivedState'; @Service() export class PaymentReceivesApplication { @@ -49,6 +50,9 @@ export class PaymentReceivesApplication { @Inject() private getPaymentReceivePdfService: GetPaymentReceivedPdf; + @Inject() + private getPaymentReceivedStateService: GetPaymentReceivedState; + /** * Creates a new payment receive. * @param {number} tenantId @@ -223,4 +227,10 @@ export class PaymentReceivesApplication { paymentReceiveId ); }; + + public getPaymentReceivedState = (tenantId: number) => { + return this.getPaymentReceivedStateService.getPaymentReceivedState( + tenantId + ); + }; } diff --git a/packages/server/src/services/Sales/Receipts/GetSaleReceiptState.ts b/packages/server/src/services/Sales/Receipts/GetSaleReceiptState.ts new file mode 100644 index 000000000..ff32c42a8 --- /dev/null +++ b/packages/server/src/services/Sales/Receipts/GetSaleReceiptState.ts @@ -0,0 +1,18 @@ +import { Service } from 'typedi'; +import { ISaleReceiptState } from '@/interfaces'; + +@Service() +export class GetSaleReceiptState { + /** + * Retireves the sale receipt state. + * @param {Number} tenantId - + * @return {Promise} + */ + public async getSaleReceiptState( + tenantId: number + ): Promise { + return { + defaultTemplateId: 1, + }; + } +} diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptApplication.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptApplication.ts index d4c87df29..b68f195f0 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptApplication.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptApplication.ts @@ -4,6 +4,7 @@ import { IFilterMeta, IPaginationMeta, ISaleReceipt, + ISaleReceiptState, ISalesReceiptsFilter, SaleReceiptMailOpts, SaleReceiptMailOptsDTO, @@ -16,6 +17,7 @@ import { CloseSaleReceipt } from './CloseSaleReceipt'; import { SaleReceiptsPdf } from './SaleReceiptsPdfService'; import { SaleReceiptNotifyBySms } from './SaleReceiptNotifyBySms'; import { SaleReceiptMailNotification } from './SaleReceiptMailNotification'; +import { GetSaleReceiptState } from './GetSaleReceiptState'; @Service() export class SaleReceiptApplication { @@ -46,6 +48,9 @@ export class SaleReceiptApplication { @Inject() private saleReceiptNotifyByMailService: SaleReceiptMailNotification; + @Inject() + private getSaleReceiptStateService: GetSaleReceiptState; + /** * Creates a new sale receipt with associated entries. * @param {number} tenantId @@ -207,4 +212,13 @@ export class SaleReceiptApplication { saleReceiptId ); } + + /** + * Retrieves the current state of the sale receipt. + * @param {number} tenantId - The ID of the tenant. + * @returns {Promise} - A promise resolving to the sale receipt state. + */ + public getSaleReceiptState(tenantId: number): Promise { + return this.getSaleReceiptStateService.getSaleReceiptState(tenantId); + } } diff --git a/packages/webapp/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormProvider.tsx b/packages/webapp/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormProvider.tsx index 19c02eb61..85c8d1e6c 100644 --- a/packages/webapp/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormProvider.tsx +++ b/packages/webapp/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormProvider.tsx @@ -17,10 +17,19 @@ import { useBranches, useSettingsCreditNotes, useInvoice, + useGetCreditNoteState, + CreditNoteStateResponse, } from '@/hooks/query'; import { useGetPdfTemplates } from '@/hooks/query/pdf-templates'; -const CreditNoteFormContext = React.createContext(); +interface CreditNoteFormProviderValue { + creditNoteState: CreditNoteStateResponse; + isCreditNoteStateLoading: boolean; +} + +const CreditNoteFormContext = React.createContext( + {} as CreditNoteFormProviderValue, +); /** * Credit note data provider. @@ -78,6 +87,10 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) { const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } = useGetPdfTemplates({ resource: 'PaymentReceive' }); + // Fetches the credit note state. + const { data: creditNoteState, isLoading: isCreditNoteStateLoading } = + useGetCreditNoteState(); + // Handle fetching settings. useSettingsCreditNotes(); @@ -124,6 +137,10 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) { // Branding templates. brandingTemplates, isBrandingTemplatesLoading, + + // Credit note state + creditNoteState, + isCreditNoteStateLoading, }; const isLoading = @@ -140,6 +157,7 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) { ); } -const useCreditNoteFormContext = () => React.useContext(CreditNoteFormContext); +const useCreditNoteFormContext = () => + React.useContext(CreditNoteFormContext); export { CreditNoteFormProvider, useCreditNoteFormContext }; diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateForm.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateForm.tsx index aff0888d6..aa9b0bb21 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateForm.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateForm.tsx @@ -58,6 +58,7 @@ function EstimateForm({ submitPayload, createEstimateMutate, editEstimateMutate, + saleEstimateState, } = useEstimateFormContext(); const estimateNumber = transactionNumber( @@ -79,6 +80,7 @@ function EstimateForm({ currency_code: base_currency, terms_conditions: defaultTo(estimateTermsConditions, ''), note: defaultTo(estimateCustomerNotes, ''), + pdf_template_id: saleEstimateState?.defaultTemplateId, }), }; diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormProvider.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormProvider.tsx index 6cba0125e..0366e6178 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormProvider.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormProvider.tsx @@ -11,6 +11,8 @@ import { useSettingsEstimates, useCreateEstimate, useEditEstimate, + useGetSaleEstimatesState, + ISaleEstimatesStateResponse, } from '@/hooks/query'; import { useProjects } from '@/containers/Projects/hooks'; import { useGetPdfTemplates } from '@/hooks/query/pdf-templates'; @@ -18,7 +20,12 @@ import { Features } from '@/constants'; import { useFeatureCan } from '@/hooks/state'; import { ITEMS_FILTER_ROLES } from './utils'; -const EstimateFormContext = createContext(); +interface EstimateFormProviderValues { + saleEstimateState: ISaleEstimatesStateResponse; + isSaleEstimateStateLoading: boolean; +} + +const EstimateFormContext = createContext({} as EstimateFormProviderValues); /** * Estimate form provider. @@ -76,6 +83,10 @@ function EstimateFormProvider({ query, estimateId, ...props }) { const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } = useGetPdfTemplates({ resource: 'SaleEstimate' }); + // Fetches the sale estimate state. + const { data: saleEstimateState, isLoading: isSaleEstimateStateLoading } = + useGetSaleEstimatesState(); + // Handle fetch settings. useSettingsEstimates(); @@ -118,15 +129,21 @@ function EstimateFormProvider({ query, estimateId, ...props }) { createEstimateMutate, editEstimateMutate, + // Branding templates brandingTemplates, isBrandingTemplatesLoading, + + // Estimate state + saleEstimateState, + isSaleEstimateStateLoading, }; const isLoading = isCustomersLoading || isItemsLoading || isEstimateLoading || - isBrandingTemplatesLoading; + isBrandingTemplatesLoading || + isSaleEstimateStateLoading; return ( @@ -135,6 +152,7 @@ function EstimateFormProvider({ query, estimateId, ...props }) { ); } -const useEstimateFormContext = () => useContext(EstimateFormContext); +const useEstimateFormContext = () => + useContext(EstimateFormContext); export { EstimateFormProvider, useEstimateFormContext }; diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.tsx index 6b9f234ac..3c1d1c981 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.tsx @@ -61,6 +61,7 @@ function InvoiceForm({ createInvoiceMutate, editInvoiceMutate, submitPayload, + saleInvoiceState } = useInvoiceFormContext(); // Invoice number. @@ -83,6 +84,7 @@ function InvoiceForm({ currency_code: base_currency, invoice_message: defaultTo(invoiceCustomerNotes, ''), terms_conditions: defaultTo(invoiceTermsConditions, ''), + pdf_template_id: saleInvoiceState?.defaultTemplateId, ...newInvoice, }), }; diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormProvider.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormProvider.tsx index 172dcda94..c57c1e939 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormProvider.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormProvider.tsx @@ -16,13 +16,22 @@ import { useEditInvoice, useSettingsInvoices, useEstimate, + useGetSaleInvoiceState, + GetSaleInvoiceStateResponse, } from '@/hooks/query'; import { useProjects } from '@/containers/Projects/hooks'; import { useTaxRates } from '@/hooks/query/taxRates'; import { useGetPdfTemplates } from '@/hooks/query/pdf-templates'; import { useGetPaymentServices } from '@/hooks/query/payment-services'; -const InvoiceFormContext = createContext(); +interface InvoiceFormContextValue { + saleInvoiceState: GetSaleInvoiceStateResponse | null; + isInvoiceStateLoading: boolean; +} + +const InvoiceFormContext = createContext( + {} as InvoiceFormContextValue, +); /** * Accounts chart data provider. @@ -100,6 +109,9 @@ function InvoiceFormProvider({ invoiceId, baseCurrency, ...props }) { isSuccess: isBranchesSuccess, } = useBranches({}, { enabled: isBranchFeatureCan }); + const { data: saleInvoiceState, isLoading: isInvoiceStateLoading } = + useGetSaleInvoiceState(); + // Handle fetching settings. const { isLoading: isSettingsLoading } = useSettingsInvoices(); @@ -154,6 +166,10 @@ function InvoiceFormProvider({ invoiceId, baseCurrency, ...props }) { // Payment Services paymentServices, isPaymentServicesLoading, + + // Invoice state + saleInvoiceState, + isInvoiceStateLoading, }; return ( @@ -172,6 +188,7 @@ function InvoiceFormProvider({ invoiceId, baseCurrency, ...props }) { ); } -const useInvoiceFormContext = () => React.useContext(InvoiceFormContext); +const useInvoiceFormContext = () => + React.useContext(InvoiceFormContext); export { InvoiceFormProvider, useInvoiceFormContext }; diff --git a/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveForm.tsx b/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveForm.tsx index 8d595c0fc..406f365a1 100644 --- a/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveForm.tsx +++ b/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveForm.tsx @@ -69,6 +69,7 @@ function PaymentReceiveForm({ editPaymentReceiveMutate, createPaymentReceiveMutate, isExcessConfirmed, + paymentReceivedState, } = usePaymentReceiveFormContext(); // Payment receive number. @@ -77,29 +78,21 @@ function PaymentReceiveForm({ paymentReceiveNextNumber, ); // Form initial values. - const initialValues = useMemo( - () => ({ - ...(!isEmpty(paymentReceiveEditPage) - ? transformToEditForm(paymentReceiveEditPage, paymentEntriesEditPage) - : { - ...defaultPaymentReceive, - // If the auto-increment mode is enabled, take the next payment - // number from the settings. - ...(paymentReceiveAutoIncrement && { - payment_receive_no: nextPaymentNumber, - }), - deposit_account_id: defaultTo(preferredDepositAccount, ''), - currency_code: base_currency, + const initialValues = { + ...(!isEmpty(paymentReceiveEditPage) + ? transformToEditForm(paymentReceiveEditPage, paymentEntriesEditPage) + : { + ...defaultPaymentReceive, + // If the auto-increment mode is enabled, take the next payment + // number from the settings. + ...(paymentReceiveAutoIncrement && { + payment_receive_no: nextPaymentNumber, }), - }), - [ - paymentReceiveEditPage, - nextPaymentNumber, - paymentEntriesEditPage, - paymentReceiveAutoIncrement, - preferredDepositAccount, - ], - ); + deposit_account_id: defaultTo(preferredDepositAccount, ''), + currency_code: base_currency, + pdf_template_id: paymentReceivedState.defaultTemplateId, + }), + }; // Handle form submit. const handleSubmitForm = ( values, diff --git a/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveFormProvider.tsx b/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveFormProvider.tsx index d98e26b4d..b82566fd2 100644 --- a/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveFormProvider.tsx +++ b/packages/webapp/src/containers/Sales/PaymentsReceived/PaymentReceiveForm/PaymentReceiveFormProvider.tsx @@ -12,11 +12,21 @@ import { useBranches, useCreatePaymentReceive, useEditPaymentReceive, + usePaymentReceivedState, + PaymentReceivedStateResponse, } from '@/hooks/query'; import { useGetPdfTemplates } from '@/hooks/query/pdf-templates'; +interface PaymentReceivedFormContextValue { + isPaymentReceivedStateLoading: boolean; + paymentReceivedState: PaymentReceivedStateResponse; +} + // Payment receive form context. -const PaymentReceiveFormContext = createContext(); +const PaymentReceiveFormContext = + createContext( + {} as PaymentReceivedFormContextValue, + ); /** * Payment receive form provider. @@ -70,6 +80,12 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) { const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } = useGetPdfTemplates({ resource: 'PaymentReceive' }); + // Fetches the payment received initial state. + const { + data: paymentReceivedState, + isLoading: isPaymentReceivedStateLoading, + } = usePaymentReceivedState(); + // Detarmines whether the new mode. const isNewMode = !paymentReceiveId; @@ -111,6 +127,10 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) { // Branding templates brandingTemplates, isBrandingTemplatesLoading, + + // Payment received state + isPaymentReceivedStateLoading, + paymentReceivedState, }; const isLoading = @@ -127,6 +147,6 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) { } const usePaymentReceiveFormContext = () => - useContext(PaymentReceiveFormContext); + useContext(PaymentReceiveFormContext); export { PaymentReceiveFormProvider, usePaymentReceiveFormContext }; diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptForm.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptForm.tsx index 990c083ba..32aba2e11 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptForm.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptForm.tsx @@ -63,6 +63,7 @@ function ReceiptForm({ createReceiptMutate, submitPayload, isNewMode, + saleReceiptState, } = useReceiptFormContext(); // The next receipt number. @@ -84,6 +85,7 @@ function ReceiptForm({ currency_code: base_currency, receipt_message: receiptMessage, terms_conditions: receiptTermsConditions, + pdf_template_id: saleReceiptState?.pdfTemplateId, }), }; // Handle the form submit. diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormProvider.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormProvider.tsx index e4a8a1095..6f3c84001 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormProvider.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/ReceiptFormProvider.tsx @@ -13,11 +13,20 @@ import { useItems, useCreateReceipt, useEditReceipt, + useGetReceiptState, + IGetReceiptStateResponse, } from '@/hooks/query'; import { useProjects } from '@/containers/Projects/hooks'; import { useGetPdfTemplates } from '@/hooks/query/pdf-templates'; -const ReceiptFormContext = createContext(); +const ReceiptFormContext = createContext( + {} as ReceiptFormProviderValue, +); + +interface ReceiptFormProviderValue { + isSaleReceiptStateLoading: boolean; + saleReceiptState: IGetReceiptStateResponse; +} /** * Receipt form provider. @@ -96,6 +105,10 @@ function ReceiptFormProvider({ receiptId, ...props }) { const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } = useGetPdfTemplates({ resource: 'SaleReceipt' }); + // Fetches the sale receipt state. + const { data: saleReceiptState, isLoading: isSaleReceiptStateLoading } = + useGetReceiptState(); + // Fetch receipt settings. const { isLoading: isSettingLoading } = useSettingsReceipts(); @@ -137,6 +150,10 @@ function ReceiptFormProvider({ receiptId, ...props }) { // Branding templates brandingTemplates, isBrandingTemplatesLoading, + + // State + isSaleReceiptStateLoading, + saleReceiptState, }; const isLoading = isReceiptLoading || @@ -144,7 +161,8 @@ function ReceiptFormProvider({ receiptId, ...props }) { isCustomersLoading || isItemsLoading || isSettingLoading || - isBrandingTemplatesLoading; + isBrandingTemplatesLoading || + isSaleReceiptStateLoading; return ( @@ -153,6 +171,7 @@ function ReceiptFormProvider({ receiptId, ...props }) { ); } -const useReceiptFormContext = () => React.useContext(ReceiptFormContext); +const useReceiptFormContext = () => + React.useContext(ReceiptFormContext); export { ReceiptFormProvider, useReceiptFormContext }; diff --git a/packages/webapp/src/hooks/query/creditNote.tsx b/packages/webapp/src/hooks/query/creditNote.tsx index 99a40df8e..c55365db7 100644 --- a/packages/webapp/src/hooks/query/creditNote.tsx +++ b/packages/webapp/src/hooks/query/creditNote.tsx @@ -1,7 +1,7 @@ // @ts-nocheck import { useQueryClient, useMutation } from 'react-query'; import { useRequestQuery } from '../useQueryRequest'; -import { transformPagination } from '@/utils'; +import { transformPagination, transformToCamelCase } from '@/utils'; import useApiRequest from '../useRequest'; import { useRequestPdf } from '../useRequestPdf'; import t from './types'; @@ -356,3 +356,21 @@ export function useRefundCreditTransaction(id, props, requestProps) { export function usePdfCreditNote(creditNoteId) { return useRequestPdf({ url: `sales/credit_notes/${creditNoteId}` }); } + +export interface CreditNoteStateResponse { + defaultTemplateId: number; +} +export function useGetCreditNoteState( + options?: UseQueryOptions, +): UseQueryResult { + const apiRequest = useApiRequest(); + + return useQuery( + ['CREDIT_NOTE_STATE'], + () => + apiRequest + .get('/sales/credit_notes/state') + .then((res) => transformToCamelCase(res.data?.data)), + { ...options }, + ); +} diff --git a/packages/webapp/src/hooks/query/estimates.tsx b/packages/webapp/src/hooks/query/estimates.tsx index 8607fd54c..1356b33eb 100644 --- a/packages/webapp/src/hooks/query/estimates.tsx +++ b/packages/webapp/src/hooks/query/estimates.tsx @@ -2,7 +2,7 @@ import { useQueryClient, useMutation } from 'react-query'; import { useRequestQuery } from '../useQueryRequest'; import useApiRequest from '../useRequest'; -import { transformPagination } from '@/utils'; +import { transformPagination, transformToCamelCase } from '@/utils'; import t from './types'; import { useRequestPdf } from '../useRequestPdf'; @@ -270,3 +270,22 @@ export function useSaleEstimateDefaultOptions(estimateId, props) { }, ); } + +export interface ISaleEstimatesStateResponse { + defaultTemplateId: number; +} + +export function useGetSaleEstimatesState( + options?: UseQueryOptions, +): UseQueryResult { + const apiRequest = useApiRequest(); + + return useQuery( + ['SALE_ESTIMATES_STATE'], + () => + apiRequest + .get('/sales/estimates/state') + .then((res) => transformToCamelCase(res.data?.data)), + { ...options }, + ); +} diff --git a/packages/webapp/src/hooks/query/invoices.tsx b/packages/webapp/src/hooks/query/invoices.tsx index 660b54c9d..46c927cf7 100644 --- a/packages/webapp/src/hooks/query/invoices.tsx +++ b/packages/webapp/src/hooks/query/invoices.tsx @@ -1,7 +1,7 @@ // @ts-nocheck -import { useQueryClient, useMutation } from 'react-query'; +import { useQueryClient, useMutation, useQuery } from 'react-query'; import { useRequestQuery } from '../useQueryRequest'; -import { transformPagination } from '@/utils'; +import { transformPagination, transformToCamelCase } from '@/utils'; import useApiRequest from '../useRequest'; import { useRequestPdf } from '../useRequestPdf'; import t from './types'; @@ -341,3 +341,22 @@ export function useSaleInvoiceDefaultOptions(invoiceId, props) { }, ); } + +export interface GetSaleInvoiceStateResponse { + defaultTemplateId: number; +} + +export function useGetSaleInvoiceState( + options?: UseQueryOptions, +): UseQueryResult { + const apiRequest = useApiRequest(); + + return useQuery( + ['SALE_INVOICE_STATE'], + () => + apiRequest + .get(`/sales/invoices/state`) + .then((res) => transformToCamelCase(res.data?.data)), + { ...options }, + ); +} diff --git a/packages/webapp/src/hooks/query/paymentReceives.tsx b/packages/webapp/src/hooks/query/paymentReceives.tsx index e8b2e82c9..608f66cea 100644 --- a/packages/webapp/src/hooks/query/paymentReceives.tsx +++ b/packages/webapp/src/hooks/query/paymentReceives.tsx @@ -1,11 +1,17 @@ // @ts-nocheck -import { useMutation, useQueryClient } from 'react-query'; -import { useRequestQuery } from '../useQueryRequest'; +import { + useMutation, + useQueryClient, + QueryClient, + UseQueryOptions, + UseQueryResult, + useQuery, +} from 'react-query'; import useApiRequest from '../useRequest'; +import { useRequestQuery } from '../useQueryRequest'; import { transformPagination, saveInvoke } from '@/utils'; - -import t from './types'; import { useRequestPdf } from '../useRequestPdf'; +import t from './types'; // Common invalidate queries. const commonInvalidateQueries = (client) => { @@ -269,3 +275,31 @@ export function usePaymentReceiveDefaultOptions(paymentReceiveId, props) { }, ); } + +export interface PaymentReceivedStateResponse { + defaultTemplateId: number; +} + +/** + * Retrieves the payment receive state. + * @param {Record} query - Query parameters for the request. + * @param {UseQueryOptions} options - Optional query options. + * @returns {UseQueryResult} The query result. + */ +export function usePaymentReceivedState( + query: Record, + options?: UseQueryOptions, +): UseQueryResult { + const apiRequest = useApiRequest(); + + return useQuery( + [t.PAYMENT_RECEIVE_STATE, query], + () => + apiRequest + .get('/sales/payment_receives/state', { params: query }) + .then((res) => res.data), + { + ...options, + }, + ); +} diff --git a/packages/webapp/src/hooks/query/receipts.tsx b/packages/webapp/src/hooks/query/receipts.tsx index 7f72910c4..579253ead 100644 --- a/packages/webapp/src/hooks/query/receipts.tsx +++ b/packages/webapp/src/hooks/query/receipts.tsx @@ -1,5 +1,11 @@ // @ts-nocheck -import { useQueryClient, useMutation } from 'react-query'; +import { + useQueryClient, + useMutation, + UseQueryResult, + UseQueryOptions, + useQuery, +} from 'react-query'; import { useRequestQuery } from '../useQueryRequest'; import useApiRequest from '../useRequest'; import { transformPagination } from '@/utils'; @@ -244,3 +250,22 @@ export function useSaleReceiptDefaultOptions(invoiceId, props) { }, ); } + +export interface IGetReceiptStateResponse { + pdfTemplateId: number; +} + +export function useGetReceiptState( + options?: UseQueryOptions, +): UseQueryResult { + const apiRequest = useApiRequest(); + + return useQuery( + ['SALE_RECEIPT_STATE'], + () => + apiRequest + .get(`/sales/receipts/state`) + .then((res) => transformToCamelCase(res.data?.data)), + { ...options }, + ); +}