Skip to content
This repository was archived by the owner on Oct 6, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { Invoice } from './classes/invoice';
import { httpServer } from './server'
import { MailService } from './services/mail-service'
import { Env } from './utils/env';
import { startIntervalsOverdue } from './services/invoice-service';
import { Logger } from './utils/logger';

if (process.env.NODE_ENV == null || process.env.NODE_ENV === 'develepmont') {
dotenv.config();
dotenv.config();
}

try {
Env.validateMandatoryKeys();
} catch (err) {
console.error('.env not properly configured: ', err);
Logger.error('.env not properly configured: ', err);
process.exit(-1);
}

Expand All @@ -22,5 +23,6 @@ try {

const PORT: any = process.env.PORT || 6060;
httpServer.listen(PORT, () => Logger.info(`The server is running on port ${PORT}`));

//TODO move to schedule
startIntervalsOverdue();
})();
9 changes: 9 additions & 0 deletions src/classes/invoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ export interface Invoice {
period_end: Date
}

//move to db table?
export enum InvoiceStatus {
sent,
paid,
overdue,
generated,
notGenerated
}

export const invoiceSchema = Joi.object({
invoice_id: Joi.number().integer().min(0).required(),
contract_id: Joi.number().integer().min(0).required(),
Expand Down
17 changes: 16 additions & 1 deletion src/controllers/invoice-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,19 @@ export const getInvoiceByUserId: RequestHandler = async (req: Request, res: Resp
message: 'There was an error when fetching invoices'
});
}
}
};

//TODO add authentication
export const getOverdueInvoices: RequestHandler = async (req: Request, res: Response) => {
try {
const invoice = await invoiceService.getOverdueInvoices();
res.status(200).json(invoice);
} catch (error) {

console.log(error);
res.status(500).json({
message: 'There was an error when fetching overdue invoices'
});
}
};

1 change: 1 addition & 0 deletions src/routes/invoice-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { UserRole } from '../models/userrole';

const router = express.Router();

router.get('/overdue', auth.authenticate([UserRole.ACCOUNTANT, UserRole.ADMIN]), invoiceController.getOverdueInvoices);
router.get('/', auth.authenticate([UserRole.ADMIN, UserRole.ACCOUNTANT]), invoiceController.getAllInvoices);
router.get('/:id/pdf', invoiceController.getInvoicePdfById);
router.get('/:id', auth.authenticate([UserRole.ADMIN, UserRole.ACCOUNTANT]), invoiceController.getInvoiceById);
Expand Down
12 changes: 0 additions & 12 deletions src/routes/invoice.ts

This file was deleted.

8 changes: 0 additions & 8 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,6 @@ if (process.env.NODE_ENV == null || process.env.NODE_ENV === 'development') {
dotenv.config();
}

try {
Env.validateMandatoryKeys();
} catch (err) {
console.error('.env not properly configured: ', err);
process.exit(-1);
}


const router: Express = express();

/** Middleware for CORS */
Expand Down
45 changes: 38 additions & 7 deletions src/services/invoice-service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {execute} from "../utils/mysql.connector";
import {Invoice} from "../classes/invoice";
import {invoiceQueries} from "../queries/invoice-queries";
import {Contract} from "../classes/contracts";
import {InvoicePdf} from "../classes/invoice-pdf";
import { execute } from "../utils/mysql.connector";
import { Invoice, InvoiceStatus } from "../classes/invoice";
import { invoiceQueries } from "../queries/invoice-queries";
import { setInterval } from "timers";
import { MailService } from "./mail-service";
import { Contract } from "../classes/contracts";
import { InvoicePdf } from "../classes/invoice-pdf";

