Skip to content

Commit

Permalink
feature(drivers-service): create fleet driver earnings + type update (#…
Browse files Browse the repository at this point in the history
…532)

* feature(drivers-service): create fleet driver earnings + type update

* feature: implement get all drivers payouts
  • Loading branch information
afkzoro authored Dec 27, 2024
1 parent 3d3a1c7 commit 6f8fc0f
Show file tree
Hide file tree
Showing 15 changed files with 297 additions and 11 deletions.
26 changes: 26 additions & 0 deletions apps/drivers-service/src/fleet/fleet.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
AcceptFleetInviteDto,
CreateAccountWithOrganizationDto, CurrentUser, Delivery, Driver, DriverStatGroup, FleetMember,
FleetOrganization,
FleetPayout,
RegisterDriverDto,
ResponseWithStatus, UpdateFleetMemberProfileDto, UpdateFleetOwnershipStatusDto
} from '@app/common'
Expand Down Expand Up @@ -201,4 +202,29 @@ export class FleetController {
throw new HttpException(error.message, error.status)
}
}

@UseGuards(FleetJwtAuthGuard)
@Get('payout/:driverId')
async getDriverPayout (
@CurrentUser() member: FleetMember,
@Param('driverId') driverId: string
): Promise<FleetPayout[]> {
try {
return await this.fleetService.getDriverPayout(member.organization, driverId)
} catch (error) {
throw new HttpException(error.message, error.status)
}
}

