feat: gate treasury as an org module and group money inputs
Link optional invoice and cheque settlements to cash accounts, and let superadmin enable the finance module per organization. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import type {
|
||||
TreasuryDashboardDto,
|
||||
} from '@mento/shared';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { LocalFileStorage } from '../storage/local-file.storage';
|
||||
import type { Actor } from '../common/record-scope';
|
||||
import { TreasuryReceivablesService } from './treasury-receivables.service';
|
||||
|
||||
@@ -58,6 +59,7 @@ export class TreasuryService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly receivables: TreasuryReceivablesService,
|
||||
private readonly files: LocalFileStorage,
|
||||
) {}
|
||||
|
||||
async ensureCategories(organizationId: string) {
|
||||
@@ -507,6 +509,16 @@ export class TreasuryService {
|
||||
data: { status, accountId: account.id },
|
||||
}),
|
||||
]);
|
||||
await this.prisma.financialAuditLog.create({
|
||||
data: {
|
||||
organizationId: actor.organizationId,
|
||||
entityType: 'document',
|
||||
entityId: doc.id,
|
||||
action: 'settle',
|
||||
after: { amount: amount.toFixed(2), accountId: account.id, status },
|
||||
actorUserId: actor.userId,
|
||||
},
|
||||
}).catch(() => undefined);
|
||||
if (status === 'settled') {
|
||||
await this.prisma.financialRecurringOccurrence.updateMany({
|
||||
where: { documentId: doc.id, status: 'pending' },
|
||||
@@ -543,6 +555,28 @@ export class TreasuryService {
|
||||
return this.toDocumentDto(fresh);
|
||||
}
|
||||
|
||||
async addAttachment(actor: Actor, documentId: string, file: { originalname: string; mimetype: string; buffer: Buffer }) {
|
||||
const doc = await this.prisma.financialDocument.findFirst({
|
||||
where: { id: documentId, organizationId: actor.organizationId },
|
||||
select: { id: true },
|
||||
});
|
||||
if (!doc) throw new NotFoundException('سند پیدا نشد.');
|
||||
const safeName = file.originalname.replace(/[^\w.\-\u0600-\u06FF]+/g, '_').slice(0, 80);
|
||||
const storageKey = `treasury/${actor.organizationId}/${doc.id}/${randomUUID()}-${safeName || 'file'}`;
|
||||
await this.files.save(storageKey, file.buffer);
|
||||
return this.prisma.financialAttachment.create({
|
||||
data: {
|
||||
organizationId: actor.organizationId,
|
||||
documentId: doc.id,
|
||||
fileName: file.originalname,
|
||||
mimeType: file.mimetype || 'application/octet-stream',
|
||||
storageKey,
|
||||
createdById: actor.userId,
|
||||
},
|
||||
select: { id: true, fileName: true, createdAt: true },
|
||||
});
|
||||
}
|
||||
|
||||
async dashboard(
|
||||
actor: Actor,
|
||||
range?: { from?: string; to?: string },
|
||||
|
||||
Reference in New Issue
Block a user