export const getAllInvoices = async () => {
return await execute<Invoice[]>(invoiceQueries.getAllInvoices, [], "rows");
Expand All @@ -13,8 +15,8 @@ export const getInvoiceById = async (id: Invoice['invoice_id']) => {
return invoices[0];
};

export const getInvoiceByPeriod = async (id: Invoice['contract_id'],periodStart: Invoice['period_start'], periodEnd: Invoice['period_end']) => {
const invoices = await execute<Invoice[]>(invoiceQueries.getInvoiceByPeriod, [periodStart,periodEnd], "rows");
export const getInvoiceByPeriod = async (id: Invoice['contract_id'], periodStart: Invoice['period_start'], periodEnd: Invoice['period_end']) => {
const invoices = await execute<Invoice[]>(invoiceQueries.getInvoiceByPeriod, [periodStart, periodEnd], "rows");
return invoices[0];
};

Expand Down Expand Up @@ -79,3 +81,32 @@ export const getInvoicePdfData = async (id: Invoice['invoice_id']) => {
export const getInvoiceByUserId = async (id: Invoice['invoice_id']) => {
return await execute<Invoice[]>(invoiceQueries.getInvoiceByUserId, [id], "rows");
};

//this gives all the overdue invoices even if status id isn't InvoiceStatus.overdue
export const getOverdueInvoices = async () => {
const query = `SELECT * FROM invoices WHERE status_id = $1;`;
const invoicesSql = await execute<{ rows: Invoice[] }>(query, [InvoiceStatus.overdue])
.catch(e => console.log("overdue invoices error: ", e));

if (!invoicesSql || !invoicesSql.rows)
return undefined
console.log(invoicesSql.rows.length)
return invoicesSql.rows;
};

//TODO use scheduler (invoice branch)
export async function setOverdue() {
const querySelect = `select * from "invoices" WHERE "status_id" = $1 AND "due_date" <= $2`;
const queryUpdate = `UPDATE "invoices" SET "status_id" = $1 WHERE "status_id" = $2 AND "due_date" <= $3`
const invoicesSql = await execute<{ rows: Invoice[] }>(querySelect, [InvoiceStatus.sent, new Date().toISOString()]);
if(!invoicesSql)
return undefined
execute<unknown>(queryUpdate, [InvoiceStatus.overdue,InvoiceStatus.sent, new Date().toISOString()]).catch(e => console.log(e));

const ms = new MailService();
invoicesSql.rows.forEach(o => ms.overdueInvoice(o));
}

export async function startIntervalsOverdue() {
setInterval(setOverdue, 10000);
}
109 changes: 67 additions & 42 deletions src/services/mail-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class MailService {
private transport: Transporter<SMTPTransport.SentMessageInfo>;
private hostEmail: string;
private from : string;

private endMail = `Best regards,\n${mailConfig.company}`;
//supposed to be gotten from the data base
private customer: Customer = {
Expand All @@ -18,7 +18,7 @@ export class MailService {
lastName: "rohan48",
email: "AD0830686@PE2022.com"
};

constructor() {
this.checkEnv()
this.hostEmail = process.env.MAILSERVER_U + "@" + mailConfig.domain;
Expand All @@ -28,58 +28,57 @@ export class MailService {
//console.log(this.transport);
this.verify()
};



private checkEnv() {
if (!process.env.MAILSERVER_P)
throw new Error("Please define MAILSERVER_U in your env file");

throw new Error("Please define MAILSERVER_U in your env file");
if (!process.env.MAILSERVER_U)
throw new Error("Please define MAILSERVER_P in your env file");
}

private createTrans() {
//using https://ethereal.email includes authentication, testing
//mailserver doesn't have authentication (no TLS)
return createTransport({
from: this.from,
host: mailConfig.host,
port: mailConfig.port,
auth: {
user: this.hostEmail,
pass: process.env.MAILSERVER_P
},
//logger: true,
//transactionLog: true,
secure: false,
requireTLS: false
})
}

private async verify(): Promise<void> {
if (!(await this.transport.verify()))
}
private createTrans() {
//using https://ethereal.email includes authentication, testing
//mailserver doesn't have authentication (no TLS)
return createTransport({
from: this.from,
host: mailConfig.host,
port: mailConfig.port,
auth: {
user: this.hostEmail,
pass: process.env.MAILSERVER_P
},
//logger: true,
//transactionLog: true,
secure: false,
requireTLS: false
})
}
private async verify(): Promise<void> {
if (!(await this.transport.verify()))
throw new Error("transport in mailService isn't valid");
}

public sendInvoice(invoice: Invoice): SentMessageInfo {
//info from db
//TODO Get customer data from db
const title = `Dear ${this.customer.firstName} ${this.customer.lastName}`;
const body = [
`your invoice ${invoice.InvoiceID} of ${invoice.Price} is due at ${invoice.DueDate.toDateString()} Please pay this as soon as possible.`,
}

public sendInvoice(invoice: Invoice): SentMessageInfo {
//TODO Get customer data from db
const title = `Dear ${this.customer.firstName} ${this.customer.lastName}`;
const body = [
`your invoice ${invoice.invoice_id} of ${invoice.price} is due at ${invoice.due_date.toDateString()} Please pay this as soon as possible.`,
"if you wish to see more details and/or pay please visit <a href='https://templates.office.com/en-us/Invoices'>this link</a>"
];

return this.transport.sendMail({
from: this.from,
to: this.customer.email,
subject: `Invoice: ${invoice.InvoiceID}`,
subject: `Invoice: ${invoice.invoice_id}`,
text: this.textFormat(title, body),
html: this.htmlFormat(title, body),
}).catch((e) => { Logger.error(e); })
}

public sendWorkOrder(/*employee: Employee | User*/): SentMessageInfo{
const employee = this.customer;
//info from db
Expand All @@ -97,7 +96,7 @@ export class MailService {
html: this.htmlFormat(title, body),
}).catch((e) => { Logger.error(e); });
}

public sendAppointment(/*appointment: Appointment*/): SentMessageInfo{
const Customer = this.customer;
//info from db
Expand All @@ -116,6 +115,30 @@ export class MailService {
}).catch((e) => { Logger.error(e); });
}

overdueInvoice(o: Invoice) : SentMessageInfo{
const Customer = this.customer;
//info from db
const title = `Dear ${Customer.firstName} ${Customer.lastName}`;
const body = [
`Your invoice ${o.invoice_id} is overdue`,
`Please pay the invoice at <a href="./invoice">this link</a>.`,
`<br>`,
`Invoice: ${o.invoice_id}`,
`date: ${o.creation_date}`,
`price: ${o.price}`,
];

console.log(o);

return this.transport.sendMail({
from: this.from,
to: this.customer.email,
subject: `Invoice: ${o.invoice_id}`,
text: this.textFormat(title, body),
html: this.htmlFormat(title, body),
}).catch((e) => { Logger.error(e); });
}

textFormat(title: string, body: string[]): string {
let text = title;
for (const b of body) {
Expand All @@ -124,7 +147,7 @@ export class MailService {
text += "\n\n" + this.endMail;
return text;
}

htmlFormat(title: string, body: string[]): string {
let text = "<p>" + title + "</p>";
for (const b of body) {
Expand All @@ -134,5 +157,7 @@ export class MailService {
text += "<p>" + this.endMail.replace("\n", "<br>") + "</p>";
return text;
}


};