Merge pull request #678 from bigcapitalhq/pdf-templates-company-customer-address

feat: Pdf templates customer/company addresses
This commit is contained in:
Ahmed Bouhuolia
2024-09-30 11:21:14 +02:00
committed by GitHub
50 changed files with 566 additions and 318 deletions

View File

@@ -48,8 +48,8 @@ block head
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-flow: wrap; flex-flow: wrap;
-webkit-box-align: center; -webkit-box-align: flex-start;
align-items: center; align-items: flex-start;
-webkit-box-pack: start; -webkit-box-pack: start;
justify-content: flex-start; justify-content: flex-start;
gap: 10px; gap: 10px;
@@ -150,17 +150,14 @@ block content
div(class=`${prefix}-terms-item__value`) #{creditNoteDate} div(class=`${prefix}-terms-item__value`) #{creditNoteDate}
div(class=`${prefix}-address-section`) div(class=`${prefix}-address-section`)
if showBilledFromAddress if showCompanyAddress
div(class=`${prefix}-address`) div(class=`${prefix}-address-from`)
strong #{companyName} div !{companyAddress}
each address in billedFromAddress
div #{address}
if showBilledToAddress if showCustomerAddress
div(class=`${prefix}-address`) div(class=`${prefix}-address-to`)
strong #{billedToLabel} strong #{billedToLabel}
each address in billedToAddress div !{customerAddress}
div #{address}
table(class=`${prefix}-table`) table(class=`${prefix}-table`)
thead thead

View File

@@ -47,9 +47,7 @@ block head
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-flow: wrap; flex-flow: wrap;
-webkit-box-align: center; align-items: flex-start;
align-items: center;
-webkit-box-pack: start;
justify-content: flex-start; justify-content: flex-start;
gap: 10px; gap: 10px;
margin-bottom: 24px; margin-bottom: 24px;
@@ -156,17 +154,14 @@ block content
//- Addresses (Group section) //- Addresses (Group section)
div(class=`${prefix}-addresses`) div(class=`${prefix}-addresses`)
if showBilledFromAddress if showCompanyAddress
div(class=`${prefix}-address`) div(class=`${prefix}-address-from`)
strong #{companyName} div !{companyAddress}
each item in billedFromAddress
div(class=`${prefix}-address__item`) #{item}
if showBilledToAddress if showCustomerAddress
div(class=`${prefix}-address`) div(class=`${prefix}-address-to`)
strong #{billedToLabel} strong #{billedToLabel}
each item in billedToAddress div !{customerAddress}
div(class=`${prefix}-address__item`) #{item}
//- Table section (Line items) //- Table section (Line items)
table(class=`${prefix}-table`) table(class=`${prefix}-table`)

View File

@@ -48,9 +48,7 @@ block head
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-flow: wrap; flex-flow: wrap;
-webkit-box-align: center; align-items: flex-start;
align-items: center;
-webkit-box-pack: start;
justify-content: flex-start; justify-content: flex-start;
gap: 10px; gap: 10px;
margin-bottom: 24px; margin-bottom: 24px;
@@ -167,17 +165,14 @@ block content
//- Address section //- Address section
div(class=`${prefix}-address-root`) div(class=`${prefix}-address-root`)
if showBilledFromAddress if showCompanyAddress
div(class=`${prefix}-address-from`) div(class=`${prefix}-address-from`)
strong #{companyName} div !{companyAddress}
each item in billedFromAddres
div(class=`${prefix}-address-from__item`) #{item}
if showBillingToAddress if showCustomerAddress
div(class=`${prefix}-address-to`) div(class=`${prefix}-address-to`)
strong #{billedToLabel} strong #{billedToLabel}
each item in billedToAddress div !{customerAddress}
div(class=`${prefix}-address-to__item`) #{item}
//- Invoice table //- Invoice table
table(class=`${prefix}-table`) table(class=`${prefix}-table`)

View File

@@ -46,9 +46,7 @@ block head
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-flow: wrap; flex-flow: wrap;
-webkit-box-align: center; align-items: flex-start;
align-items: center;
-webkit-box-pack: start;
justify-content: flex-start; justify-content: flex-start;
gap: 10px; gap: 10px;
margin-bottom: 24px; margin-bottom: 24px;
@@ -140,17 +138,14 @@ block content
div(class=`${prefix}-terms-item__value`) #{paymentReceivedDate} div(class=`${prefix}-terms-item__value`) #{paymentReceivedDate}
div(class=`${prefix}-addresses`) div(class=`${prefix}-addresses`)
if showBilledFromAddress if showCompanyAddress
div(class=`${prefix}-address`) div(class=`${prefix}-address-from`)
strong(class=`${prefix}-address__item`) #{companyName} div !{companyAddress}
each addressLine in billedFromAddress
div(class=`${prefix}-address__item`) #{addressLine}
if showBillingToAddress if showCustomerAddress
div(class=`${prefix}-address`) div(class=`${prefix}-address-to`)
strong(class=`${prefix}-address__item`) #{billedToLabel} strong #{billedToLabel}
each addressLine in billedToAddress div !{customerAddress}
div(class=`${prefix}-address__item`) #{addressLine}
table(class=`${prefix}-table`) table(class=`${prefix}-table`)
thead thead

View File

@@ -46,8 +46,8 @@ block head
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-flow: wrap; flex-flow: wrap;
-webkit-box-align: center; -webkit-box-align: flex-start;
align-items: center; align-items: flex-start;
-webkit-box-pack: start; -webkit-box-pack: start;
justify-content: flex-start; justify-content: flex-start;
gap: 10px; gap: 10px;
@@ -146,17 +146,14 @@ block content
//- Address Section //- Address Section
div(class=`${prefix}-address-section`) div(class=`${prefix}-address-section`)
if showBilledFromAddress if showCompanyAddress
div(class=`${prefix}-address`) div(class=`${prefix}-address-from`)
strong= companyName div !{companyAddress}
each addressLine in billedFromAddress
div= addressLine
if showBilledToAddress if showCustomerAddress
div(class=`${prefix}-address`) div(class=`${prefix}-address-to`)
strong= billedToLabel strong #{billedToLabel}
each addressLine in billedToAddress div !{customerAddress}
div= addressLine
//- Table Section //- Table Section
table(class=`${prefix}-table`) table(class=`${prefix}-table`)

View File

@@ -31,6 +31,7 @@ export class PdfTemplatesController extends BaseController {
this.validationResult, this.validationResult,
this.editPdfTemplate.bind(this) this.editPdfTemplate.bind(this)
); );
router.get('/state', this.getOrganizationBrandingState.bind(this));
router.get( router.get(
'/', '/',
[query('resource').optional()], [query('resource').optional()],
@@ -175,4 +176,20 @@ export class PdfTemplatesController extends BaseController {
next(error); next(error);
} }
} }
async getOrganizationBrandingState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
const data =
await this.pdfTemplateApplication.getPdfTemplateBrandingState(tenantId);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
} }

View File

