Implement prior archive cutoff API

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-17 17:01:52 +03:30
co-authored by Cursor
parent 79ba146118
commit 65d66bb331
5 changed files with 199 additions and 2 deletions
+7
View File
@@ -12,6 +12,7 @@ import {
} from 'class-validator';
import { Type } from 'class-transformer';
import { BUSINESS_FIELDS } from '@mento/shared';
import type { UpdatePriorArchiveCutoffRequest } from '@mento/shared';
export class ExchangeRateInputDto {
@IsString()
@@ -67,3 +68,9 @@ export class UpdateOrganizationDto {
@IsIn([...BUSINESS_FIELDS])
businessField?: (typeof BUSINESS_FIELDS)[number];
}
export class UpdatePriorArchiveCutoffDto
implements UpdatePriorArchiveCutoffRequest
{
cutoffAt!: string | null;
}
@@ -1,4 +1,12 @@
import { Body, Controller, Get, Patch, Post, UseGuards } from '@nestjs/common';
import {
BadRequestException,
Body,
Controller,
Get,
Patch,
Post,
UseGuards,
} from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@@ -6,6 +14,7 @@ import { CurrentUser } from '../auth/current-user.decorator';
import { AuthService } from '../auth/auth.service';
import {
CreateOrganizationDto,
UpdatePriorArchiveCutoffDto,
UpdateOrgCurrenciesDto,
UpdateOrganizationDto,
} from './dto';
@@ -58,4 +67,19 @@ export class OrganizationsController {
dto,
);
}
@Patch('current/prior-archive-cutoff')
@Roles('owner')
updatePriorArchiveCutoff(
@CurrentUser() user: { organizationId: string },
@Body() dto: UpdatePriorArchiveCutoffDto,
) {
if (!Object.prototype.hasOwnProperty.call(dto ?? {}, 'cutoffAt')) {
throw new BadRequestException('تاریخ قطع باید مشخص شود.');
}
return this.organizationsService.updatePriorArchiveCutoff(
user.organizationId,
dto.cutoffAt,
);
}
}
@@ -7,6 +7,7 @@ import {
import type {
OrganizationDto,
UpdateOrgCurrenciesRequest,
UpdatePriorArchiveCutoffRequest,
} from '@mento/shared';
import { CURRENCY_CATALOG, isBusinessField } from '@mento/shared';
import { Prisma } from '@prisma/client';
@@ -214,6 +215,39 @@ export class OrganizationsService {
return this.getCurrent(organizationId);
}
async updatePriorArchiveCutoff(
organizationId: string,
cutoffAt: UpdatePriorArchiveCutoffRequest['cutoffAt'],
): Promise<OrganizationDto> {
const org = await this.prisma.organization.findUnique({
where: { id: organizationId },
select: { enabledModules: true },
});
if (!org) throw new NotFoundException('سازمان پیدا نشد');
const modules = this.billing.normalizeModules(org.enabledModules);
if (!modules.includes('prior_data_archive')) {
throw new BadRequestException(
'ماژول بایگانی اطلاعات قبل برای این محیط فعال نیست.',
);
}
let value: Date | null = null;
if (cutoffAt !== null && cutoffAt !== '') {
const parsed = new Date(cutoffAt);
if (Number.isNaN(parsed.getTime())) {
throw new BadRequestException('تاریخ قطع معتبر نیست.');
}
value = parsed;
}
await this.prisma.organization.update({
where: { id: organizationId },
data: { priorArchiveEnabledAt: value },
});
return this.getCurrent(organizationId);
}
catalog() {
return CURRENCY_CATALOG;
}
@@ -226,6 +260,7 @@ export class OrganizationsService {
baseCurrency: string;
enabledCurrencies: string[];
enabledModules?: string[];
priorArchiveEnabledAt?: Date | null;
exchangeRates: Array<{
currency: string;
rateToBase: Prisma.Decimal;
@@ -243,6 +278,7 @@ export class OrganizationsService {
baseCurrency: org.baseCurrency,
enabledCurrencies: org.enabledCurrencies,
enabledModules: this.billing.normalizeModules(org.enabledModules),
priorArchiveCutoffAt: org.priorArchiveEnabledAt?.toISOString() ?? null,
exchangeRates: org.exchangeRates.map((r) => ({
currency: r.currency,
rateToBase: Number(r.rateToBase),