@UseGuards(FleetJwtAuthGuard)
@Get('payout/all')
async getAllDriverPayout (
@CurrentUser() member: FleetMember
): Promise<FleetPayout[]> {
try {
return await this.fleetService.getAllDriversPayout(member.organization)
} catch (error) {
throw new HttpException(error.message, error.status)
}
}
}
45 changes: 43 additions & 2 deletions apps/drivers-service/src/fleet/fleet.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { HttpStatus, Injectable, Logger, NotFoundException } from '@nestjs/common'
import { HttpException, HttpStatus, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'
import {
AcceptFleetInviteDto,
CreateAccountWithOrganizationDto,
Delivery,
Driver,
DriverStatGroup,
FitRpcException, FleetMember, FleetOrganization,
FleetPayout,
internationalisePhoneNumber,
IRpcException,
OrderStatus,
QUEUE_MESSAGE,
QUEUE_SERVICE,
RandomGen,
RegisterDriverDto,
ResponseWithStatus, SOCKET_MESSAGE, UpdateFleetMemberProfileDto, UpdateFleetOwnershipStatusDto,
Expand All @@ -22,6 +26,8 @@ import { DriversServiceService } from '../drivers-service.service'
import { DriverRepository } from '../drivers-service.repository'
import { OdsaRepository } from '../ODSA/odsa.repository'
import { ODSA } from '../ODSA/odsa.service'
import { ClientProxy } from '@nestjs/microservices'
import { lastValueFrom, catchError } from 'rxjs'

@Injectable()
export class FleetService {
Expand All @@ -34,7 +40,10 @@ export class FleetService {
private readonly driverRepository: DriverRepository,
private readonly eventsGateway: EventsGateway,
private readonly odsaRepository: OdsaRepository,
private readonly odsaService: ODSA
private readonly odsaService: ODSA,

@Inject(QUEUE_SERVICE.PAYMENT_SERVICE)
private readonly paymentClient: ClientProxy
) {}

public async getProfile (id: string): Promise<FleetMember> {
Expand Down Expand Up @@ -454,6 +463,38 @@ export class FleetService {
return await this.odsaService.getDriverStats(driverId)
}

async getDriverPayout (organization: string, driverId: string): Promise<FleetPayout[]> {
const checkDriver = await this.driverRepository.findOne({
_id: driverId,
organization
})

if (checkDriver === null) {
throw new FitRpcException(
'Driver not found',
HttpStatus.NOT_FOUND
)
}

return await lastValueFrom<FleetPayout[]>(
this.paymentClient.send(QUEUE_MESSAGE.FLEET_GET_PAYOUT_DRIVER, { driverId }).pipe(
catchError<any, any>((error: IRpcException) => {
throw new HttpException(error.message, error.status)
})
)
)
}

async getAllDriversPayout (organization: string): Promise<FleetPayout[]> {
return await lastValueFrom<FleetPayout[]>(
this.paymentClient.send(QUEUE_MESSAGE.FLEET_GET_ALL_PAYOUTS, { organization }).pipe(
catchError<any, any>((error: IRpcException) => {
throw new HttpException(error.message, error.status)
})
)
)
}

// @Crons

/**
Expand Down
5 changes: 3 additions & 2 deletions apps/orders-service/src/test/stub/orders.stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Types } from 'mongoose'
export function OrderStub (): Order {
const objectId = '63f93c9f248f6c43d0b76502' as unknown as Types.ObjectId
return {
pickupAddress: "",
pickupAddress: '',
precisePickupLocation: {
type: 'Point',
coordinates: [12.34, 56.78]
Expand Down Expand Up @@ -53,7 +53,8 @@ export function OrderStub (): Order {
deliveryFee: 10.0,
vat: 12.0
},
txRefId: 'transaction_reference_123'
txRefId: 'transaction_reference_123',
fleetOrderType: 'GROCERIES'
}
}

Expand Down
51 changes: 51 additions & 0 deletions apps/payment-service/src/fleets-payout/fleets-payout.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
FleetPayout,
QUEUE_MESSAGE,
RmqService

} from '@app/common'
import { Controller } from '@nestjs/common'
import {
Ctx,
MessagePattern,
Payload,
RmqContext,
RpcException
} from '@nestjs/microservices'
import { FleetPayoutService } from './fleets-payout.service'

@Controller()
export class FleetPayoutController {
constructor (
private readonly fleetPayoutService: FleetPayoutService,
private readonly rmqService: RmqService
) {}

@MessagePattern(QUEUE_MESSAGE.FLEET_GET_PAYOUT_DRIVER)
async getDrivePayout (
@Payload() { driverId }: { driverId: string },
@Ctx() context: RmqContext
): Promise<FleetPayout[]> {
try {
return await this.fleetPayoutService.getDriverPayout(driverId)
} catch (error) {
throw new RpcException(error)
} finally {
this.rmqService.ack(context)
}
}

@MessagePattern(QUEUE_MESSAGE.FLEET_GET_ALL_PAYOUTS)
async getAllDriversPayout (
@Payload() { organization }: { organization: string },
@Ctx() context: RmqContext
): Promise<FleetPayout[]> {
try {
return await this.fleetPayoutService.getAllDriversPayout(organization)
} catch (error) {
throw new RpcException(error)
} finally {
this.rmqService.ack(context)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Injectable, Logger } from '@nestjs/common'
import { AbstractRepository, FleetPayout } from '@app/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'

@Injectable()
export class FleetPayoutRepository extends AbstractRepository<FleetPayout> {
protected readonly logger = new Logger(FleetPayout.name)

constructor (
@InjectModel(FleetPayout.name) payoutModel: Model<FleetPayout>
) {
super(payoutModel)
}
}
93 changes: 93 additions & 0 deletions apps/payment-service/src/fleets-payout/fleets-payout.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { OrderStatus, QUEUE_MESSAGE, IRpcException, FitRpcException, RandomGen, FleetPayout, QUEUE_SERVICE, DeliveryI, Delivery, DriverI } from '@app/common'
import { Inject, Injectable } from '@nestjs/common'
import { ClientProxy } from '@nestjs/microservices'
import { FilterQuery } from 'mongoose'
import { lastValueFrom, catchError } from 'rxjs'
import { FleetPayoutRepository } from './fleets-payout.respository'
import { Cron, CronExpression } from '@nestjs/schedule'
import { DriverRepository } from 'apps/drivers-service/src/drivers-service.repository'

@Injectable()
export class FleetPayoutService {
constructor (
private readonly fleetPayoutRepository: FleetPayoutRepository,
private readonly driverRepository: DriverRepository,
@Inject(QUEUE_SERVICE.DRIVER_SERVICE)
private readonly driverClient: ClientProxy

) {

}

async getDriverPayout (driver: string): Promise<FleetPayout[]> {
return await this.fleetPayoutRepository.findOneAndPopulate({ driver }, ['deliveries'])
}

async getAllDriversPayout (organization: string): Promise<FleetPayout[]> {
const drivers: DriverI[] = await this.driverRepository.find({ organization })

const driverIds = drivers.map((driver) => driver._id)

return await this.fleetPayoutRepository.findAndPopulate(
{ driver: { $in: driverIds } },
['deliveries']
)
}

@Cron(CronExpression.EVERY_DAY_AT_10PM, {
timeZone: 'Africa/Lagos'
})
async handlePayoutComputation (): Promise<void> {
const today = new Date()
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)

const start = new Date(yesterday)
start.setHours(0, 0, 0, 0)

const end = new Date(yesterday)
end.setHours(23, 59, 59, 999)

const filter: FilterQuery<Delivery> = {
createdAt: {
$gte: start.toISOString(),
$lt: end.toISOString()
},
status: OrderStatus.FULFILLED
}
const deliveries = await lastValueFrom<DeliveryI[]>(
this.driverClient.send(QUEUE_MESSAGE.ADMIN_GET_DELIVERIES, filter).pipe(
catchError((error: IRpcException) => {
throw new FitRpcException(error.message, error.status)
})
)
)

const deliveryId = deliveries.map((delivery) => delivery._id)

// Compute earnings for each driver
const driverEarnings = new Map<string, number>()

deliveries.forEach((delivery) => {
const driverId = delivery.driver._id.toString()
const earnings = driverEarnings.get(driverId) ?? 0
driverEarnings.set(
driverId,
earnings + Number(delivery.deliveryFee)
)
})

const payoutsToMake: Array<Partial<FleetPayout>> = []

for (const [driverId, earnings] of driverEarnings) {
payoutsToMake.push({
refId: RandomGen.genRandomNum(10, 7),
driver: driverId,
earnings,
paid: false,
deliveries: deliveryId
})
}

await this.fleetPayoutRepository.insertMany(payoutsToMake)
}
}
20 changes: 17 additions & 3 deletions apps/payment-service/src/payment.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import {
Coupon,
CouponSchema,
ListingMenu,
ListingMenuSchema
ListingMenuSchema,
FleetPayout,
FleetPayoutSchema
} from '@app/common'
import { ScheduleModule } from '@nestjs/schedule'
import { PaymentRepository } from './charge/charge.repository'
Expand Down Expand Up @@ -51,6 +53,9 @@ import { UserWalletController } from './wallet/user/wallet.controller'
import { UserWalletService } from './wallet/user/wallet.service'
import { UserWallet, UserWalletSchema } from '@app/common/database/schemas/user-wallet.schema'
import { UserWalletRepository } from './wallet/user/wallet.repository'
import { FleetPayoutService } from './fleets-payout/fleets-payout.service'
import { FleetPayoutRepository } from './fleets-payout/fleets-payout.respository'
import { FleetPayoutController } from './fleets-payout/fleets-payout.controller'

@Module({
imports: [
Expand Down Expand Up @@ -95,6 +100,12 @@ import { UserWalletRepository } from './wallet/user/wallet.repository'
schema: ListingMenuSchema
}
]),
MongooseModule.forFeature([
{
name: FleetPayout.name,
schema: FleetPayoutSchema
}
]),
MongooseModule.forFeature([
{
name: Payment.name,
Expand Down Expand Up @@ -132,7 +143,8 @@ import { UserWalletRepository } from './wallet/user/wallet.repository'
PaymentController,
DriverWalletController,
UserWalletController,
CouponController
CouponController,
FleetPayoutController
],
providers: [
VendorPayoutService,
Expand All @@ -147,7 +159,9 @@ import { UserWalletRepository } from './wallet/user/wallet.repository'
CouponService,
CouponRepository,
UserWalletService,
UserWalletRepository
UserWalletRepository,
FleetPayoutService,
FleetPayoutRepository
]
})
export class PaymentServiceModule {}
32 changes: 32 additions & 0 deletions libs/common/src/database/schemas/fleet-payout.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
import { AbstractDocument } from '../abstract.schema'
import { Types } from 'mongoose'

@Schema({ versionKey: false, timestamps: true })
export class FleetPayout extends AbstractDocument {
@Prop({
type: Types.ObjectId,
ref: 'Driver'
})
public driver: string

@Prop(Number)
earnings: number

@Prop({ type: Boolean, default: false })
paid: boolean

@Prop(Date)
updatedAt: string

@Prop(Date)
createdAt: string

@Prop({ type: [Types.ObjectId], ref: 'Delivery' })
deliveries: string[]

@Prop(Number)
refId: number
}

export const FleetPayoutSchema = SchemaFactory.createForClass(FleetPayout)
Loading

0 comments on commit 6f8fc0f

Please sign in to comment.