Compare commits

...

1 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
a10b58a7c6 refactor: mail services to nestjs 2025-01-13 16:06:55 +02:00
61 changed files with 1620 additions and 1304 deletions

View File

@@ -36,6 +36,7 @@
"@nestjs/swagger": "^7.4.2", "@nestjs/swagger": "^7.4.2",
"@nestjs/throttler": "^6.2.1", "@nestjs/throttler": "^6.2.1",
"@supercharge/promise-pool": "^3.2.0", "@supercharge/promise-pool": "^3.2.0",
"@types/nodemailer": "^6.4.17",
"@types/passport-local": "^1.0.38", "@types/passport-local": "^1.0.38",
"@types/ramda": "^0.30.2", "@types/ramda": "^0.30.2",
"accounting": "^0.4.1", "accounting": "^0.4.1",
@@ -51,8 +52,8 @@
"express-validator": "^7.2.0", "express-validator": "^7.2.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"fp-ts": "^2.16.9", "fp-ts": "^2.16.9",
"js-money": "^0.6.3",
"is-my-json-valid": "^2.20.5", "is-my-json-valid": "^2.20.5",
"js-money": "^0.6.3",
"knex": "^3.1.0", "knex": "^3.1.0",
"lamda": "^0.4.1", "lamda": "^0.4.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@@ -61,6 +62,7 @@
"mysql2": "^3.11.3", "mysql2": "^3.11.3",
"nestjs-cls": "^4.4.1", "nestjs-cls": "^4.4.1",
"nestjs-i18n": "^10.4.9", "nestjs-i18n": "^10.4.9",
"nodemailer": "^6.3.0",
"object-hash": "^2.0.3", "object-hash": "^2.0.3",
"objection": "^3.1.5", "objection": "^3.1.5",
"passport": "^0.7.0", "passport": "^0.7.0",

View File

