Skip to content

Commit

Permalink
Add supplier contracts to api
Browse files Browse the repository at this point in the history
  • Loading branch information
aelassas committed Nov 17, 2024
1 parent fefe30a commit a9836e5
Show file tree
Hide file tree
Showing 22 changed files with 560 additions and 75 deletions.
2 changes: 2 additions & 0 deletions api/.env.docker.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ BC_CDN_CARS=/var/www/cdn/bookcars/cars
BC_CDN_TEMP_CARS=/var/www/cdn/bookcars/temp/cars
BC_CDN_LOCATIONS=/var/www/cdn/bookcars/locations
BC_CDN_TEMP_LOCATIONS=/var/www/cdn/bookcars/temp/locations
BC_CDN_CONTRACTS=/var/www/cdn/bookcars/contracts
BC_CDN_TEMP_CONTRACTS=/var/www/cdn/bookcars/temp/contracts
BC_DEFAULT_LANGUAGE=en
BC_BACKEND_HOST=http://localhost:3001/
BC_FRONTEND_HOST=http://localhost/
Expand Down
2 changes: 2 additions & 0 deletions api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ BC_CDN_CARS=/var/www/cdn/bookcars/cars
BC_CDN_TEMP_CARS=/var/www/cdn/bookcars/temp/cars
BC_CDN_LOCATIONS=/var/www/cdn/bookcars/locations
BC_CDN_TEMP_LOCATIONS=/var/www/cdn/bookcars/temp/locations
BC_CDN_CONTRACTS=/var/www/cdn/bookcars/contracts
BC_CDN_TEMP_CONTRACTS=/var/www/cdn/bookcars/temp/contracts
BC_DEFAULT_LANGUAGE=en
BC_BACKEND_HOST=http://localhost:3001/
BC_FRONTEND_HOST=http://localhost:3002/
Expand Down
2 changes: 2 additions & 0 deletions api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,7 @@ await helper.mkdir(env.CDN_CARS)
await helper.mkdir(env.CDN_TEMP_CARS)
await helper.mkdir(env.CDN_LOCATIONS)
await helper.mkdir(env.CDN_TEMP_LOCATIONS)
await helper.mkdir(env.CDN_CONTRACTS)
await helper.mkdir(env.CDN_TEMP_CONTRACTS)

export default app
15 changes: 15 additions & 0 deletions api/src/config/env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,20 @@ export const CDN_LOCATIONS = __env__('BC_CDN_LOCATIONS', true)
*/
export const CDN_TEMP_LOCATIONS = __env__('BC_CDN_TEMP_LOCATIONS', true)

/**
* Contracts' cdn folder path.
*
* @type {string}
*/
export const CDN_CONTRACTS = __env__('BC_CDN_CONTRACTS', true)

/**
* Contracts' temp cdn folder path.
*
* @type {string}
*/
export const CDN_TEMP_CONTRACTS = __env__('BC_CDN_TEMP_CONTRACTS', true)