@@ -262,16 +262,24 @@ export type ICreditNoteGLCommonEntry = Pick<
>; >;
export interface CreditNotePdfTemplateAttributes { export interface CreditNotePdfTemplateAttributes {
// # Primary color
primaryColor: string; primaryColor: string;
secondaryColor: string; secondaryColor: string;
// # Company logo
showCompanyLogo: boolean; showCompanyLogo: boolean;
companyLogo: string; companyLogo: string;
// # Company name
companyName: string; companyName: string;
billedToAddress: string[]; // # Customer Address
billedFromAddress: string[]; showCustomerAddress: boolean;
showBilledToAddress: boolean; customerAddress: string;
showBilledFromAddress: boolean;
// # Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string; billedToLabel: string;
total: string; total: string;

View File

@@ -207,10 +207,13 @@ export interface PaymentReceivedPdfTemplateAttributes {
companyLogo: string; companyLogo: string;
companyName: string; companyName: string;
billedToAddress: string[]; // Customer Address
billedFromAddress: string[]; showCustomerAddress: boolean;
showBilledFromAddress: boolean; customerAddress: string;
showBillingToAddress: boolean;
// Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string; billedToLabel: string;
total: string; total: string;

View File

@@ -294,8 +294,13 @@ export interface InvoicePdfTemplateAttributes {
invoiceNumber: string; invoiceNumber: string;
showInvoiceNumber: boolean; showInvoiceNumber: boolean;
showBillingToAddress: boolean; // Customer Address
showBilledFromAddress: boolean; showCustomerAddress: boolean;
customerAddress: string;
// Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string; billedToLabel: string;
lineItemLabel: string; lineItemLabel: string;
@@ -333,7 +338,4 @@ export interface InvoicePdfTemplateAttributes {
statementLabel: string; statementLabel: string;
showStatement: boolean; showStatement: boolean;
statement: string; statement: string;
billedToAddress: string[];
billedFromAddres: string[];
} }

View File

@@ -163,11 +163,13 @@ export interface ISaleReceiptBrandingTemplateAttributes {
companyLogo: string; companyLogo: string;
companyName: string; companyName: string;
// Address // Customer Address
billedToAddress: string[]; showCustomerAddress: boolean;
billedFromAddress: string[]; customerAddress: string;
showBilledFromAddress: boolean;
showBilledToAddress: boolean; // Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string; billedToLabel: string;
// Total // Total

View File

@@ -0,0 +1,7 @@
import path from 'path';
import config from '@/config';
export const getUploadedObjectUri = (objectKey: string) => {
return path.join(config.s3.endpoint, config.s3.bucket, objectKey);
}

View File

@@ -1,26 +1,44 @@
import { Inject } from "typedi"; import { Inject } from 'typedi';
import { GetPdfTemplate } from "../PdfTemplate/GetPdfTemplate"; import { GetPdfTemplate } from '../PdfTemplate/GetPdfTemplate';
import { defaultCreditNoteBrandingAttributes } from "./constants"; import { defaultCreditNoteBrandingAttributes } from './constants';
import { mergePdfTemplateWithDefaultAttributes } from "../Sales/Invoices/utils"; import { mergePdfTemplateWithDefaultAttributes } from '../Sales/Invoices/utils';
import { GetOrganizationBrandingAttributes } from '../PdfTemplate/GetOrganizationBrandingAttributes';
export class CreditNoteBrandingTemplate { export class CreditNoteBrandingTemplate {
@Inject() @Inject()
private getPdfTemplateService: GetPdfTemplate; private getPdfTemplateService: GetPdfTemplate;
@Inject()
private getOrgBrandingAttributes: GetOrganizationBrandingAttributes;
/** /**
* Retrieves the credit note branding template. * Retrieves the credit note branding template.
* @param {number} tenantId * @param {number} tenantId
* @param {number} templateId * @param {number} templateId
* @returns {} * @returns {}
*/ */
public async getCreditNoteBrandingTemplate(tenantId: number, templateId: number) { public async getCreditNoteBrandingTemplate(
tenantId: number,
templateId: number
) {
const template = await this.getPdfTemplateService.getPdfTemplate( const template = await this.getPdfTemplateService.getPdfTemplate(
tenantId, tenantId,
templateId templateId
); );
// Retrieves the organization branding attributes.
const commonOrgBrandingAttrs =
await this.getOrgBrandingAttributes.getOrganizationBrandingAttributes(
tenantId
);
// Merges the default branding attributes with common organization branding attrs.
const organizationBrandingAttrs = {
...defaultCreditNoteBrandingAttributes,
...commonOrgBrandingAttrs,
};
const attributes = mergePdfTemplateWithDefaultAttributes( const attributes = mergePdfTemplateWithDefaultAttributes(
template.attributes, template.attributes,
defaultCreditNoteBrandingAttributes organizationBrandingAttrs
); );
return { return {
...template, ...template,

View File

@@ -34,8 +34,6 @@ export default class GetCreditNotePdf {
tenantId, tenantId,
creditNoteId creditNoteId
); );
console.log(brandingAttributes, 'brandingAttributes');
const htmlContent = await this.templateInjectable.render( const htmlContent = await this.templateInjectable.render(
tenantId, tenantId,
'modules/credit-note-standard', 'modules/credit-note-standard',

View File

@@ -80,24 +80,13 @@ export const defaultCreditNoteBrandingAttributes = {
// # Company name // # Company name
companyName: 'Bigcapital Technology, Inc.', companyName: 'Bigcapital Technology, Inc.',
// Address // # Customer address
billedToAddress: [ showCustomerAddress: true,
'Bigcapital Technology, Inc.', customerAddress: '',
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713', // # Company address
'United States', showCompanyAddress: true,
'+1 762-339-5634', companyAddress: '',
'ahmed@bigcapital.app',
],
billedFromAddress: [
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713',
'United States',
'+1 762-339-5634',
'ahmed@bigcapital.app',
],
showBilledToAddress: true,
showBilledFromAddress: true,
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
// Total // Total

View File

@@ -1,4 +1,5 @@
import { CreditNotePdfTemplateAttributes, ICreditNote } from '@/interfaces'; import { CreditNotePdfTemplateAttributes, ICreditNote } from '@/interfaces';
import { contactAddressTextFormat } from '@/utils/address-text-format';
export const transformCreditNoteToPdfTemplate = ( export const transformCreditNoteToPdfTemplate = (
creditNote: ICreditNote creditNote: ICreditNote
@@ -19,5 +20,6 @@ export const transformCreditNoteToPdfTemplate = (
})), })),
customerNote: creditNote.note, customerNote: creditNote.note,
termsConditions: creditNote.termsConditions, termsConditions: creditNote.termsConditions,
customerAddress: contactAddressTextFormat(creditNote.customer),
}; };
}; };

View File

@@ -0,0 +1,31 @@
import { Service } from 'typedi';
import { TenantMetadata } from '@/system/models';
import { CommonOrganizationBrandingAttributes } from './types';
@Service()
export class GetOrganizationBrandingAttributes {
/**
* Retrieves the given organization branding attributes initial state.
* @param {number} tenantId
* @returns {Promise<CommonOrganizationBrandingAttributes>}
*/
async getOrganizationBrandingAttributes(
tenantId: number
): Promise<CommonOrganizationBrandingAttributes> {
const tenantMetadata = await TenantMetadata.query().findOne({ tenantId });
const companyName = tenantMetadata?.name;
const primaryColor = tenantMetadata?.primaryColor;
const companyLogoKey = tenantMetadata?.logoKey;
const companyLogoUri = tenantMetadata?.logoUri;
const companyAddress = tenantMetadata?.addressTextFormatted;
return {
companyName,
companyAddress,
companyLogoUri,
companyLogoKey,
primaryColor,
};
}
}

View File

@@ -0,0 +1,15 @@
import { Inject, Service } from 'typedi';
import { GetOrganizationBrandingAttributes } from './GetOrganizationBrandingAttributes';
@Service()
export class GetPdfTemplateBrandingState {
@Inject()
private getOrgBrandingAttributes: GetOrganizationBrandingAttributes;
getBrandingState(tenantId: number) {
const brandingAttributes =
this.getOrgBrandingAttributes.getOrganizationBrandingAttributes(tenantId);
return brandingAttributes;
}
}

View File

@@ -1,5 +1,6 @@
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { getTransactionTypeLabel } from '@/utils/transactions-types'; import { getTransactionTypeLabel } from '@/utils/transactions-types';
import { getUploadedObjectUri } from '../Attachments/utils';
export class GetPdfTemplateTransformer extends Transformer { export class GetPdfTemplateTransformer extends Transformer {
/** /**
@@ -56,7 +57,7 @@ class GetPdfTemplateAttributesTransformer extends Transformer {
*/ */
protected companyLogoUri(template) { protected companyLogoUri(template) {
return template.companyLogoKey return template.companyLogoKey
? `https://bigcapital.sfo3.digitaloceanspaces.com/${template.companyLogoKey}` ? getUploadedObjectUri(template.companyLogoKey)
: ''; : '';
} }
} }

View File

@@ -6,6 +6,7 @@ import { GetPdfTemplate } from './GetPdfTemplate';
import { GetPdfTemplates } from './GetPdfTemplates'; import { GetPdfTemplates } from './GetPdfTemplates';
import { EditPdfTemplate } from './EditPdfTemplate'; import { EditPdfTemplate } from './EditPdfTemplate';
import { AssignPdfTemplateDefault } from './AssignPdfTemplateDefault'; import { AssignPdfTemplateDefault } from './AssignPdfTemplateDefault';
import { GetPdfTemplateBrandingState } from './GetPdfTemplateBrandingState';
@Service() @Service()
export class PdfTemplateApplication { export class PdfTemplateApplication {
@@ -27,6 +28,9 @@ export class PdfTemplateApplication {
@Inject() @Inject()
private assignPdfTemplateDefaultService: AssignPdfTemplateDefault; private assignPdfTemplateDefaultService: AssignPdfTemplateDefault;
@Inject()
private getPdfTemplateBrandingStateService: GetPdfTemplateBrandingState;
/** /**
* Creates a new PDF template. * Creates a new PDF template.
* @param {number} tenantId - * @param {number} tenantId -
@@ -120,4 +124,12 @@ export class PdfTemplateApplication {
templateId templateId
); );
} }
/**
*
* @param {number} tenantId
*/
public async getPdfTemplateBrandingState(tenantId: number) {
return this.getPdfTemplateBrandingStateService.getBrandingState(tenantId);
}
} }

View File

@@ -65,3 +65,12 @@ export interface ICreateInvoicePdfTemplateDTO {
statementLabel?: string; statementLabel?: string;
showStatement?: boolean; showStatement?: boolean;
} }
export interface CommonOrganizationBrandingAttributes {
companyName?: string;
primaryColor?: string;
companyLogoKey?: string;
companyLogoUri?: string;
companyAddress?: string;
}

View File

@@ -185,23 +185,10 @@ export const defaultEstimatePdfBrandingAttributes = {
companyName: '', companyName: '',
billedToAddress: [ customerAddress: '',
'Bigcapital Technology, Inc.', companyAddress: '',
'131 Continental Dr Suite 305 Newark,', showCustomerAddress: true,
'Delaware 19713', showCompanyAddress: true,
'United States',
'+1 762-339-5634',
'ahmed@bigcapital.app',
],
billedFromAddress: [
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713',
'United States',
'+1 762-339-5634',
'ahmed@bigcapital.app',
],
showBilledFromAddress: true,
showBilledToAddress: true,
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
total: '$1000.00', total: '$1000.00',
@@ -244,7 +231,6 @@ export const defaultEstimatePdfBrandingAttributes = {
expirationDate: 'September 3, 2024', expirationDate: 'September 3, 2024',
}; };
interface EstimatePdfBrandingLineItem { interface EstimatePdfBrandingLineItem {
item: string; item: string;
description: string; description: string;
@@ -260,10 +246,13 @@ export interface EstimatePdfBrandingAttributes {
companyLogo: string; companyLogo: string;
companyName: string; companyName: string;
billedToAddress: string[]; // Customer Address
billedFromAddress: string[]; showCustomerAddress: boolean;
showBilledFromAddress: boolean; customerAddress: string;
showBilledToAddress: boolean;
// Company Address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string; billedToLabel: string;
total: string; total: string;
@@ -295,4 +284,4 @@ export interface EstimatePdfBrandingAttributes {
expirationDateLabel: string; expirationDateLabel: string;
showExpirationDate: boolean; showExpirationDate: boolean;
expirationDate: string; expirationDate: string;
} }

View File

@@ -1,3 +1,4 @@
import { contactAddressTextFormat } from '@/utils/address-text-format';
import { EstimatePdfBrandingAttributes } from './constants'; import { EstimatePdfBrandingAttributes } from './constants';
export const transformEstimateToPdfTemplate = ( export const transformEstimateToPdfTemplate = (
@@ -18,5 +19,6 @@ export const transformEstimateToPdfTemplate = (
subtotal: estimate.formattedSubtotal, subtotal: estimate.formattedSubtotal,
customerNote: estimate.customerNote, customerNote: estimate.customerNote,
termsConditions: estimate.termsConditions, termsConditions: estimate.termsConditions,
customerAddress: contactAddressTextFormat(estimate.customer),
}; };
}; };

View File

@@ -2,12 +2,16 @@ import { Inject, Service } from 'typedi';
import { mergePdfTemplateWithDefaultAttributes } from './utils'; import { mergePdfTemplateWithDefaultAttributes } from './utils';
import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate'; import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate';
import { defaultEstimatePdfBrandingAttributes } from '../Estimates/constants'; import { defaultEstimatePdfBrandingAttributes } from '../Estimates/constants';
import { GetOrganizationBrandingAttributes } from '@/services/PdfTemplate/GetOrganizationBrandingAttributes';
@Service() @Service()
export class SaleEstimatePdfTemplate { export class SaleEstimatePdfTemplate {
@Inject() @Inject()
private getPdfTemplateService: GetPdfTemplate; private getPdfTemplateService: GetPdfTemplate;
@Inject()
private getOrgBrandingAttrs: GetOrganizationBrandingAttributes;
/** /**
* Retrieves the estimate pdf template. * Retrieves the estimate pdf template.
* @param {number} tenantId * @param {number} tenantId
@@ -19,9 +23,19 @@ export class SaleEstimatePdfTemplate {
tenantId, tenantId,
estimateTemplateId estimateTemplateId
); );
// Retreives the organization branding attributes.
const commonOrgBrandingAttrs =
await this.getOrgBrandingAttrs.getOrganizationBrandingAttributes(
tenantId
);
// Merge the default branding attributes with organization attrs.
const orgainizationBrandingAttrs = {
...defaultEstimatePdfBrandingAttributes,
...commonOrgBrandingAttrs,
};
const attributes = mergePdfTemplateWithDefaultAttributes( const attributes = mergePdfTemplateWithDefaultAttributes(
template.attributes, template.attributes,
defaultEstimatePdfBrandingAttributes orgainizationBrandingAttrs
); );
return { return {
...template, ...template,

View File

@@ -2,26 +2,39 @@ import { Inject, Service } from 'typedi';
import { mergePdfTemplateWithDefaultAttributes } from './utils'; import { mergePdfTemplateWithDefaultAttributes } from './utils';
import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate'; import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate';
import { defaultInvoicePdfTemplateAttributes } from './constants'; import { defaultInvoicePdfTemplateAttributes } from './constants';
import { GetOrganizationBrandingAttributes } from '@/services/PdfTemplate/GetOrganizationBrandingAttributes';
@Service() @Service()
export class SaleInvoicePdfTemplate { export class SaleInvoicePdfTemplate {
@Inject() @Inject()
private getPdfTemplateService: GetPdfTemplate; private getPdfTemplateService: GetPdfTemplate;
@Inject()
private getOrgBrandingAttributes: GetOrganizationBrandingAttributes;
/** /**
* Retrieves the invoice pdf template. * Retrieves the invoice pdf template.
* @param {number} tenantId * @param {number} tenantId
* @param {number} invoiceTemplateId * @param {number} invoiceTemplateId
* @returns * @returns
*/ */
async getInvoicePdfTemplate(tenantId: number, invoiceTemplateId: number){ async getInvoicePdfTemplate(tenantId: number, invoiceTemplateId: number) {
const template = await this.getPdfTemplateService.getPdfTemplate( const template = await this.getPdfTemplateService.getPdfTemplate(
tenantId, tenantId,
invoiceTemplateId invoiceTemplateId
); );
// Retrieves the organization branding attributes.
const commonOrgBrandingAttrs =
await this.getOrgBrandingAttributes.getOrganizationBrandingAttributes(
tenantId
);
const organizationBrandingAttrs = {
...defaultInvoicePdfTemplateAttributes,
...commonOrgBrandingAttrs,
};
const attributes = mergePdfTemplateWithDefaultAttributes( const attributes = mergePdfTemplateWithDefaultAttributes(
template.attributes, template.attributes,
defaultInvoicePdfTemplateAttributes organizationBrandingAttrs
); );
return { return {
...template, ...template,

View File

@@ -163,7 +163,7 @@ export const SaleInvoicesSampleData = [
}, },
]; ];
export const defaultInvoicePdfTemplateAttributes = { export const defaultInvoicePdfTemplateAttributes = {
primaryColor: 'red', primaryColor: 'red',
secondaryColor: 'red', secondaryColor: 'red',
@@ -179,13 +179,17 @@ export const defaultInvoicePdfTemplateAttributes = {
dateIssueLabel: 'Date of issue', dateIssueLabel: 'Date of issue',
showDateIssue: true, showDateIssue: true,
// dateIssue, // # Invoice number,
invoiceNumberLabel: 'Invoice number', invoiceNumberLabel: 'Invoice number',
showInvoiceNumber: true, showInvoiceNumber: true,
// Address // # Customer address
showBillingToAddress: true, showCustomerAddress: true,
showBilledFromAddress: true, customerAddress: '',
// # Company address
showCompanyAddress: true,
companyAddress: '',
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
// Entries // Entries
@@ -229,22 +233,7 @@ export const defaultInvoicePdfTemplateAttributes = {
{ label: 'Sample Tax2 (7.00%)', amount: '21.74' }, { label: 'Sample Tax2 (7.00%)', amount: '21.74' },
], ],
// # Statement
statementLabel: 'Statement', statementLabel: 'Statement',
showStatement: true, showStatement: true,
billedToAddress: [ };
'Bigcapital Technology, Inc.',
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713',
'United States',
'+1 762-339-5634',
'ahmed@bigcapital.app',
],
billedFromAddres: [
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713',
'United States',
'+1 762-339-5634',
'ahmed@bigcapital.app',
],
}

View File

@@ -1,5 +1,6 @@
import { pickBy } from 'lodash'; import { pickBy } from 'lodash';
import { InvoicePdfTemplateAttributes, ISaleInvoice } from '@/interfaces'; import { InvoicePdfTemplateAttributes, ISaleInvoice } from '@/interfaces';
import { contactAddressTextFormat } from '@/utils/address-text-format';
export const mergePdfTemplateWithDefaultAttributes = ( export const mergePdfTemplateWithDefaultAttributes = (
brandingTemplate?: Record<string, any>, brandingTemplate?: Record<string, any>,
@@ -42,5 +43,7 @@ export const transformInvoiceToPdfTemplate = (
label: tax.name, label: tax.name,
amount: tax.taxRateAmountFormatted, amount: tax.taxRateAmountFormatted,
})), })),
customerAddress: contactAddressTextFormat(invoice.customer),
}; };
}; };

View File

@@ -3,29 +3,43 @@ import { Inject, Service } from 'typedi';
import { mergePdfTemplateWithDefaultAttributes } from '../Invoices/utils'; import { mergePdfTemplateWithDefaultAttributes } from '../Invoices/utils';
import { defaultPaymentReceivedPdfTemplateAttributes } from './constants'; import { defaultPaymentReceivedPdfTemplateAttributes } from './constants';
import { PdfTemplate } from '@/models/PdfTemplate'; import { PdfTemplate } from '@/models/PdfTemplate';
import { GetOrganizationBrandingAttributes } from '@/services/PdfTemplate/GetOrganizationBrandingAttributes';
@Service() @Service()
export class PaymentReceivedBrandingTemplate { export class PaymentReceivedBrandingTemplate {
@Inject() @Inject()
private getPdfTemplateService: GetPdfTemplate; private getPdfTemplateService: GetPdfTemplate;
@Inject()
private getOrgBrandingAttributes: GetOrganizationBrandingAttributes;
/** /**
* Retrieves the payment received pdf template. * Retrieves the payment received pdf template.
* @param {number} tenantId * @param {number} tenantId
* @param {number} paymentTemplateId * @param {number} paymentTemplateId
* @returns * @returns
*/ */
public async getPaymentReceivedPdfTemplate( public async getPaymentReceivedPdfTemplate(
tenantId: number, tenantId: number,
paymentTemplateId: number paymentTemplateId: number
) { ) {
const template = await this.getPdfTemplateService.getPdfTemplate( const template = await this.getPdfTemplateService.getPdfTemplate(
tenantId, tenantId,
paymentTemplateId paymentTemplateId
); );
// Retrieves the organization branding attributes.
const commonOrgBrandingAttrs =
await this.getOrgBrandingAttributes.getOrganizationBrandingAttributes(
tenantId
);
// Merges the default branding attributes with common organization branding attrs.
const organizationBrandingAttrs = {
...defaultPaymentReceivedPdfTemplateAttributes,
...commonOrgBrandingAttrs,
};
const attributes = mergePdfTemplateWithDefaultAttributes( const attributes = mergePdfTemplateWithDefaultAttributes(
template.attributes, template.attributes,
defaultPaymentReceivedPdfTemplateAttributes organizationBrandingAttrs
); );
return { return {
...template, ...template,

View File

@@ -58,24 +58,13 @@ export const defaultPaymentReceivedPdfTemplateAttributes = {
// # Company name // # Company name
companyName: 'Bigcapital Technology, Inc.', companyName: 'Bigcapital Technology, Inc.',
// Address // # Customer address
billedToAddress: [ showCustomerAddress: true,
'Bigcapital Technology, Inc.', customerAddress: '',
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713', // # Company address
'United States', showCompanyAddress: true,
'+1 762-339-5634', companyAddress: '',
'ahmed@bigcapital.app',
],
billedFromAddress: [
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713',
'United States',
'+1 762-339-5634',
'ahmed@bigcapital.app',
],
showBilledFromAddress: true,
showBillingToAddress: true,
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
// Total // Total

View File

@@ -2,6 +2,7 @@ import {
IPaymentReceived, IPaymentReceived,
PaymentReceivedPdfTemplateAttributes, PaymentReceivedPdfTemplateAttributes,
} from '@/interfaces'; } from '@/interfaces';
import { contactAddressTextFormat } from '@/utils/address-text-format';
export const transformPaymentReceivedToPdfTemplate = ( export const transformPaymentReceivedToPdfTemplate = (
payment: IPaymentReceived payment: IPaymentReceived
@@ -17,5 +18,6 @@ export const transformPaymentReceivedToPdfTemplate = (
invoiceAmount: entry.invoice.totalFormatted, invoiceAmount: entry.invoice.totalFormatted,
paidAmount: entry.paymentAmountFormatted, paidAmount: entry.paymentAmountFormatted,
})), })),
customerAddress: contactAddressTextFormat(payment.customer),
}; };
}; };

View File

@@ -2,12 +2,15 @@ import { GetPdfTemplate } from '@/services/PdfTemplate/GetPdfTemplate';
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import { defaultSaleReceiptBrandingAttributes } from './constants'; import { defaultSaleReceiptBrandingAttributes } from './constants';
import { mergePdfTemplateWithDefaultAttributes } from '../Invoices/utils'; import { mergePdfTemplateWithDefaultAttributes } from '../Invoices/utils';
import { GetOrganizationBrandingAttributes } from '@/services/PdfTemplate/GetOrganizationBrandingAttributes';
@Service() @Service()
export class SaleReceiptBrandingTemplate { export class SaleReceiptBrandingTemplate {
@Inject() @Inject()
private getPdfTemplateService: GetPdfTemplate; private getPdfTemplateService: GetPdfTemplate;
@Inject()
private getOrgBrandingAttributes: GetOrganizationBrandingAttributes;
/** /**
* Retrieves the sale receipt branding template. * Retrieves the sale receipt branding template.
@@ -23,9 +26,20 @@ export class SaleReceiptBrandingTemplate {
tenantId, tenantId,
templateId templateId
); );
// Retrieves the organization branding attributes.
const commonOrgBrandingAttrs =
await this.getOrgBrandingAttributes.getOrganizationBrandingAttributes(
tenantId
);
// Merges the default branding attributes with organization common branding attrs.
const organizationBrandingAttrs = {
...defaultSaleReceiptBrandingAttributes,
...commonOrgBrandingAttrs,
};
const attributes = mergePdfTemplateWithDefaultAttributes( const attributes = mergePdfTemplateWithDefaultAttributes(
template.attributes, template.attributes,
defaultSaleReceiptBrandingAttributes organizationBrandingAttrs
); );
return { return {
...template, ...template,

View File

@@ -76,26 +76,16 @@ export const defaultSaleReceiptBrandingAttributes = {
companyLogoUri: '', companyLogoUri: '',
companyLogoKey: '', companyLogoKey: '',
// # Address // # Customer address
billedToAddress: [ showCustomerAddress: true,
'Bigcapital Technology, Inc.', customerAddress: '',
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713', // # Company address
'United States', showCompanyAddress: true,
'+1 762-339-5634', companyAddress: '',
'ahmed@bigcapital.app',
],
billedFromAddress: [
'131 Continental Dr Suite 305 Newark,',
'Delaware 19713',
'United States',
'+1 762-339-5634',
'ahmed@bigcapital.app',
],
showBilledFromAddress: true,
showBilledToAddress: true,
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
// # Total
total: '$1000.00', total: '$1000.00',
totalLabel: 'Total', totalLabel: 'Total',
showTotal: true, showTotal: true,

View File

@@ -1,4 +1,5 @@
import { ISaleReceipt, ISaleReceiptBrandingTemplateAttributes } from "@/interfaces"; import { ISaleReceipt, ISaleReceiptBrandingTemplateAttributes } from "@/interfaces";
import { contactAddressTextFormat } from "@/utils/address-text-format";
@@ -13,8 +14,8 @@ export const transformReceiptToBrandingTemplateAttributes = (saleReceipt: ISaleR
quantity: entry.quantityFormatted, quantity: entry.quantityFormatted,
total: entry.totalFormatted, total: entry.totalFormatted,
})), })),
receiptNumber: saleReceipt.receiptNumber, receiptNumber: saleReceipt.receiptNumber,
receiptDate: saleReceipt.formattedReceiptDate, receiptDate: saleReceipt.formattedReceiptDate,
customerAddress: contactAddressTextFormat(saleReceipt.customer),
}; };
} }

View File

@@ -1,5 +1,6 @@
import { addressTextFormat } from '@/utils/address-text-format'; import { organizationAddressTextFormat } from '@/utils/address-text-format';
import BaseModel from 'models/Model'; import BaseModel from 'models/Model';
import { getUploadedObjectUri } from '../../services/Attachments/utils';
export default class TenantMetadata extends BaseModel { export default class TenantMetadata extends BaseModel {
baseCurrency!: string; baseCurrency!: string;
@@ -58,9 +59,7 @@ export default class TenantMetadata extends BaseModel {
* @returns {string | null} * @returns {string | null}
*/ */
public get logoUri() { public get logoUri() {
return this.logoKey return this.logoKey ? getUploadedObjectUri(this.logoKey) : null;
? `https://bigcapital.sfo3.digitaloceanspaces.com/${this.logoKey}`
: null;
} }
/** /**
@@ -75,7 +74,7 @@ export default class TenantMetadata extends BaseModel {
{POSTAL_CODE}, {POSTAL_CODE},
{COUNTRY} {COUNTRY}
`; `;
return addressTextFormat(defaultMessage, { return organizationAddressTextFormat(defaultMessage, {
organizationName: this.name, organizationName: this.name,
address1: this.address?.address1, address1: this.address?.address1,
address2: this.address?.address2, address2: this.address?.address2,

View File

@@ -1,3 +1,5 @@
import { IContact } from '@/interfaces';
interface OrganizationAddressFormatArgs { interface OrganizationAddressFormatArgs {
organizationName?: string; organizationName?: string;
address1?: string; address1?: string;
@@ -17,7 +19,19 @@ const defaultMessage = `
{COUNTRY} {COUNTRY}
`; `;
export const addressTextFormat = ( const formatText = (message: string, replacements: Record<string, string>) => {
let formattedMessage = Object.entries(replacements).reduce(
(msg, [key, value]) => {
return msg.split(`{${key}}`).join(value || '');
},
message
);
formattedMessage = formattedMessage.replace(/\n/g, '<br />');
return formattedMessage.trim();
};
export const organizationAddressTextFormat = (
message: string, message: string,
args: OrganizationAddressFormatArgs args: OrganizationAddressFormatArgs
) => { ) => {
@@ -30,13 +44,53 @@ export const addressTextFormat = (
POSTAL_CODE: args.postalCode || '', POSTAL_CODE: args.postalCode || '',
COUNTRY: args.country || '', COUNTRY: args.country || '',
}; };
let formattedMessage = Object.entries(replacements).reduce( return formatText(message, replacements);
(msg, [key, value]) => { };
return value ? msg.split(`{${key}}`).join(value) : msg;
}, interface ContactAddressTextFormatArgs {
message displayName?: string;
); state?: string;
formattedMessage = formattedMessage.replace(/\n/g, '<br />'); postalCode?: string;
email?: string;
return formattedMessage.trim(); country?: string;
city?: string;
address2?: string;
address1?: string;
}
const contactFormatMessage = `{CONTACT_NAME}
{ADDRESS_1}
{ADDRESS_2}
{CITY} {STATE}
{POSTAL_CODE}
{COUNTRY}
{EMAIL}
`;
export const contactAddressTextFormat = (
contact: IContact,
message: string = contactFormatMessage
) => {
const args = {
displayName: contact.displayName,
address1: contact.billingAddress1,
address2: contact.billingAddress2,
state: contact.billingAddressState,
country: contact.billingAddressCountry,
postalCode: contact?.billingAddressPostcode,
city: contact?.billingAddressCity,
email: contact?.email,
} as ContactAddressTextFormatArgs;
const replacements: Record<string, string> = {
CONTACT_NAME: args.displayName || '',
ADDRESS_1: args.address1 || '',
ADDRESS_2: args.address2 || '',
CITY: args.city || '',
STATE: args.state || '',
POSTAL_CODE: args.postalCode || '',
COUNTRY: args.country || '',
EMAIL: args?.email || '',
};
return formatText(message, replacements);
}; };

View File

@@ -17,10 +17,9 @@ United States, <br />
ahmed@bigcapital.app ahmed@bigcapital.app
`; `;
export const DefaultPdfTemplateAddressBilledFrom = `131 Continental Dr Suite 305 Newark, <br /> export const DefaultPdfTemplateAddressBilledFrom = `131 Continental Dr Suite 305 Newark, <br />
Delaware 19713,<br /> Delaware 19713,<br />
United States, <br /> United States, <br />
+1 762-339-5634, <br /> +1 762-339-5634, <br />
ahmed@bigcapital.app ahmed@bigcapital.app
`; `;

View File

@@ -1,7 +1,9 @@
import React, { createContext, useContext } from 'react'; import React, { createContext, useContext } from 'react';
import { import {
GetPdfTemplateBrandingStateResponse,
GetPdfTemplateResponse, GetPdfTemplateResponse,
useGetPdfTemplate, useGetPdfTemplate,
useGetPdfTemplateBrandingState,
} from '@/hooks/query/pdf-templates'; } from '@/hooks/query/pdf-templates';
import { Spinner } from '@blueprintjs/core'; import { Spinner } from '@blueprintjs/core';
@@ -9,6 +11,10 @@ interface PdfTemplateContextValue {
templateId: number | string; templateId: number | string;
pdfTemplate: GetPdfTemplateResponse | undefined; pdfTemplate: GetPdfTemplateResponse | undefined;
isPdfTemplateLoading: boolean; isPdfTemplateLoading: boolean;
// Branding state.
brandingTemplateState: GetPdfTemplateBrandingStateResponse | undefined;
isBrandingTemplateLoading: boolean;
} }
interface BrandingTemplateProps { interface BrandingTemplateProps {
@@ -28,15 +34,23 @@ export const BrandingTemplateBoot = ({
useGetPdfTemplate(templateId, { useGetPdfTemplate(templateId, {
enabled: !!templateId, enabled: !!templateId,
}); });
// Retreives the branding template state.
const { data: brandingTemplateState, isLoading: isBrandingTemplateLoading } =
useGetPdfTemplateBrandingState();
const value = { const value = {
templateId, templateId,
pdfTemplate, pdfTemplate,
isPdfTemplateLoading, isPdfTemplateLoading,
brandingTemplateState,
isBrandingTemplateLoading,
}; };
if (isPdfTemplateLoading) { const isLoading = isPdfTemplateLoading || isBrandingTemplateLoading;
return <Spinner size={20} />
if (isLoading) {
return <Spinner size={20} />;
} }
return ( return (
<PdfTemplateContext.Provider value={value}> <PdfTemplateContext.Provider value={value}>

View File

@@ -44,15 +44,16 @@ export const useBrandingTemplateFormInitialValues = <
>( >(
initialValues = {}, initialValues = {},
) => { ) => {
const { pdfTemplate } = useBrandingTemplateBoot(); const { pdfTemplate, brandingTemplateState } = useBrandingTemplateBoot();
const defaultPdfTemplate = { const brandingAttributes = {
templateName: pdfTemplate?.templateName, templateName: pdfTemplate?.templateName,
...brandingTemplateState,
...pdfTemplate?.attributes, ...pdfTemplate?.attributes,
}; };
return { return {
...initialValues, ...initialValues,
...(transformToForm(defaultPdfTemplate, initialValues) as T), ...(transformToForm(brandingAttributes, initialValues) as T),
}; };
}; };

View File

@@ -14,10 +14,12 @@ import {
export interface CreditNotePaperTemplateProps extends PaperTemplateProps { export interface CreditNotePaperTemplateProps extends PaperTemplateProps {
// Address // Address
billedToAddress?: string; showCustomerAddress?: boolean;
billedFromAddress?: string; customerAddress?: string;
showBilledToAddress?: boolean;
showBilledFromAddress?: boolean; showCompanyAddress?: boolean;
companyAddress?: string;
billedToLabel?: string; billedToLabel?: string;
// Total // Total
@@ -72,11 +74,12 @@ export function CreditNotePaperTemplate({
companyName = 'Bigcapital Technology, Inc.', companyName = 'Bigcapital Technology, Inc.',
// Address // Address
billedToAddress = DefaultPdfTemplateAddressBilledTo, showCustomerAddress = true,
billedFromAddress = DefaultPdfTemplateAddressBilledFrom, customerAddress = DefaultPdfTemplateAddressBilledTo,
showCompanyAddress = true,
companyAddress = DefaultPdfTemplateAddressBilledFrom,
showBilledFromAddress = true,
showBilledToAddress = true,
billedToLabel = 'Billed To', billedToLabel = 'Billed To',
// Total // Total
@@ -141,16 +144,16 @@ export function CreditNotePaperTemplate({
</PaperTemplate.TermsList> </PaperTemplate.TermsList>
<PaperTemplate.AddressesGroup> <PaperTemplate.AddressesGroup>
{showBilledFromAddress && ( {showCompanyAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{companyName}</strong> <Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
<Box dangerouslySetInnerHTML={{ __html: billedFromAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
{showBilledToAddress && (
{showCustomerAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{billedToLabel}</strong> <strong>{billedToLabel}</strong>
<Box dangerouslySetInnerHTML={{ __html: billedToAddress }} /> <Box dangerouslySetInnerHTML={{ __html: customerAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
</PaperTemplate.AddressesGroup> </PaperTemplate.AddressesGroup>

View File

@@ -11,9 +11,10 @@ export const initialValues = {
companyLogoUri: '', companyLogoUri: '',
// Address // Address
showBilledToAddress: true, showCustomerAddress: true,
showBilledFromAddress: true, showCompanyAddress: true,
billedToLabel: 'Bill To', companyAddress: '',
billedToLabel: 'Billed To',
// Entries // Entries
itemNameLabel: 'Item', itemNameLabel: 'Item',

View File

@@ -32,11 +32,11 @@ export interface EstimatePaperTemplateProps extends PaperTemplateProps {
companyName?: string; companyName?: string;
// Address // Address
showBilledToAddress?: boolean; showCustomerAddress?: boolean;
billedToAddress?: string; customerAddress?: string;
showBilledFromAddress?: boolean; showCompanyAddress?: boolean;
billedFromAddress?: string; companyAddress?: string;
billedToLabel?: string; billedToLabel?: string;
// Totals // Totals
@@ -71,23 +71,27 @@ export function EstimatePaperTemplate({
primaryColor, primaryColor,
secondaryColor, secondaryColor,
// # Company logo
showCompanyLogo = true, showCompanyLogo = true,
companyLogoUri = '', companyLogoUri = '',
companyName, companyName,
// # Address // # Company address
billedToAddress = DefaultPdfTemplateAddressBilledTo, companyAddress = DefaultPdfTemplateAddressBilledFrom,
billedFromAddress = DefaultPdfTemplateAddressBilledFrom, showCompanyAddress = true,
showBilledFromAddress = true,
showBilledToAddress = true, // # Customer address
customerAddress = DefaultPdfTemplateAddressBilledTo,
showCustomerAddress = true,
billedToLabel = 'Billed To', billedToLabel = 'Billed To',
// #Total // # Total
total = '$1000.00', total = '$1000.00',
totalLabel = 'Total', totalLabel = 'Total',
showTotal = true, showTotal = true,
// # Subtotal
subtotal = '1000/00', subtotal = '1000/00',
subtotalLabel = 'Subtotal', subtotalLabel = 'Subtotal',
showSubtotal = true, showSubtotal = true,
@@ -111,14 +115,18 @@ export function EstimatePaperTemplate({
total: '$1000.00', total: '$1000.00',
}, },
], ],
// Estimate number
showEstimateNumber = true, showEstimateNumber = true,
estimateNumberLabel = 'Estimate Number', estimateNumberLabel = 'Estimate Number',
estimateNumebr = '346D3D40-0001', estimateNumebr = '346D3D40-0001',
// Estimate date
estimateDate = 'September 3, 2024', estimateDate = 'September 3, 2024',
showEstimateDate = true, showEstimateDate = true,
estimateDateLabel = 'Estimate Date', estimateDateLabel = 'Estimate Date',
// Expiration date
expirationDateLabel = 'Expiration Date', expirationDateLabel = 'Expiration Date',
showExpirationDate = true, showExpirationDate = true,
expirationDate = 'September 3, 2024', expirationDate = 'September 3, 2024',
@@ -151,16 +159,15 @@ export function EstimatePaperTemplate({
</PaperTemplate.TermsList> </PaperTemplate.TermsList>
<PaperTemplate.AddressesGroup> <PaperTemplate.AddressesGroup>
{showBilledFromAddress && ( {showCompanyAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{companyName}</strong> <Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
<Box dangerouslySetInnerHTML={{ __html: billedFromAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
{showBilledToAddress && ( {showCustomerAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{billedToLabel}</strong> <strong>{billedToLabel}</strong>
<Box dangerouslySetInnerHTML={{ __html: billedToAddress }} /> <Box dangerouslySetInnerHTML={{ __html: customerAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
</PaperTemplate.AddressesGroup> </PaperTemplate.AddressesGroup>

View File

@@ -23,9 +23,12 @@ export const initialValues = {
// Company name // Company name
companyName: 'Bigcapital Technology, Inc.', companyName: 'Bigcapital Technology, Inc.',
// Addresses // Customer address
showBilledFromAddress: true, showCustomerAddress: true,
showBilledToAddress: true,
// Company address
showCompanyAddress: true,
companyAddress: '',
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
// Entries // Entries
@@ -45,6 +48,7 @@ export const initialValues = {
showCustomerNote: true, showCustomerNote: true,
customerNoteLabel: 'Customer Note', customerNoteLabel: 'Customer Note',
// Terms & Conditions
showTermsConditions: true, showTermsConditions: true,
termsConditionsLabel: 'Terms & Conditions', termsConditionsLabel: 'Terms & Conditions',
}; };

View File

@@ -45,8 +45,12 @@ export interface InvoicePaperTemplateProps {
bigtitle?: string; bigtitle?: string;
// Address // Address
showBillingToAddress?: boolean; showCustomerAddress?: boolean;
showBilledFromAddress?: boolean; customerAddress?: string;
showCompanyAddress?: boolean;
companyAddress?: string;
billedToLabel?: string; billedToLabel?: string;
// Entries // Entries
@@ -90,9 +94,6 @@ export interface InvoicePaperTemplateProps {
lines?: Array<PapaerLine>; lines?: Array<PapaerLine>;
taxes?: Array<PaperTax>; taxes?: Array<PaperTax>;
billedFromAddres?: string;
billedToAddress?: string;
} }
export function InvoicePaperTemplate({ export function InvoicePaperTemplate({
@@ -118,8 +119,12 @@ export function InvoicePaperTemplate({
showInvoiceNumber = true, showInvoiceNumber = true,
// Address // Address
showBillingToAddress = true, showCustomerAddress = true,
showBilledFromAddress = true, customerAddress = DefaultPdfTemplateAddressBilledTo,
showCompanyAddress = true,
companyAddress = DefaultPdfTemplateAddressBilledFrom,
billedToLabel = 'Billed To', billedToLabel = 'Billed To',
// Entries // Entries
@@ -171,8 +176,6 @@ export function InvoicePaperTemplate({
statementLabel = 'Statement', statementLabel = 'Statement',
showStatement = true, showStatement = true,
statement = DefaultPdfTemplateStatement, statement = DefaultPdfTemplateStatement,
billedToAddress = DefaultPdfTemplateAddressBilledTo,
billedFromAddres = DefaultPdfTemplateAddressBilledFrom,
}: InvoicePaperTemplateProps) { }: InvoicePaperTemplateProps) {
return ( return (
<PaperTemplate <PaperTemplate
@@ -202,16 +205,16 @@ export function InvoicePaperTemplate({
</PaperTemplate.TermsList> </PaperTemplate.TermsList>
<PaperTemplate.AddressesGroup> <PaperTemplate.AddressesGroup>
{showBilledFromAddress && ( {showCompanyAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{companyName}</strong> <Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
<Box dangerouslySetInnerHTML={{ __html: billedFromAddres }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
{showBillingToAddress && (
{showCustomerAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{billedToLabel}</strong> <strong>{billedToLabel}</strong>
<Box dangerouslySetInnerHTML={{ __html: billedToAddress }} /> <Box dangerouslySetInnerHTML={{ __html: customerAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
</PaperTemplate.AddressesGroup> </PaperTemplate.AddressesGroup>

View File

@@ -120,19 +120,18 @@ PaperTemplate.MutedText = () => {};
PaperTemplate.Text = () => {}; PaperTemplate.Text = () => {};
PaperTemplate.AddressesGroup = (props: GroupProps) => { PaperTemplate.AddressesGroup = (props: GroupProps) => {
return <Group spacing={10} {...props} className={styles.addressRoot} />;
};
PaperTemplate.Address = ({
children,
}: {
children: React.ReactNode;
}) => {
return ( return (
<Box> <Group
{children} spacing={10}
</Box> align={'flex-start'}
{...props}
className={styles.addressRoot}
/>
); );
}; };
PaperTemplate.Address = ({ children }: { children: React.ReactNode }) => {
return <Box>{children}</Box>;
};
PaperTemplate.Statement = ({ PaperTemplate.Statement = ({
label, label,

View File

@@ -26,8 +26,9 @@ export const initialValues = {
companyName: 'Bigcapital Technology, Inc.', companyName: 'Bigcapital Technology, Inc.',
// Addresses // Addresses
showBilledFromAddress: true, showCustomerAddress: true,
showBillingToAddress: true, showCompanyAddress: true,
companyAddress: '',
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
// Entries // Entries

View File

@@ -10,11 +10,14 @@ import {
} from '@/constants/PdfTemplates'; } from '@/constants/PdfTemplates';
export interface PaymentReceivedPaperTemplateProps extends PaperTemplateProps { export interface PaymentReceivedPaperTemplateProps extends PaperTemplateProps {
billedToAddress?: string; // Customer address
showBilledToAddress?: boolean; showCustomerAddress?: boolean;
customerAddress?: string;
// Company address
showCompanyAddress?: boolean;
companyAddress?: string;
billedFromAddress?: string;
showBilledFromAddress?: boolean;
billedToLabel?: string; billedToLabel?: string;
// Total. // Total.
@@ -56,10 +59,14 @@ export function PaymentReceivedPaperTemplate({
// # Company name // # Company name
companyName = 'Bigcapital Technology, Inc.', companyName = 'Bigcapital Technology, Inc.',
billedToAddress = DefaultPdfTemplateAddressBilledTo, // # Customer address
billedFromAddress = DefaultPdfTemplateAddressBilledFrom, showCustomerAddress = true,
showBilledFromAddress, customerAddress = DefaultPdfTemplateAddressBilledTo,
showBilledToAddress,
// # Company address
showCompanyAddress = true,
companyAddress = DefaultPdfTemplateAddressBilledFrom,
billedToLabel = 'Billed To', billedToLabel = 'Billed To',
total = '$1000.00', total = '$1000.00',
@@ -109,16 +116,16 @@ export function PaymentReceivedPaperTemplate({
</PaperTemplate.TermsList> </PaperTemplate.TermsList>
<PaperTemplate.AddressesGroup> <PaperTemplate.AddressesGroup>
{showBilledFromAddress && ( {showCompanyAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{companyName}</strong> <Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
<Box dangerouslySetInnerHTML={{ __html: billedFromAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
{showBilledToAddress && (
{showCustomerAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{billedToLabel}</strong> <strong>{billedToLabel}</strong>
<Box dangerouslySetInnerHTML={{ __html: billedToAddress }} /> <Box dangerouslySetInnerHTML={{ __html: customerAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
</PaperTemplate.AddressesGroup> </PaperTemplate.AddressesGroup>

View File

@@ -14,15 +14,19 @@ export const initialValues = {
showPaymentReceivedNumber: true, showPaymentReceivedNumber: true,
paymentReceivedNumberLabel: 'Payment number', paymentReceivedNumberLabel: 'Payment number',
// Payment number
showPaymentReceivedDate: true, showPaymentReceivedDate: true,
paymentReceivedDateLabel: 'Date of Issue', paymentReceivedDateLabel: 'Date of Issue',
// Company name // Company name
companyName: 'Bigcapital Technology, Inc.', companyName: 'Bigcapital Technology, Inc.',
// Addresses // Customer address
showBilledFromAddress: true, showCompanyAddress: true,
showBillingToAddress: true, companyAddress: '',
// Company address
showCustomerAddress: true,
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
// Entries // Entries
@@ -31,10 +35,11 @@ export const initialValues = {
itemRateLabel: 'Rate', itemRateLabel: 'Rate',
itemTotalLabel: 'Total', itemTotalLabel: 'Total',
// Totals // Subtotal
showSubtotal: true, showSubtotal: true,
subtotalLabel: 'Subtotal', subtotalLabel: 'Subtotal',
// Total
showTotal: true, showTotal: true,
totalLabel: 'Total', totalLabel: 'Total',
}; };

View File

@@ -12,18 +12,23 @@ export interface PaymentReceivedCustomizeValues extends BrandingTemplateValues {
showInvoiceNumber?: boolean; showInvoiceNumber?: boolean;
invoiceNumberLabel?: string; invoiceNumberLabel?: string;
// # Issue date
showDateIssue?: boolean; showDateIssue?: boolean;
dateIssueLabel?: string; dateIssueLabel?: string;
// # Due date
showDueDate?: boolean; showDueDate?: boolean;
dueDateLabel?: string; dueDateLabel?: string;
// Company name // # Company name
companyName?: string; companyName?: string;
// Addresses // # Customer address
showBilledFromAddress?: boolean; showCustomerAddress?: boolean;
showBillingToAddress?: boolean;
// # Company address
showCompanyAddress?: boolean;
companyAddress?: string;
billedToLabel?: string; billedToLabel?: string;
// Entries // Entries
@@ -54,6 +59,7 @@ export interface PaymentReceivedCustomizeValues extends BrandingTemplateValues {
termsConditionsLabel?: string; termsConditionsLabel?: string;
showTermsConditions?: boolean; showTermsConditions?: boolean;
// # Statement
statementLabel?: string; statementLabel?: string;
showStatement?: boolean; showStatement?: boolean;
} }

View File

@@ -14,10 +14,12 @@ import {
export interface ReceiptPaperTemplateProps extends PaperTemplateProps { export interface ReceiptPaperTemplateProps extends PaperTemplateProps {
// Addresses // Addresses
billedToAddress?: string; showCustomerAddress?: boolean;
billedFromAddress?: string; customerAddress?: string;
showBilledFromAddress?: boolean;
showBilledToAddress?: boolean; showCompanyAddress?: boolean;
companyAddress?: string;
billedToLabel?: string; billedToLabel?: string;
// Total // Total
@@ -73,10 +75,12 @@ export function ReceiptPaperTemplate({
companyName = 'Bigcapital Technology, Inc.', companyName = 'Bigcapital Technology, Inc.',
// # Address // # Address
billedToAddress = DefaultPdfTemplateAddressBilledTo, showCustomerAddress = true,
billedFromAddress = DefaultPdfTemplateAddressBilledFrom, customerAddress = DefaultPdfTemplateAddressBilledTo,
showBilledFromAddress = true,
showBilledToAddress = true, showCompanyAddress = true,
companyAddress = DefaultPdfTemplateAddressBilledFrom,
billedToLabel = 'Billed To', billedToLabel = 'Billed To',
total = '$1000.00', total = '$1000.00',
@@ -135,16 +139,16 @@ export function ReceiptPaperTemplate({
</PaperTemplate.TermsList> </PaperTemplate.TermsList>
<PaperTemplate.AddressesGroup> <PaperTemplate.AddressesGroup>
{showBilledFromAddress && ( {showCompanyAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{companyName}</strong> <Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
<Box dangerouslySetInnerHTML={{ __html: billedFromAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
{showBilledToAddress && (
{showCustomerAddress && (
<PaperTemplate.Address> <PaperTemplate.Address>
<strong>{billedToLabel}</strong> <strong>{billedToLabel}</strong>
<Box dangerouslySetInnerHTML={{ __html: billedToAddress }} /> <Box dangerouslySetInnerHTML={{ __html: customerAddress }} />
</PaperTemplate.Address> </PaperTemplate.Address>
)} )}
</PaperTemplate.AddressesGroup> </PaperTemplate.AddressesGroup>

View File

@@ -21,9 +21,12 @@ export const initialValues = {
// Company name // Company name
companyName: 'Bigcapital Technology, Inc.', companyName: 'Bigcapital Technology, Inc.',
// Addresses // Customer address
showBilledFromAddress: true, showCustomerAddress: true,
showBilledToAddress: true,
// Company address
companyAddress: '',
showCompanyAddress: true,
billedToLabel: 'Billed To', billedToLabel: 'Billed To',
// Entries // Entries

View File

@@ -203,3 +203,28 @@ export const useAssignPdfTemplateAsDefault = (
}, },
); );
}; };
// Retrieve organization branding state.
// --------------------------------------------------
export interface GetPdfTemplateBrandingStateResponse {
companyName: string;
companyAddress: string;
companyLogoUri: string;
companyLogoKey: string;
primaryColor: string;
}
export const useGetPdfTemplateBrandingState = (
options?: UseQueryOptions<GetPdfTemplateBrandingStateResponse, Error>,
): UseQueryResult<GetPdfTemplateBrandingStateResponse, Error> => {
const apiRequest = useApiRequest();
return useQuery<GetPdfTemplateBrandingStateResponse, Error>(
[PdfTemplatesQueryKey, 'state'],
() =>
apiRequest
.get('/pdf-templates/state')
.then((res) => transformToCamelCase(res.data?.data)),
options,
);
};