import { Body,Controller,Get,Param,Post,Req,UseGuards } from '@nestjs/common';
import type { z } from 'zod';
import { AuthGuard } from '../../../common/auth/auth.guard';
import { OrganizationContextGuard } from '../../../common/auth/organization-context.guard';
import { LocationContextGuard,type LocationScopedRequest } from '../../../common/auth/location-context.guard';
import { PermissionGuard } from '../../../common/auth/permission.guard';
import { RequirePermissions } from '../../../common/auth/permissions.decorator';
import { ZodValidationPipe } from '../../../common/validation/zod-validation.pipe';
import {
cashCloseSchema,cashCountSchema,cashMovementSchema,cashOpenSchema,
} from '../../../common/validation/critical-write.schemas';
type CashOpenBody = z.infer<typeof cashOpenSchema>;
type CashMovementBody = z.infer<typeof cashMovementSchema>;
type CashCountBody = z.infer<typeof cashCountSchema>;
type CashCloseBody = z.infer<typeof cashCloseSchema>;
import { BusinessDateService } from '../../../common/time/business-date.service';
import { CashService } from '../application/cash.service';
import { CashShiftDetailService } from '../read-models/cash-shift-detail.service';
import { CashOverviewService } from '../read-models/cash-overview.service';
@Controller('/cash')
@UseGuards(AuthGuard,OrganizationContextGuard,LocationContextGuard,PermissionGuard)
export class CashController {
constructor(
private readonly cash:CashService,
private readonly detail:CashShiftDetailService,
private readonly overviewReadModel:CashOverviewService,
private readonly businessDates:BusinessDateService,
) {}
@Get('/overview')
@RequirePermissions('cash.read')
async overview(@Req() req:LocationScopedRequest) {
const businessDate=await this.businessDates.forLocation({
organizationId:req.organizationId!,locationId:req.locationId!,
});
return this.overviewReadModel.get({
organizationId:req.organizationId!,locationId:req.locationId!,businessDate,
});
}
@Get('/shifts/:id')
@RequirePermissions('cash.read')
get(@Req() req:LocationScopedRequest,@Param('id') id:string) {
return this.detail.get(req.organizationId!,req.locationId!,id);
}
@Post('/shifts')
@RequirePermissions('cash.open_shift')
async open(
@Req() req:LocationScopedRequest,
@Body(new ZodValidationPipe(cashOpenSchema)) body:CashOpenBody,
) {
const businessDate=await this.businessDates.forLocation({
organizationId:req.organizationId!,locationId:req.locationId!,
});
return this.cash.openShift({
organizationId:req.organizationId!,locationId:req.locationId!,
tillId:body.tillId,businessDate,
openingFloatMinor:body.openingFloatMinor,currencyCode:body.currencyCode,
userId:req.auth?.userId,
});
}
@Post('/shifts/:id/movements')
@RequirePermissions('cash.move')
movement(
@Req() req:LocationScopedRequest,@Param('id') id:string,
@Body(new ZodValidationPipe(cashMovementSchema)) body:CashMovementBody,
) {
return this.cash.addMovement({
organizationId:req.organizationId!,locationId:req.locationId!,cashShiftId:id,
movementType:body.movementType,amountMinor:body.amountMinor,
currencyCode:body.currencyCode,reason:body.reason,userId:req.auth?.userId,
});
}
@Post('/shifts/:id/count')
@RequirePermissions('cash.count')
count(
@Req() req:LocationScopedRequest,@Param('id') id:string,
@Body(new ZodValidationPipe(cashCountSchema)) body:CashCountBody,
) {
return this.cash.count({
organizationId:req.organizationId!,locationId:req.locationId!,cashShiftId:id,totalMinor:body.totalMinor,
currencyCode:body.currencyCode,denominationCounts:body.denominationCounts,
userId:req.auth?.userId,
});
}
@Post('/shifts/:id/close')
@RequirePermissions('cash.close_shift')
close(
@Req() req:LocationScopedRequest,@Param('id') id:string,
@Body(new ZodValidationPipe(cashCloseSchema)) body:CashCloseBody,
) {
return this.cash.close({
organizationId:req.organizationId!,locationId:req.locationId!,cashShiftId:id,notes:body.notes,userId:req.auth?.userId,
});
}
}
