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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ import { Knex } from 'knex';
import { DeleteCashflowTransaction } from './commands/DeleteCashflowTransaction.service';
import { CreateBankTransactionService } from './commands/CreateBankTransaction.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 { GetBankAccountsService } from './queries/GetBankAccounts.service';
@@ -48,9 +48,9 @@ export class BankingTransactionsApplication {
/**
* Retrieves the cashflow accounts.
* @param {ICashflowAccountsFilter} filterDTO
* @param {IBankAccountsFilter} filterDTO
*/
public getBankAccounts(filterDTO: ICashflowAccountsFilter) {
public getBankAccounts(filterDTO: IBankAccountsFilter) {
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 { CashflowAccountTransformer } from './BankAccountTransformer';
import { ACCOUNT_TYPE } from '@/constants/accounts';
import { IBankAccountsFilter } from '../types/BankingTransactions.types';
@Injectable()
export class GetBankAccountsService {
@@ -12,7 +13,7 @@ export class GetBankAccountsService {
private readonly transformer: TransformerInjectable,
@Inject(BankAccount.name)
private readonly bankAccountModel: typeof BankAccount
private readonly bankAccountModel: typeof BankAccount,
) {}
/**
@@ -21,7 +22,7 @@ export class GetBankAccountsService {
* @returns {ICashflowAccount[]}
*/
public async getBankAccounts(
filterDTO: ICashflowAccountsFilter,
filterDTO: IBankAccountsFilter,
): Promise<BankAccount[]> {
// Parsees accounts list filter DTO.
const filter = this.dynamicListService.parseStringifiedFilter(filterDTO);

View File

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

View File

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

View File

@@ -1,14 +1,14 @@
import { CreateBill } from './commands/CreateBill.service';
import { EditBillService } from './commands/EditBill.service';
import { GetBill } from './queries/GetBill';
// import { GetBills } from './queries/GetBills';
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 { OpenBillService } from './commands/OpenBill.service';
import { GetBillPayments } from './queries/GetBillPayments';
import { Injectable } from '@nestjs/common';
import { GetBillsService } from './queries/GetBills.service';
// import { GetBillPayments } from './queries/GetBillPayments';
// import { GetBills } from './queries/GetBills';
@Injectable()
export class BillsApplication {

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
import * as R from 'ramda';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.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 { CreditNoteTransformer } from './CreditNoteTransformer';
import { Inject } from '@nestjs/common';

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import { Model, raw } from 'objection';
import { castArray } from 'lodash';
import moment from 'moment';
import moment, { unitOfTime } from 'moment';
import { BaseModel } from '@/models/Model';
import { getTransactionTypeLabel } from '@/modules/BankingTransactions/utils';
import { TInventoryTransactionDirection } from '../types/InventoryCost.types';
@@ -48,7 +48,12 @@ export class InventoryTransaction extends BaseModel {
*/
static get modifiers() {
return {
filterDateRange(query, startDate, endDate, type = 'day') {
filterDateRange(
query,
startDate,
endDate,
type: unitOfTime.StartOf = 'day',
) {
const dateFormat = 'YYYY-MM-DD';
const fromDate = moment(startDate).startOf(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 { ManualJournal } from '../models/ManualJournal';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { IManualJournalsFilter } from '../types/ManualJournals.types';
@Injectable()
export class GetManualJournals {
@@ -26,7 +27,6 @@ export class GetManualJournals {
/**
* Retrieve manual journals datatable list.
* @param {number} tenantId -
* @param {IManualJournalsFilter} filter -
*/
public getManualJournals = async (
@@ -44,7 +44,8 @@ export class GetManualJournals {
ManualJournal,
filter,
);
const { results, pagination } = await this.manualJournalModel.query()
const { results, pagination } = await this.manualJournalModel
.query()
.onBuild((builder) => {
dynamicService.buildQuery()(builder);
builder.withGraphFetched('entries.account');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,129 +1,117 @@
// import { Inject, Service } from 'typedi';
// import { SaleInvoiceMailOptions } from '@/interfaces';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import { GetSaleInvoice } from '../queries/GetSaleInvoice.service';
// import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification';
// import {
// DEFAULT_INVOICE_MAIL_CONTENT,
// DEFAULT_INVOICE_MAIL_SUBJECT,
// } from '../constants';
// import { GetInvoicePaymentMail } from '../queries/GetInvoicePaymentMail.service';
// import { GenerateShareLink } from './GenerateInvoicePaymentLink.service';
import { GetSaleInvoice } from '../queries/GetSaleInvoice.service';
import {
DEFAULT_INVOICE_MAIL_CONTENT,
DEFAULT_INVOICE_MAIL_SUBJECT,
} from '../constants';
import { GetInvoicePaymentMail } from '../queries/GetInvoicePaymentMail.service';
import { GenerateShareLink } from './GenerateInvoicePaymentLink.service';
import { Inject, Injectable } from '@nestjs/common';
import { SaleInvoice } from '../models/SaleInvoice';
import { ContactMailNotification } from '@/modules/MailNotification/ContactMailNotification';
// @Service()
// export class SendSaleInvoiceMailCommon {
// constructor(
// private getSaleInvoiceService: GetSaleInvoice,
// private contactMailNotification: ContactMailNotification,
// private getInvoicePaymentMail: GetInvoicePaymentMail,
// private generatePaymentLinkService: GenerateShareLink,
// ) {}
@Injectable()
export class SendSaleInvoiceMailCommon {
constructor(
private getSaleInvoiceService: GetSaleInvoice,
private contactMailNotification: ContactMailNotification,
private getInvoicePaymentMail: GetInvoicePaymentMail,
private generatePaymentLinkService: GenerateShareLink,
// /**
// * Retrieves the mail options.
// * @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);
@Inject(SaleInvoice.name)
private readonly saleInvoiceModel: typeof SaleInvoice,
) {}
// const saleInvoice = await SaleInvoice.query()
// .findById(invoiceId)
// .throwIfNotFound();
/**
* Retrieves the mail options.
* @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 =
// await this.contactMailNotification.getDefaultMailOptions(
// tenantId,
// saleInvoice.customerId,
// );
// const formatArgs = await this.getInvoiceFormatterArgs(tenantId, invoiceId);
const contactMailDefaultOptions =
await this.contactMailNotification.getDefaultMailOptions(
saleInvoice.customerId,
);
const formatArgs = await this.getInvoiceFormatterArgs(invoiceId);
// return {
// ...contactMailDefaultOptions,
// subject: defaultSubject,
// message: defaultMessage,
// attachInvoice: true,
// formatArgs,
// };
// }
return {
...contactMailDefaultOptions,
subject: defaultSubject,
message: defaultMessage,
attachInvoice: true,
formatArgs,
};
}
// /**
// * Formats the given invoice mail options.
// * @param {number} tenantId
// * @param {number} invoiceId
// * @param {SaleInvoiceMailOptions} mailOptions
// * @returns {Promise<SaleInvoiceMailOptions>}
// */
// public async formatInvoiceMailOptions(
// tenantId: number,
// invoiceId: number,
// mailOptions: SaleInvoiceMailOptions,
// ): Promise<SaleInvoiceMailOptions> {
// const formatterArgs = await this.getInvoiceFormatterArgs(
// tenantId,
// invoiceId,
// );
// const formattedOptions =
// await this.contactMailNotification.formatMailOptions(
// tenantId,
// mailOptions,
// formatterArgs,
// );
// // Generates the a new payment link for the given invoice.
// const paymentLink =
// await this.generatePaymentLinkService.generatePaymentLink(
// tenantId,
// invoiceId,
// 'public',
// );
// const message = await this.getInvoicePaymentMail.getMailTemplate(
// tenantId,
// invoiceId,
// {
// // # Invoice message
// invoiceMessage: formattedOptions.message,
// preview: formattedOptions.message,
/**
* Formats the given invoice mail options.
* @param {number} invoiceId
* @param {SaleInvoiceMailOptions} mailOptions
* @returns {Promise<SaleInvoiceMailOptions>}
*/
public async formatInvoiceMailOptions(
invoiceId: number,
mailOptions: SaleInvoiceMailOptions,
): Promise<SaleInvoiceMailOptions> {
const formatterArgs = await this.getInvoiceFormatterArgs(invoiceId);
const formattedOptions =
await this.contactMailNotification.formatMailOptions(
mailOptions,
formatterArgs,
);
// Generates the a new payment link for the given invoice.
const paymentLink =
await this.generatePaymentLinkService.generatePaymentLink(
invoiceId,
'public',
);
const message = await this.getInvoicePaymentMail.getMailTemplate(
invoiceId,
{
// # Invoice message
invoiceMessage: formattedOptions.message,
preview: formattedOptions.message,
// // # Payment link
// viewInvoiceButtonUrl: paymentLink.link,
// },
// );
// return { ...formattedOptions, message };
// }
// # Payment link
viewInvoiceButtonUrl: paymentLink.link,
},
);
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 (
// 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,
// };
// };
// }
/**
* 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,
};
};
}

View File

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

View File

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

View File

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

View File

@@ -1,80 +1,65 @@
// import { Inject, Service } from 'typedi';
// import * as R from 'ramda';
// import {
// IFilterMeta,
// IPaginationMeta,
// ISaleInvoice,
// ISalesInvoicesFilter,
// } from '@/interfaces';
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
// import HasTenancyService from '@/services/Tenancy/TenancyService';
// import DynamicListingService from '@/services/DynamicListing/DynamicListService';
// import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
import * as R from 'ramda';
import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
import { Injectable } from '@nestjs/common';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { SaleInvoice } from '../models/SaleInvoice';
import { ISalesInvoicesFilter } from '../SaleInvoice.types';
// @Service()
// export class GetSaleInvoices {
// @Inject()
// private tenancy: HasTenancyService;
@Injectable()
export class GetSaleInvoicesService {
constructor(
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()
// private transformer: TransformerInjectable;
// Dynamic list service.
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);
// /**
// * Retrieve sales invoices filterable and paginated list.
// * @param {Request} req
// * @param {Response} res
// * @param {NextFunction} next
// */
// public async getSaleInvoices(
// filterDTO: ISalesInvoicesFilter
// ): Promise<{
// salesInvoices: ISaleInvoice[];
// pagination: IPaginationMeta;
// filterMeta: IFilterMeta;
// }> {
// const { SaleInvoice } = this.tenancy.models(tenantId);
// Retrieves the transformed sale invoices.
const salesInvoices = await this.transformer.transform(
results,
new SaleInvoiceTransformer(),
);
// // Parses stringified filter roles.
// const filter = this.parseListFilterDTO(filterDTO);
return {
salesInvoices,
pagination,
filterMeta: dynamicFilter.getResponseMeta(),
};
}
// // Dynamic list service.
// const dynamicFilter = await this.dynamicListService.dynamicList(
// tenantId,
// 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.
// 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);
// }
// }
/**
* 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,
ISaleReceiptState,
ISalesReceiptsFilter,
SaleReceiptMailOpts,
} from './types/SaleReceipts.types';
import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service';
import { SaleReceipt } from './models/SaleReceipt';
import { IPaginationMeta } from '@/interfaces/Model';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { SaleReceiptMailNotification } from './commands/SaleReceiptMailNotification';
@Injectable()
export class SaleReceiptApplication {
@@ -28,8 +30,8 @@ export class SaleReceiptApplication {
private closeSaleReceiptService: CloseSaleReceipt,
private getSaleReceiptPdfService: SaleReceiptsPdfService,
// private saleReceiptNotifyBySmsService: SaleReceiptNotifyBySms,
// private saleReceiptNotifyByMailService: SaleReceiptMailNotification,
private getSaleReceiptStateService: GetSaleReceiptState,
private saleReceiptNotifyByMailService: SaleReceiptMailNotification,
) {}
/**
@@ -158,19 +160,14 @@ export class SaleReceiptApplication {
/**
* Retrieves the default mail options of the given sale receipt.
* @param {number} tenantId
* @param {number} saleReceiptId
* @param {number} saleReceiptId - Sale receipt identifier.
* @returns {Promise<SaleReceiptMailOpts>}
*/
// public getSaleReceiptMail(
// tenantId: number,
// saleReceiptId: number,
// ): Promise<SaleReceiptMailOpts> {
// return this.saleReceiptNotifyByMailService.getMailOptions(
// tenantId,
// saleReceiptId,
// );
// }
public getSaleReceiptMail(
saleReceiptId: number,
): Promise<SaleReceiptMailOpts> {
return this.saleReceiptNotifyByMailService.getMailOptions(saleReceiptId);
}
/**
* 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 { SaleReceiptInventoryTransactionsSubscriber } from './inventory/SaleReceiptWriteInventoryTransactions';
import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service';
import { SaleReceiptMailNotification } from './commands/SaleReceiptMailNotification';
@Module({
controllers: [SaleReceiptsController],
@@ -57,7 +58,8 @@ import { GetSaleReceiptsService } from './queries/GetSaleReceipts.service';
SaleReceiptGLEntries,
SaleReceiptGLEntriesSubscriber,
SaleReceiptInventoryTransactionsSubscriber,
GetSaleReceiptsService
GetSaleReceiptsService,
SaleReceiptMailNotification
],
})
export class SaleReceiptsModule {}

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ import { CreateVendorCreditService } from './commands/CreateVendorCredit.service
import { DeleteVendorCreditService } from './commands/DeleteVendorCredit.service';
import { EditVendorCreditService } from './commands/EditVendorCredit.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 { Injectable } from '@nestjs/common';
import { OpenVendorCreditService } from './commands/OpenVendorCredit.service';

View File

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

View File

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

View File

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

View File

@@ -13,6 +13,7 @@ import {
IVendorEditDTO,
IVendorNewDTO,
IVendorOpeningBalanceEditDTO,
IVendorsFilter,
} from './types/Vendors.types';
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 { Container } from 'typedi';
import path from 'path';
import { IMailAttachment } from '@/interfaces';
@@ -40,23 +39,6 @@ export default class Mail {
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.
* @param {string} to -