diff --git a/myteamwallet_backend/src/cashbox-export/cashbox-export.controller.ts b/myteamwallet_backend/src/cashbox-export/cashbox-export.controller.ts index d5ddb71..72587f1 100644 --- a/myteamwallet_backend/src/cashbox-export/cashbox-export.controller.ts +++ b/myteamwallet_backend/src/cashbox-export/cashbox-export.controller.ts @@ -1,8 +1,10 @@ import { + Body, Controller, Get, Param, ParseIntPipe, + Put, Query, Request, Res, @@ -12,7 +14,9 @@ import { AuthGuard } from '@nestjs/passport'; import { ApiBearerAuth } from '@nestjs/swagger'; import type { Response } from 'express'; import { CashboxExportQueryDto } from './dto/cashbox-export-query.dto'; +import { UpsertCashboxExportSubscriptionDTO } from './dto/upsert-cashbox-export-subscription.dto'; import { CashboxExportService } from './cashbox-export.service'; +import { CashboxExportSubscriptionService } from './cashbox-export-subscription.service'; type AuthenticatedRequest = { user: { id: number } }; @@ -20,7 +24,10 @@ type AuthenticatedRequest = { user: { id: number } }; @UseGuards(AuthGuard('jwt')) @Controller({ path: 'cashbox-export', version: '1' }) export class CashboxExportController { - constructor(private readonly service: CashboxExportService) {} + constructor( + private readonly service: CashboxExportService, + private readonly subscriptionService: CashboxExportSubscriptionService, + ) {} @Get(':teamId') async exportCashbox( @@ -42,4 +49,21 @@ export class CashboxExportController { }); res.send(buffer); } + + @Get(':teamId/subscription') + getSubscription( + @Request() request: AuthenticatedRequest, + @Param('teamId', ParseIntPipe) teamId: number, + ) { + return this.subscriptionService.getSubscription(teamId, request.user.id); + } + + @Put(':teamId/subscription') + upsertSubscription( + @Request() request: AuthenticatedRequest, + @Param('teamId', ParseIntPipe) teamId: number, + @Body() dto: UpsertCashboxExportSubscriptionDTO, + ) { + return this.subscriptionService.upsertSubscription(teamId, request.user.id, dto); + } } diff --git a/myteamwallet_backend/src/cashbox-export/cashbox-export.http.spec.ts b/myteamwallet_backend/src/cashbox-export/cashbox-export.http.spec.ts index c89e4be..7ca0d20 100644 --- a/myteamwallet_backend/src/cashbox-export/cashbox-export.http.spec.ts +++ b/myteamwallet_backend/src/cashbox-export/cashbox-export.http.spec.ts @@ -7,20 +7,29 @@ import { import { AuthGuard } from '@nestjs/passport'; import { Test } from '@nestjs/testing'; import * as request from 'supertest'; +import { RecurringTransactionIntervalEnum } from '../recurring-transactions/recurring-transaction-interval.enum'; import validationOptions from '../utils/validation-options'; import { CashboxExportController } from './cashbox-export.controller'; import { CashboxExportService } from './cashbox-export.service'; +import { CashboxExportSubscriptionService } from './cashbox-export-subscription.service'; describe('cashbox export HTTP boundary', () => { let app: INestApplication; const service = { exportForUser: jest.fn(), }; + const subscriptionService = { + getSubscription: jest.fn(), + upsertSubscription: jest.fn(), + }; beforeAll(async () => { const module = await Test.createTestingModule({ controllers: [CashboxExportController], - providers: [{ provide: CashboxExportService, useValue: service }], + providers: [ + { provide: CashboxExportService, useValue: service }, + { provide: CashboxExportSubscriptionService, useValue: subscriptionService }, + ], }) .overrideGuard(AuthGuard('jwt')) .useValue({ @@ -95,4 +104,52 @@ describe('cashbox export HTTP boundary', () => { expect(response.headers['content-type']).toBe('application/pdf'); expect(Buffer.from(response.body).equals(pdfBuffer)).toBe(true); }); + + it('reads the current subscription', async () => { + const subscription = { + recipients: ['vorstand@example.com'], + interval: RecurringTransactionIntervalEnum.monthly, + active: true, + nextRunDate: '2026-09-01T00:00:00.000Z', + }; + subscriptionService.getSubscription.mockResolvedValue(subscription); + + await request(app.getHttpServer()) + .get('/api/v1/cashbox-export/5/subscription') + .set('Authorization', 'Bearer user') + .expect(200) + .expect(subscription); + expect(subscriptionService.getSubscription).toHaveBeenCalledWith(5, 42); + }); + + it('rejects an invalid recipient email on upsert', async () => { + await request(app.getHttpServer()) + .put('/api/v1/cashbox-export/5/subscription') + .set('Authorization', 'Bearer user') + .send({ recipients: ['not-an-email'], interval: 'monthly', active: true }) + .expect(422); + expect(subscriptionService.upsertSubscription).not.toHaveBeenCalled(); + }); + + it('accepts a valid subscription upsert', async () => { + const subscription = { + recipients: ['vorstand@example.com'], + interval: RecurringTransactionIntervalEnum.monthly, + active: true, + nextRunDate: '2026-09-01T00:00:00.000Z', + }; + subscriptionService.upsertSubscription.mockResolvedValue(subscription); + + await request(app.getHttpServer()) + .put('/api/v1/cashbox-export/5/subscription') + .set('Authorization', 'Bearer user') + .send({ recipients: ['vorstand@example.com'], interval: 'monthly', active: true }) + .expect(200) + .expect(subscription); + expect(subscriptionService.upsertSubscription).toHaveBeenCalledWith(5, 42, { + recipients: ['vorstand@example.com'], + interval: 'monthly', + active: true, + }); + }); });