@@ -65,6 +65,7 @@ import { SettingsModule } from '../Settings/Settings.module';
import { InventoryAdjustmentsModule } from '../InventoryAdjutments/InventoryAdjustments.module'; import { InventoryAdjustmentsModule } from '../InventoryAdjutments/InventoryAdjustments.module';
import { PostHogModule } from '../EventsTracker/postHog.module'; import { PostHogModule } from '../EventsTracker/postHog.module';
import { EventTrackerModule } from '../EventsTracker/EventTracker.module'; import { EventTrackerModule } from '../EventsTracker/EventTracker.module';
import { MailModule } from '../Mail/Mail.module';
@Module({ @Module({
imports: [ imports: [
@@ -120,6 +121,7 @@ import { EventTrackerModule } from '../EventsTracker/EventTracker.module';
TenancyModelsModule, TenancyModelsModule,
ChromiumlyTenancyModule, ChromiumlyTenancyModule,
TransformerModule, TransformerModule,
MailModule,
ItemsModule, ItemsModule,
ItemCategoryModule, ItemCategoryModule,
AccountsModule, AccountsModule,

View File

@@ -8,7 +8,10 @@ import {
Query, Query,
} from '@nestjs/common'; } from '@nestjs/common';
import { BankingTransactionsApplication } from './BankingTransactionsApplication.service'; import { BankingTransactionsApplication } from './BankingTransactionsApplication.service';
import { ICashflowNewCommandDTO } from './types/BankingTransactions.types'; import {
IBankAccountsFilter,
ICashflowNewCommandDTO,
} from './types/BankingTransactions.types';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
@Controller('banking/transactions') @Controller('banking/transactions')
@@ -19,7 +22,7 @@ export class BankingTransactionsController {
) {} ) {}
@Get('') @Get('')
async getBankAccounts(@Query() filterDTO: ICashflowAccountsFilter) { async getBankAccounts(@Query() filterDTO: IBankAccountsFilter) {
return this.bankingTransactionsApplication.getBankAccounts(filterDTO); return this.bankingTransactionsApplication.getBankAccounts(filterDTO);
} }

View File

@@ -2,7 +2,7 @@ import { Knex } from 'knex';
import { DeleteCashflowTransaction } from './commands/DeleteCashflowTransaction.service'; import { DeleteCashflowTransaction } from './commands/DeleteCashflowTransaction.service';
import { CreateBankTransactionService } from './commands/CreateBankTransaction.service'; import { CreateBankTransactionService } from './commands/CreateBankTransaction.service';
import { GetBankTransactionService } from './queries/GetBankTransaction.service'; import { GetBankTransactionService } from './queries/GetBankTransaction.service';
import { ICashflowNewCommandDTO } from './types/BankingTransactions.types'; import { IBankAccountsFilter, ICashflowNewCommandDTO } from './types/BankingTransactions.types';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { GetBankAccountsService } from './queries/GetBankAccounts.service'; import { GetBankAccountsService } from './queries/GetBankAccounts.service';
@@ -48,9 +48,9 @@ export class BankingTransactionsApplication {
/** /**
* Retrieves the cashflow accounts. * Retrieves the cashflow accounts.
* @param {ICashflowAccountsFilter} filterDTO * @param {IBankAccountsFilter} filterDTO
*/ */
public getBankAccounts(filterDTO: ICashflowAccountsFilter) { public getBankAccounts(filterDTO: IBankAccountsFilter) {
return this.getBankAccountsService.getBankAccounts(filterDTO); return this.getBankAccountsService.getBankAccounts(filterDTO);
} }
} }

View File

@@ -4,6 +4,7 @@ import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { CashflowAccountTransformer } from './BankAccountTransformer'; import { CashflowAccountTransformer } from './BankAccountTransformer';
import { ACCOUNT_TYPE } from '@/constants/accounts'; import { ACCOUNT_TYPE } from '@/constants/accounts';
import { IBankAccountsFilter } from '../types/BankingTransactions.types';
@Injectable() @Injectable()
export class GetBankAccountsService { export class GetBankAccountsService {
@@ -12,7 +13,7 @@ export class GetBankAccountsService {
private readonly transformer: TransformerInjectable, private readonly transformer: TransformerInjectable,
@Inject(BankAccount.name) @Inject(BankAccount.name)
private readonly bankAccountModel: typeof BankAccount private readonly bankAccountModel: typeof BankAccount,
) {} ) {}
/** /**
@@ -21,7 +22,7 @@ export class GetBankAccountsService {
* @returns {ICashflowAccount[]} * @returns {ICashflowAccount[]}
*/ */
public async getBankAccounts( public async getBankAccounts(
filterDTO: ICashflowAccountsFilter, filterDTO: IBankAccountsFilter,
): Promise<BankAccount[]> { ): Promise<BankAccount[]> {
// Parsees accounts list filter DTO. // Parsees accounts list filter DTO.
const filter = this.dynamicListService.parseStringifiedFilter(filterDTO); const filter = this.dynamicListService.parseStringifiedFilter(filterDTO);

View File

@@ -1,6 +1,6 @@
import { Knex } from "knex"; import { Knex } from 'knex';
import { UncategorizedBankTransaction } from "../models/UncategorizedBankTransaction"; import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction';
import { BankTransaction } from "../models/BankTransaction"; import { BankTransaction } from '../models/BankTransaction';
export interface IPendingTransactionRemovingEventPayload { export interface IPendingTransactionRemovingEventPayload {
uncategorizedTransactionId: number; uncategorizedTransactionId: number;
@@ -49,6 +49,13 @@ export interface ICashflowNewCommandDTO extends ICashflowCommandDTO {
uncategorizedTransactionId?: number; uncategorizedTransactionId?: number;
} }
export interface IBankAccountsFilter {
inactiveMode: boolean;
stringifiedFilterRoles?: string;
sortOrder: string;
columnSortBy: string;
}
export enum CashflowDirection { export enum CashflowDirection {
IN = 'in', IN = 'in',
OUT = 'out', OUT = 'out',

View File

@@ -6,6 +6,7 @@ import { EditBillPayment } from './commands/EditBillPayment.service';
import { GetBillPayment } from './queries/GetBillPayment.service'; import { GetBillPayment } from './queries/GetBillPayment.service';
import { GetPaymentBills } from './queries/GetPaymentBills.service'; import { GetPaymentBills } from './queries/GetPaymentBills.service';
import { IBillPaymentDTO } from './types/BillPayments.types'; import { IBillPaymentDTO } from './types/BillPayments.types';
import { GetBillPayments } from '../Bills/queries/GetBillPayments';
/** /**
* Bill payments application. * Bill payments application.

View File

@@ -1,14 +1,14 @@
import { CreateBill } from './commands/CreateBill.service'; import { CreateBill } from './commands/CreateBill.service';
import { EditBillService } from './commands/EditBill.service'; import { EditBillService } from './commands/EditBill.service';
import { GetBill } from './queries/GetBill'; import { GetBill } from './queries/GetBill';
// import { GetBills } from './queries/GetBills';
import { DeleteBill } from './commands/DeleteBill.service'; import { DeleteBill } from './commands/DeleteBill.service';
import { IBillDTO, IBillEditDTO } from './Bills.types'; import { IBillDTO, IBillEditDTO, IBillsFilter } from './Bills.types';
import { GetDueBills } from './queries/GetDueBills.service'; import { GetDueBills } from './queries/GetDueBills.service';
import { OpenBillService } from './commands/OpenBill.service'; import { OpenBillService } from './commands/OpenBill.service';
import { GetBillPayments } from './queries/GetBillPayments';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { GetBillsService } from './queries/GetBills.service'; import { GetBillsService } from './queries/GetBills.service';
// import { GetBillPayments } from './queries/GetBillPayments';
// import { GetBills } from './queries/GetBills';
@Injectable() @Injectable()
export class BillsApplication { export class BillsApplication {

View File

@@ -18,7 +18,7 @@ export class GetBillsService {
/** /**
* Retrieve bills data table list. * Retrieve bills data table list.
* @param {IBillsFilter} billsFilter - * @param {IBillsFilter} billsFilter -
*/ */
public async getBills(filterDTO: IBillsFilter): Promise<{ public async getBills(filterDTO: IBillsFilter): Promise<{
bills: Bill; bills: Bill;

View File

@@ -14,12 +14,12 @@ import { GetCreditNotesService } from './queries/GetCreditNotes.service';
@Injectable() @Injectable()
export class CreditNoteApplication { export class CreditNoteApplication {
constructor( constructor(
private createCreditNoteService: CreateCreditNoteService, private readonly createCreditNoteService: CreateCreditNoteService,
private editCreditNoteService: EditCreditNoteService, private readonly editCreditNoteService: EditCreditNoteService,
private openCreditNoteService: OpenCreditNoteService, private readonly openCreditNoteService: OpenCreditNoteService,
private deleteCreditNoteService: DeleteCreditNoteService, private readonly deleteCreditNoteService: DeleteCreditNoteService,
private getCreditNotePdfService: GetCreditNotePdf, private readonly getCreditNotePdfService: GetCreditNotePdf,
private getCreditNotesService: GetCreditNotesService, private readonly getCreditNotesService: GetCreditNotesService,
) {} ) {}
/** /**

View File

@@ -1,7 +1,7 @@
import * as R from 'ramda'; import * as R from 'ramda';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { ICreditNotesQueryDTO } from '../types/CreditNotes.types'; import { GetCreditNotesResponse, ICreditNotesQueryDTO } from '../types/CreditNotes.types';
import { CreditNote } from '../models/CreditNote'; import { CreditNote } from '../models/CreditNote';
import { CreditNoteTransformer } from './CreditNoteTransformer'; import { CreditNoteTransformer } from './CreditNoteTransformer';
import { Inject } from '@nestjs/common'; import { Inject } from '@nestjs/common';

View File

@@ -3,6 +3,8 @@ import { CreditNote } from '../models/CreditNote';
import { RefundCreditNote } from '../../CreditNoteRefunds/models/RefundCreditNote'; import { RefundCreditNote } from '../../CreditNoteRefunds/models/RefundCreditNote';
import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types'; import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types'; import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
export interface ICreditNoteEntryNewDTO extends IItemEntryDTO {} export interface ICreditNoteEntryNewDTO extends IItemEntryDTO {}
@@ -91,17 +93,12 @@ export interface ICreditNoteOpenedPayload {
oldCreditNote: CreditNote; oldCreditNote: CreditNote;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
export interface ICreditNotesQueryDTO extends IDynamicListFilter {
export interface ICreditNotesQueryDTO { page: number;
filterQuery?: (query: any) => void; pageSize: number;
searchKeyword?: string;
} }
// export interface ICreditNotesQueryDTO extends IDynamicListFilter {
// page: number;
// pageSize: number;
// searchKeyword?: string;
// }
export interface ICreditNoteRefundDTO { export interface ICreditNoteRefundDTO {
fromAccountId: number; fromAccountId: number;
amount: number; amount: number;
@@ -129,6 +126,12 @@ export interface ICreditNoteRefundDTO {
// | 'branchId' // | 'branchId'
// >; // >;
export interface GetCreditNotesResponse {
creditNotes: CreditNote[];
pagination: IPaginationMeta;
filterMeta: IFilterMeta;
}
export interface CreditNotePdfTemplateAttributes { export interface CreditNotePdfTemplateAttributes {
// # Primary color // # Primary color
primaryColor: string; primaryColor: string;

View File

@@ -1,6 +1,6 @@
import { Model } from 'objection'; import { Model } from 'objection';
import { castArray } from 'lodash'; import { castArray } from 'lodash';
import moment from 'moment'; import moment, { unitOfTime } from 'moment';
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice'; import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
import { SaleReceipt } from '@/modules/SaleReceipts/models/SaleReceipt'; import { SaleReceipt } from '@/modules/SaleReceipts/models/SaleReceipt';
import { Item } from '@/modules/Items/models/Item'; import { Item } from '@/modules/Items/models/Item';
@@ -55,7 +55,7 @@ export class InventoryCostLotTracker extends BaseModel {
query.groupBy('date'); query.groupBy('date');
query.groupBy('item_id'); query.groupBy('item_id');
}, },
filterDateRange(query, startDate, endDate, type = 'day') { filterDateRange(query, startDate, endDate, type: unitOfTime.StartOf = 'day') {
const dateFormat = 'YYYY-MM-DD'; const dateFormat = 'YYYY-MM-DD';
const fromDate = moment(startDate).startOf(type).format(dateFormat); const fromDate = moment(startDate).startOf(type).format(dateFormat);
const toDate = moment(endDate).endOf(type).format(dateFormat); const toDate = moment(endDate).endOf(type).format(dateFormat);

View File

@@ -1,6 +1,6 @@
import { Model, raw } from 'objection'; import { Model, raw } from 'objection';
import { castArray } from 'lodash'; import { castArray } from 'lodash';
import moment from 'moment'; import moment, { unitOfTime } from 'moment';
import { BaseModel } from '@/models/Model'; import { BaseModel } from '@/models/Model';
import { getTransactionTypeLabel } from '@/modules/BankingTransactions/utils'; import { getTransactionTypeLabel } from '@/modules/BankingTransactions/utils';
import { TInventoryTransactionDirection } from '../types/InventoryCost.types'; import { TInventoryTransactionDirection } from '../types/InventoryCost.types';
@@ -48,7 +48,12 @@ export class InventoryTransaction extends BaseModel {
*/ */
static get modifiers() { static get modifiers() {
return { return {
filterDateRange(query, startDate, endDate, type = 'day') { filterDateRange(
query,
startDate,
endDate,
type: unitOfTime.StartOf = 'day',
) {
const dateFormat = 'YYYY-MM-DD'; const dateFormat = 'YYYY-MM-DD';
const fromDate = moment(startDate).startOf(type).format(dateFormat); const fromDate = moment(startDate).startOf(type).format(dateFormat);
const toDate = moment(endDate).endOf(type).format(dateFormat); const toDate = moment(endDate).endOf(type).format(dateFormat);

View File

@@ -0,0 +1 @@
export const MAIL_TRANSPORTER_PROVIDER = 'MAIL_TRANSPORTER';

View File

@@ -0,0 +1,33 @@
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { createTransport } from 'nodemailer';
import { MAIL_TRANSPORTER_PROVIDER } from './Mail.constants';
import { MailTransporter } from './MailTransporter.service';
@Module({
imports: [
{
module: MailModule,
providers: [
{
provide: MAIL_TRANSPORTER_PROVIDER,
useFactory: (configService: ConfigService) => {
// Create reusable transporter object using the default SMTP transport
const transporter = createTransport({
host: configService.get('mail.host'),
port: configService.get('mail.port'),
secure: configService.get('mail.secure'), // true for 465, false for other ports
auth: {
user: configService.get('mail.username'),
pass: configService.get('mail.password'),
},
});
return transporter;
},
},
],
},
MailTransporter
],
})
export class MailModule {}

View File

@@ -0,0 +1,131 @@
import * as fs from 'fs';
import * as Mustache from 'mustache';
import * as path from 'path';
import { IMailAttachment } from './Mail.types';
export class Mail {
view: string;
subject: string = '';
content: string = '';
to: string | string[];
cc: string | string[];
bcc: string | string[];
replyTo: string | string[];
from: string = `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`;
data: { [key: string]: string | number };
attachments: IMailAttachment[];
/**
* Mail options.
*/
public get mailOptions() {
return {
to: this.to,
from: this.from,
cc: this.cc,
bcc: this.bcc,
subject: this.subject,
html: this.html,
attachments: this.attachments,
replyTo: this.replyTo,
};
}
/**
* Retrieves the html content of the mail.
* @returns {string}
*/
public get html() {
return this.view ? Mail.render(this.view, this.data) : this.content;
}
/**
* Set send mail to address.
* @param {string} to -
*/
setTo(to: string | string[]) {
this.to = to;
return this;
}
setCC(cc: string | string[]) {
this.cc = cc;
return this;
}
setBCC(bcc: string | string[]) {
this.bcc = bcc;
return this;
}
setReplyTo(replyTo: string | string[]) {
this.replyTo = replyTo;
return this;
}
/**
* Sets from address to the mail.
* @param {string} from
* @return {}
*/
setFrom(from: string) {
this.from = from;
return this;
}
/**
* Set attachments to the mail.
* @param {IMailAttachment[]} attachments
* @returns {Mail}
*/
setAttachments(attachments: IMailAttachment[]) {
this.attachments = attachments;
return this;
}
/**
* Set mail subject.
* @param {string} subject
*/
setSubject(subject: string) {
this.subject = subject;
return this;
}
/**
* Set view directory.
* @param {string} view
*/
setView(view: string) {
this.view = view;
return this;
}
setData(data) {
this.data = data;
return this;
}
setContent(content: string) {
this.content = content;
return this;
}
/**
* Renders the view template with the given data.
* @param {object} data
* @return {string}
*/
static render(view: string, data: Record<string, any>): string {
const viewContent = Mail.getViewContent(view);
return Mustache.render(viewContent, data);
}
/**
* Retrieve view content from the view directory.
*/
static getViewContent(view: string): string {
const filePath = path.join(global.__views_dir, `/${view}`);
return fs.readFileSync(filePath, 'utf8');
}
}

View File

@@ -0,0 +1,11 @@
export type IMailAttachment = MailAttachmentPath | MailAttachmentContent;
export interface MailAttachmentPath {
filename: string;
path: string;
cid: string;
}
export interface MailAttachmentContent {
filename: string;
content: Buffer;
}

View File

@@ -0,0 +1,15 @@
import { Transporter } from 'nodemailer';
import { Mail } from './Mail';
import { Inject } from '@nestjs/common';
import { MAIL_TRANSPORTER_PROVIDER } from './Mail.constants';
export class MailTransporter {
constructor(
@Inject(MAIL_TRANSPORTER_PROVIDER)
private readonly transporter: Transporter,
) {}
send(mail: Mail) {
return this.transporter.sendMail(mail.mailOptions);
}
}

View File

@@ -0,0 +1,80 @@
import { castArray } from 'lodash';
import { Inject, Injectable } from '@nestjs/common';
import { MailTenancy } from '../MailTenancy/MailTenancy.service';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { Customer } from '../Customers/models/Customer';
import { CommonMailOptions } from './MailNotification.types';
@Injectable()
export class ContactMailNotification {
constructor(
private readonly mailTenancy: MailTenancy,
private readonly tenantContext: TenancyContext,
@Inject(Customer.name)
private readonly customerModel: typeof Customer,
) {}
/**
* Gets the default mail address of the given contact.
* @param {number} invoiceId - Contact id.
* @returns {Promise<Pick<CommonMailOptions, 'to' | 'from'>>}
*/
public async getDefaultMailOptions(
customerId: number,
): Promise<
Pick<CommonMailOptions, 'to' | 'from' | 'toOptions' | 'fromOptions'>
> {
const customer = await this.customerModel
.query()
.findById(customerId)
.throwIfNotFound();
const toOptions = customer.contactAddresses;
const fromOptions = await this.mailTenancy.senders();
const toAddress = toOptions.find((a) => a.primary);
const fromAddress = fromOptions.find((a) => a.primary);
const to = toAddress?.mail ? castArray(toAddress?.mail) : [];
const from = fromAddress?.mail ? castArray(fromAddress?.mail) : [];
return { to, from, toOptions, fromOptions };
}
/**
* Retrieves the mail options of the given contact.
* @param {number} tenantId - Tenant id.
* @returns {Promise<CommonMailOptions>}
*/
public async formatMailOptions(
mailOptions: CommonMailOptions,
formatterArgs?: Record<string, any>,
): Promise<CommonMailOptions> {
const commonFormatArgs = await this.getCommonFormatArgs();
const formatArgs = {
...commonFormatArgs,
...formatterArgs,
};
const subjectFormatted = formatSmsMessage(mailOptions?.subject, formatArgs);
const messageFormatted = formatSmsMessage(mailOptions?.message, formatArgs);
return {
...mailOptions,
subject: subjectFormatted,
message: messageFormatted,
};
}
/**
* Retrieves the common format args.
* @returns {Promise<Record<string, string>>}
*/
public async getCommonFormatArgs(): Promise<Record<string, string>> {
const tenantMetadata = await this.tenantContext.getTenantMetadata();
return {
['Company Name']: tenantMetadata.name,
};
}
}

View File

@@ -0,0 +1,8 @@
import { Module } from '@nestjs/common';
import { ContactMailNotification } from './ContactMailNotification';
@Module({
imports: [ContactMailNotification],
exports: [ContactMailNotification],
})
export class MailNotificationModule {}

View File

@@ -0,0 +1,44 @@
export type IMailAttachment = MailAttachmentPath | MailAttachmentContent;
export interface MailAttachmentPath {
filename: string;
path: string;
cid: string;
}
export interface MailAttachmentContent {
filename: string;
content: Buffer;
}
export interface IMailable {
constructor(view: string, data?: { [key: string]: string | number });
send(): Promise<any>;
build(): void;
setData(data: { [key: string]: string | number }): IMailable;
setTo(to: string): IMailable;
setFrom(from: string): IMailable;
setSubject(subject: string): IMailable;
setView(view: string): IMailable;
render(data?: { [key: string]: string | number }): string;
getViewContent(): string;
}
export interface AddressItem {
label: string;
mail: string;
primary?: boolean;
}
export interface CommonMailOptions {
from: Array<string>;
subject: string;
message: string;
to: Array<string>;
cc?: Array<string>;
bcc?: Array<string>;
formatArgs?: Record<string, any>;
toOptions: Array<AddressItem>;
fromOptions: Array<AddressItem>;
}
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {}

View File

@@ -0,0 +1,6 @@
export const ERRORS = {
MAIL_FROM_NOT_FOUND: 'Mail from address not found',
MAIL_TO_NOT_FOUND: 'Mail to address not found',
MAIL_SUBJECT_NOT_FOUND: 'Mail subject not found',
MAIL_BODY_NOT_FOUND: 'Mail body not found',
};

View File

@@ -0,0 +1,56 @@
import { castArray, isEmpty } from 'lodash';
import { ServiceError } from '@/exceptions';
import { CommonMailOptions } from '@/interfaces';
import { ERRORS } from './constants';
/**
* Merges the mail options with incoming options.
* @param {Partial<SaleInvoiceMailOptions>} mailOptions
* @param {Partial<SendInvoiceMailDTO>} overridedOptions
*/
export function parseMailOptions(
mailOptions: CommonMailOptions,
overridedOptions: Partial<CommonMailOptions>
): CommonMailOptions {
const mergedMessageOptions = {
...mailOptions,
...overridedOptions,
};
const parsedMessageOptions = {
...mergedMessageOptions,
from: mergedMessageOptions?.from
? castArray(mergedMessageOptions?.from)
: [],
to: mergedMessageOptions?.to ? castArray(mergedMessageOptions?.to) : [],
cc: mergedMessageOptions?.cc ? castArray(mergedMessageOptions?.cc) : [],
bcc: mergedMessageOptions?.bcc ? castArray(mergedMessageOptions?.bcc) : [],
};
return parsedMessageOptions;
}
export function validateRequiredMailOptions(
mailOptions: Partial<CommonMailOptions>
) {
if (isEmpty(mailOptions.from)) {
throw new ServiceError(ERRORS.MAIL_FROM_NOT_FOUND);
}
if (isEmpty(mailOptions.to)) {
throw new ServiceError(ERRORS.MAIL_TO_NOT_FOUND);
}
if (isEmpty(mailOptions.subject)) {
throw new ServiceError(ERRORS.MAIL_SUBJECT_NOT_FOUND);
}
if (isEmpty(mailOptions.message)) {
throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND);
}
}
export const mergeAndValidateMailOptions = (
mailOptions: CommonMailOptions,
overridedOptions: Partial<CommonMailOptions>
): CommonMailOptions => {
const parsedMessageOptions = parseMailOptions(mailOptions, overridedOptions);
validateRequiredMailOptions(parsedMessageOptions);
return parsedMessageOptions;
};

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { MailTenancy } from './MailTenancy.service';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
@Module({
imports: [],
providers: [MailTenancy, TenancyContext],
})
export class MailTenancyModule {}

View File

@@ -0,0 +1,27 @@
import { Injectable } from '@nestjs/common';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class MailTenancy {
constructor(
private readonly tenancyContext: TenancyContext,
private readonly config: ConfigService
) {}
/**
* Retrieves the senders mails of the given tenant.
*/
public async senders() {
const tenantMetadata = await this.tenancyContext.getTenantMetadata();
const from = this.config.get('mail.from');
return [
{
mail: from,
label: tenantMetadata.name,
primary: true,
}
].filter((item) => item.mail)
}
}

View File

@@ -5,6 +5,7 @@ import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectab
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { ManualJournal } from '../models/ManualJournal'; import { ManualJournal } from '../models/ManualJournal';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { IManualJournalsFilter } from '../types/ManualJournals.types';
@Injectable() @Injectable()
export class GetManualJournals { export class GetManualJournals {
@@ -26,7 +27,6 @@ export class GetManualJournals {
/** /**
* Retrieve manual journals datatable list. * Retrieve manual journals datatable list.
* @param {number} tenantId -
* @param {IManualJournalsFilter} filter - * @param {IManualJournalsFilter} filter -
*/ */
public getManualJournals = async ( public getManualJournals = async (
@@ -44,7 +44,8 @@ export class GetManualJournals {
ManualJournal, ManualJournal,
filter, filter,
); );
const { results, pagination } = await this.manualJournalModel.query() const { results, pagination } = await this.manualJournalModel
.query()
.onBuild((builder) => { .onBuild((builder) => {
dynamicService.buildQuery()(builder); dynamicService.buildQuery()(builder);
builder.withGraphFetched('entries.account'); builder.withGraphFetched('entries.account');

View File

@@ -5,6 +5,7 @@ import { Knex } from 'knex';
// import { AttachmentLinkDTO } from './Attachments'; // import { AttachmentLinkDTO } from './Attachments';
import { ManualJournal } from '../models/ManualJournal'; import { ManualJournal } from '../models/ManualJournal';
import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types'; import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
export interface IManualJournalEntryDTO { export interface IManualJournalEntryDTO {
index: number; index: number;
@@ -31,11 +32,11 @@ export interface IManualJournalDTO {
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];
} }
// export interface IManualJournalsFilter extends IDynamicListFilterDTO { export interface IManualJournalsFilter extends IDynamicListFilter {
// stringifiedFilterRoles?: string; stringifiedFilterRoles?: string;
// page: number; page: number;
// pageSize: number; pageSize: number;
// } }
export interface IManualJournalEventPublishedPayload { export interface IManualJournalEventPublishedPayload {
// tenantId: number; // tenantId: number;

View File

@@ -2,6 +2,7 @@ import {
IPaymentReceivedCreateDTO, IPaymentReceivedCreateDTO,
IPaymentReceivedEditDTO, IPaymentReceivedEditDTO,
IPaymentReceivedSmsDetails, IPaymentReceivedSmsDetails,
IPaymentsReceivedFilter,
// IPaymentsReceivedFilter, // IPaymentsReceivedFilter,
// ISystemUser, // ISystemUser,
// PaymentReceiveMailOptsDTO, // PaymentReceiveMailOptsDTO,

View File

@@ -78,11 +78,10 @@ export class PaymentReceivesController {
@Get(':id/pdf') @Get(':id/pdf')
public getPaymentReceivePdf( public getPaymentReceivePdf(
@Param('id', ParseIntPipe) paymentReceiveId: number, @Param('id', ParseIntPipe) paymentReceivedId: number,
) { ) {
return this.paymentReceivesApplication.getPaymentReceivePdf( return this.paymentReceivesApplication.getPaymentReceivePdf(
1, paymentReceivedId,
paymentReceiveId,
); );
} }
} }

View File

@@ -26,6 +26,7 @@ import { PaymentReceivedSyncInvoicesSubscriber } from './subscribers/PaymentRece
import { PaymentReceivedInvoiceSync } from './commands/PaymentReceivedInvoiceSync.service'; import { PaymentReceivedInvoiceSync } from './commands/PaymentReceivedInvoiceSync.service';
import { LedgerModule } from '../Ledger/Ledger.module'; import { LedgerModule } from '../Ledger/Ledger.module';
import { AccountsModule } from '../Accounts/Accounts.module'; import { AccountsModule } from '../Accounts/Accounts.module';
import { SendPaymentReceiveMailNotification } from './commands/PaymentReceivedMailNotification';
@Module({ @Module({
controllers: [PaymentReceivesController], controllers: [PaymentReceivesController],
@@ -48,6 +49,7 @@ import { AccountsModule } from '../Accounts/Accounts.module';
PaymentReceivedAutoIncrementSubscriber, PaymentReceivedAutoIncrementSubscriber,
PaymentReceivedGLEntriesSubscriber, PaymentReceivedGLEntriesSubscriber,
PaymentReceivedSyncInvoicesSubscriber, PaymentReceivedSyncInvoicesSubscriber,
SendPaymentReceiveMailNotification
], ],
exports: [PaymentReceivesApplication, CreatePaymentReceivedService], exports: [PaymentReceivesApplication, CreatePaymentReceivedService],
imports: [ imports: [

View File

@@ -1,162 +1,159 @@
// import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
// import { import {
// PaymentReceiveMailOpts, DEFAULT_PAYMENT_MAIL_CONTENT,
// PaymentReceiveMailOptsDTO, DEFAULT_PAYMENT_MAIL_SUBJECT,
// PaymentReceiveMailPresendEvent, } from '../constants';
// SendInvoiceMailDTO, import { transformPaymentReceivedToMailDataArgs } from './utils';
// } from './types/PaymentReceived.types'; import { EventEmitter2 } from '@nestjs/event-emitter';
// import Mail from '@/lib/Mail'; import { events } from '@/common/events/events';
// import { import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
// DEFAULT_PAYMENT_MAIL_CONTENT, import { PaymentReceived } from '../models/PaymentReceived';
// DEFAULT_PAYMENT_MAIL_SUBJECT, import { GetPaymentReceivedService } from '../queries/GetPaymentReceived.service';
// } from './constants'; import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils';
// import { GetPaymentReceived } from './queries/GetPaymentReceived.service'; import { PaymentReceiveMailOptsDTO } from '../types/PaymentReceived.types';
// import { transformPaymentReceivedToMailDataArgs } from './utils'; import { PaymentReceiveMailOpts } from '../types/PaymentReceived.types';
// import { PaymentReceived } from './models/PaymentReceived'; import { PaymentReceiveMailPresendEvent } from '../types/PaymentReceived.types';
// import { EventEmitter2 } from '@nestjs/event-emitter'; import { SendInvoiceMailDTO } from '@/modules/SaleInvoices/SaleInvoice.types';
// import { events } from '@/common/events/events';
// @Injectable() @Injectable()
// export class SendPaymentReceiveMailNotification { export class SendPaymentReceiveMailNotification {
// constructor( constructor(
// private getPaymentService: GetPaymentReceived, private getPaymentService: GetPaymentReceivedService,
// private contactMailNotification: ContactMailNotification, private contactMailNotification: ContactMailNotification,
private eventEmitter: EventEmitter2,
// @Inject('agenda') private agenda: any, @Inject(PaymentReceived.name)
// private eventPublisher: EventEmitter2, private paymentReceiveModel: typeof PaymentReceived,
) {}
// @Inject(PaymentReceived.name) /**
// private paymentReceiveModel: typeof PaymentReceived, * Sends the mail of the given payment receive.
// ) {} * @param {number} tenantId
* @param {number} paymentReceiveId
* @param {PaymentReceiveMailOptsDTO} messageDTO
* @returns {Promise<void>}
*/
public async triggerMail(
paymentReceiveId: number,
messageDTO: PaymentReceiveMailOptsDTO,
): Promise<void> {
const payload = {
paymentReceiveId,
messageDTO,
};
// await this.agenda.now('payment-receive-mail-send', payload);
// /** // Triggers `onPaymentReceivePreMailSend` event.
// * Sends the mail of the given payment receive. await this.eventEmitter.emitAsync(events.paymentReceive.onPreMailSend, {
// * @param {number} tenantId paymentReceiveId,
// * @param {number} paymentReceiveId messageOptions: messageDTO,
// * @param {PaymentReceiveMailOptsDTO} messageDTO } as PaymentReceiveMailPresendEvent);
// * @returns {Promise<void>} }
// */
// public async triggerMail(
// paymentReceiveId: number,
// messageDTO: PaymentReceiveMailOptsDTO,
// ): Promise<void> {
// const payload = {
// paymentReceiveId,
// messageDTO,
// };
// await this.agenda.now('payment-receive-mail-send', payload);
// // Triggers `onPaymentReceivePreMailSend` event. /**
// await this.eventPublisher.emitAsync(events.paymentReceive.onPreMailSend, { * Retrieves the default payment mail options.
// paymentReceiveId, * @param {number} paymentReceiveId - Payment receive id.
// messageOptions: messageDTO, * @returns {Promise<PaymentReceiveMailOpts>}
// } as PaymentReceiveMailPresendEvent); */
// } public getMailOptions = async (
paymentId: number,
): Promise<PaymentReceiveMailOpts> => {
const paymentReceive = await this.paymentReceiveModel
.query()
.findById(paymentId)
.throwIfNotFound();
// /** const formatArgs = await this.textFormatter(paymentId);
// * Retrieves the default payment mail options.
// * @param {number} paymentReceiveId - Payment receive id.
// * @returns {Promise<PaymentReceiveMailOpts>}
// */
// public getMailOptions = async (
// paymentId: number,
// ): Promise<PaymentReceiveMailOpts> => {
// const paymentReceive = await this.paymentReceiveModel
// .query()
// .findById(paymentId)
// .throwIfNotFound();
// const formatArgs = await this.textFormatter(paymentId); const mailOptions =
await this.contactMailNotification.getDefaultMailOptions(
paymentReceive.customerId,
);
return {
...mailOptions,
subject: DEFAULT_PAYMENT_MAIL_SUBJECT,
message: DEFAULT_PAYMENT_MAIL_CONTENT,
...formatArgs,
};
};
// const mailOptions = /**
// await this.contactMailNotification.getDefaultMailOptions( * Retrieves the formatted text of the given sale invoice.
// paymentReceive.customerId, * @param {number} invoiceId - Sale invoice id.
// ); * @returns {Promise<Record<string, string>>}
// return { */
// ...mailOptions, public textFormatter = async (
// subject: DEFAULT_PAYMENT_MAIL_SUBJECT, invoiceId: number,
// message: DEFAULT_PAYMENT_MAIL_CONTENT, ): Promise<Record<string, string>> => {
// ...formatArgs, const payment = await this.getPaymentService.getPaymentReceive(invoiceId);
// }; return transformPaymentReceivedToMailDataArgs(payment);
// }; };
// /** /**
// * Retrieves the formatted text of the given sale invoice. * Retrieves the formatted mail options of the given payment receive.
// * @param {number} invoiceId - Sale invoice id. * @param {number} tenantId
// * @returns {Promise<Record<string, string>>} * @param {number} paymentReceiveId
// */ * @param {SendInvoiceMailDTO} messageDTO
// public textFormatter = async ( * @returns {Promise<PaymentReceiveMailOpts>}
// invoiceId: number, */
// ): Promise<Record<string, string>> => { public getFormattedMailOptions = async (
// const payment = await this.getPaymentService.getPaymentReceive(invoiceId); paymentReceiveId: number,
// return transformPaymentReceivedToMailDataArgs(payment); messageDTO: SendInvoiceMailDTO,
// }; ) => {
const formatterArgs = await this.textFormatter(paymentReceiveId);
// /** // Default message options.
// * Retrieves the formatted mail options of the given payment receive. const defaultMessageOpts = await this.getMailOptions(paymentReceiveId);
// * @param {number} tenantId // Parsed message opts with default options.
// * @param {number} paymentReceiveId const parsedMessageOpts = mergeAndValidateMailOptions(
// * @param {SendInvoiceMailDTO} messageDTO defaultMessageOpts,
// * @returns {Promise<PaymentReceiveMailOpts>} messageDTO,
// */ );
// public getFormattedMailOptions = async ( // Formats the message options.
// paymentReceiveId: number, return this.contactMailNotification.formatMailOptions(
// messageDTO: SendInvoiceMailDTO, parsedMessageOpts,
// ) => { formatterArgs,
// const formatterArgs = await this.textFormatter(paymentReceiveId); );
};
// // Default message options. /**
// const defaultMessageOpts = await this.getMailOptions(paymentReceiveId); * Triggers the mail invoice.
// // Parsed message opts with default options. * @param {number} tenantId
// const parsedMessageOpts = mergeAndValidateMailOptions( * @param {number} saleInvoiceId - Invoice id.
// defaultMessageOpts, * @param {SendInvoiceMailDTO} messageDTO - Message options.
// messageDTO, * @returns {Promise<void>}
// ); */
// // Formats the message options. public async sendMail(
// return this.contactMailNotification.formatMailOptions( paymentReceiveId: number,
// parsedMessageOpts, messageDTO: PaymentReceiveMailOptsDTO,
// formatterArgs, ): Promise<void> {
// ); // Retrieves the formatted mail options.
// }; const formattedMessageOptions = await this.getFormattedMailOptions(
paymentReceiveId,
messageDTO,
);
const mail = new Mail()
.setSubject(formattedMessageOptions.subject)
.setTo(formattedMessageOptions.to)
.setCC(formattedMessageOptions.cc)
.setBCC(formattedMessageOptions.bcc)
.setContent(formattedMessageOptions.message);
// /** const eventPayload = {
// * Triggers the mail invoice. paymentReceiveId,
// * @param {number} tenantId messageOptions: formattedMessageOptions,
// * @param {number} saleInvoiceId - Invoice id. };
// * @param {SendInvoiceMailDTO} messageDTO - Message options. // Triggers `onPaymentReceiveMailSend` event.
// * @returns {Promise<void>} await this.eventEmitter.emitAsync(
// */ events.paymentReceive.onMailSend,
// public async sendMail( eventPayload,
// paymentReceiveId: number, );
// messageDTO: SendInvoiceMailDTO, await mail.send();
// ): Promise<void> {
// // Retrieves the formatted mail options.
// const formattedMessageOptions = await this.getFormattedMailOptions(
// paymentReceiveId,
// messageDTO,
// );
// const mail = new Mail()
// .setSubject(formattedMessageOptions.subject)
// .setTo(formattedMessageOptions.to)
// .setCC(formattedMessageOptions.cc)
// .setBCC(formattedMessageOptions.bcc)
// .setContent(formattedMessageOptions.message);
// const eventPayload = { // Triggers `onPaymentReceiveMailSent` event.
// paymentReceiveId, await this.eventEmitter.emitAsync(
// messageOptions: formattedMessageOptions, events.paymentReceive.onMailSent,
// }; eventPayload,
// // Triggers `onPaymentReceiveMailSend` event. );
// await this.eventPublisher.emitAsync( }
// events.paymentReceive.onMailSend, }
// eventPayload,
// );
// await mail.send();
// // Triggers `onPaymentReceiveMailSent` event.
// await this.eventPublisher.emitAsync(
// events.paymentReceive.onMailSent,
// eventPayload,
// );
// }
// }

View File

@@ -2,6 +2,7 @@ import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { PaymentReceived } from '../models/PaymentReceived'; import { PaymentReceived } from '../models/PaymentReceived';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { CommonMailOptions, CommonMailOptionsDTO } from '@/modules/MailNotification/MailNotification.types';
export interface IPaymentReceivedCreateDTO { export interface IPaymentReceivedCreateDTO {
customerId: number; customerId: number;
@@ -144,15 +145,12 @@ export enum PaymentReceiveAction {
// | 'branchId' // | 'branchId'
// >; // >;
// export interface PaymentReceiveMailOpts extends CommonMailOptions {} export interface PaymentReceiveMailOpts extends CommonMailOptions {}
export interface PaymentReceiveMailOptsDTO extends CommonMailOptionsDTO {}
// export interface PaymentReceiveMailOptsDTO extends CommonMailOptionsDTO {} export interface PaymentReceiveMailPresendEvent {
paymentReceiveId: number;
// export interface PaymentReceiveMailPresendEvent { messageOptions: PaymentReceiveMailOptsDTO;
// tenantId: number; }
// paymentReceiveId: number;
// messageOptions: PaymentReceiveMailOptsDTO;
// }
export interface PaymentReceivedPdfLineItem { export interface PaymentReceivedPdfLineItem {
item: string; item: string;

View File

@@ -4,6 +4,8 @@ import {
// IPaginationMeta, // IPaginationMeta,
// IPaymentReceivedSmsDetails, // IPaymentReceivedSmsDetails,
ISaleEstimateDTO, ISaleEstimateDTO,
ISalesEstimatesFilter,
SaleEstimateMailOptionsDTO,
// ISalesEstimatesFilter, // ISalesEstimatesFilter,
// SaleEstimateMailOptions, // SaleEstimateMailOptions,
// SaleEstimateMailOptionsDTO, // SaleEstimateMailOptionsDTO,
@@ -16,12 +18,12 @@ import { DeliverSaleEstimateService } from './commands/DeliverSaleEstimate.servi
import { ApproveSaleEstimateService } from './commands/ApproveSaleEstimate.service'; import { ApproveSaleEstimateService } from './commands/ApproveSaleEstimate.service';
import { RejectSaleEstimateService } from './commands/RejectSaleEstimate.service'; import { RejectSaleEstimateService } from './commands/RejectSaleEstimate.service';
// import { SaleEstimateNotifyBySms } from './commands/SaleEstimateSmsNotify'; // import { SaleEstimateNotifyBySms } from './commands/SaleEstimateSmsNotify';
// import { SaleEstimatesPdf } from './queries/SaleEstimatesPdf'; import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail';
// import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail';
import { GetSaleEstimateState } from './queries/GetSaleEstimateState.service'; import { GetSaleEstimateState } from './queries/GetSaleEstimateState.service';
import { GetSaleEstimatesService } from './queries/GetSaleEstimates.service';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { GetSaleEstimatesService } from './queries/GetSaleEstimates.service'; import { GetSaleEstimatePdf } from './queries/GetSaleEstimatePdf';
@Injectable() @Injectable()
export class SaleEstimatesApplication { export class SaleEstimatesApplication {
@@ -34,10 +36,10 @@ export class SaleEstimatesApplication {
private readonly deliverSaleEstimateService: DeliverSaleEstimateService, private readonly deliverSaleEstimateService: DeliverSaleEstimateService,
private readonly approveSaleEstimateService: ApproveSaleEstimateService, private readonly approveSaleEstimateService: ApproveSaleEstimateService,
private readonly rejectSaleEstimateService: RejectSaleEstimateService, private readonly rejectSaleEstimateService: RejectSaleEstimateService,
// private readonly saleEstimateNotifyBySmsService: SaleEstimateNotifyBySms, private readonly sendEstimateMailService: SendSaleEstimateMail,
// private readonly saleEstimatesPdfService: SaleEstimatesPdf,
// private readonly sendEstimateMailService: SendSaleEstimateMail,
private readonly getSaleEstimateStateService: GetSaleEstimateState, private readonly getSaleEstimateStateService: GetSaleEstimateState,
private readonly saleEstimatesPdfService: GetSaleEstimatePdf,
// private readonly saleEstimateNotifyBySmsService: SaleEstimateNotifyBySms,
) {} ) {}
/** /**
@@ -136,25 +138,28 @@ export class SaleEstimatesApplication {
/** /**
* Retrieve the PDF content of the given sale estimate. * Retrieve the PDF content of the given sale estimate.
* @param {number} saleEstimateId - Sale estimate ID. * @param {number} saleEstimateId - Sale estimate ID.
* @returns * @returns {Promise<[Buffer, string]>}
*/ */
public getSaleEstimatePdf(saleEstimateId: number) { public getSaleEstimatePdf(saleEstimateId: number) {
// return this.saleEstimatesPdfService.getSaleEstimatePdf( return this.saleEstimatesPdfService.getSaleEstimatePdf(
// saleEstimateId, saleEstimateId,
// ); );
} }
/** /**
* Send the reminder mail of the given sale estimate. * Send the reminder mail of the given sale estimate.
* @param {number} saleEstimateId - Sale estimate ID. * @param {number} saleEstimateId - Sale estimate ID.
* @param {SaleEstimateMailOptionsDTO} saleEstimateMailOpts - Sale estimate mail options.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public sendSaleEstimateMail() // saleEstimateMailOpts: SaleEstimateMailOptionsDTO, // saleEstimateId: number, public sendSaleEstimateMail(
{ saleEstimateId: number,
// return this.sendEstimateMailService.triggerMail( saleEstimateMailOpts: SaleEstimateMailOptionsDTO,
// saleEstimateId, ) {
// saleEstimateMailOpts, return this.sendEstimateMailService.triggerMail(
// ); saleEstimateId,
saleEstimateMailOpts,
);
} }
/** /**
@@ -163,9 +168,9 @@ export class SaleEstimatesApplication {
* @returns {Promise<SaleEstimateMailOptions>} * @returns {Promise<SaleEstimateMailOptions>}
*/ */
public getSaleEstimateMail(saleEstimateId: number) { public getSaleEstimateMail(saleEstimateId: number) {
// return this.sendEstimateMailService.getMailOptions( return this.sendEstimateMailService.getMailOptions(
// saleEstimateId, saleEstimateId,
// ); );
} }
/** /**

View File

@@ -12,8 +12,8 @@ import {
import { SaleEstimatesApplication } from './SaleEstimates.application'; import { SaleEstimatesApplication } from './SaleEstimates.application';
import { import {
ISaleEstimateDTO, ISaleEstimateDTO,
// ISalesEstimatesFilter, ISalesEstimatesFilter,
// SaleEstimateMailOptionsDTO, SaleEstimateMailOptionsDTO,
} from './types/SaleEstimates.types'; } from './types/SaleEstimates.types';
import { SaleEstimate } from './models/SaleEstimate'; import { SaleEstimate } from './models/SaleEstimate';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
@@ -107,16 +107,16 @@ export class SaleEstimatesController {
return this.saleEstimatesApplication.getSaleEstimatePdf(saleEstimateId); return this.saleEstimatesApplication.getSaleEstimatePdf(saleEstimateId);
} }
// @Post(':id/mail') @Post(':id/mail')
// public sendSaleEstimateMail( public sendSaleEstimateMail(
// @Param('id', ParseIntPipe) saleEstimateId: number, @Param('id', ParseIntPipe) saleEstimateId: number,
// @Body() mailOptions: SaleEstimateMailOptionsDTO, @Body() mailOptions: SaleEstimateMailOptionsDTO,
// ) { ) {
// return this.saleEstimatesApplication.sendSaleEstimateMail( return this.saleEstimatesApplication.sendSaleEstimateMail(
// saleEstimateId, saleEstimateId,
// mailOptions, mailOptions,
// ); );
// } }
@Get(':id/mail') @Get(':id/mail')
public getSaleEstimateMail( public getSaleEstimateMail(

View File

@@ -23,6 +23,7 @@ import { SaleEstimatesApplication } from './SaleEstimates.application';
import { DeleteSaleEstimate } from './commands/DeleteSaleEstimate.service'; import { DeleteSaleEstimate } from './commands/DeleteSaleEstimate.service';
import { GetSaleEstimate } from './queries/GetSaleEstimate.service'; import { GetSaleEstimate } from './queries/GetSaleEstimate.service';
import { GetSaleEstimateState } from './queries/GetSaleEstimateState.service'; import { GetSaleEstimateState } from './queries/GetSaleEstimateState.service';
import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail';
// import { SaleEstimateNotifyBySms } from './commands/SaleEstimateSmsNotify'; // import { SaleEstimateNotifyBySms } from './commands/SaleEstimateSmsNotify';
// import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail'; // import { SendSaleEstimateMail } from './commands/SendSaleEstimateMail';
// //
@@ -51,9 +52,9 @@ import { GetSaleEstimateState } from './queries/GetSaleEstimateState.service';
SaleEstimateDTOTransformer, SaleEstimateDTOTransformer,
TenancyContext, TenancyContext,
TransformerInjectable, TransformerInjectable,
SaleEstimatesApplication SaleEstimatesApplication,
SendSaleEstimateMail,
// SaleEstimateNotifyBySms, // SaleEstimateNotifyBySms,
// SendSaleEstimateMail,p
], ],
}) })
export class SaleEstimatesModule {} export class SaleEstimatesModule {}

View File

@@ -1,205 +1,187 @@
// import { Inject, Service } from 'typedi'; import { Inject, Injectable } from '@nestjs/common';
// import Mail from '@/lib/Mail'; import { EventEmitter2 } from '@nestjs/event-emitter';
// import HasTenancyService from '@/services/Tenancy/TenancyService'; import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
// import { import {
// DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT, DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT,
// DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT, DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT,
// } from '../constants'; } from '../constants';
// import { SaleEstimatesPdf } from '../queries/SaleEstimatesPdf'; import { GetSaleEstimate } from '../queries/GetSaleEstimate.service';
// import { GetSaleEstimate } from '../queries/GetSaleEstimate.service'; import { transformEstimateToMailDataArgs } from '../utils';
// import { import { GetSaleEstimatePdf } from '../queries/GetSaleEstimatePdf';
// ISaleEstimateMailPresendEvent, import { events } from '@/common/events/events';
// SaleEstimateMailOptions, import { SaleEstimate } from '../models/SaleEstimate';
// SaleEstimateMailOptionsDTO, import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils';
// } from '@/interfaces'; import {
// import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; ISaleEstimateMailPresendEvent,
// import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils'; SaleEstimateMailOptionsDTO,
// import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; } from '../types/SaleEstimates.types';
// import events from '@/subscribers/events'; import { SaleEstimateMailOptions } from '../types/SaleEstimates.types';
// import { transformEstimateToMailDataArgs } from '../utils'; import { Mail } from '@/modules/Mail/Mail';
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
// @Service() @Injectable()
// export class SendSaleEstimateMail { export class SendSaleEstimateMail {
// @Inject() /**
// private tenancy: HasTenancyService; * @param {GetSaleEstimatePdf} estimatePdf - Estimate pdf service.
* @param {GetSaleEstimate} getSaleEstimateService - Get sale estimate service.
* @param {ContactMailNotification} contactMailNotification - Contact mail notification service.
* @param {EventEmitter2} eventPublisher - Event emitter.
* @param {MailTransporter} mailTransporter - Mail transporter service.
* @param {typeof SaleEstimate} saleEstimateModel - Sale estimate model.
*/
constructor(
private readonly estimatePdf: GetSaleEstimatePdf,
private readonly getSaleEstimateService: GetSaleEstimate,
private readonly contactMailNotification: ContactMailNotification,
private readonly eventPublisher: EventEmitter2,
private readonly mailTransporter: MailTransporter,
// @Inject() @Inject(SaleEstimate.name)
// private estimatePdf: SaleEstimatesPdf; private readonly saleEstimateModel: typeof SaleEstimate,
) {}
// @Inject() /**
// private getSaleEstimateService: GetSaleEstimate; * Triggers the reminder mail of the given sale estimate.
* @param {number} saleEstimateId - Sale estimate id.
* @param {SaleEstimateMailOptionsDTO} messageOptions - Sale estimate mail options.
* @returns {Promise<void>}
*/
public async triggerMail(
saleEstimateId: number,
messageOptions: SaleEstimateMailOptionsDTO,
): Promise<void> {
const payload = {
saleEstimateId,
messageOptions,
};
// await this.agenda.now('sale-estimate-mail-send', payload);
// @Inject() // Triggers `onSaleEstimatePreMailSend` event.
// private contactMailNotification: ContactMailNotification; await this.eventPublisher.emitAsync(events.saleEstimate.onPreMailSend, {
saleEstimateId,
messageOptions,
} as ISaleEstimateMailPresendEvent);
}
// @Inject('agenda') /**
// private agenda: any; * Formate the text of the mail.
* @param {number} estimateId - Estimate id.
* @returns {Promise<Record<string, any>>}
*/
public formatterArgs = async (estimateId: number) => {
const estimate = await this.getSaleEstimateService.getEstimate(estimateId);
return transformEstimateToMailDataArgs(estimate);
};
// @Inject() /**
// private eventPublisher: EventPublisher; * Retrieves the mail options.
* @param {number} saleEstimateId - Sale estimate id.
* @param {string} defaultSubject - Default subject.
* @param {string} defaultMessage - Default message.
* @returns {Promise<SaleEstimateMailOptions>}
*/
public getMailOptions = async (
saleEstimateId: number,
defaultSubject: string = DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT,
defaultMessage: string = DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT,
): Promise<SaleEstimateMailOptions> => {
const saleEstimate = await this.saleEstimateModel
.query()
.findById(saleEstimateId)
.throwIfNotFound();
// /** const formatArgs = await this.formatterArgs(saleEstimateId);
// * Triggers the reminder mail of the given sale estimate.
// * @param {number} tenantId -
// * @param {number} saleEstimateId -
// * @param {SaleEstimateMailOptionsDTO} messageOptions -
// * @returns {Promise<void>}
// */
// public async triggerMail(
// tenantId: number,
// saleEstimateId: number,
// messageOptions: SaleEstimateMailOptionsDTO
// ): Promise<void> {
// const payload = {
// tenantId,
// saleEstimateId,
// messageOptions,
// };
// await this.agenda.now('sale-estimate-mail-send', payload);
// // Triggers `onSaleEstimatePreMailSend` event. const mailOptions =
// await this.eventPublisher.emitAsync(events.saleEstimate.onPreMailSend, { await this.contactMailNotification.getDefaultMailOptions(
// tenantId, saleEstimate.customerId,
// saleEstimateId, );
// messageOptions, return {
// } as ISaleEstimateMailPresendEvent); ...mailOptions,
// } message: defaultMessage,
subject: defaultSubject,
attachEstimate: true,
formatArgs,
};
};
// /** /**
// * Formate the text of the mail. * Formats the given mail options.
// * @param {number} tenantId - Tenant id. * @param {number} saleEstimateId - Sale estimate id.
// * @param {number} estimateId - Estimate id. * @param {SaleEstimateMailOptions} mailOptions - Sale estimate mail options.
// * @returns {Promise<Record<string, any>>} * @returns {Promise<SaleEstimateMailOptions>}
// */ */
// public formatterArgs = async (tenantId: number, estimateId: number) => { public formatMailOptions = async (
// const estimate = await this.getSaleEstimateService.getEstimate( saleEstimateId: number,
// tenantId, mailOptions: SaleEstimateMailOptions,
// estimateId ): Promise<SaleEstimateMailOptions> => {
// ); const formatterArgs = await this.formatterArgs(saleEstimateId);
// return transformEstimateToMailDataArgs(estimate); const formattedOptions =
// }; await this.contactMailNotification.formatMailOptions(
mailOptions,
formatterArgs,
);
return { ...formattedOptions };
};
// /** /**
// * Retrieves the mail options. * Sends the mail notification of the given sale estimate.
// * @param {number} tenantId * @param {number} saleEstimateId - Sale estimate id.
// * @param {number} saleEstimateId * @param {SaleEstimateMailOptions} messageOptions - Sale estimate mail options.
// * @returns {Promise<SaleEstimateMailOptions>} * @returns {Promise<void>}
// */ */
// public getMailOptions = async ( public async sendMail(
// tenantId: number, saleEstimateId: number,
// saleEstimateId: number, messageOptions: SaleEstimateMailOptionsDTO,
// defaultSubject: string = DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT, ): Promise<void> {
// defaultMessage: string = DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT const localMessageOpts = await this.getMailOptions(saleEstimateId);
// ): Promise<SaleEstimateMailOptions> => { // Overrides and validates the given mail options.
// const { SaleEstimate } = this.tenancy.models(tenantId); const parsedMessageOptions = mergeAndValidateMailOptions(
localMessageOpts,
messageOptions,
) as SaleEstimateMailOptions;
// const saleEstimate = await SaleEstimate.query() const formattedOptions = await this.formatMailOptions(
// .findById(saleEstimateId) saleEstimateId,
// .throwIfNotFound(); parsedMessageOptions,
);
const mail = new Mail()
.setSubject(formattedOptions.subject)
.setTo(formattedOptions.to)
.setCC(formattedOptions.cc)
.setBCC(formattedOptions.bcc)
.setContent(formattedOptions.message);
// const formatArgs = await this.formatterArgs(tenantId, saleEstimateId); // Attaches the estimate pdf to the mail.
if (formattedOptions.attachEstimate) {
// Retrieves the estimate pdf and attaches it to the mail.
const [estimatePdfBuffer, estimateFilename] =
await this.estimatePdf.getSaleEstimatePdf(saleEstimateId);
// const mailOptions = mail.setAttachments([
// await this.contactMailNotification.getDefaultMailOptions( {
// tenantId, filename: `${estimateFilename}.pdf`,
// saleEstimate.customerId content: estimatePdfBuffer,
// ); },
// return { ]);
// ...mailOptions, }
// message: defaultMessage,
// subject: defaultSubject,
// attachEstimate: true,
// formatArgs,
// };
// };
// /** const eventPayload = {
// * Formats the given mail options. saleEstimateId,
// * @param {number} tenantId messageOptions,
// * @param {number} saleEstimateId formattedOptions,
// * @param {SaleEstimateMailOptions} mailOptions };
// * @returns {Promise<SaleEstimateMailOptions>} // Triggers `onSaleEstimateMailSend` event.
// */ await this.eventPublisher.emitAsync(
// public formatMailOptions = async ( events.saleEstimate.onMailSend,
// tenantId: number, eventPayload as ISaleEstimateMailPresendEvent,
// saleEstimateId: number, );
// mailOptions: SaleEstimateMailOptions await this.mailTransporter.send(mail);
// ): Promise<SaleEstimateMailOptions> => {
// const formatterArgs = await this.formatterArgs(tenantId, saleEstimateId);
// const formattedOptions =
// await this.contactMailNotification.formatMailOptions(
// tenantId,
// mailOptions,
// formatterArgs
// );
// return { ...formattedOptions };
// };
// /** // Triggers `onSaleEstimateMailSent` event.
// * Sends the mail notification of the given sale estimate. await this.eventPublisher.emitAsync(
// * @param {number} tenantId events.saleEstimate.onMailSent,
// * @param {number} saleEstimateId eventPayload as ISaleEstimateMailPresendEvent,
// * @param {SaleEstimateMailOptions} messageOptions );
// * @returns {Promise<void>} }
// */ }
// public async sendMail(
// tenantId: number,
// saleEstimateId: number,
// messageOptions: SaleEstimateMailOptionsDTO
// ): Promise<void> {
// const localMessageOpts = await this.getMailOptions(
// tenantId,
// saleEstimateId
// );
// // Overrides and validates the given mail options.
// const parsedMessageOptions = mergeAndValidateMailOptions(
// localMessageOpts,
// messageOptions
// ) as SaleEstimateMailOptions;
// const formattedOptions = await this.formatMailOptions(
// tenantId,
// saleEstimateId,
// parsedMessageOptions
// );
// const mail = new Mail()
// .setSubject(formattedOptions.subject)
// .setTo(formattedOptions.to)
// .setCC(formattedOptions.cc)
// .setBCC(formattedOptions.bcc)
// .setContent(formattedOptions.message);
// // Attaches the estimate pdf to the mail.
// if (formattedOptions.attachEstimate) {
// // Retrieves the estimate pdf and attaches it to the mail.
// const [estimatePdfBuffer, estimateFilename] =
// await this.estimatePdf.getSaleEstimatePdf(tenantId, saleEstimateId);
// mail.setAttachments([
// {
// filename: `${estimateFilename}.pdf`,
// content: estimatePdfBuffer,
// },
// ]);
// }
// const eventPayload = {
// tenantId,
// saleEstimateId,
// messageOptions,
// formattedOptions,
// };
// // Triggers `onSaleEstimateMailSend` event.
// await this.eventPublisher.emitAsync(
// events.saleEstimate.onMailSend,
// eventPayload as ISaleEstimateMailPresendEvent
// );
// await mail.send();
// // Triggers `onSaleEstimateMailSent` event.
// await this.eventPublisher.emitAsync(
// events.saleEstimate.onMailSent,
// eventPayload as ISaleEstimateMailPresendEvent
// );
// }
// }

View File

@@ -0,0 +1,93 @@
import { GetSaleEstimate } from './GetSaleEstimate.service';
import { transformEstimateToPdfTemplate } from '../utils';
import { EstimatePdfBrandingAttributes } from '../constants';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { SaleEstimatePdfTemplate } from '@/modules/SaleInvoices/queries/SaleEstimatePdfTemplate.service';
import { TemplateInjectable } from '@/modules/TemplateInjectable/TemplateInjectable.service';
import { ChromiumlyTenancy } from '@/modules/ChromiumlyTenancy/ChromiumlyTenancy.service';
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
import { Inject, Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { SaleEstimate } from '../models/SaleEstimate';
@Injectable()
export class GetSaleEstimatePdf {
constructor(
private readonly chromiumlyTenancy: ChromiumlyTenancy,
private readonly templateInjectable: TemplateInjectable,
private readonly getSaleEstimate: GetSaleEstimate,
private readonly estimatePdfTemplate: SaleEstimatePdfTemplate,
private readonly eventPublisher: EventEmitter2,
@Inject(PdfTemplateModel.name)
private readonly pdfTemplateModel: typeof PdfTemplateModel,
@Inject(SaleEstimate.name)
private readonly saleEstimateModel: typeof SaleEstimate,
) {}
/**
* Retrieve sale invoice pdf content.
* @param {number} tenantId -
* @param {ISaleInvoice} saleInvoice -
*/
public async getSaleEstimatePdf(
saleEstimateId: number,
): Promise<[Buffer, string]> {
const filename = await this.getSaleEstimateFilename(saleEstimateId);
const brandingAttributes =
await this.getEstimateBrandingAttributes(saleEstimateId);
const htmlContent = await this.templateInjectable.render(
'modules/estimate-regular',
brandingAttributes,
);
const content =
await this.chromiumlyTenancy.convertHtmlContent(htmlContent);
const eventPayload = { saleEstimateId };
// Triggers the `onSaleEstimatePdfViewed` event.
await this.eventPublisher.emitAsync(
events.saleEstimate.onPdfViewed,
eventPayload,
);
return [content, filename];
}
/**
* Retrieves the filename file document of the given estimate.
* @param {number} estimateId - Estimate id.
* @returns {Promise<string>}
*/
private async getSaleEstimateFilename(estimateId: number) {
const estimate = await this.saleEstimateModel.query().findById(estimateId);
return `Estimate-${estimate.estimateNumber}`;
}
/**
* Retrieves the given estimate branding attributes.
* @param {number} tenantId - Tenant id.
* @param {number} estimateId - Estimate id.
* @returns {Promise<EstimatePdfBrandingAttributes>}
*/
async getEstimateBrandingAttributes(
estimateId: number,
): Promise<EstimatePdfBrandingAttributes> {
const saleEstimate = await this.getSaleEstimate.getEstimate(estimateId);
// Retrieve the invoice template id of not found get the default template id.
const templateId =
saleEstimate.pdfTemplateId ??
(
await this.pdfTemplateModel.query().findOne({
resource: 'SaleEstimate',
default: true,
})
)?.id;
const brandingTemplate =
await this.estimatePdfTemplate.getEstimatePdfTemplate(templateId);
return {
...brandingTemplate.attributes,
...transformEstimateToPdfTemplate(saleEstimate),
};
}
}

View File

@@ -1,116 +0,0 @@
// import { Inject, Service } from 'typedi';
// import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
// import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
// import { GetSaleEstimate } from './GetSaleEstimate.service';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { SaleEstimatePdfTemplate } from '../Invoices/SaleEstimatePdfTemplate';
// import { transformEstimateToPdfTemplate } from '../utils';
// import { EstimatePdfBrandingAttributes } from '../constants';
// import events from '@/subscribers/events';
// import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
// @Service()
// export class SaleEstimatesPdf {
// @Inject()
// private tenancy: HasTenancyService;
// @Inject()
// private chromiumlyTenancy: ChromiumlyTenancy;
// @Inject()
// private templateInjectable: TemplateInjectable;
// @Inject()
// private getSaleEstimate: GetSaleEstimate;
// @Inject()
// private estimatePdfTemplate: SaleEstimatePdfTemplate;
// @Inject()
// private eventPublisher: EventPublisher;
// /**
// * Retrieve sale invoice pdf content.
// * @param {number} tenantId -
// * @param {ISaleInvoice} saleInvoice -
// */
// public async getSaleEstimatePdf(
// tenantId: number,
// saleEstimateId: number
// ): Promise<[Buffer, string]> {
// const filename = await this.getSaleEstimateFilename(
// tenantId,
// saleEstimateId
// );
// const brandingAttributes = await this.getEstimateBrandingAttributes(
// tenantId,
// saleEstimateId
// );
// const htmlContent = await this.templateInjectable.render(
// tenantId,
// 'modules/estimate-regular',
// brandingAttributes
// );
// const content = await this.chromiumlyTenancy.convertHtmlContent(
// tenantId,
// htmlContent
// );
// const eventPayload = { tenantId, saleEstimateId };
// // Triggers the `onSaleEstimatePdfViewed` event.
// await this.eventPublisher.emitAsync(
// events.saleEstimate.onPdfViewed,
// eventPayload
// );
// return [content, filename];
// }
// /**
// * Retrieves the filename file document of the given estimate.
// * @param {number} tenantId
// * @param {number} estimateId
// * @returns {Promise<string>}
// */
// private async getSaleEstimateFilename(tenantId: number, estimateId: number) {
// const { SaleEstimate } = this.tenancy.models(tenantId);
// const estimate = await SaleEstimate.query().findById(estimateId);
// return `Estimate-${estimate.estimateNumber}`;
// }
// /**
// * Retrieves the given estimate branding attributes.
// * @param {number} tenantId - Tenant id.
// * @param {number} estimateId - Estimate id.
// * @returns {Promise<EstimatePdfBrandingAttributes>}
// */
// async getEstimateBrandingAttributes(
// tenantId: number,
// estimateId: number
// ): Promise<EstimatePdfBrandingAttributes> {
// const { PdfTemplate } = this.tenancy.models(tenantId);
// const saleEstimate = await this.getSaleEstimate.getEstimate(
// tenantId,
// estimateId
// );
// // Retrieve the invoice template id of not found get the default template id.
// const templateId =
// saleEstimate.pdfTemplateId ??
// (
// await PdfTemplate.query().findOne({
// resource: 'SaleEstimate',
// default: true,
// })
// )?.id;
// const brandingTemplate =
// await this.estimatePdfTemplate.getEstimatePdfTemplate(
// tenantId,
// templateId
// );
// return {
// ...brandingTemplate.attributes,
// ...transformEstimateToPdfTemplate(saleEstimate),
// };
// }
// }

View File

@@ -5,6 +5,8 @@ import { SaleEstimate } from '../models/SaleEstimate';
import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types'; import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types'; import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types'; import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { CommonMailOptionsDTO } from '@/modules/MailNotification/MailNotification.types';
import { CommonMailOptions } from '@/modules/MailNotification/MailNotification.types';
export interface ISaleEstimateDTO { export interface ISaleEstimateDTO {
customerId: number; customerId: number;
@@ -104,19 +106,18 @@ export interface ISaleEstimateApprovedEvent {
trx: Knex.Transaction; trx: Knex.Transaction;
} }
// export interface SaleEstimateMailOptions extends CommonMailOptions { export interface SaleEstimateMailOptions extends CommonMailOptions {
// attachEstimate?: boolean; attachEstimate?: boolean;
// } }
// export interface SaleEstimateMailOptionsDTO extends CommonMailOptionsDTO { export interface SaleEstimateMailOptionsDTO extends CommonMailOptionsDTO {
// attachEstimate?: boolean; attachEstimate?: boolean;
// } }
// export interface ISaleEstimateMailPresendEvent { export interface ISaleEstimateMailPresendEvent {
// // tenantId: number; saleEstimateId: number;
// saleEstimateId: number; messageOptions: SaleEstimateMailOptionsDTO;
// messageOptions: SaleEstimateMailOptionsDTO; }
// }
export interface ISaleEstimateState { export interface ISaleEstimateState {
defaultTemplateId: number; defaultTemplateId: number;

View File

@@ -2,6 +2,8 @@ import { Knex } from 'knex';
import { IItemEntryDTO } from '../TransactionItemEntry/ItemEntry.types'; import { IItemEntryDTO } from '../TransactionItemEntry/ItemEntry.types';
import { AttachmentLinkDTO } from '../Attachments/Attachments.types'; import { AttachmentLinkDTO } from '../Attachments/Attachments.types';
import { SaleInvoice } from './models/SaleInvoice'; import { SaleInvoice } from './models/SaleInvoice';
import { IDynamicListFilter } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
import { CommonMailOptionsDTO } from '../MailNotification/MailNotification.types';
// import SaleInvoice from './models/SaleInvoice'; // import SaleInvoice from './models/SaleInvoice';
// import { SystemUser } from '../System/models/SystemUser'; // import { SystemUser } from '../System/models/SystemUser';
// import { ISystemUser, IAccount, ITaxTransaction } from '@/interfaces'; // import { ISystemUser, IAccount, ITaxTransaction } from '@/interfaces';
@@ -66,12 +68,12 @@ export interface ISaleInvoiceCreateDTO extends ISaleInvoiceDTO {
export interface ISaleInvoiceEditDTO extends ISaleInvoiceDTO {} export interface ISaleInvoiceEditDTO extends ISaleInvoiceDTO {}
// export interface ISalesInvoicesFilter extends IDynamicListFilter { export interface ISalesInvoicesFilter extends IDynamicListFilter {
// page: number; page: number;
// pageSize: number; pageSize: number;
// searchKeyword?: string; searchKeyword?: string;
// filterQuery?: (q: Knex.QueryBuilder) => void; filterQuery?: (q: Knex.QueryBuilder) => void;
// } }
export interface ISaleInvoiceWriteoffDTO { export interface ISaleInvoiceWriteoffDTO {
expenseAccountId: number; expenseAccountId: number;
@@ -212,28 +214,25 @@ export enum SaleInvoiceAction {
// entries?: Array<{ label: string; total: string; quantity: string | number }>; // entries?: Array<{ label: string; total: string; quantity: string | number }>;
// } // }
// export interface SendInvoiceMailDTO extends CommonMailOptionsDTO { export interface SendInvoiceMailDTO extends CommonMailOptionsDTO {
// attachInvoice?: boolean; attachInvoice?: boolean;
// } }
// export interface ISaleInvoiceNotifyPayload { export interface ISaleInvoiceNotifyPayload {
// tenantId: number; saleInvoiceId: number;
// saleInvoiceId: number; messageDTO: SendInvoiceMailDTO;
// messageDTO: SendInvoiceMailDTO; }
// }
// export interface ISaleInvoiceMailSend { export interface ISaleInvoiceMailSend {
// tenantId: number; saleInvoiceId: number;
// saleInvoiceId: number; messageOptions: SendInvoiceMailDTO;
// messageOptions: SendInvoiceMailDTO; // formattedMessageOptions: SaleInvoiceMailOptions;
// formattedMessageOptions: SaleInvoiceMailOptions; }
// }
// export interface ISaleInvoiceMailSent { export interface ISaleInvoiceMailSent {
// tenantId: number; saleInvoiceId: number;
// saleInvoiceId: number; messageOptions: SendInvoiceMailDTO;
// messageOptions: SendInvoiceMailDTO; }
// }
// Invoice Pdf Document // Invoice Pdf Document
export interface InvoicePdfLine { export interface InvoicePdfLine {

View File

@@ -18,7 +18,9 @@ import {
ISaleInvoiceCreateDTO, ISaleInvoiceCreateDTO,
ISaleInvoiceEditDTO, ISaleInvoiceEditDTO,
ISaleInvoiceWriteoffDTO, ISaleInvoiceWriteoffDTO,
ISalesInvoicesFilter,
} from './SaleInvoice.types'; } from './SaleInvoice.types';
import { GetSaleInvoicesService } from './queries/GetSaleInvoices';
@Injectable() @Injectable()
export class SaleInvoiceApplication { export class SaleInvoiceApplication {
@@ -26,18 +28,18 @@ export class SaleInvoiceApplication {
private createSaleInvoiceService: CreateSaleInvoice, private createSaleInvoiceService: CreateSaleInvoice,
private deleteSaleInvoiceService: DeleteSaleInvoice, private deleteSaleInvoiceService: DeleteSaleInvoice,
private getSaleInvoiceService: GetSaleInvoice, private getSaleInvoiceService: GetSaleInvoice,
// private getSaleInvoicesService: GetSaleInvoices, private getSaleInvoicesService: GetSaleInvoicesService,
private editSaleInvoiceService: EditSaleInvoice, private editSaleInvoiceService: EditSaleInvoice,
private deliverSaleInvoiceService: DeliverSaleInvoice, private deliverSaleInvoiceService: DeliverSaleInvoice,
private getReceivableSaleInvoicesService: GetSaleInvoicesPayable, private getReceivableSaleInvoicesService: GetSaleInvoicesPayable,
private writeoffInvoiceService: WriteoffSaleInvoice, private writeoffInvoiceService: WriteoffSaleInvoice,
private getInvoicePaymentsService: GetInvoicePaymentsService, private getInvoicePaymentsService: GetInvoicePaymentsService,
private pdfSaleInvoiceService: SaleInvoicePdf, private pdfSaleInvoiceService: SaleInvoicePdf,
private getSaleInvoiceStateService: GetSaleInvoiceState,
// private invoiceSms: SaleInvoiceNotifyBySms, // private invoiceSms: SaleInvoiceNotifyBySms,
// private sendInvoiceReminderService: SendInvoiceMailReminder, private sendInvoiceReminderService: SendInvoiceMailReminder,
// private sendSaleInvoiceMailService: SendSaleInvoiceMail, // private sendSaleInvoiceMailService: SendSaleInvoiceMail,
// private getSaleInvoiceMailStateService: GetSaleInvoiceMailState, // private getSaleInvoiceMailStateService: GetSaleInvoiceMailState,
private getSaleInvoiceStateService: GetSaleInvoiceState,
) {} ) {}
/** /**
@@ -77,13 +79,12 @@ export class SaleInvoiceApplication {
/** /**
* Retrieves the given sale invoice details. * Retrieves the given sale invoice details.
* @param {number} tenantId
* @param {ISalesInvoicesFilter} filterDTO * @param {ISalesInvoicesFilter} filterDTO
* @returns * @returns {Promise<{ salesInvoices: SaleInvoice[]; pagination: IPaginationMeta; filterMeta: IFilterMeta; }>}
*/ */
// public getSaleInvoices(filterDTO: ISalesInvoicesFilter) { public getSaleInvoices(filterDTO: ISalesInvoicesFilter) {
// return this.getSaleInvoicesService.getSaleInvoices(filterDTO); return this.getSaleInvoicesService.getSaleInvoices(filterDTO);
// } }
/** /**
* Retrieves sale invoice details. * Retrieves sale invoice details.

View File

@@ -8,8 +8,6 @@ import { DeliverSaleInvoice } from './commands/DeliverSaleInvoice.service';
import { EditSaleInvoice } from './commands/EditSaleInvoice.service'; import { EditSaleInvoice } from './commands/EditSaleInvoice.service';
import { GenerateShareLink } from './commands/GenerateInvoicePaymentLink.service'; import { GenerateShareLink } from './commands/GenerateInvoicePaymentLink.service';
import { SaleInvoiceIncrement } from './commands/SaleInvoiceIncrement.service'; import { SaleInvoiceIncrement } from './commands/SaleInvoiceIncrement.service';
// import { SendSaleInvoiceMail } from './commands/SendSaleInvoiceMail';
// import { SendSaleInvoiceReminderMailJob } from './commands/SendSaleInvoiceMailReminderJob';
import { GetInvoicePaymentMail } from './queries/GetInvoicePaymentMail.service'; import { GetInvoicePaymentMail } from './queries/GetInvoicePaymentMail.service';
import { GetSaleInvoice } from './queries/GetSaleInvoice.service'; import { GetSaleInvoice } from './queries/GetSaleInvoice.service';
import { GetSaleInvoicesPayable } from './queries/GetSaleInvoicesPayable.service'; import { GetSaleInvoicesPayable } from './queries/GetSaleInvoicesPayable.service';
@@ -37,6 +35,10 @@ import { LedgerModule } from '../Ledger/Ledger.module';
import { AccountsModule } from '../Accounts/Accounts.module'; import { AccountsModule } from '../Accounts/Accounts.module';
import SaleInvoiceWriteoffSubscriber from './subscribers/SaleInvoiceWriteoffSubscriber'; import SaleInvoiceWriteoffSubscriber from './subscribers/SaleInvoiceWriteoffSubscriber';
import { SaleInvoiceWriteoffGLStorage } from './commands/writeoff/SaleInvoiceWriteoffGLStorage'; import { SaleInvoiceWriteoffGLStorage } from './commands/writeoff/SaleInvoiceWriteoffGLStorage';
import { InvoiceInventoryTransactions } from './commands/inventory/InvoiceInventoryTransactions';
import { SendSaleEstimateMail } from '../SaleEstimates/commands/SendSaleEstimateMail';
import { SendInvoiceMailReminder } from './commands/SendSaleInvoiceMailReminder';
import { MailModule } from '../Mail/Mail.module';
@Module({ @Module({
imports: [ imports: [
@@ -48,7 +50,8 @@ import { SaleInvoiceWriteoffGLStorage } from './commands/writeoff/SaleInvoiceWri
WarehousesModule, WarehousesModule,
TaxRatesModule, TaxRatesModule,
LedgerModule, LedgerModule,
AccountsModule AccountsModule,
MailModule,
], ],
controllers: [SaleInvoicesController], controllers: [SaleInvoicesController],
providers: [ providers: [
@@ -57,7 +60,6 @@ import { SaleInvoiceWriteoffGLStorage } from './commands/writeoff/SaleInvoiceWri
DeleteSaleInvoice, DeleteSaleInvoice,
GetSaleInvoicesPayable, GetSaleInvoicesPayable,
DeliverSaleInvoice, DeliverSaleInvoice,
// SendSaleInvoiceMail,
GenerateShareLink, GenerateShareLink,
GetInvoicePaymentMail, GetInvoicePaymentMail,
SaleInvoiceIncrement, SaleInvoiceIncrement,
@@ -79,7 +81,10 @@ import { SaleInvoiceWriteoffGLStorage } from './commands/writeoff/SaleInvoiceWri
SaleInvoiceGLEntries, SaleInvoiceGLEntries,
InvoiceGLEntriesSubscriber, InvoiceGLEntriesSubscriber,
SaleInvoiceWriteoffGLStorage, SaleInvoiceWriteoffGLStorage,
SaleInvoiceWriteoffSubscriber SaleInvoiceWriteoffSubscriber,
InvoiceInventoryTransactions,
SendSaleEstimateMail,
SendInvoiceMailReminder,
], ],
}) })
export class SaleInvoicesModule {} export class SaleInvoicesModule {}

View File

@@ -1,129 +1,117 @@
// import { Inject, Service } from 'typedi'; import { GetSaleInvoice } from '../queries/GetSaleInvoice.service';
// import { SaleInvoiceMailOptions } from '@/interfaces'; import {
// import HasTenancyService from '@/services/Tenancy/TenancyService'; DEFAULT_INVOICE_MAIL_CONTENT,
// import { GetSaleInvoice } from '../queries/GetSaleInvoice.service'; DEFAULT_INVOICE_MAIL_SUBJECT,
// import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; } from '../constants';
// import { import { GetInvoicePaymentMail } from '../queries/GetInvoicePaymentMail.service';
// DEFAULT_INVOICE_MAIL_CONTENT, import { GenerateShareLink } from './GenerateInvoicePaymentLink.service';
// DEFAULT_INVOICE_MAIL_SUBJECT, import { Inject, Injectable } from '@nestjs/common';
// } from '../constants'; import { SaleInvoice } from '../models/SaleInvoice';
// import { GetInvoicePaymentMail } from '../queries/GetInvoicePaymentMail.service'; import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
// import { GenerateShareLink } from './GenerateInvoicePaymentLink.service';
// @Service() @Injectable()
// export class SendSaleInvoiceMailCommon { export class SendSaleInvoiceMailCommon {
// constructor( constructor(
// private getSaleInvoiceService: GetSaleInvoice, private getSaleInvoiceService: GetSaleInvoice,
// private contactMailNotification: ContactMailNotification, private contactMailNotification: ContactMailNotification,
// private getInvoicePaymentMail: GetInvoicePaymentMail, private getInvoicePaymentMail: GetInvoicePaymentMail,
// private generatePaymentLinkService: GenerateShareLink, private generatePaymentLinkService: GenerateShareLink,
// ) {}
// /** @Inject(SaleInvoice.name)
// * Retrieves the mail options. private readonly saleInvoiceModel: typeof SaleInvoice,
// * @param {number} tenantId - Tenant id. ) {}
// * @param {number} invoiceId - Invoice id.
// * @param {string} defaultSubject - Subject text.
// * @param {string} defaultBody - Subject body.
// * @returns {Promise<SaleInvoiceMailOptions>}
// */
// public async getInvoiceMailOptions(
// invoiceId: number,
// defaultSubject: string = DEFAULT_INVOICE_MAIL_SUBJECT,
// defaultMessage: string = DEFAULT_INVOICE_MAIL_CONTENT,
// ): Promise<SaleInvoiceMailOptions> {
// const { SaleInvoice } = this.tenancy.models(tenantId);
// const saleInvoice = await SaleInvoice.query() /**
// .findById(invoiceId) * Retrieves the mail options.
// .throwIfNotFound(); * @param {number} invoiceId - Invoice id.
* @param {string} defaultSubject - Subject text.
* @param {string} defaultBody - Subject body.
* @returns {Promise<SaleInvoiceMailOptions>}
*/
public async getInvoiceMailOptions(
invoiceId: number,
defaultSubject: string = DEFAULT_INVOICE_MAIL_SUBJECT,
defaultMessage: string = DEFAULT_INVOICE_MAIL_CONTENT,
): Promise<SaleInvoiceMailOptions> {
const saleInvoice = await this.saleInvoiceModel
.query()
.findById(invoiceId)
.throwIfNotFound();
// const contactMailDefaultOptions = const contactMailDefaultOptions =
// await this.contactMailNotification.getDefaultMailOptions( await this.contactMailNotification.getDefaultMailOptions(
// tenantId, saleInvoice.customerId,
// saleInvoice.customerId, );
// ); const formatArgs = await this.getInvoiceFormatterArgs(invoiceId);
// const formatArgs = await this.getInvoiceFormatterArgs(tenantId, invoiceId);
// return { return {
// ...contactMailDefaultOptions, ...contactMailDefaultOptions,
// subject: defaultSubject, subject: defaultSubject,
// message: defaultMessage, message: defaultMessage,
// attachInvoice: true, attachInvoice: true,
// formatArgs, formatArgs,
// }; };
// } }
// /** /**
// * Formats the given invoice mail options. * Formats the given invoice mail options.
// * @param {number} tenantId * @param {number} invoiceId
// * @param {number} invoiceId * @param {SaleInvoiceMailOptions} mailOptions
// * @param {SaleInvoiceMailOptions} mailOptions * @returns {Promise<SaleInvoiceMailOptions>}
// * @returns {Promise<SaleInvoiceMailOptions>} */
// */ public async formatInvoiceMailOptions(
// public async formatInvoiceMailOptions( invoiceId: number,
// tenantId: number, mailOptions: SaleInvoiceMailOptions,
// invoiceId: number, ): Promise<SaleInvoiceMailOptions> {
// mailOptions: SaleInvoiceMailOptions, const formatterArgs = await this.getInvoiceFormatterArgs(invoiceId);
// ): Promise<SaleInvoiceMailOptions> { const formattedOptions =
// const formatterArgs = await this.getInvoiceFormatterArgs( await this.contactMailNotification.formatMailOptions(
// tenantId, mailOptions,
// invoiceId, formatterArgs,
// ); );
// const formattedOptions = // Generates the a new payment link for the given invoice.
// await this.contactMailNotification.formatMailOptions( const paymentLink =
// tenantId, await this.generatePaymentLinkService.generatePaymentLink(
// mailOptions, invoiceId,
// formatterArgs, 'public',
// ); );
// // Generates the a new payment link for the given invoice. const message = await this.getInvoicePaymentMail.getMailTemplate(
// const paymentLink = invoiceId,
// await this.generatePaymentLinkService.generatePaymentLink( {
// tenantId, // # Invoice message
// invoiceId, invoiceMessage: formattedOptions.message,
// 'public', preview: formattedOptions.message,
// );
// const message = await this.getInvoicePaymentMail.getMailTemplate(
// tenantId,
// invoiceId,
// {
// // # Invoice message
// invoiceMessage: formattedOptions.message,
// preview: formattedOptions.message,
// // # Payment link // # Payment link
// viewInvoiceButtonUrl: paymentLink.link, viewInvoiceButtonUrl: paymentLink.link,
// }, },
// ); );
// return { ...formattedOptions, message }; return { ...formattedOptions, message };
// } }
// /** /**
// * Retrieves the formatted text of the given sale invoice. * Retrieves the formatted text of the given sale invoice.
// * @param {number} tenantId - Tenant id. * @param {number} tenantId - Tenant id.
// * @param {number} invoiceId - Sale invoice id. * @param {number} invoiceId - Sale invoice id.
// * @param {string} text - The given text. * @param {string} text - The given text.
// * @returns {Promise<string>} * @returns {Promise<string>}
// */ */
// public getInvoiceFormatterArgs = async ( public getInvoiceFormatterArgs = async (
// tenantId: number, invoiceId: number,
// invoiceId: number, ): Promise<Record<string, string | number>> => {
// ): Promise<Record<string, string | number>> => { const invoice = await this.getSaleInvoiceService.getSaleInvoice(invoiceId);
// const invoice = await this.getSaleInvoiceService.getSaleInvoice( const commonArgs =
// tenantId, await this.contactMailNotification.getCommonFormatArgs(tenantId);
// invoiceId, return {
// ); ...commonArgs,
// const commonArgs = 'Customer Name': invoice.customer.displayName,
// await this.contactMailNotification.getCommonFormatArgs(tenantId); 'Invoice Number': invoice.invoiceNo,
// return { 'Invoice Due Amount': invoice.dueAmountFormatted,
// ...commonArgs, 'Invoice Due Date': invoice.dueDateFormatted,
// 'Customer Name': invoice.customer.displayName, 'Invoice Date': invoice.invoiceDateFormatted,
// 'Invoice Number': invoice.invoiceNo, 'Invoice Amount': invoice.totalFormatted,
// 'Invoice Due Amount': invoice.dueAmountFormatted, 'Overdue Days': invoice.overdueDays,
// 'Invoice Due Date': invoice.dueDateFormatted, };
// 'Invoice Date': invoice.invoiceDateFormatted, };
// 'Invoice Amount': invoice.totalFormatted, }
// 'Overdue Days': invoice.overdueDays,
// };
// };
// }

View File

@@ -1,136 +1,124 @@
// import { Inject, Service } from 'typedi'; import { Injectable } from '@nestjs/common';
// import Mail from '@/lib/Mail'; import { SaleInvoicePdf } from '../queries/SaleInvoicePdf.service';
// import { import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon.service';
// ISaleInvoiceMailSend, import { EventEmitter2 } from '@nestjs/event-emitter';
// SaleInvoiceMailOptions, import { events } from '@/common/events/events';
// SendInvoiceMailDTO, import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils';
// } from '@/interfaces'; import { SendInvoiceMailDTO } from '../SaleInvoice.types';
// import { SaleInvoicePdf } from '../queries/SaleInvoicePdf.service'; import { ISaleInvoiceMailSend } from '../SaleInvoice.types';
// import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon.service'; import { Mail } from '@/modules/Mail/Mail';
// import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils'; import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
// import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
// import events from '@/subscribers/events';
// @Service() @Injectable()
// export class SendSaleInvoiceMail { export class SendSaleInvoiceMail {
// @Inject() /**
// private invoicePdf: SaleInvoicePdf; * @param {SaleInvoicePdf} invoicePdf - Sale invoice pdf service.
* @param {SendSaleInvoiceMailCommon} invoiceMail - Sale invoice mail service.
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {MailTransporter} mailTransporter - Mail transporter service.
*/
constructor(
private readonly invoicePdf: SaleInvoicePdf,
private readonly invoiceMail: SendSaleInvoiceMailCommon,
private readonly eventEmitter: EventEmitter2,
private readonly mailTransporter: MailTransporter,
) {}
// @Inject() /**
// private invoiceMail: SendSaleInvoiceMailCommon; * Sends the invoice mail of the given sale invoice.
* @param {number} tenantId
* @param {number} saleInvoiceId
* @param {SendInvoiceMailDTO} messageDTO
*/
public async triggerMail(
saleInvoiceId: number,
messageOptions: SendInvoiceMailDTO,
) {
const payload = {
saleInvoiceId,
messageOptions,
};
// await this.agenda.now('sale-invoice-mail-send', payload);
// @Inject() // Triggers the event `onSaleInvoicePreMailSend`.
// private eventPublisher: EventPublisher; await this.eventEmitter.emitAsync(events.saleInvoice.onPreMailSend, {
saleInvoiceId,
messageOptions,
} as ISaleInvoiceMailSend);
}
// @Inject('agenda') /**
// private agenda: any; * Retrieves the formatted mail options.
* @param {number} saleInvoiceId
* @param {SendInvoiceMailDTO} messageOptions
* @returns {Promise<SaleInvoiceMailOptions>}
*/
async getFormattedMailOptions(
saleInvoiceId: number,
messageOptions: SendInvoiceMailDTO,
): Promise<SaleInvoiceMailOptions> {
const defaultMessageOptions =
await this.invoiceMail.getInvoiceMailOptions(saleInvoiceId);
// /** // Merges message options with default options and parses the options values.
// * Sends the invoice mail of the given sale invoice. const parsedMessageOptions = mergeAndValidateMailOptions(
// * @param {number} tenantId defaultMessageOptions,
// * @param {number} saleInvoiceId messageOptions,
// * @param {SendInvoiceMailDTO} messageDTO );
// */ return this.invoiceMail.formatInvoiceMailOptions(
// public async triggerMail( saleInvoiceId,
// tenantId: number, parsedMessageOptions,
// saleInvoiceId: number, );
// messageOptions: SendInvoiceMailDTO }
// ) {
// const payload = {
// tenantId,
// saleInvoiceId,
// messageOptions,
// };
// await this.agenda.now('sale-invoice-mail-send', payload);
// // Triggers the event `onSaleInvoicePreMailSend`. /**
// await this.eventPublisher.emitAsync(events.saleInvoice.onPreMailSend, { * Triggers the mail invoice.
// tenantId, * @param {number} saleInvoiceId - Sale invoice id.
// saleInvoiceId, * @param {SendInvoiceMailDTO} messageDTO - Message options.
// messageOptions, * @returns {Promise<void>}
// } as ISaleInvoiceMailSend); */
// } public async sendMail(
saleInvoiceId: number,
messageOptions: SendInvoiceMailDTO,
) {
const formattedMessageOptions = await this.getFormattedMailOptions(
saleInvoiceId,
messageOptions,
);
const mail = new Mail()
.setSubject(formattedMessageOptions.subject)
.setTo(formattedMessageOptions.to)
.setCC(formattedMessageOptions.cc)
.setBCC(formattedMessageOptions.bcc)
.setContent(formattedMessageOptions.message);
// /** // Attach invoice document.
// * Retrieves the formatted mail options. if (formattedMessageOptions.attachInvoice) {
// * @param {number} tenantId // Retrieves document buffer of the invoice pdf document.
// * @param {number} saleInvoiceId const [invoicePdfBuffer, invoiceFilename] =
// * @param {SendInvoiceMailDTO} messageOptions await this.invoicePdf.getSaleInvoicePdf(saleInvoiceId);
// * @returns {Promise<SaleInvoiceMailOptions>}
// */
// async getFormattedMailOptions(
// tenantId: number,
// saleInvoiceId: number,
// messageOptions: SendInvoiceMailDTO
// ): Promise<SaleInvoiceMailOptions> {
// const defaultMessageOptions = await this.invoiceMail.getInvoiceMailOptions(
// tenantId,
// saleInvoiceId
// );
// // Merges message options with default options and parses the options values.
// const parsedMessageOptions = mergeAndValidateMailOptions(
// defaultMessageOptions,
// messageOptions
// );
// return this.invoiceMail.formatInvoiceMailOptions(
// tenantId,
// saleInvoiceId,
// parsedMessageOptions
// );
// }
// /** mail.setAttachments([
// * Triggers the mail invoice. { filename: `${invoiceFilename}.pdf`, content: invoicePdfBuffer },
// * @param {number} tenantId ]);
// * @param {number} saleInvoiceId }
// * @param {SendInvoiceMailDTO} messageDTO const eventPayload = {
// * @returns {Promise<void>} saleInvoiceId,
// */ messageOptions,
// public async sendMail( formattedMessageOptions,
// tenantId: number, } as ISaleInvoiceMailSend;
// saleInvoiceId: number,
// messageOptions: SendInvoiceMailDTO
// ) {
// const formattedMessageOptions = await this.getFormattedMailOptions(
// tenantId,
// saleInvoiceId,
// messageOptions
// );
// const mail = new Mail()
// .setSubject(formattedMessageOptions.subject)
// .setTo(formattedMessageOptions.to)
// .setCC(formattedMessageOptions.cc)
// .setBCC(formattedMessageOptions.bcc)
// .setContent(formattedMessageOptions.message);
// // Attach invoice document. // Triggers the event `onSaleInvoiceSend`.
// if (formattedMessageOptions.attachInvoice) { await this.eventEmitter.emitAsync(
// // Retrieves document buffer of the invoice pdf document. events.saleInvoice.onMailSend,
// const [invoicePdfBuffer, invoiceFilename] = eventPayload,
// await this.invoicePdf.saleInvoicePdf(tenantId, saleInvoiceId); );
await this.mailTransporter.send(mail);
// mail.setAttachments([ // Triggers the event `onSaleInvoiceSend`.
// { filename: `${invoiceFilename}.pdf`, content: invoicePdfBuffer }, await this.eventEmitter.emitAsync(
// ]); events.saleInvoice.onMailSent,
// } eventPayload,
// const eventPayload = { );
// tenantId, }
// saleInvoiceId, }
// messageOptions,
// formattedMessageOptions,
// } as ISaleInvoiceMailSend;
// // Triggers the event `onSaleInvoiceSend`.
// await this.eventPublisher.emitAsync(
// events.saleInvoice.onMailSend,
// eventPayload
// );
// await mail.send();
// // Triggers the event `onSaleInvoiceSend`.
// await this.eventPublisher.emitAsync(
// events.saleInvoice.onMailSent,
// eventPayload
// );
// }
// }

View File

@@ -1,112 +1,96 @@
// import { Inject, Service } from 'typedi'; import { Injectable } from '@nestjs/common';
// import { import {
// ISaleInvoiceMailSend, DEFAULT_INVOICE_REMINDER_MAIL_CONTENT,
// ISaleInvoiceMailSent, DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT,
// SendInvoiceMailDTO, } from '../constants';
// } from '@/interfaces'; import { SaleInvoicePdf } from '../queries/SaleInvoicePdf.service';
// import Mail from '@/lib/Mail'; import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon.service';
// import { SaleInvoicePdf } from '../queries/SaleInvoicePdf'; import { EventEmitter2 } from '@nestjs/event-emitter';
// import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon'; import { events } from '@/common/events/events';
// import { import { ISaleInvoiceMailSend, ISaleInvoiceMailSent, SendInvoiceMailDTO } from '../SaleInvoice.types';
// DEFAULT_INVOICE_REMINDER_MAIL_CONTENT, import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils';
// DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT, import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
// } from '../constants'; import { Mail } from '@/modules/Mail/Mail';
// import { parseAndValidateMailOptions } from '@/services/MailNotification/utils';
// import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
// import events from '@/subscribers/events';
// @Service() @Injectable()
// export class SendInvoiceMailReminder { export class SendInvoiceMailReminder {
// @Inject('agenda') constructor(
// private agenda: any; private readonly invoicePdf: SaleInvoicePdf,
private readonly invoiceCommonMail: SendSaleInvoiceMailCommon,
private readonly eventEmitter: EventEmitter2,
private readonly mailTransporter: MailTransporter,
) {}
// @Inject() /**
// private invoicePdf: SaleInvoicePdf; * Triggers the reminder mail of the given sale invoice.
* @param {number} saleInvoiceId
*/
public async triggerMail(
saleInvoiceId: number,
messageOptions: SendInvoiceMailDTO,
) {
const payload = {
saleInvoiceId,
messageOptions,
};
// await this.agenda.now('sale-invoice-reminder-mail-send', payload);
}
// @Inject() /**
// private invoiceCommonMail: SendSaleInvoiceMailCommon; * Retrieves the mail options of the given sale invoice.
* @param {number} saleInvoiceId - The sale invocie id.
* @returns {Promise<SaleInvoiceMailOptions>}
*/
public async getMailOption(saleInvoiceId: number) {
return this.invoiceCommonMail.getMailOption(
saleInvoiceId,
DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT,
DEFAULT_INVOICE_REMINDER_MAIL_CONTENT,
);
}
// @Inject() /**
// private eventPublisher: EventPublisher; * Triggers the mail invoice.
* @param {number} saleInvoiceId - Sale invoice id.
* @param {SendInvoiceMailDTO} messageOptions - The message options.
* @returns {Promise<void>}
*/
public async sendMail(
saleInvoiceId: number,
messageOptions: SendInvoiceMailDTO,
) {
const localMessageOpts = await this.getMailOption(saleInvoiceId);
// /** const messageOpts = mergeAndValidateMailOptions(
// * Triggers the reminder mail of the given sale invoice. localMessageOpts,
// * @param {number} tenantId messageOptions,
// * @param {number} saleInvoiceId );
// */ const mail = new Mail()
// public async triggerMail( .setSubject(messageOpts.subject)
// tenantId: number, .setTo(messageOpts.to)
// saleInvoiceId: number, .setContent(messageOpts.body);
// messageOptions: SendInvoiceMailDTO
// ) {
// const payload = {
// tenantId,
// saleInvoiceId,
// messageOptions,
// };
// await this.agenda.now('sale-invoice-reminder-mail-send', payload);
// }
// /** if (messageOpts.attachInvoice) {
// * Retrieves the mail options of the given sale invoice. // Retrieves document buffer of the invoice pdf document.
// * @param {number} tenantId const [invoicePdfBuffer, filename] = await this.invoicePdf.getSaleInvoicePdf(
// * @param {number} saleInvoiceId saleInvoiceId,
// * @returns {Promise<SaleInvoiceMailOptions>} );
// */ mail.setAttachments([
// public async getMailOption(tenantId: number, saleInvoiceId: number) { { filename, content: invoicePdfBuffer },
// return this.invoiceCommonMail.getMailOption( ]);
// tenantId, }
// saleInvoiceId, // Triggers the event `onSaleInvoiceSend`.
// DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT, await this.eventEmitter.emitAsync(events.saleInvoice.onMailReminderSend, {
// DEFAULT_INVOICE_REMINDER_MAIL_CONTENT saleInvoiceId,
// ); messageOptions,
// } } as ISaleInvoiceMailSend);
// /** await this.mailTransporter.send(mail);
// * Triggers the mail invoice.
// * @param {number} tenantId
// * @param {number} saleInvoiceId
// * @param {SendInvoiceMailDTO} messageOptions
// * @returns {Promise<void>}
// */
// public async sendMail(
// tenantId: number,
// saleInvoiceId: number,
// messageOptions: SendInvoiceMailDTO
// ) {
// const localMessageOpts = await this.getMailOption(tenantId, saleInvoiceId);
// const messageOpts = parseAndValidateMailOptions( // Triggers the event `onSaleInvoiceSent`.
// localMessageOpts, await this.eventEmitter.emitAsync(events.saleInvoice.onMailReminderSent, {
// messageOptions saleInvoiceId,
// ); messageOptions,
// const mail = new Mail() } as ISaleInvoiceMailSent);
// .setSubject(messageOpts.subject) }
// .setTo(messageOpts.to) }
// .setContent(messageOpts.body);
// if (messageOpts.attachInvoice) {
// // Retrieves document buffer of the invoice pdf document.
// const invoicePdfBuffer = await this.invoicePdf.saleInvoicePdf(
// tenantId,
// saleInvoiceId
// );
// mail.setAttachments([
// { filename: 'invoice.pdf', content: invoicePdfBuffer },
// ]);
// }
// // Triggers the event `onSaleInvoiceSend`.
// await this.eventPublisher.emitAsync(events.saleInvoice.onMailReminderSend, {
// saleInvoiceId,
// messageOptions,
// } as ISaleInvoiceMailSend);
// await mail.send();
// // Triggers the event `onSaleInvoiceSent`.
// await this.eventPublisher.emitAsync(events.saleInvoice.onMailReminderSent, {
// saleInvoiceId,
// messageOptions,
// } as ISaleInvoiceMailSent);
// }
// }

View File

@@ -16,6 +16,12 @@ import { ServiceError } from '../../Items/ServiceError';
@Injectable() @Injectable()
export class WriteoffSaleInvoice { export class WriteoffSaleInvoice {
/**
* @param {EventEmitter2} eventPublisher - Event emitter.
* @param {UnitOfWork} uow - Unit of work.
* @param {CommandSaleInvoiceValidators} validators - Command sale invoice validators.
* @param {typeof SaleInvoice} saleInvoiceModel - Sale invoice model.
*/
constructor( constructor(
private readonly eventPublisher: EventEmitter2, private readonly eventPublisher: EventEmitter2,
private readonly uow: UnitOfWork, private readonly uow: UnitOfWork,

View File

@@ -1,38 +1,34 @@
import { Inject, Service } from 'typedi'; import { InventoryService } from '@/modules/InventoryCost/Inventory';
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
import { Injectable } from '@nestjs/common';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { ISaleInvoice } from '@/interfaces'; import { SaleInvoice } from '../../models/SaleInvoice';
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
import InventoryService from '@/services/Inventory/Inventory';
@Service() @Injectable()
export class InvoiceInventoryTransactions { export class InvoiceInventoryTransactions {
@Inject() constructor(
private itemsEntriesService: ItemsEntriesService; private readonly itemsEntriesService: ItemsEntriesService,
private readonly inventoryService: InventoryService,
@Inject() ) {}
private inventoryService: InventoryService;
/** /**
* Records the inventory transactions of the given sale invoice in case * Records the inventory transactions of the given sale invoice in case
* the invoice has inventory entries only. * the invoice has inventory entries only.
*
* @param {number} tenantId - Tenant id.
* @param {SaleInvoice} saleInvoice - Sale invoice DTO. * @param {SaleInvoice} saleInvoice - Sale invoice DTO.
* @param {number} saleInvoiceId - Sale invoice id. * @param {number} saleInvoiceId - Sale invoice id.
* @param {boolean} override - Allow to override old transactions. * @param {boolean} override - Allow to override old transactions.
* @return {Promise<void>} * @return {Promise<void>}
*/ */
public async recordInventoryTranscactions( public async recordInventoryTranscactions(
saleInvoice: ISaleInvoice, saleInvoice: SaleInvoice,
override?: boolean, override?: boolean,
trx?: Knex.Transaction trx?: Knex.Transaction,
): Promise<void> { ): Promise<void> {
// Loads the inventory items entries of the given sale invoice. // Loads the inventory items entries of the given sale invoice.
const inventoryEntries = const inventoryEntries =
await this.itemsEntriesService.filterInventoryEntries( await this.itemsEntriesService.filterInventoryEntries(
tenantId,
saleInvoice.entries, saleInvoice.entries,
trx trx,
); );
const transaction = { const transaction = {
transactionId: saleInvoice.id, transactionId: saleInvoice.id,
@@ -48,30 +44,27 @@ export class InvoiceInventoryTransactions {
createdAt: saleInvoice.createdAt, createdAt: saleInvoice.createdAt,
}; };
await this.inventoryService.recordInventoryTransactionsFromItemsEntries( await this.inventoryService.recordInventoryTransactionsFromItemsEntries(
tenantId,
transaction, transaction,
override, override,
trx trx,
); );
} }
/** /**
* Reverting the inventory transactions once the invoice deleted. * Reverting the inventory transactions once the invoice deleted.
* @param {number} tenantId - Tenant id.
* @param {number} billId - Bill id. * @param {number} billId - Bill id.
* @return {Promise<void>} * @return {Promise<void>}
*/ */
public async revertInventoryTransactions( public async revertInventoryTransactions(
saleInvoiceId: number, saleInvoiceId: number,
trx?: Knex.Transaction trx?: Knex.Transaction,
): Promise<void> { ): Promise<void> {
// Delete the inventory transaction of the given sale invoice. // Delete the inventory transaction of the given sale invoice.
const { oldInventoryTransactions } = const { oldInventoryTransactions } =
await this.inventoryService.deleteInventoryTransactions( await this.inventoryService.deleteInventoryTransactions(
tenantId,
saleInvoiceId, saleInvoiceId,
'SaleInvoice', 'SaleInvoice',
trx trx,
); );
} }
} }

View File

@@ -1,80 +1,65 @@
// import { Inject, Service } from 'typedi'; import * as R from 'ramda';
// import * as R from 'ramda'; import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
// import { import { Injectable } from '@nestjs/common';
// IFilterMeta, import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
// IPaginationMeta, import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
// ISaleInvoice, import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
// ISalesInvoicesFilter, import { SaleInvoice } from '../models/SaleInvoice';
// } from '@/interfaces'; import { ISalesInvoicesFilter } from '../SaleInvoice.types';
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import DynamicListingService from '@/services/DynamicListing/DynamicListService';
// import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
// @Service() @Injectable()
// export class GetSaleInvoices { export class GetSaleInvoicesService {
// @Inject() constructor(
// private tenancy: HasTenancyService; private readonly dynamicListService: DynamicListService,
private readonly transformer: TransformerInjectable,
) {}
// @Inject() /**
// private dynamicListService: DynamicListingService; * Retrieve sales invoices filterable and paginated list.
* @param {ISalesInvoicesFilter} filterDTO -
* @returns {Promise<{ salesInvoices: SaleInvoice[]; pagination: IPaginationMeta; filterMeta: IFilterMeta; }>}
*/
public async getSaleInvoices(filterDTO: ISalesInvoicesFilter): Promise<{
salesInvoices: SaleInvoice[];
pagination: IPaginationMeta;
filterMeta: IFilterMeta;
}> {
// Parses stringified filter roles.
const filter = this.parseListFilterDTO(filterDTO);
// @Inject() // Dynamic list service.
// private transformer: TransformerInjectable; const dynamicFilter = await this.dynamicListService.dynamicList(
SaleInvoice,
filter,
);
const { results, pagination } = await SaleInvoice.query()
.onBuild((builder) => {
builder.withGraphFetched('entries.item');
builder.withGraphFetched('customer');
dynamicFilter.buildQuery()(builder);
filterDTO?.filterQuery && filterDTO?.filterQuery(builder);
})
.pagination(filter.page - 1, filter.pageSize);
// /** // Retrieves the transformed sale invoices.
// * Retrieve sales invoices filterable and paginated list. const salesInvoices = await this.transformer.transform(
// * @param {Request} req results,
// * @param {Response} res new SaleInvoiceTransformer(),
// * @param {NextFunction} next );
// */
// public async getSaleInvoices(
// filterDTO: ISalesInvoicesFilter
// ): Promise<{
// salesInvoices: ISaleInvoice[];
// pagination: IPaginationMeta;
// filterMeta: IFilterMeta;
// }> {
// const { SaleInvoice } = this.tenancy.models(tenantId);
// // Parses stringified filter roles. return {
// const filter = this.parseListFilterDTO(filterDTO); salesInvoices,
pagination,
filterMeta: dynamicFilter.getResponseMeta(),
};
}
// // Dynamic list service. /**
// const dynamicFilter = await this.dynamicListService.dynamicList( * Parses the sale invoice list filter DTO.
// tenantId, * @param filterDTO
// SaleInvoice, * @returns
// filter */
// ); private parseListFilterDTO(filterDTO) {
// const { results, pagination } = await SaleInvoice.query() return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
// .onBuild((builder) => { }
// builder.withGraphFetched('entries.item'); }
// builder.withGraphFetched('customer');
// dynamicFilter.buildQuery()(builder);
// filterDTO?.filterQuery && filterDTO?.filterQuery(builder);
// })
// .pagination(filter.page - 1, filter.pageSize);
// // Retrieves the transformed sale invoices.
// const salesInvoices = await this.transformer.transform(
// tenantId,
// results,
// new SaleInvoiceTransformer()
// );
// return {
// salesInvoices,
// pagination,
// filterMeta: dynamicFilter.getResponseMeta(),
// };
// }
// /**
// * Parses the sale invoice list filter DTO.
// * @param filterDTO
// * @returns
// */
// private parseListFilterDTO(filterDTO) {
// return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
// }
// }

View File

@@ -12,10 +12,12 @@ import {
ISaleReceiptDTO, ISaleReceiptDTO,
ISaleReceiptState, ISaleReceiptState,
ISalesReceiptsFilter, ISalesReceiptsFilter,
SaleReceiptMailOpts,
} from './types/SaleReceipts.types'; } from './types/SaleReceipts.types';
import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service'; import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service';
import { SaleReceipt } from './models/SaleReceipt'; import { SaleReceipt } from './models/SaleReceipt';
import { IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { SaleReceiptMailNotification } from './commands/SaleReceiptMailNotification';
@Injectable() @Injectable()
export class SaleReceiptApplication { export class SaleReceiptApplication {
@@ -28,8 +30,8 @@ export class SaleReceiptApplication {
private closeSaleReceiptService: CloseSaleReceipt, private closeSaleReceiptService: CloseSaleReceipt,
private getSaleReceiptPdfService: SaleReceiptsPdfService, private getSaleReceiptPdfService: SaleReceiptsPdfService,
// private saleReceiptNotifyBySmsService: SaleReceiptNotifyBySms, // private saleReceiptNotifyBySmsService: SaleReceiptNotifyBySms,
// private saleReceiptNotifyByMailService: SaleReceiptMailNotification,
private getSaleReceiptStateService: GetSaleReceiptState, private getSaleReceiptStateService: GetSaleReceiptState,
private saleReceiptNotifyByMailService: SaleReceiptMailNotification,
) {} ) {}
/** /**
@@ -158,19 +160,14 @@ export class SaleReceiptApplication {
/** /**
* Retrieves the default mail options of the given sale receipt. * Retrieves the default mail options of the given sale receipt.
* @param {number} tenantId * @param {number} saleReceiptId - Sale receipt identifier.
* @param {number} saleReceiptId
* @returns {Promise<SaleReceiptMailOpts>} * @returns {Promise<SaleReceiptMailOpts>}
*/ */
// public getSaleReceiptMail( public getSaleReceiptMail(
// tenantId: number, saleReceiptId: number,
// saleReceiptId: number, ): Promise<SaleReceiptMailOpts> {
// ): Promise<SaleReceiptMailOpts> { return this.saleReceiptNotifyByMailService.getMailOptions(saleReceiptId);
// return this.saleReceiptNotifyByMailService.getMailOptions( }
// tenantId,
// saleReceiptId,
// );
// }
/** /**
* Retrieves the current state of the sale receipt. * Retrieves the current state of the sale receipt.

View File

@@ -26,6 +26,7 @@ import { LedgerModule } from '../Ledger/Ledger.module';
import { AccountsModule } from '../Accounts/Accounts.module'; import { AccountsModule } from '../Accounts/Accounts.module';
import { SaleReceiptInventoryTransactionsSubscriber } from './inventory/SaleReceiptWriteInventoryTransactions'; import { SaleReceiptInventoryTransactionsSubscriber } from './inventory/SaleReceiptWriteInventoryTransactions';
import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service'; import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service';
import { SaleReceiptMailNotification } from './commands/SaleReceiptMailNotification';
@Module({ @Module({
controllers: [SaleReceiptsController], controllers: [SaleReceiptsController],
@@ -57,7 +58,8 @@ import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service';
SaleReceiptGLEntries, SaleReceiptGLEntries,
SaleReceiptGLEntriesSubscriber, SaleReceiptGLEntriesSubscriber,
SaleReceiptInventoryTransactionsSubscriber, SaleReceiptInventoryTransactionsSubscriber,
GetSaleReceiptsService GetSaleReceiptsService,
SaleReceiptMailNotification
], ],
}) })
export class SaleReceiptsModule {} export class SaleReceiptsModule {}

View File

@@ -1,220 +1,192 @@
// import HasTenancyService from '@/services/Tenancy/TenancyService'; import {
// import { Inject, Service } from 'typedi'; DEFAULT_RECEIPT_MAIL_CONTENT,
// import Mail from '@/lib/Mail'; DEFAULT_RECEIPT_MAIL_SUBJECT,
// import { GetSaleReceipt } from '../queries/GetSaleReceipt'; } from '../constants';
// import { SaleReceiptsPdf } from '../queries/SaleReceiptsPdfService'; import { mergeAndValidateMailOptions } from '@/modules/MailNotification/utils';
// import { import { transformReceiptToMailDataArgs } from '../utils';
// DEFAULT_RECEIPT_MAIL_CONTENT, import { Injectable } from '@nestjs/common';
// DEFAULT_RECEIPT_MAIL_SUBJECT, import { GetSaleReceipt } from '../queries/GetSaleReceipt.service';
// } from '../constants'; import { SaleReceiptsPdfService } from '../queries/SaleReceiptsPdf.service';
// import { import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
// ISaleReceiptMailPresend, import { EventEmitter2 } from '@nestjs/event-emitter';
// SaleReceiptMailOpts, import { events } from '@/common/events/events';
// SaleReceiptMailOptsDTO, import {
// } from '@/interfaces'; ISaleReceiptMailPresend,
// import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; SaleReceiptMailOpts,
// import { mergeAndValidateMailOptions } from '@/services/MailNotification/utils'; SaleReceiptMailOptsDTO,
// import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; } from '../types/SaleReceipts.types';
// import events from '@/subscribers/events'; import { SaleReceipt } from '../models/SaleReceipt';
// import { transformReceiptToMailDataArgs } from '../utils'; import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
import { Mail } from '@/modules/Mail/Mail';
// @Service() @Injectable()
// export class SaleReceiptMailNotification { export class SaleReceiptMailNotification {
// @Inject() /**
// private tenancy: HasTenancyService; * @param {GetSaleReceipt} getSaleReceiptService - Get sale receipt service.
* @param {SaleReceiptsPdfService} receiptPdfService - Sale receipt pdf service.
* @param {ContactMailNotification} contactMailNotification - Contact mail notification service.
* @param {EventEmitter2} eventEmitter - Event emitter.
* @param {MailTransporter} mailTransporter - Mail transporter service.
*/
constructor(
private readonly getSaleReceiptService: GetSaleReceipt,
private readonly receiptPdfService: SaleReceiptsPdfService,
private readonly contactMailNotification: ContactMailNotification,
private readonly eventEmitter: EventEmitter2,
private readonly mailTransporter: MailTransporter,
) {}
// @Inject() /**
// private getSaleReceiptService: GetSaleReceipt; * Sends the receipt mail of the given sale receipt.
* @param {number} tenantId
* @param {number} saleReceiptId
* @param {SaleReceiptMailOptsDTO} messageDTO
*/
public async triggerMail(
saleReceiptId: number,
messageOptions: SaleReceiptMailOptsDTO,
) {
const payload = {
saleReceiptId,
messageOpts: messageOptions,
};
// await this.agenda.now('sale-receipt-mail-send', payload);
// @Inject() // Triggers the event `onSaleReceiptPreMailSend`.
// private receiptPdfService: SaleReceiptsPdf; await this.eventEmitter.emitAsync(events.saleReceipt.onPreMailSend, {
saleReceiptId,
messageOptions,
} as ISaleReceiptMailPresend);
}
// @Inject() /**
// private contactMailNotification: ContactMailNotification; * Retrieves the mail options of the given sale receipt.
* @param {number} saleReceiptId
* @returns {Promise<SaleReceiptMailOptsDTO>}
*/
public async getMailOptions(
saleReceiptId: number,
): Promise<SaleReceiptMailOpts> {
const saleReceipt = await SaleReceipt.query()
.findById(saleReceiptId)
.throwIfNotFound();
// @Inject() const formatArgs = await this.textFormatterArgs(saleReceiptId);
// private eventPublisher: EventPublisher; const mailOptions =
await this.contactMailNotification.getDefaultMailOptions(
saleReceipt.customerId,
);
return {
...mailOptions,
message: DEFAULT_RECEIPT_MAIL_CONTENT,
subject: DEFAULT_RECEIPT_MAIL_SUBJECT,
attachReceipt: true,
formatArgs,
};
}
// @Inject('agenda') /**
// private agenda: any; * Retrieves the formatted text of the given sale receipt.
* @param {number} tenantId - Tenant id.
* @param {number} receiptId - Sale receipt id.
* @param {string} text - The given text.
* @returns {Promise<string>}
*/
public textFormatterArgs = async (
receiptId: number,
): Promise<Record<string, string>> => {
const receipt = await this.getSaleReceiptService.getSaleReceipt(receiptId);
// /** return transformReceiptToMailDataArgs(receipt);
// * Sends the receipt mail of the given sale receipt. };
// * @param {number} tenantId
// * @param {number} saleReceiptId
// * @param {SaleReceiptMailOptsDTO} messageDTO
// */
// public async triggerMail(
// tenantId: number,
// saleReceiptId: number,
// messageOptions: SaleReceiptMailOptsDTO
// ) {
// const payload = {
// tenantId,
// saleReceiptId,
// messageOpts: messageOptions,
// };
// await this.agenda.now('sale-receipt-mail-send', payload);
// // Triggers the event `onSaleReceiptPreMailSend`. /**
// await this.eventPublisher.emitAsync(events.saleReceipt.onPreMailSend, { * Formats the mail options of the given sale receipt.
// tenantId, * @param {number} tenantId
// saleReceiptId, * @param {number} receiptId
// messageOptions, * @param {SaleReceiptMailOpts} mailOptions
// } as ISaleReceiptMailPresend); * @returns {Promise<SaleReceiptMailOpts>}
// } */
public async formatEstimateMailOptions(
receiptId: number,
mailOptions: SaleReceiptMailOpts,
): Promise<SaleReceiptMailOpts> {
const formatterArgs = await this.textFormatterArgs(receiptId);
const formattedOptions =
(await this.contactMailNotification.formatMailOptions(
mailOptions,
formatterArgs,
)) as SaleReceiptMailOpts;
return formattedOptions;
}
// /** /**
// * Retrieves the mail options of the given sale receipt. * Retrieves the formatted mail options of the given sale receipt.
// * @param {number} tenantId * @param {number} tenantId
// * @param {number} saleReceiptId * @param {number} saleReceiptId
// * @returns {Promise<SaleReceiptMailOptsDTO>} * @param {SaleReceiptMailOptsDTO} messageOpts
// */ * @returns {Promise<SaleReceiptMailOpts>}
// public async getMailOptions( */
// tenantId: number, public getFormatMailOptions = async (
// saleReceiptId: number saleReceiptId: number,
// ): Promise<SaleReceiptMailOpts> { messageOpts: SaleReceiptMailOptsDTO,
// const { SaleReceipt } = this.tenancy.models(tenantId); ): Promise<SaleReceiptMailOpts> => {
const defaultMessageOptions = await this.getMailOptions(saleReceiptId);
// Merges message opts with default options.
const parsedMessageOpts = mergeAndValidateMailOptions(
defaultMessageOptions,
messageOpts,
) as SaleReceiptMailOpts;
// const saleReceipt = await SaleReceipt.query() // Formats the message options.
// .findById(saleReceiptId) return this.formatEstimateMailOptions(saleReceiptId, parsedMessageOpts);
// .throwIfNotFound(); };
// const formatArgs = await this.textFormatterArgs(tenantId, saleReceiptId); /**
* Triggers the mail notification of the given sale receipt.
* @param {number} saleReceiptId - Sale receipt id.
* @param {SaleReceiptMailOpts} messageDTO - message options.
* @returns {Promise<void>}
*/
public async sendMail(
saleReceiptId: number,
messageOpts: SaleReceiptMailOptsDTO,
) {
// Formats the message options.
const formattedMessageOptions = await this.getFormatMailOptions(
saleReceiptId,
messageOpts,
);
const mail = new Mail()
.setSubject(formattedMessageOptions.subject)
.setTo(formattedMessageOptions.to)
.setCC(formattedMessageOptions.cc)
.setBCC(formattedMessageOptions.bcc)
.setContent(formattedMessageOptions.message);
// const mailOptions = // Attaches the receipt pdf document.
// await this.contactMailNotification.getDefaultMailOptions( if (formattedMessageOptions.attachReceipt) {
// tenantId, // Retrieves document buffer of the receipt pdf document.
// saleReceipt.customerId const [receiptPdfBuffer, filename] =
// ); await this.receiptPdfService.saleReceiptPdf(saleReceiptId);
// return {
// ...mailOptions,
// message: DEFAULT_RECEIPT_MAIL_CONTENT,
// subject: DEFAULT_RECEIPT_MAIL_SUBJECT,
// attachReceipt: true,
// formatArgs,
// };
// }
// /** mail.setAttachments([
// * Retrieves the formatted text of the given sale receipt. { filename: `${filename}.pdf`, content: receiptPdfBuffer },
// * @param {number} tenantId - Tenant id. ]);
// * @param {number} receiptId - Sale receipt id. }
// * @param {string} text - The given text. const eventPayload = {
// * @returns {Promise<string>} saleReceiptId,
// */ messageOptions: {},
// public textFormatterArgs = async ( };
// tenantId: number, await this.eventEmitter.emitAsync(
// receiptId: number events.saleReceipt.onMailSend,
// ): Promise<Record<string, string>> => { eventPayload,
// const receipt = await this.getSaleReceiptService.getSaleReceipt( );
// tenantId, await this.mailTransporter.send(mail);
// receiptId
// );
// return transformReceiptToMailDataArgs(receipt);
// };
// /** await this.eventEmitter.emitAsync(
// * Formats the mail options of the given sale receipt. events.saleReceipt.onMailSent,
// * @param {number} tenantId eventPayload,
// * @param {number} receiptId );
// * @param {SaleReceiptMailOpts} mailOptions }
// * @returns {Promise<SaleReceiptMailOpts>} }
// */
// public async formatEstimateMailOptions(
// tenantId: number,
// receiptId: number,
// mailOptions: SaleReceiptMailOpts
// ): Promise<SaleReceiptMailOpts> {
// const formatterArgs = await this.textFormatterArgs(tenantId, receiptId);
// const formattedOptions =
// (await this.contactMailNotification.formatMailOptions(
// tenantId,
// mailOptions,
// formatterArgs
// )) as SaleReceiptMailOpts;
// return formattedOptions;
// }
// /**
// * Retrieves the formatted mail options of the given sale receipt.
// * @param {number} tenantId
// * @param {number} saleReceiptId
// * @param {SaleReceiptMailOptsDTO} messageOpts
// * @returns {Promise<SaleReceiptMailOpts>}
// */
// public getFormatMailOptions = async (
// tenantId: number,
// saleReceiptId: number,
// messageOpts: SaleReceiptMailOptsDTO
// ): Promise<SaleReceiptMailOpts> => {
// const defaultMessageOptions = await this.getMailOptions(
// tenantId,
// saleReceiptId
// );
// // Merges message opts with default options.
// const parsedMessageOpts = mergeAndValidateMailOptions(
// defaultMessageOptions,
// messageOpts
// ) as SaleReceiptMailOpts;
// // Formats the message options.
// return this.formatEstimateMailOptions(
// tenantId,
// saleReceiptId,
// parsedMessageOpts
// );
// };
// /**
// * Triggers the mail notification of the given sale receipt.
// * @param {number} tenantId - Tenant id.
// * @param {number} saleReceiptId - Sale receipt id.
// * @param {SaleReceiptMailOpts} messageDTO - message options.
// * @returns {Promise<void>}
// */
// public async sendMail(
// tenantId: number,
// saleReceiptId: number,
// messageOpts: SaleReceiptMailOptsDTO
// ) {
// // Formats the message options.
// const formattedMessageOptions = await this.getFormatMailOptions(
// tenantId,
// saleReceiptId,
// messageOpts
// );
// const mail = new Mail()
// .setSubject(formattedMessageOptions.subject)
// .setTo(formattedMessageOptions.to)
// .setCC(formattedMessageOptions.cc)
// .setBCC(formattedMessageOptions.bcc)
// .setContent(formattedMessageOptions.message);
// // Attaches the receipt pdf document.
// if (formattedMessageOptions.attachReceipt) {
// // Retrieves document buffer of the receipt pdf document.
// const [receiptPdfBuffer, filename] =
// await this.receiptPdfService.saleReceiptPdf(tenantId, saleReceiptId);
// mail.setAttachments([
// { filename: `${filename}.pdf`, content: receiptPdfBuffer },
// ]);
// }
// const eventPayload = {
// tenantId,
// saleReceiptId,
// messageOptions: {},
// };
// await this.eventPublisher.emitAsync(
// events.saleReceipt.onMailSend,
// eventPayload
// );
// await mail.send();
// await this.eventPublisher.emitAsync(
// events.saleReceipt.onMailSent,
// eventPayload
// );
// }
// }

View File

@@ -1,11 +1,11 @@
import * as R from 'ramda'; import * as R from 'ramda';
import { SaleReceiptTransformer } from './SaleReceiptTransformer';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { SaleReceiptTransformer } from './SaleReceiptTransformer';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { ISalesReceiptsFilter } from '../types/SaleReceipts.types'; import { ISalesReceiptsFilter } from '../types/SaleReceipts.types';
import { SaleReceipt } from '../models/SaleReceipt'; import { SaleReceipt } from '../models/SaleReceipt';
import { IPaginationMeta } from '@/interfaces/Model';
interface GetSaleReceiptsSettings { interface GetSaleReceiptsSettings {
fetchEntriesGraph?: boolean; fetchEntriesGraph?: boolean;
@@ -22,8 +22,7 @@ export class GetSaleReceiptsService {
/** /**
* Retrieve sales receipts paginated and filterable list. * Retrieve sales receipts paginated and filterable list.
* @param {number} tenantId * @param {ISalesReceiptsFilter} salesReceiptsFilter - Sales receipts filter.
* @param {ISaleReceiptFilter} salesReceiptsFilter
*/ */
public async getSaleReceipts(filterDTO: ISalesReceiptsFilter): Promise<{ public async getSaleReceipts(filterDTO: ISalesReceiptsFilter): Promise<{
data: SaleReceipt[]; data: SaleReceipt[];

View File

@@ -3,6 +3,8 @@ import { Knex } from 'knex';
// import { CommonMailOptions, CommonMailOptionsDTO } from '../SaleInvoices/types/Mailable'; // import { CommonMailOptions, CommonMailOptionsDTO } from '../SaleInvoices/types/Mailable';
import { AttachmentLinkDTO } from '../../Attachments/Attachments.types'; import { AttachmentLinkDTO } from '../../Attachments/Attachments.types';
import { SaleReceipt } from '../models/SaleReceipt'; import { SaleReceipt } from '../models/SaleReceipt';
import { CommonMailOptionsDTO } from '@/modules/MailNotification/MailNotification.types';
import { CommonMailOptions } from '@/modules/MailNotification/MailNotification.types';
export interface ISalesReceiptsFilter { export interface ISalesReceiptsFilter {
filterQuery?: (query: any) => void; filterQuery?: (query: any) => void;
@@ -92,19 +94,19 @@ export interface ISaleReceiptDeletingPayload {
trx: Knex.Transaction; trx: Knex.Transaction;
} }
// export interface SaleReceiptMailOpts extends CommonMailOptions { export interface SaleReceiptMailOpts extends CommonMailOptions {
// attachReceipt: boolean; attachReceipt: boolean;
// } }
// export interface SaleReceiptMailOptsDTO extends CommonMailOptionsDTO { export interface SaleReceiptMailOptsDTO extends CommonMailOptionsDTO {
// attachReceipt?: boolean; attachReceipt?: boolean;
// } }
// export interface ISaleReceiptMailPresend { export interface ISaleReceiptMailPresend {
// tenantId: number; tenantId: number;
// saleReceiptId: number; saleReceiptId: number;
// messageOptions: SaleReceiptMailOptsDTO; messageOptions: SaleReceiptMailOptsDTO;
// } }
export interface ISaleReceiptBrandingTemplateAttributes { export interface ISaleReceiptBrandingTemplateAttributes {
primaryColor: string; primaryColor: string;
@@ -162,7 +164,6 @@ export interface ISaleReceiptBrandingTemplateAttributes {
receiptDateLabel: string; receiptDateLabel: string;
} }
export interface ISaleReceiptState { export interface ISaleReceiptState {
defaultTemplateId: number; defaultTemplateId: number;
} }

View File

@@ -12,6 +12,7 @@ import { VendorCreditsApplicationService } from './VendorCreditsApplication.serv
import { import {
IVendorCreditCreateDTO, IVendorCreditCreateDTO,
IVendorCreditEditDTO, IVendorCreditEditDTO,
IVendorCreditsQueryDTO,
} from './types/VendorCredit.types'; } from './types/VendorCredit.types';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
@@ -33,7 +34,7 @@ export class VendorCreditsController {
} }
@Get() @Get()
async getVendorCredits(@Query() filterDTO: IVendorCreditsFilter) { async getVendorCredits(@Query() filterDTO: IVendorCreditsQueryDTO) {
return this.vendorCreditsApplication.getVendorCredits(filterDTO); return this.vendorCreditsApplication.getVendorCredits(filterDTO);
} }

View File

@@ -3,7 +3,7 @@ import { CreateVendorCreditService } from './commands/CreateVendorCredit.service
import { DeleteVendorCreditService } from './commands/DeleteVendorCredit.service'; import { DeleteVendorCreditService } from './commands/DeleteVendorCredit.service';
import { EditVendorCreditService } from './commands/EditVendorCredit.service'; import { EditVendorCreditService } from './commands/EditVendorCredit.service';
import { GetVendorCreditService } from './queries/GetVendorCredit.service'; import { GetVendorCreditService } from './queries/GetVendorCredit.service';
import { IVendorCreditEditDTO } from './types/VendorCredit.types'; import { IVendorCreditEditDTO, IVendorCreditsQueryDTO } from './types/VendorCredit.types';
import { IVendorCreditCreateDTO } from './types/VendorCredit.types'; import { IVendorCreditCreateDTO } from './types/VendorCredit.types';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { OpenVendorCreditService } from './commands/OpenVendorCredit.service'; import { OpenVendorCreditService } from './commands/OpenVendorCredit.service';

View File

@@ -4,6 +4,7 @@ import { VendorCreditTransformer } from './VendorCreditTransformer';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { VendorCredit } from '../models/VendorCredit'; import { VendorCredit } from '../models/VendorCredit';
import { IVendorCreditsQueryDTO } from '../types/VendorCredit.types';
@Injectable() @Injectable()
export class GetVendorCreditsService { export class GetVendorCreditsService {

View File

@@ -38,7 +38,7 @@ export default class VendorCreditInventoryTransactionsSubscriber {
*/ */
@OnEvent(events.vendorCredit.onEdited) @OnEvent(events.vendorCredit.onEdited)
public async rewriteInventroyTransactionsOnceEdited({ public async rewriteInventroyTransactionsOnceEdited({
vendorCreditId, oldVendorCredit,
vendorCredit, vendorCredit,
trx, trx,
}: IVendorCreditEditedPayload) { }: IVendorCreditEditedPayload) {
@@ -46,7 +46,7 @@ export default class VendorCreditInventoryTransactionsSubscriber {
if (!vendorCredit.openedAt) return null; if (!vendorCredit.openedAt) return null;
await this.inventoryTransactions.editInventoryTransactions( await this.inventoryTransactions.editInventoryTransactions(
vendorCreditId, oldVendorCredit.id,
vendorCredit, vendorCredit,
trx, trx,
); );

View File

@@ -4,6 +4,7 @@ import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
import { IRefundVendorCreditDTO } from '@/modules/VendorCreditsRefund/types/VendorCreditRefund.types'; import { IRefundVendorCreditDTO } from '@/modules/VendorCreditsRefund/types/VendorCreditRefund.types';
import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types'; import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
import { DiscountType } from '@/common/types/Discount'; import { DiscountType } from '@/common/types/Discount';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
export enum VendorCreditAction { export enum VendorCreditAction {
Create = 'Create', Create = 'Create',
@@ -15,12 +16,12 @@ export enum VendorCreditAction {
export interface IVendorCreditEntryDTO extends IItemEntryDTO {} export interface IVendorCreditEntryDTO extends IItemEntryDTO {}
// export interface IVendorCreditsQueryDTO extends IDynamicListFilter { export interface IVendorCreditsQueryDTO extends IDynamicListFilter {
// page: number; page: number;
// pageSize: number; pageSize: number;
// searchKeyword?: string; searchKeyword?: string;
// filterQuery?: (q: any) => void; filterQuery?: (q: any) => void;
// } }
export interface IVendorCreditDTO { export interface IVendorCreditDTO {
vendorId: number; vendorId: number;

View File

@@ -13,6 +13,7 @@ import {
IVendorEditDTO, IVendorEditDTO,
IVendorNewDTO, IVendorNewDTO,
IVendorOpeningBalanceEditDTO, IVendorOpeningBalanceEditDTO,
IVendorsFilter,
} from './types/Vendors.types'; } from './types/Vendors.types';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';

View File

@@ -1,6 +1,5 @@
import fs from 'fs'; import * as fs from 'fs';
import Mustache from 'mustache'; import Mustache from 'mustache';
import { Container } from 'typedi';
import path from 'path'; import path from 'path';
import { IMailAttachment } from '@/interfaces'; import { IMailAttachment } from '@/interfaces';
@@ -40,23 +39,6 @@ export default class Mail {
return this.view ? Mail.render(this.view, this.data) : this.content; return this.view ? Mail.render(this.view, this.data) : this.content;
} }
/**
* Sends the given mail to the target address.
*/
public send() {
return new Promise((resolve, reject) => {
const Mail = Container.get('mail');
Mail.sendMail(this.mailOptions, (error) => {
if (error) {
reject(error);
return;
}
resolve(true);
});
});
}
/** /**
* Set send mail to address. * Set send mail to address.
* @param {string} to - * @param {string} to -