/**
* Backend host.
*
Expand Down Expand Up @@ -358,6 +372,7 @@ export interface User extends Document {
blacklisted?: boolean
payLater?: boolean
customerId?: string
contracts?: bookcarsTypes.Contract[]
expireAt?: Date
}

Expand Down
4 changes: 4 additions & 0 deletions api/src/config/supplierRoutes.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ const routes = {
getAllSuppliers: '/api/all-suppliers',
getFrontendSuppliers: '/api/frontend-suppliers',
getBackendSuppliers: '/api/backend-suppliers',
createContract: '/api/create-contract/:language',
updateContract: '/api/update-contract/:id/:language',
deleteContract: '/api/delete-contract/:id/:language',
deleteTempContract: '/api/delete-temp-contract/:file',
}

export default routes
54 changes: 31 additions & 23 deletions api/src/controllers/bookingController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import escapeStringRegexp from 'escape-string-regexp'
import { Expo, ExpoPushMessage, ExpoPushTicket } from 'expo-server-sdk'
import { Request, Response } from 'express'
import nodemailer from 'nodemailer'
import path from 'node:path'
import * as bookcarsTypes from ':bookcars-types'
import i18n from '../lang/i18n'
import Booking from '../models/Booking'
Expand Down Expand Up @@ -106,7 +107,7 @@ export const notify = async (driver: env.User, bookingId: string, user: env.User
* @param {boolean} payLater
* @returns {unknown}
*/
export const confirm = async (user: env.User, booking: env.Booking, payLater: boolean) => {
export const confirm = async (user: env.User, supplier: env.User, booking: env.Booking, payLater: boolean = false) => {
const { language } = user
const locale = language === 'fr' ? 'fr-FR' : 'en-US'
const options: Intl.DateTimeFormatOptions = {
Expand Down Expand Up @@ -138,6 +139,14 @@ export const confirm = async (user: env.User, booking: env.Booking, payLater: bo
}
const dropOffLocationName = dropOffLocation.values.filter((value) => value.language === language)[0].value

let contractFile: string | null = null
if (supplier.contracts) {
contractFile = supplier.contracts.find((c) => c.language === user.language)?.file || null
if (!contractFile) {
contractFile = supplier.contracts.find((c) => c.language === 'en')?.file || null
}
}

const mailOptions: nodemailer.SendMailOptions = {
from: env.SMTP_FROM,
to: user.email,
Expand All @@ -158,6 +167,13 @@ export const confirm = async (user: env.User, booking: env.Booking, payLater: bo
</p>`,
}

if (contractFile) {
const file = path.join(env.CDN_CONTRACTS, contractFile)
if (await helper.exists(file)) {
mailOptions.attachments = [{ path: file }]
}
}

await mailHelper.sendMail(mailOptions)

return true
Expand All @@ -179,7 +195,7 @@ export const checkout = async (req: Request, res: Response) => {
const { driver } = body

if (!body.booking) {
throw new Error('Booking missing')
throw new Error('Booking not found')
}

if (driver) {
Expand Down Expand Up @@ -214,17 +230,14 @@ export const checkout = async (req: Request, res: Response) => {
}

if (!user) {
logger.info('Driver not found', body)
return res.sendStatus(204)
throw new Error('Driver not found')
}

if (!body.payLater) {
const { paymentIntentId, sessionId } = body

if (!paymentIntentId && !sessionId) {
const message = 'Payment intent and session missing'
logger.error(message, body)
return res.status(400).send(message)
throw new Error('paymentIntentId and sessionId not found')
}

body.booking.customerId = body.customerId
Expand Down Expand Up @@ -288,43 +301,38 @@ export const checkout = async (req: Request, res: Response) => {
if (booking.status === bookcarsTypes.BookingStatus.Paid && body.paymentIntentId && body.customerId) {
const car = await Car.findById(booking.car)
if (!car) {
logger.info(`Car ${booking.car} not found`)
return res.sendStatus(204)
throw new Error(`Car ${booking.car} not found`)
}
car.trips += 1
await car.save()
}

if (!await confirm(user, booking, false)) {
return res.sendStatus(400)
if (body.payLater || (booking.status === bookcarsTypes.BookingStatus.Paid && body.paymentIntentId && body.customerId)) {
const supplier = await User.findById(booking.supplier)
if (!supplier) {
throw new Error(`Supplier ${booking.supplier} not found`)
}
}

if (body.payLater) {
// Send confirmation email
if (!await confirm(user, booking, body.payLater)) {
// Send confirmation email to customer
if (!await confirm(user, supplier, booking, body.payLater)) {
return res.sendStatus(400)
}

// Notify supplier
const supplier = await User.findById(booking.supplier)
if (!supplier) {
logger.info(`Supplier ${booking.supplier} not found`)
return res.sendStatus(204)
}
i18n.locale = supplier.language
let message = body.payLater ? i18n.t('BOOKING_PAY_LATER_NOTIFICATION') : i18n.t('BOOKING_PAID_NOTIFICATION')
await notify(user, booking._id.toString(), supplier, message)
await notify(user, booking.id, supplier, message)

// Notify admin
const admin = !!env.ADMIN_EMAIL && await User.findOne({ email: env.ADMIN_EMAIL, type: bookcarsTypes.UserType.Admin })
if (admin) {
i18n.locale = admin.language
message = body.payLater ? i18n.t('BOOKING_PAY_LATER_NOTIFICATION') : i18n.t('BOOKING_PAID_NOTIFICATION')
await notify(user, booking._id.toString(), admin, message)
await notify(user, booking.id, admin, message)
}
}

return res.status(200).send({ bookingId: booking._id })
return res.status(200).send({ bookingId: booking.id })
} catch (err) {
logger.error(`[booking.checkout] ${i18n.t('ERROR')}`, err)
return res.status(400).send(i18n.t('ERROR') + err)
Expand Down
6 changes: 2 additions & 4 deletions api/src/controllers/locationController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,12 +707,10 @@ export const deleteTempImage = async (req: Request, res: Response) => {

try {
const imageFile = path.join(env.CDN_TEMP_LOCATIONS, image)
if (!await helper.exists(imageFile)) {
throw new Error(`[location.deleteTempImage] temp image ${imageFile} not found`)
if (await helper.exists(imageFile)) {
await fs.unlink(imageFile)
}

await fs.unlink(imageFile)

res.sendStatus(200)
} catch (err) {
logger.error(`[location.deleteTempImage] ${i18n.t('DB_ERROR')} ${image}`, err)
Expand Down
20 changes: 9 additions & 11 deletions api/src/controllers/stripeController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ export const checkCheckoutSession = async (req: Request, res: Response) => {
// (Set BookingStatus to Paid and remove expireAt TTL index)
//
if (session.payment_status === 'paid') {
const supplier = await User.findById(booking.supplier)
if (!supplier) {
throw new Error(`Supplier ${booking.supplier} not found`)
}

booking.expireAt = undefined
booking.status = bookcarsTypes.BookingStatus.Paid
await booking.save()
Expand All @@ -135,32 +140,25 @@ export const checkCheckoutSession = async (req: Request, res: Response) => {
// await Car.updateOne({ _id: booking.car }, { available: false })
const car = await Car.findById(booking.car)
if (!car) {
logger.info(`Car ${booking.car} not found`)
return res.sendStatus(204)
throw new Error(`Car ${booking.car} not found`)
}
car.trips += 1
await car.save()

// Send confirmation email
// Send confirmation email to customer
const user = await User.findById(booking.driver)
if (!user) {
logger.info(`Driver ${booking.driver} not found`)
return res.sendStatus(204)
throw new Error(`Driver ${booking.driver} not found`)
}

user.expireAt = undefined
await user.save()

if (!await bookingController.confirm(user, booking, false)) {
if (!await bookingController.confirm(user, supplier, booking, false)) {
return res.sendStatus(400)
}

// Notify supplier
const supplier = await User.findById(booking.supplier)
if (!supplier) {
logger.info(`Supplier ${booking.supplier} not found`)
return res.sendStatus(204)
}
i18n.locale = supplier.language
let message = i18n.t('BOOKING_PAID_NOTIFICATION')
await bookingController.notify(user, booking.id, supplier, message)
Expand Down
Loading

0 comments on commit a9836e5

Please sign in to comment.