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

@@ -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
// * Sends the mail of the given payment receive. * @returns {Promise<void>}
// * @param {number} tenantId */
// * @param {number} paymentReceiveId public async triggerMail(
// * @param {PaymentReceiveMailOptsDTO} messageDTO paymentReceiveId: number,
// * @returns {Promise<void>} messageDTO: PaymentReceiveMailOptsDTO,
// */ ): Promise<void> {
// public async triggerMail( const payload = {
// paymentReceiveId: number, paymentReceiveId,
// messageDTO: PaymentReceiveMailOptsDTO, messageDTO,
// ): Promise<void> { };
// const payload = {
// paymentReceiveId,
// messageDTO,
// };
// await this.agenda.now('payment-receive-mail-send', payload); // await this.agenda.now('payment-receive-mail-send', payload);
// // Triggers `onPaymentReceivePreMailSend` event. // Triggers `onPaymentReceivePreMailSend` event.
// await this.eventPublisher.emitAsync(events.paymentReceive.onPreMailSend, { await this.eventEmitter.emitAsync(events.paymentReceive.onPreMailSend, {
// paymentReceiveId, paymentReceiveId,
// messageOptions: messageDTO, messageOptions: messageDTO,
// } as PaymentReceiveMailPresendEvent); } as PaymentReceiveMailPresendEvent);
// } }
// /** /**
// * Retrieves the default payment mail options. * Retrieves the default payment mail options.
// * @param {number} paymentReceiveId - Payment receive id. * @param {number} paymentReceiveId - Payment receive id.
// * @returns {Promise<PaymentReceiveMailOpts>} * @returns {Promise<PaymentReceiveMailOpts>}
// */ */
// public getMailOptions = async ( public getMailOptions = async (
// paymentId: number, paymentId: number,
// ): Promise<PaymentReceiveMailOpts> => { ): Promise<PaymentReceiveMailOpts> => {
// const paymentReceive = await this.paymentReceiveModel const paymentReceive = await this.paymentReceiveModel
// .query() .query()
// .findById(paymentId) .findById(paymentId)
// .throwIfNotFound(); .throwIfNotFound();
// const formatArgs = await this.textFormatter(paymentId); const formatArgs = await this.textFormatter(paymentId);
// const mailOptions = const mailOptions =
// await this.contactMailNotification.getDefaultMailOptions( await this.contactMailNotification.getDefaultMailOptions(
// paymentReceive.customerId, paymentReceive.customerId,
// ); );
// return { return {
// ...mailOptions, ...mailOptions,
// subject: DEFAULT_PAYMENT_MAIL_SUBJECT, subject: DEFAULT_PAYMENT_MAIL_SUBJECT,
// message: DEFAULT_PAYMENT_MAIL_CONTENT, message: DEFAULT_PAYMENT_MAIL_CONTENT,
// ...formatArgs, ...formatArgs,
// }; };
// }; };
// /** /**
// * Retrieves the formatted text of the given sale invoice. * Retrieves the formatted text of the given sale invoice.
// * @param {number} invoiceId - Sale invoice id. * @param {number} invoiceId - Sale invoice id.
// * @returns {Promise<Record<string, string>>} * @returns {Promise<Record<string, string>>}
// */ */
// public textFormatter = async ( public textFormatter = async (
// invoiceId: number, invoiceId: number,
// ): Promise<Record<string, string>> => { ): Promise<Record<string, string>> => {
// const payment = await this.getPaymentService.getPaymentReceive(invoiceId); const payment = await this.getPaymentService.getPaymentReceive(invoiceId);
// return transformPaymentReceivedToMailDataArgs(payment); return transformPaymentReceivedToMailDataArgs(payment);
// }; };
// /** /**
// * Retrieves the formatted mail options of the given payment receive. * Retrieves the formatted mail options of the given payment receive.
// * @param {number} tenantId * @param {number} tenantId
// * @param {number} paymentReceiveId * @param {number} paymentReceiveId
// * @param {SendInvoiceMailDTO} messageDTO * @param {SendInvoiceMailDTO} messageDTO
// * @returns {Promise<PaymentReceiveMailOpts>} * @returns {Promise<PaymentReceiveMailOpts>}
// */ */
// public getFormattedMailOptions = async ( public getFormattedMailOptions = async (
// paymentReceiveId: number, paymentReceiveId: number,
// messageDTO: SendInvoiceMailDTO, messageDTO: SendInvoiceMailDTO,
// ) => { ) => {
// const formatterArgs = await this.textFormatter(paymentReceiveId); const formatterArgs = await this.textFormatter(paymentReceiveId);
// // Default message options. // Default message options.
// const defaultMessageOpts = await this.getMailOptions(paymentReceiveId); const defaultMessageOpts = await this.getMailOptions(paymentReceiveId);
// // Parsed message opts with default options. // Parsed message opts with default options.
// const parsedMessageOpts = mergeAndValidateMailOptions( const parsedMessageOpts = mergeAndValidateMailOptions(
// defaultMessageOpts, defaultMessageOpts,
// messageDTO, messageDTO,
// ); );
// // Formats the message options. // Formats the message options.
// return this.contactMailNotification.formatMailOptions( return this.contactMailNotification.formatMailOptions(
// parsedMessageOpts, parsedMessageOpts,
// formatterArgs, formatterArgs,
// ); );
// }; };
// /** /**
// * Triggers the mail invoice. * Triggers the mail invoice.
// * @param {number} tenantId * @param {number} tenantId
// * @param {number} saleInvoiceId - Invoice id. * @param {number} saleInvoiceId - Invoice id.
// * @param {SendInvoiceMailDTO} messageDTO - Message options. * @param {SendInvoiceMailDTO} messageDTO - Message options.
// * @returns {Promise<void>} * @returns {Promise<void>}
// */ */
// public async sendMail( public async sendMail(
// paymentReceiveId: number, paymentReceiveId: number,
// messageDTO: SendInvoiceMailDTO, messageDTO: PaymentReceiveMailOptsDTO,
// ): Promise<void> { ): Promise<void> {
// // Retrieves the formatted mail options. // Retrieves the formatted mail options.
// const formattedMessageOptions = await this.getFormattedMailOptions( const formattedMessageOptions = await this.getFormattedMailOptions(
// paymentReceiveId, paymentReceiveId,
// messageDTO, messageDTO,
// ); );
// const mail = new Mail() const mail = new Mail()
// .setSubject(formattedMessageOptions.subject) .setSubject(formattedMessageOptions.subject)
// .setTo(formattedMessageOptions.to) .setTo(formattedMessageOptions.to)
// .setCC(formattedMessageOptions.cc) .setCC(formattedMessageOptions.cc)
// .setBCC(formattedMessageOptions.bcc) .setBCC(formattedMessageOptions.bcc)
// .setContent(formattedMessageOptions.message); .setContent(formattedMessageOptions.message);
// const eventPayload = { const eventPayload = {
// paymentReceiveId, paymentReceiveId,
// messageOptions: formattedMessageOptions, messageOptions: formattedMessageOptions,
// }; };
// // Triggers `onPaymentReceiveMailSend` event. // Triggers `onPaymentReceiveMailSend` event.
// await this.eventPublisher.emitAsync( await this.eventEmitter.emitAsync(
// events.paymentReceive.onMailSend, events.paymentReceive.onMailSend,
// eventPayload, eventPayload,
// ); );
// await mail.send(); await mail.send();
// // Triggers `onPaymentReceiveMailSent` event. // Triggers `onPaymentReceiveMailSent` event.
// await this.eventPublisher.emitAsync( await this.eventEmitter.emitAsync(
// events.paymentReceive.onMailSent, events.paymentReceive.onMailSent,
// eventPayload, 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.
// @Inject() * @param {SaleEstimateMailOptionsDTO} messageOptions - Sale estimate mail options.
// private contactMailNotification: ContactMailNotification; * @returns {Promise<void>}
*/
// @Inject('agenda') public async triggerMail(
// private agenda: any; saleEstimateId: number,
messageOptions: SaleEstimateMailOptionsDTO,
// @Inject() ): Promise<void> {
// private eventPublisher: EventPublisher; const payload = {
saleEstimateId,
// /** messageOptions,
// * 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); // await this.agenda.now('sale-estimate-mail-send', payload);
// // Triggers `onSaleEstimatePreMailSend` event. // Triggers `onSaleEstimatePreMailSend` event.
// await this.eventPublisher.emitAsync(events.saleEstimate.onPreMailSend, { await this.eventPublisher.emitAsync(events.saleEstimate.onPreMailSend, {
// tenantId, saleEstimateId,
// saleEstimateId, messageOptions,
// messageOptions, } as ISaleEstimateMailPresendEvent);
// } as ISaleEstimateMailPresendEvent); }
// }
// /** /**
// * Formate the text of the mail. * Formate the text of the mail.
// * @param {number} tenantId - Tenant id. * @param {number} estimateId - Estimate id.
// * @param {number} estimateId - Estimate id. * @returns {Promise<Record<string, any>>}
// * @returns {Promise<Record<string, any>>} */
// */ public formatterArgs = async (estimateId: number) => {
// public formatterArgs = async (tenantId: number, estimateId: number) => { const estimate = await this.getSaleEstimateService.getEstimate(estimateId);
// const estimate = await this.getSaleEstimateService.getEstimate( return transformEstimateToMailDataArgs(estimate);
// tenantId, };
// estimateId
// );
// return transformEstimateToMailDataArgs(estimate);
// };
// /** /**
// * Retrieves the mail options. * Retrieves the mail options.
// * @param {number} tenantId * @param {number} saleEstimateId - Sale estimate id.
// * @param {number} saleEstimateId * @param {string} defaultSubject - Default subject.
// * @returns {Promise<SaleEstimateMailOptions>} * @param {string} defaultMessage - Default message.
// */ * @returns {Promise<SaleEstimateMailOptions>}
// public getMailOptions = async ( */
// tenantId: number, public getMailOptions = async (
// saleEstimateId: number, saleEstimateId: number,
// defaultSubject: string = DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT, defaultSubject: string = DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT,
// defaultMessage: string = DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT defaultMessage: string = DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT,
// ): Promise<SaleEstimateMailOptions> => { ): Promise<SaleEstimateMailOptions> => {
// const { SaleEstimate } = this.tenancy.models(tenantId); const saleEstimate = await this.saleEstimateModel
.query()
.findById(saleEstimateId)
.throwIfNotFound();
// const saleEstimate = await SaleEstimate.query() const formatArgs = await this.formatterArgs(saleEstimateId);
// .findById(saleEstimateId)
// .throwIfNotFound();
// const formatArgs = await this.formatterArgs(tenantId, saleEstimateId); const mailOptions =
await this.contactMailNotification.getDefaultMailOptions(
saleEstimate.customerId,
);
return {
...mailOptions,
message: defaultMessage,
subject: defaultSubject,
attachEstimate: true,
formatArgs,
};
};
// const mailOptions = /**
// await this.contactMailNotification.getDefaultMailOptions( * Formats the given mail options.
// tenantId, * @param {number} saleEstimateId - Sale estimate id.
// saleEstimate.customerId * @param {SaleEstimateMailOptions} mailOptions - Sale estimate mail options.
// ); * @returns {Promise<SaleEstimateMailOptions>}
// return { */
// ...mailOptions, public formatMailOptions = async (
// message: defaultMessage, saleEstimateId: number,
// subject: defaultSubject, mailOptions: SaleEstimateMailOptions,
// attachEstimate: true, ): Promise<SaleEstimateMailOptions> => {
// formatArgs, const formatterArgs = await this.formatterArgs(saleEstimateId);
// }; const formattedOptions =
// }; await this.contactMailNotification.formatMailOptions(
mailOptions,
formatterArgs,
);
return { ...formattedOptions };
};
// /** /**
// * Formats the given 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.
// * @param {SaleEstimateMailOptions} mailOptions * @returns {Promise<void>}
// * @returns {Promise<SaleEstimateMailOptions>} */
// */ public async sendMail(
// public formatMailOptions = async ( saleEstimateId: number,
// tenantId: number, messageOptions: SaleEstimateMailOptionsDTO,
// saleEstimateId: number, ): Promise<void> {
// mailOptions: SaleEstimateMailOptions const localMessageOpts = await this.getMailOptions(saleEstimateId);
// ): Promise<SaleEstimateMailOptions> => { // Overrides and validates the given mail options.
// const formatterArgs = await this.formatterArgs(tenantId, saleEstimateId); const parsedMessageOptions = mergeAndValidateMailOptions(
// const formattedOptions = localMessageOpts,
// await this.contactMailNotification.formatMailOptions( messageOptions,
// tenantId, ) as SaleEstimateMailOptions;
// mailOptions,
// formatterArgs
// );
// return { ...formattedOptions };
// };
// /** const formattedOptions = await this.formatMailOptions(
// * Sends the mail notification of the given sale estimate. saleEstimateId,
// * @param {number} tenantId parsedMessageOptions,
// * @param {number} saleEstimateId );
// * @param {SaleEstimateMailOptions} messageOptions const mail = new Mail()
// * @returns {Promise<void>} .setSubject(formattedOptions.subject)
// */ .setTo(formattedOptions.to)
// public async sendMail( .setCC(formattedOptions.cc)
// tenantId: number, .setBCC(formattedOptions.bcc)
// saleEstimateId: number, .setContent(formattedOptions.message);
// 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( // Attaches the estimate pdf to the mail.
// tenantId, if (formattedOptions.attachEstimate) {
// saleEstimateId, // Retrieves the estimate pdf and attaches it to the mail.
// parsedMessageOptions const [estimatePdfBuffer, estimateFilename] =
// ); await this.estimatePdf.getSaleEstimatePdf(saleEstimateId);
// 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. mail.setAttachments([
// if (formattedOptions.attachEstimate) { {
// // Retrieves the estimate pdf and attaches it to the mail. filename: `${estimateFilename}.pdf`,
// const [estimatePdfBuffer, estimateFilename] = content: estimatePdfBuffer,
// await this.estimatePdf.getSaleEstimatePdf(tenantId, saleEstimateId); },
]);
}
// mail.setAttachments([ const eventPayload = {
// { saleEstimateId,
// filename: `${estimateFilename}.pdf`, messageOptions,
// content: estimatePdfBuffer, formattedOptions,
// }, };
// ]); // Triggers `onSaleEstimateMailSend` event.
// } await this.eventPublisher.emitAsync(
events.saleEstimate.onMailSend,
eventPayload as ISaleEstimateMailPresendEvent,
);
await this.mailTransporter.send(mail);
// const eventPayload = { // Triggers `onSaleEstimateMailSent` event.
// tenantId, await this.eventPublisher.emitAsync(
// saleEstimateId, events.saleEstimate.onMailSent,
// messageOptions, eventPayload as ISaleEstimateMailPresendEvent,
// 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.
* @param {number} tenantId - Tenant id.
* @param {number} invoiceId - Sale invoice id.
* @param {string} text - The given text.
* @returns {Promise<string>}
*/
public getInvoiceFormatterArgs = async (
invoiceId: number,
): Promise<Record<string, string | number>> => {
const invoice = await this.getSaleInvoiceService.getSaleInvoice(invoiceId);
const commonArgs =
await this.contactMailNotification.getCommonFormatArgs(tenantId);
return {
...commonArgs,
'Customer Name': invoice.customer.displayName,
'Invoice Number': invoice.invoiceNo,
'Invoice Due Amount': invoice.dueAmountFormatted,
'Invoice Due Date': invoice.dueDateFormatted,
'Invoice Date': invoice.invoiceDateFormatted,
'Invoice Amount': invoice.totalFormatted,
'Overdue Days': invoice.overdueDays,
};
};
}
// /**
// * Retrieves the formatted text of the given sale invoice.
// * @param {number} tenantId - Tenant id.
// * @param {number} invoiceId - Sale invoice id.
// * @param {string} text - The given text.
// * @returns {Promise<string>}
// */
// public getInvoiceFormatterArgs = async (
// tenantId: number,
// invoiceId: number,
// ): Promise<Record<string, string | number>> => {
// const invoice = await this.getSaleInvoiceService.getSaleInvoice(
// tenantId,
// invoiceId,
// );
// const commonArgs =
// await this.contactMailNotification.getCommonFormatArgs(tenantId);
// return {
// ...commonArgs,
// 'Customer Name': invoice.customer.displayName,
// 'Invoice Number': invoice.invoiceNo,
// 'Invoice Due Amount': invoice.dueAmountFormatted,
// '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
// @Inject() * @param {number} saleInvoiceId
// private eventPublisher: EventPublisher; * @param {SendInvoiceMailDTO} messageDTO
*/
// @Inject('agenda') public async triggerMail(
// private agenda: any; saleInvoiceId: number,
messageOptions: SendInvoiceMailDTO,
// /** ) {
// * Sends the invoice mail of the given sale invoice. const payload = {
// * @param {number} tenantId saleInvoiceId,
// * @param {number} saleInvoiceId messageOptions,
// * @param {SendInvoiceMailDTO} messageDTO };
// */
// public async triggerMail(
// tenantId: number,
// saleInvoiceId: number,
// messageOptions: SendInvoiceMailDTO
// ) {
// const payload = {
// tenantId,
// saleInvoiceId,
// messageOptions,
// };
// await this.agenda.now('sale-invoice-mail-send', payload); // await this.agenda.now('sale-invoice-mail-send', payload);
// // Triggers the event `onSaleInvoicePreMailSend`. // Triggers the event `onSaleInvoicePreMailSend`.
// await this.eventPublisher.emitAsync(events.saleInvoice.onPreMailSend, { await this.eventEmitter.emitAsync(events.saleInvoice.onPreMailSend, {
// tenantId, saleInvoiceId,
// saleInvoiceId, messageOptions,
// messageOptions, } as ISaleInvoiceMailSend);
// } as ISaleInvoiceMailSend); }
// }
// /** /**
// * Retrieves the formatted mail options. * Retrieves the formatted mail options.
// * @param {number} tenantId * @param {number} saleInvoiceId
// * @param {number} saleInvoiceId * @param {SendInvoiceMailDTO} messageOptions
// * @param {SendInvoiceMailDTO} messageOptions * @returns {Promise<SaleInvoiceMailOptions>}
// * @returns {Promise<SaleInvoiceMailOptions>} */
// */ async getFormattedMailOptions(
// async getFormattedMailOptions( saleInvoiceId: number,
// tenantId: number, messageOptions: SendInvoiceMailDTO,
// saleInvoiceId: number, ): Promise<SaleInvoiceMailOptions> {
// messageOptions: SendInvoiceMailDTO const defaultMessageOptions =
// ): Promise<SaleInvoiceMailOptions> { await this.invoiceMail.getInvoiceMailOptions(saleInvoiceId);
// 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
// );
// }
// /** // Merges message options with default options and parses the options values.
// * Triggers the mail invoice. const parsedMessageOptions = mergeAndValidateMailOptions(
// * @param {number} tenantId defaultMessageOptions,
// * @param {number} saleInvoiceId messageOptions,
// * @param {SendInvoiceMailDTO} messageDTO );
// * @returns {Promise<void>} return this.invoiceMail.formatInvoiceMailOptions(
// */ saleInvoiceId,
// public async sendMail( parsedMessageOptions,
// tenantId: number, );
// 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. /**
// if (formattedMessageOptions.attachInvoice) { * Triggers the mail invoice.
// // Retrieves document buffer of the invoice pdf document. * @param {number} saleInvoiceId - Sale invoice id.
// const [invoicePdfBuffer, invoiceFilename] = * @param {SendInvoiceMailDTO} messageDTO - Message options.
// await this.invoicePdf.saleInvoicePdf(tenantId, saleInvoiceId); * @returns {Promise<void>}
*/
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);
// mail.setAttachments([ // Attach invoice document.
// { filename: `${invoiceFilename}.pdf`, content: invoicePdfBuffer }, if (formattedMessageOptions.attachInvoice) {
// ]); // Retrieves document buffer of the invoice pdf document.
// } const [invoicePdfBuffer, invoiceFilename] =
// const eventPayload = { await this.invoicePdf.getSaleInvoicePdf(saleInvoiceId);
// tenantId,
// saleInvoiceId,
// messageOptions,
// formattedMessageOptions,
// } as ISaleInvoiceMailSend;
// // Triggers the event `onSaleInvoiceSend`. mail.setAttachments([
// await this.eventPublisher.emitAsync( { filename: `${invoiceFilename}.pdf`, content: invoicePdfBuffer },
// events.saleInvoice.onMailSend, ]);
// eventPayload }
// ); const eventPayload = {
// await mail.send(); saleInvoiceId,
messageOptions,
formattedMessageOptions,
} as ISaleInvoiceMailSend;
// // Triggers the event `onSaleInvoiceSend`. // Triggers the event `onSaleInvoiceSend`.
// await this.eventPublisher.emitAsync( await this.eventEmitter.emitAsync(
// events.saleInvoice.onMailSent, events.saleInvoice.onMailSend,
// eventPayload eventPayload,
// ); );
// } await this.mailTransporter.send(mail);
// }
// Triggers the event `onSaleInvoiceSend`.
await this.eventEmitter.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
// @Inject() */
// private invoiceCommonMail: SendSaleInvoiceMailCommon; public async triggerMail(
saleInvoiceId: number,
// @Inject() messageOptions: SendInvoiceMailDTO,
// private eventPublisher: EventPublisher; ) {
const payload = {
// /** saleInvoiceId,
// * Triggers the reminder mail of the given sale invoice. messageOptions,
// * @param {number} tenantId };
// * @param {number} saleInvoiceId
// */
// public async triggerMail(
// tenantId: number,
// saleInvoiceId: number,
// messageOptions: SendInvoiceMailDTO
// ) {
// const payload = {
// tenantId,
// saleInvoiceId,
// messageOptions,
// };
// await this.agenda.now('sale-invoice-reminder-mail-send', payload); // await this.agenda.now('sale-invoice-reminder-mail-send', payload);
// } }
// /** /**
// * Retrieves the mail options of the given sale invoice. * Retrieves the mail options of the given sale invoice.
// * @param {number} tenantId * @param {number} saleInvoiceId - The sale invocie id.
// * @param {number} saleInvoiceId * @returns {Promise<SaleInvoiceMailOptions>}
// * @returns {Promise<SaleInvoiceMailOptions>} */
// */ public async getMailOption(saleInvoiceId: number) {
// public async getMailOption(tenantId: number, saleInvoiceId: number) { return this.invoiceCommonMail.getMailOption(
// return this.invoiceCommonMail.getMailOption( saleInvoiceId,
// tenantId, DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT,
// saleInvoiceId, DEFAULT_INVOICE_REMINDER_MAIL_CONTENT,
// DEFAULT_INVOICE_REMINDER_MAIL_SUBJECT, );
// DEFAULT_INVOICE_REMINDER_MAIL_CONTENT }
// );
// }
// /** /**
// * Triggers the mail invoice. * Triggers the mail invoice.
// * @param {number} tenantId * @param {number} saleInvoiceId - Sale invoice id.
// * @param {number} saleInvoiceId * @param {SendInvoiceMailDTO} messageOptions - The message options.
// * @param {SendInvoiceMailDTO} messageOptions * @returns {Promise<void>}
// * @returns {Promise<void>} */
// */ public async sendMail(
// public async sendMail( saleInvoiceId: number,
// tenantId: number, messageOptions: SendInvoiceMailDTO,
// saleInvoiceId: number, ) {
// messageOptions: SendInvoiceMailDTO const localMessageOpts = await this.getMailOption(saleInvoiceId);
// ) {
// const localMessageOpts = await this.getMailOption(tenantId, saleInvoiceId);
// const messageOpts = parseAndValidateMailOptions( const messageOpts = mergeAndValidateMailOptions(
// localMessageOpts, localMessageOpts,
// messageOptions messageOptions,
// ); );
// const mail = new Mail() const mail = new Mail()
// .setSubject(messageOpts.subject) .setSubject(messageOpts.subject)
// .setTo(messageOpts.to) .setTo(messageOpts.to)
// .setContent(messageOpts.body); .setContent(messageOpts.body);
// if (messageOpts.attachInvoice) { if (messageOpts.attachInvoice) {
// // Retrieves document buffer of the invoice pdf document. // Retrieves document buffer of the invoice pdf document.
// const invoicePdfBuffer = await this.invoicePdf.saleInvoicePdf( const [invoicePdfBuffer, filename] = await this.invoicePdf.getSaleInvoicePdf(
// tenantId, saleInvoiceId,
// saleInvoiceId );
// ); mail.setAttachments([
// mail.setAttachments([ { filename, content: invoicePdfBuffer },
// { filename: 'invoice.pdf', content: invoicePdfBuffer }, ]);
// ]); }
// } // Triggers the event `onSaleInvoiceSend`.
// // Triggers the event `onSaleInvoiceSend`. await this.eventEmitter.emitAsync(events.saleInvoice.onMailReminderSend, {
// await this.eventPublisher.emitAsync(events.saleInvoice.onMailReminderSend, { saleInvoiceId,
// saleInvoiceId, messageOptions,
// messageOptions, } as ISaleInvoiceMailSend);
// } as ISaleInvoiceMailSend);
// await mail.send(); await this.mailTransporter.send(mail);
// // Triggers the event `onSaleInvoiceSent`. // Triggers the event `onSaleInvoiceSent`.
// await this.eventPublisher.emitAsync(events.saleInvoice.onMailReminderSent, { await this.eventEmitter.emitAsync(events.saleInvoice.onMailReminderSent, {
// saleInvoiceId, saleInvoiceId,
// messageOptions, messageOptions,
// } as ISaleInvoiceMailSent); } 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
// @Inject() * @param {number} saleReceiptId
// private receiptPdfService: SaleReceiptsPdf; * @param {SaleReceiptMailOptsDTO} messageDTO
*/
// @Inject() public async triggerMail(
// private contactMailNotification: ContactMailNotification; saleReceiptId: number,
messageOptions: SaleReceiptMailOptsDTO,
// @Inject() ) {
// private eventPublisher: EventPublisher; const payload = {
saleReceiptId,
// @Inject('agenda') messageOpts: messageOptions,
// private agenda: any; };
// /**
// * 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); // await this.agenda.now('sale-receipt-mail-send', payload);
// // Triggers the event `onSaleReceiptPreMailSend`. // Triggers the event `onSaleReceiptPreMailSend`.
// await this.eventPublisher.emitAsync(events.saleReceipt.onPreMailSend, { await this.eventEmitter.emitAsync(events.saleReceipt.onPreMailSend, {
// tenantId, saleReceiptId,
// saleReceiptId, messageOptions,
// messageOptions, } as ISaleReceiptMailPresend);
// } as ISaleReceiptMailPresend); }
// }
// /** /**
// * Retrieves the mail options of the given sale receipt. * Retrieves the mail options of the given sale receipt.
// * @param {number} tenantId * @param {number} saleReceiptId
// * @param {number} saleReceiptId * @returns {Promise<SaleReceiptMailOptsDTO>}
// * @returns {Promise<SaleReceiptMailOptsDTO>} */
// */ public async getMailOptions(
// public async getMailOptions( saleReceiptId: number,
// tenantId: number, ): Promise<SaleReceiptMailOpts> {
// saleReceiptId: number const saleReceipt = await SaleReceipt.query()
// ): Promise<SaleReceiptMailOpts> { .findById(saleReceiptId)
// const { SaleReceipt } = this.tenancy.models(tenantId); .throwIfNotFound();
// const saleReceipt = await SaleReceipt.query() const formatArgs = await this.textFormatterArgs(saleReceiptId);
// .findById(saleReceiptId) const mailOptions =
// .throwIfNotFound(); await this.contactMailNotification.getDefaultMailOptions(
saleReceipt.customerId,
);
return {
...mailOptions,
message: DEFAULT_RECEIPT_MAIL_CONTENT,
subject: DEFAULT_RECEIPT_MAIL_SUBJECT,
attachReceipt: true,
formatArgs,
};
}
// const formatArgs = await this.textFormatterArgs(tenantId, saleReceiptId); /**
* 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);
// const mailOptions = return transformReceiptToMailDataArgs(receipt);
// await this.contactMailNotification.getDefaultMailOptions( };
// tenantId,
// saleReceipt.customerId
// );
// return {
// ...mailOptions,
// message: DEFAULT_RECEIPT_MAIL_CONTENT,
// subject: DEFAULT_RECEIPT_MAIL_SUBJECT,
// attachReceipt: true,
// formatArgs,
// };
// }
// /** /**
// * Retrieves the formatted text of the given sale receipt. * Formats the mail options of the given sale receipt.
// * @param {number} tenantId - Tenant id. * @param {number} tenantId
// * @param {number} receiptId - Sale receipt id. * @param {number} receiptId
// * @param {string} text - The given text. * @param {SaleReceiptMailOpts} mailOptions
// * @returns {Promise<string>} * @returns {Promise<SaleReceiptMailOpts>}
// */ */
// public textFormatterArgs = async ( public async formatEstimateMailOptions(
// tenantId: number, receiptId: number,
// receiptId: number mailOptions: SaleReceiptMailOpts,
// ): Promise<Record<string, string>> => { ): Promise<SaleReceiptMailOpts> {
// const receipt = await this.getSaleReceiptService.getSaleReceipt( const formatterArgs = await this.textFormatterArgs(receiptId);
// tenantId, const formattedOptions =
// receiptId (await this.contactMailNotification.formatMailOptions(
// ); mailOptions,
// return transformReceiptToMailDataArgs(receipt); formatterArgs,
// }; )) as SaleReceiptMailOpts;
return formattedOptions;
}
// /** /**
// * Formats 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} receiptId * @param {number} saleReceiptId
// * @param {SaleReceiptMailOpts} mailOptions * @param {SaleReceiptMailOptsDTO} messageOpts
// * @returns {Promise<SaleReceiptMailOpts>} * @returns {Promise<SaleReceiptMailOpts>}
// */ */
// public async formatEstimateMailOptions( public getFormatMailOptions = async (
// tenantId: number, saleReceiptId: number,
// receiptId: number, messageOpts: SaleReceiptMailOptsDTO,
// mailOptions: SaleReceiptMailOpts ): Promise<SaleReceiptMailOpts> => {
// ): Promise<SaleReceiptMailOpts> { const defaultMessageOptions = await this.getMailOptions(saleReceiptId);
// const formatterArgs = await this.textFormatterArgs(tenantId, receiptId); // Merges message opts with default options.
// const formattedOptions = const parsedMessageOpts = mergeAndValidateMailOptions(
// (await this.contactMailNotification.formatMailOptions( defaultMessageOptions,
// tenantId, messageOpts,
// mailOptions, ) as SaleReceiptMailOpts;
// formatterArgs
// )) as SaleReceiptMailOpts;
// return formattedOptions;
// }
// /** // Formats the message options.
// * Retrieves the formatted mail options of the given sale receipt. return this.formatEstimateMailOptions(saleReceiptId, parsedMessageOpts);
// * @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( * Triggers the mail notification of the given sale receipt.
// tenantId, * @param {number} saleReceiptId - Sale receipt id.
// saleReceiptId, * @param {SaleReceiptMailOpts} messageDTO - message options.
// parsedMessageOpts * @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);
// /** // Attaches the receipt pdf document.
// * Triggers the mail notification of the given sale receipt. if (formattedMessageOptions.attachReceipt) {
// * @param {number} tenantId - Tenant id. // Retrieves document buffer of the receipt pdf document.
// * @param {number} saleReceiptId - Sale receipt id. const [receiptPdfBuffer, filename] =
// * @param {SaleReceiptMailOpts} messageDTO - message options. await this.receiptPdfService.saleReceiptPdf(saleReceiptId);
// * @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. mail.setAttachments([
// if (formattedMessageOptions.attachReceipt) { { filename: `${filename}.pdf`, content: receiptPdfBuffer },
// // Retrieves document buffer of the receipt pdf document. ]);
// const [receiptPdfBuffer, filename] = }
// await this.receiptPdfService.saleReceiptPdf(tenantId, saleReceiptId); const eventPayload = {
saleReceiptId,
messageOptions: {},
};
await this.eventEmitter.emitAsync(
events.saleReceipt.onMailSend,
eventPayload,
);
await this.mailTransporter.send(mail);
// mail.setAttachments([ await this.eventEmitter.emitAsync(
// { filename: `${filename}.pdf`, content: receiptPdfBuffer }, events.saleReceipt.onMailSent,
// ]); eventPayload,
// } );
// 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 -