Doing
This commit is contained in:
parent
f8e123b9f5
commit
3e64472821
@ -59,7 +59,7 @@
|
||||
"file-type-checker": "^1.0.8",
|
||||
"fp-ts": "^2.16.1",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.151",
|
||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.160",
|
||||
"module-alias": "^2.2.2",
|
||||
"monocle-ts": "^2.3.13",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
|
@ -7,13 +7,14 @@ import { Customer } from "le-coffre-resources/dist/Notary";
|
||||
import { validateOrReject } from "class-validator";
|
||||
import authHandler from "@App/middlewares/AuthHandler";
|
||||
import ruleHandler from "@App/middlewares/RulesHandler";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { Documents, Prisma } from "@prisma/client";
|
||||
import customerHandler from "@App/middlewares/OfficeMembershipHandlers/CustomerHandler";
|
||||
import DocumentsService from "@Services/notary/DocumentsService/DocumentsService";
|
||||
|
||||
@Controller()
|
||||
@Service()
|
||||
export default class CustomersController extends ApiController {
|
||||
constructor(private customersService: CustomersService) {
|
||||
constructor(private customersService: CustomersService, private documentsService: DocumentsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -215,4 +216,64 @@ export default class CustomersController extends ApiController {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Send a reminder to a specific customer by uid to signe specific documents
|
||||
*/
|
||||
@Post("/api/v1/notary/customers/:uid/send_reminder", [authHandler, ruleHandler, customerHandler])
|
||||
protected async sendReminder(req: Request, response: Response) {
|
||||
try {
|
||||
const uid = req.params["uid"];
|
||||
if (!uid) {
|
||||
this.httpBadRequest(response, "No uid provided");
|
||||
return;
|
||||
}
|
||||
|
||||
const documentsUid = req.body.documentsUid;
|
||||
if (!documentsUid || !Array.isArray(documentsUid)) {
|
||||
this.httpBadRequest(response, "Invalid or missing documents");
|
||||
return;
|
||||
}
|
||||
|
||||
const documentEntities : Documents[] = [];
|
||||
//For each document uid, use DocumentsService.getByUid to get the document entity and add it to the documents array
|
||||
for (const documentUid of documentsUid) {
|
||||
console.log("documentUid", documentUid);
|
||||
const documentEntity = await this.documentsService.getByUid(documentUid, { document_type : true, folder : true });
|
||||
console.log("documentEntity", documentEntity);
|
||||
|
||||
if (!documentEntity) {
|
||||
this.httpBadRequest(response, "Document not found");
|
||||
return;
|
||||
}
|
||||
documentEntities.push(documentEntity);
|
||||
}
|
||||
|
||||
const customerEntity = await this.customersService.getByUid(uid, { contact: true, office : true });
|
||||
console.log("customerEntity", customerEntity);
|
||||
|
||||
|
||||
if (!customerEntity) {
|
||||
this.httpNotFoundRequest(response, "customer not found");
|
||||
return;
|
||||
}
|
||||
|
||||
//Hydrate ressource with prisma entity
|
||||
const customer = Customer.hydrate<Customer>(customerEntity, { strategy: "excludeAll" });
|
||||
console.log("customer", customer);
|
||||
|
||||
|
||||
// Call service to send reminder with documents
|
||||
const reminder = await this.customersService.sendDocumentsReminder(customer, documentEntities);
|
||||
|
||||
console.log("Reminder sent", reminder);
|
||||
|
||||
|
||||
// Success
|
||||
this.httpSuccess(response, customer);
|
||||
} catch (error) {
|
||||
this.httpInternalError(response, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
207
src/app/api/notary/DocumentsNotaryController.ts
Normal file
207
src/app/api/notary/DocumentsNotaryController.ts
Normal file
@ -0,0 +1,207 @@
|
||||
import { Response, Request } from "express";
|
||||
import { Controller, Delete, Get, Post } from "@ControllerPattern/index";
|
||||
import ApiController from "@Common/system/controller-pattern/ApiController";
|
||||
import { Service } from "typedi";
|
||||
import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService";
|
||||
import { DocumentsNotary, Prisma } from "@prisma/client";
|
||||
import { Document, DocumentNotary, FileNotary } from "le-coffre-resources/dist/Notary";
|
||||
import authHandler from "@App/middlewares/AuthHandler";
|
||||
import ruleHandler from "@App/middlewares/RulesHandler";
|
||||
import documentHandler from "@App/middlewares/OfficeMembershipHandlers/DocumentHandler";
|
||||
import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService";
|
||||
import CustomersService from "@Services/admin/CustomersService/CustomersService";
|
||||
import UsersService from "@Services/notary/UsersService/UsersService";
|
||||
import FilesService from "@Services/common/FilesService/FilesService";
|
||||
// import NotificationBuilder from "@Common/notifications/NotificationBuilder";
|
||||
|
||||
@Controller()
|
||||
@Service()
|
||||
export default class DocumentsNotaryController extends ApiController {
|
||||
constructor(
|
||||
private documentsNotaryService: DocumentsNotaryService,
|
||||
private officeFoldersService: OfficeFoldersService,
|
||||
private customerService: CustomersService,
|
||||
private userService: UsersService,
|
||||
private filesService: FilesService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get all documents
|
||||
* @returns IDocument[] list of documents
|
||||
*/
|
||||
@Get("/api/v1/notary/documents_notary", [authHandler, ruleHandler])
|
||||
protected async get(req: Request, response: Response) {
|
||||
try {
|
||||
//get query
|
||||
let query: Prisma.DocumentsNotaryFindManyArgs = {};
|
||||
if (req.query["q"]) {
|
||||
query = JSON.parse(req.query["q"] as string);
|
||||
if (query.where?.uid) {
|
||||
this.httpBadRequest(response, "You can't filter by uid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
const officeId: string = req.body.user.office_Id;
|
||||
const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId };
|
||||
if (!query.where) query.where = { folder: { office: officeWhereInput } };
|
||||
query.where.folder!.office = officeWhereInput;
|
||||
|
||||
//call service to get prisma entity
|
||||
const documentEntities = await this.documentsNotaryService.get(query);
|
||||
|
||||
//Hydrate ressource with prisma entity
|
||||
const documents = Document.hydrateArray<Document>(documentEntities, { strategy: "excludeAll" });
|
||||
|
||||
//success
|
||||
this.httpSuccess(response, documents);
|
||||
} catch (error) {
|
||||
this.httpInternalError(response, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create a new document
|
||||
* @returns IDocument created
|
||||
*/
|
||||
@Post("/api/v1/notary/documents_notary", [authHandler])
|
||||
protected async post(req: Request, response: Response) {
|
||||
try {
|
||||
if(!req.file) return;
|
||||
|
||||
const customer = await this.customerService.getByUid(req.body.customerUid);
|
||||
if (!customer) return;
|
||||
console.log("customer", customer);
|
||||
|
||||
const folder = await this.officeFoldersService.getByUid(req.body.folderUid);
|
||||
if (!folder) return;
|
||||
console.log("folder", folder);
|
||||
|
||||
const user = await this.userService.getByUid(req.body.user.userId);
|
||||
if (!user) return;
|
||||
|
||||
const documentNotaryEntity = DocumentNotary.hydrate<DocumentNotary>({
|
||||
customer: customer,
|
||||
folder: folder,
|
||||
depositor: user,
|
||||
});
|
||||
|
||||
const documentNotaryEntityCreated = await this.documentsNotaryService.create(documentNotaryEntity);
|
||||
console.log("documentNotaryEntityCreated", documentNotaryEntityCreated);
|
||||
|
||||
const query = JSON.stringify({ document: { uid: documentNotaryEntityCreated.uid } });
|
||||
|
||||
const fileEntity = FileNotary.hydrate<FileNotary>(JSON.parse(query));
|
||||
|
||||
const fileEntityCreated = await this.filesService.createFileNotary(fileEntity, req.file);
|
||||
console.log("fileEntityCreated", fileEntityCreated);
|
||||
|
||||
|
||||
|
||||
// //init Document resource with request body values
|
||||
// const documentNotaryEntity = DocumentNotary.hydrate<DocumentNotary>(req.body);
|
||||
// console.log(documentNotaryEntity);
|
||||
|
||||
// const folder = await this.officeFoldersService.getByUid(documentNotaryEntity.folder?.uid!, {
|
||||
// folder_anchor: true,
|
||||
// });
|
||||
// if (!folder) {
|
||||
// this.httpBadRequest(response, "Folder not found");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const folderRessource = OfficeFolder.hydrate<OfficeFolder>(folder);
|
||||
// if (folderRessource.folder_anchor) {
|
||||
// this.httpBadRequest(response, "Cannot add document on an anchored or anchoring folder");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// //validate document
|
||||
// await validateOrReject(documentNotaryEntity, { groups: ["createDocument"], forbidUnknownValues: false });
|
||||
|
||||
// //call service to get prisma entity
|
||||
// const documentNotaryEntityCreated = await this.documentsNotaryService.create(documentNotaryEntity);
|
||||
|
||||
// //Hydrate ressource with prisma entity
|
||||
// const documentNotary = DocumentNotary.hydrate<DocumentNotary>(documentNotaryEntityCreated, {
|
||||
// strategy: "excludeAll",
|
||||
// });
|
||||
|
||||
// //success
|
||||
// this.httpCreated(response, documentNotary);
|
||||
} catch (error) {
|
||||
this.httpInternalError(response, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Delete a specific document
|
||||
*/
|
||||
@Delete("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler])
|
||||
protected async delete(req: Request, response: Response) {
|
||||
try {
|
||||
const uid = req.params["uid"];
|
||||
if (!uid) {
|
||||
this.httpBadRequest(response, "No uid provided");
|
||||
return;
|
||||
}
|
||||
|
||||
const documentFound = await this.documentsNotaryService.getByUid(uid);
|
||||
|
||||
if (!documentFound) {
|
||||
this.httpNotFoundRequest(response, "document not found");
|
||||
return;
|
||||
}
|
||||
|
||||
//call service to get prisma entity
|
||||
const documentEntity: DocumentsNotary = await this.documentsNotaryService.delete(uid);
|
||||
|
||||
//Hydrate ressource with prisma entity
|
||||
const document = Document.hydrate<Document>(documentEntity, { strategy: "excludeAll" });
|
||||
|
||||
//success
|
||||
this.httpSuccess(response, document);
|
||||
} catch (error) {
|
||||
this.httpInternalError(response, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get a specific document by uid
|
||||
*/
|
||||
@Get("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler])
|
||||
protected async getOneByUid(req: Request, response: Response) {
|
||||
try {
|
||||
const uid = req.params["uid"];
|
||||
if (!uid) {
|
||||
this.httpBadRequest(response, "No uid provided");
|
||||
return;
|
||||
}
|
||||
//get query
|
||||
let query;
|
||||
if (req.query["q"]) {
|
||||
query = JSON.parse(req.query["q"] as string);
|
||||
}
|
||||
|
||||
const documentEntity = await this.documentsNotaryService.getByUid(uid, query);
|
||||
|
||||
if (!documentEntity) {
|
||||
this.httpNotFoundRequest(response, "document not found");
|
||||
return;
|
||||
}
|
||||
|
||||
//Hydrate ressource with prisma entity
|
||||
const document = Document.hydrate<Document>(documentEntity, { strategy: "excludeAll" });
|
||||
|
||||
//success
|
||||
this.httpSuccess(response, document);
|
||||
} catch (error) {
|
||||
this.httpInternalError(response, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
87
src/app/api/notary/DocumentsReminderController.ts
Normal file
87
src/app/api/notary/DocumentsReminderController.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { Response, Request } from "express";
|
||||
import { Controller, Get } from "@ControllerPattern/index";
|
||||
import ApiController from "@Common/system/controller-pattern/ApiController";
|
||||
import { Service } from "typedi";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { DocumentReminder } from "le-coffre-resources/dist/Notary";
|
||||
|
||||
import authHandler from "@App/middlewares/AuthHandler";
|
||||
|
||||
import DocumentsReminderService from "@Services/notary/DocumentsReminder/DocumentsReminder";
|
||||
// import NotificationBuilder from "@Common/notifications/NotificationBuilder";
|
||||
|
||||
@Controller()
|
||||
@Service()
|
||||
export default class DocumentsReminderController extends ApiController {
|
||||
constructor(
|
||||
private documentsReminderService: DocumentsReminderService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get all documents
|
||||
* @returns IDocument[] list of documents
|
||||
*/
|
||||
@Get("/api/v1/notary/document_reminders", [authHandler])
|
||||
protected async get(req: Request, response: Response) {
|
||||
try {
|
||||
//get query
|
||||
let query: Prisma.DocumentsReminderFindManyArgs = {};
|
||||
if (req.query["q"]) {
|
||||
query = JSON.parse(req.query["q"] as string);
|
||||
if(query.where?.uid) {
|
||||
this.httpBadRequest(response, "You can't filter by uid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//call service to get prisma entity
|
||||
const documentReminderEntities = await this.documentsReminderService.get(query);
|
||||
|
||||
//Hydrate ressource with prisma entity
|
||||
const documentReminders = DocumentReminder.hydrateArray<DocumentReminder>(documentReminderEntities, { strategy: "excludeAll" });
|
||||
|
||||
//success
|
||||
this.httpSuccess(response, documentReminders);
|
||||
} catch (error) {
|
||||
this.httpInternalError(response, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @description Get a specific document by uid
|
||||
// */
|
||||
// @Get("/api/v1/notary/document_reminders/:uid", [authHandler, ruleHandler, documentHandler])
|
||||
// protected async getOneByUid(req: Request, response: Response) {
|
||||
// try {
|
||||
// const uid = req.params["uid"];
|
||||
// if (!uid) {
|
||||
// this.httpBadRequest(response, "No uid provided");
|
||||
// return;
|
||||
// }
|
||||
// //get query
|
||||
// let query;
|
||||
// if (req.query["q"]) {
|
||||
// query = JSON.parse(req.query["q"] as string);
|
||||
// }
|
||||
|
||||
// const documentEntity = await this.documentsNotaryService.getByUid(uid, query);
|
||||
|
||||
// if (!documentEntity) {
|
||||
// this.httpNotFoundRequest(response, "document not found");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// //Hydrate ressource with prisma entity
|
||||
// const document = Document.hydrate<Document>(documentEntity, { strategy: "excludeAll" });
|
||||
|
||||
// //success
|
||||
// this.httpSuccess(response, document);
|
||||
// } catch (error) {
|
||||
// this.httpInternalError(response, error);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
}
|
@ -55,6 +55,8 @@ import StripeWebhooks from "@Common/webhooks/stripeWebhooks";
|
||||
import RulesGroupsController from "./api/admin/RulesGroupsController";
|
||||
import NotesController from "./api/customer/NotesController";
|
||||
import MailchimpController from "./api/notary/MailchimpController";
|
||||
import DocumentsReminderController from "./api/notary/DocumentsReminderController";
|
||||
import DocumentsNotaryController from "./api/notary/DocumentsNotaryController";
|
||||
|
||||
/**
|
||||
* @description This allow to declare all controllers used in the application
|
||||
@ -118,5 +120,7 @@ export default {
|
||||
Container.get(RulesGroupsController);
|
||||
Container.get(NotesController);
|
||||
Container.get(MailchimpController);
|
||||
Container.get(DocumentsReminderController)
|
||||
Container.get(DocumentsNotaryController)
|
||||
},
|
||||
};
|
||||
|
@ -0,0 +1,45 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "documents_notary" (
|
||||
"uid" TEXT NOT NULL,
|
||||
"folder_uid" VARCHAR(255) NOT NULL,
|
||||
"depositor_uid" VARCHAR(255) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "documents_notary_pkey" PRIMARY KEY ("uid")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "files_notary" (
|
||||
"uid" TEXT NOT NULL,
|
||||
"document_uid" VARCHAR(255) NOT NULL,
|
||||
"file_path" VARCHAR(255) NOT NULL,
|
||||
"file_name" VARCHAR(255) NOT NULL,
|
||||
"mimetype" VARCHAR(255) NOT NULL,
|
||||
"hash" VARCHAR(255) NOT NULL,
|
||||
"size" INTEGER NOT NULL,
|
||||
"archived_at" TIMESTAMP(3),
|
||||
"key" VARCHAR(255),
|
||||
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "files_notary_pkey" PRIMARY KEY ("uid")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "documents_notary_uid_key" ON "documents_notary"("uid");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "files_notary_uid_key" ON "files_notary"("uid");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "files_notary_file_path_key" ON "files_notary"("file_path");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "documents_notary" ADD CONSTRAINT "documents_notary_folder_uid_fkey" FOREIGN KEY ("folder_uid") REFERENCES "office_folders"("uid") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "documents_notary" ADD CONSTRAINT "documents_notary_depositor_uid_fkey" FOREIGN KEY ("depositor_uid") REFERENCES "users"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "files_notary" ADD CONSTRAINT "files_notary_document_uid_fkey" FOREIGN KEY ("document_uid") REFERENCES "documents_notary"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -0,0 +1,16 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "documents_reminder" (
|
||||
"uid" TEXT NOT NULL,
|
||||
"document_uid" VARCHAR(255) NOT NULL,
|
||||
"reminder_date" TIMESTAMP(3) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "documents_reminder_pkey" PRIMARY KEY ("uid")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "documents_reminder_uid_key" ON "documents_reminder"("uid");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "documents_reminder" ADD CONSTRAINT "documents_reminder_document_uid_fkey" FOREIGN KEY ("document_uid") REFERENCES "documents"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `archived_at` on the `files_notary` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `key` on the `files_notary` table. All the data in the column will be lost.
|
||||
- Added the required column `customer_uid` to the `documents_notary` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "documents_notary" ADD COLUMN "customer_uid" VARCHAR(255) NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "files_notary" DROP COLUMN "archived_at",
|
||||
DROP COLUMN "key";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "documents_notary" ADD CONSTRAINT "documents_notary_customer_uid_fkey" FOREIGN KEY ("customer_uid") REFERENCES "customers"("uid") ON DELETE CASCADE ON UPDATE CASCADE;
|
@ -69,6 +69,7 @@ model Users {
|
||||
votes Votes[]
|
||||
user_notifications UserNotifications[]
|
||||
seats Seats[]
|
||||
documents_notary DocumentsNotary[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
@ -129,6 +130,7 @@ model Customers {
|
||||
office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade)
|
||||
office_uid String @db.VarChar(255)
|
||||
notes Notes[]
|
||||
documents_notary DocumentsNotary[]
|
||||
@@map("customers")
|
||||
}
|
||||
|
||||
@ -170,6 +172,7 @@ model OfficeFolders {
|
||||
stakeholders Users[] @relation("OfficeFolderHasStakeholders")
|
||||
customers Customers[] @relation("OfficeFolderHasCustomers")
|
||||
documents Documents[]
|
||||
documents_notary DocumentsNotary[]
|
||||
|
||||
folder_anchor OfficeFolderAnchors? @relation(fields: [folder_anchor_uid], references: [uid])
|
||||
folder_anchor_uid String? @unique @db.VarChar(255)
|
||||
@ -215,10 +218,37 @@ model Documents {
|
||||
updated_at DateTime? @updatedAt
|
||||
files Files[]
|
||||
document_history DocumentHistory[]
|
||||
reminders DocumentsReminder[]
|
||||
|
||||
@@map("documents")
|
||||
}
|
||||
|
||||
model DocumentsNotary {
|
||||
uid String @id @unique @default(uuid())
|
||||
folder OfficeFolders @relation(fields: [folder_uid], references: [uid])
|
||||
folder_uid String @db.VarChar(255)
|
||||
depositor Users @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade)
|
||||
depositor_uid String @db.VarChar(255)
|
||||
created_at DateTime? @default(now())
|
||||
updated_at DateTime? @updatedAt
|
||||
files FilesNotary[]
|
||||
customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade)
|
||||
customer_uid String @db.VarChar(255)
|
||||
|
||||
@@map("documents_notary")
|
||||
}
|
||||
|
||||
model DocumentsReminder {
|
||||
uid String @id @unique @default(uuid())
|
||||
document Documents @relation(fields: [document_uid], references: [uid], onDelete: Cascade)
|
||||
document_uid String @db.VarChar(255)
|
||||
reminder_date DateTime
|
||||
created_at DateTime? @default(now())
|
||||
updated_at DateTime? @updatedAt
|
||||
|
||||
@@map("documents_reminder")
|
||||
}
|
||||
|
||||
model DocumentHistory {
|
||||
uid String @id @unique @default(uuid())
|
||||
document_status EDocumentStatus @default(ASKED)
|
||||
@ -248,6 +278,21 @@ model Files {
|
||||
@@map("files")
|
||||
}
|
||||
|
||||
model FilesNotary {
|
||||
uid String @id @unique @default(uuid())
|
||||
document_notary DocumentsNotary @relation(fields: [document_uid], references: [uid], onDelete: Cascade)
|
||||
document_uid String @db.VarChar(255)
|
||||
file_path String @unique @db.VarChar(255)
|
||||
file_name String @db.VarChar(255)
|
||||
mimetype String @db.VarChar(255)
|
||||
hash String @db.VarChar(255)
|
||||
size Int
|
||||
created_at DateTime? @default(now())
|
||||
updated_at DateTime? @updatedAt
|
||||
|
||||
@@map("files_notary")
|
||||
}
|
||||
|
||||
model DocumentTypes {
|
||||
uid String @id @unique @default(uuid())
|
||||
name String @db.VarChar(255)
|
||||
|
@ -7,6 +7,7 @@ import MailchimpService from "@Services/common/MailchimpService/MailchimpService
|
||||
import { BackendVariables } from "@Common/config/variables/Variables";
|
||||
import UsersService from "@Services/super-admin/UsersService/UsersService";
|
||||
import User from "le-coffre-resources/dist/SuperAdmin";
|
||||
import { Customer } from "le-coffre-resources/dist/Notary";
|
||||
|
||||
@Service()
|
||||
export default class EmailBuilder {
|
||||
@ -138,4 +139,31 @@ export default class EmailBuilder {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async sendReminder(customer: Customer, documents: Documents[]) {
|
||||
const to = customer.contact!.email;
|
||||
const templateVariables = {
|
||||
office_name: customer.office?.name,
|
||||
last_name: customer.contact!.last_name,
|
||||
first_name: customer.contact!.first_name,
|
||||
link: this.variables.APP_HOST,
|
||||
};
|
||||
|
||||
const templateName = ETemplates.DOCUMENT_REMINDER;
|
||||
const subject = "Vous avez des documents à déposer pour votre dossier.";
|
||||
|
||||
this.mailchimpService.create({
|
||||
templateName,
|
||||
to,
|
||||
subject,
|
||||
templateVariables,
|
||||
uid: "",
|
||||
from: null,
|
||||
cc: [],
|
||||
cci: [],
|
||||
sentAt: null,
|
||||
nbTrySend: null,
|
||||
lastTrySendDate: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,5 @@ export const ETemplates = {
|
||||
DOCUMENT_REFUSED: "DOCUMENT_REFUSED",
|
||||
DOCUMENT_RECAP: "DOCUMENT_RECAP",
|
||||
SUBSCRIPTION_INVITATION: "SUBSCRIPTION_INVITATION",
|
||||
DOCUMENT_REMINDER: "DOCUMENT_REMINDER",
|
||||
};
|
102
src/common/repositories/DocumentsNotaryRepository.ts
Normal file
102
src/common/repositories/DocumentsNotaryRepository.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import Database from "@Common/databases/database";
|
||||
import BaseRepository from "@Repositories/BaseRepository";
|
||||
import { Service } from "typedi";
|
||||
import { DocumentsNotary, Prisma } from "@prisma/client";
|
||||
import { DocumentNotary } from "le-coffre-resources/dist/Notary";
|
||||
|
||||
@Service()
|
||||
export default class DocumentsNotaryRepository extends BaseRepository {
|
||||
constructor(private database: Database) {
|
||||
super();
|
||||
}
|
||||
protected get model() {
|
||||
return this.database.getClient().documentsNotary;
|
||||
}
|
||||
protected get instanceDb() {
|
||||
return this.database.getClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find many documents
|
||||
*/
|
||||
public async findMany(query: Prisma.DocumentsNotaryFindManyArgs) {
|
||||
query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows);
|
||||
return this.model.findMany(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Create a document
|
||||
*/
|
||||
public async create(document: DocumentNotary): Promise<DocumentsNotary> {
|
||||
const createArgs: Prisma.DocumentsNotaryCreateArgs = {
|
||||
data: {
|
||||
folder: {
|
||||
connect: {
|
||||
uid: document.folder!.uid,
|
||||
},
|
||||
},
|
||||
depositor: {
|
||||
connect: {
|
||||
uid: document.depositor!.uid,
|
||||
},
|
||||
},
|
||||
customer: {
|
||||
connect: {
|
||||
uid: document.customer!.uid,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const documentCreated = await this.model.create({ ...createArgs });
|
||||
|
||||
return documentCreated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Delete a document
|
||||
*/
|
||||
public async delete(uid: string): Promise<DocumentsNotary> {
|
||||
return this.model.delete({
|
||||
where: {
|
||||
uid: uid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find unique document
|
||||
*/
|
||||
public async findOneByUid(uid: string, query?: Prisma.DocumentsNotaryInclude): Promise<DocumentsNotary | null> {
|
||||
return this.model.findUnique({
|
||||
where: {
|
||||
uid: uid,
|
||||
},
|
||||
include: query,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find unique document with relations
|
||||
*/
|
||||
public async findOneByUidWithOffice(uid: string) {
|
||||
return this.model.findUnique({
|
||||
where: {
|
||||
uid: uid,
|
||||
},
|
||||
include: { folder: { include: { office: true } } },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find unique document with relations
|
||||
*/
|
||||
public async findOneByUidWithFiles(uid: string) {
|
||||
return this.model.findUnique({
|
||||
where: {
|
||||
uid: uid,
|
||||
},
|
||||
include: { files: true },
|
||||
});
|
||||
}
|
||||
}
|
46
src/common/repositories/DocumentsReminderRepository.ts
Normal file
46
src/common/repositories/DocumentsReminderRepository.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import Database from "@Common/databases/database";
|
||||
import BaseRepository from "@Repositories/BaseRepository";
|
||||
import { Service } from "typedi";
|
||||
import { DocumentsReminder, Prisma } from "@prisma/client";
|
||||
import { DocumentReminder } from "le-coffre-resources/dist/Notary";
|
||||
|
||||
@Service()
|
||||
export default class DocumentsReminderRepository extends BaseRepository {
|
||||
constructor(private database: Database) {
|
||||
super();
|
||||
}
|
||||
protected get model() {
|
||||
return this.database.getClient().documentsReminder;
|
||||
}
|
||||
protected get instanceDb() {
|
||||
return this.database.getClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find many documents
|
||||
*/
|
||||
public async findMany(query: Prisma.DocumentsReminderFindManyArgs) {
|
||||
query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows);
|
||||
return this.model.findMany(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Create a document
|
||||
*/
|
||||
public async create(documentReminder: DocumentReminder): Promise<DocumentsReminder> {
|
||||
const createArgs: Prisma.DocumentsReminderCreateArgs = {
|
||||
data: {
|
||||
document: {
|
||||
connect: {
|
||||
uid: documentReminder.document!.uid,
|
||||
},
|
||||
},
|
||||
reminder_date: new Date(),
|
||||
},
|
||||
};
|
||||
|
||||
const documentReminderCreated = await this.model.create({ ...createArgs });
|
||||
|
||||
return documentReminderCreated;
|
||||
}
|
||||
}
|
83
src/common/repositories/FilesNotaryRepository.ts
Normal file
83
src/common/repositories/FilesNotaryRepository.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import Database from "@Common/databases/database";
|
||||
import BaseRepository from "@Repositories/BaseRepository";
|
||||
import { Service } from "typedi";
|
||||
import { FilesNotary, Prisma } from "@prisma/client";
|
||||
import { FileNotary } from "le-coffre-resources/dist/Notary";
|
||||
|
||||
@Service()
|
||||
export default class FilesNotaryRepository extends BaseRepository {
|
||||
constructor(private database: Database) {
|
||||
super();
|
||||
}
|
||||
protected get model() {
|
||||
return this.database.getClient().filesNotary;
|
||||
}
|
||||
protected get instanceDb() {
|
||||
return this.database.getClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find many files
|
||||
*/
|
||||
public async findMany(query: Prisma.FilesNotaryFindManyArgs) {
|
||||
query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows);
|
||||
return this.model.findMany(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Create a file linked to a document
|
||||
*/
|
||||
public async create(file: FileNotary, key: string): Promise<FilesNotary> {
|
||||
const createArgs: Prisma.FilesNotaryCreateArgs = {
|
||||
data: {
|
||||
document_notary: {
|
||||
connect: {
|
||||
uid: file.document!.uid,
|
||||
},
|
||||
},
|
||||
file_name: file.file_name,
|
||||
file_path: file.file_path,
|
||||
mimetype: file.mimetype,
|
||||
hash: file.hash,
|
||||
size: file.size,
|
||||
},
|
||||
};
|
||||
return this.model.create({ ...createArgs, include: { document_notary: true } });
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find unique file
|
||||
*/
|
||||
public async findOneByUid(uid: string, query?: Prisma.FilesNotaryInclude) {
|
||||
return this.model.findUnique({
|
||||
where: {
|
||||
uid: uid,
|
||||
},
|
||||
include: query,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find unique file with office
|
||||
*/
|
||||
public async findOneByUidWithOffice(uid: string) {
|
||||
return this.model.findUnique({
|
||||
where: {
|
||||
uid: uid,
|
||||
},
|
||||
include: { document_notary: { include: { folder: { include: { office: true } } } } },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Find unique file with document
|
||||
*/
|
||||
public async findOneByUidWithDocument(uid: string) {
|
||||
return this.model.findUnique({
|
||||
where: {
|
||||
uid: uid,
|
||||
},
|
||||
include: { document_notary: true },
|
||||
});
|
||||
}
|
||||
}
|
@ -8,6 +8,8 @@ import { BackendVariables } from "@Common/config/variables/Variables";
|
||||
import { Readable } from "stream";
|
||||
import { v4 } from "uuid";
|
||||
import { Files, Prisma } from "@prisma/client";
|
||||
import { FileNotary } from "le-coffre-resources/dist/Notary";
|
||||
import FilesNotaryRepository from "@Repositories/FilesNotaryRepository";
|
||||
|
||||
@Service()
|
||||
export default class FilesService extends BaseService {
|
||||
@ -16,6 +18,7 @@ export default class FilesService extends BaseService {
|
||||
private ipfsService: IpfsService,
|
||||
private variables: BackendVariables,
|
||||
private cryptoService: CryptoService,
|
||||
private filesNotaryRepository: FilesNotaryRepository
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -85,6 +88,27 @@ export default class FilesService extends BaseService {
|
||||
return this.filesRepository.create(fileToCreate, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Create a new file
|
||||
* @throws {Error} If file cannot be created
|
||||
*/
|
||||
public async createFileNotary(file: FileNotary, fileData: Express.Multer.File) {
|
||||
const key = v4();
|
||||
const encryptedFile = await this.cryptoService.encrypt(fileData.buffer, key);
|
||||
const hash = await this.cryptoService.getHash(fileData.buffer);
|
||||
|
||||
const upload = await this.ipfsService.pinFile(Readable.from(encryptedFile), fileData.originalname);
|
||||
let fileToCreate: FileNotary = file;
|
||||
fileToCreate.file_name = fileData.originalname;
|
||||
fileToCreate.file_path = this.variables.PINATA_GATEWAY.concat(upload.IpfsHash);
|
||||
fileToCreate.mimetype = fileData.mimetype;
|
||||
fileToCreate.size = fileData.size;
|
||||
fileToCreate.hash = hash;
|
||||
fileToCreate.archived_at = null;
|
||||
|
||||
return this.filesNotaryRepository.create(fileToCreate, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Modify a new file
|
||||
* @throws {Error} If file cannot be modified
|
||||
|
@ -124,14 +124,12 @@ export default class IdNotService extends BaseService {
|
||||
|
||||
const token = await fetch(this.variables.IDNOT_BASE_URL + this.variables.IDNOT_CONNEXION_URL + "?" + query, { method: "POST" });
|
||||
|
||||
if(token.status !== 200) console.error(await token.text());
|
||||
if (token.status !== 200) console.error(await token.text());
|
||||
|
||||
const decodedToken = (await token.json()) as IIdNotToken;
|
||||
|
||||
|
||||
const decodedIdToken = jwt.decode(decodedToken.id_token) as IdNotJwtPayload;
|
||||
|
||||
|
||||
return decodedIdToken;
|
||||
}
|
||||
|
||||
@ -297,7 +295,8 @@ export default class IdNotService extends BaseService {
|
||||
const searchParams = new URLSearchParams({
|
||||
key: this.variables.IDNOT_API_KEY,
|
||||
});
|
||||
const officeRawData = await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entities/${office.idNot}?` + searchParams, {
|
||||
|
||||
const officeRawData = await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entites/${office.idNot}?` + searchParams, {
|
||||
method: "GET",
|
||||
});
|
||||
if (officeRawData.status === 404) {
|
||||
@ -378,7 +377,7 @@ export default class IdNotService extends BaseService {
|
||||
},
|
||||
};
|
||||
|
||||
if(!userToAdd.contact.email) {
|
||||
if (!userToAdd.contact.email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -388,7 +387,7 @@ export default class IdNotService extends BaseService {
|
||||
userHydrated = User.hydrate<User>(user);
|
||||
const userOfficeHydrated = Office.hydrate<Office>(userOffice!);
|
||||
|
||||
if(office.length === 0) {
|
||||
if (office.length === 0) {
|
||||
const officeRoles = await this.officeRolesService.get({
|
||||
where: { office: { idNot: "0000" } },
|
||||
include: { office: true, rules: true },
|
||||
@ -406,7 +405,6 @@ export default class IdNotService extends BaseService {
|
||||
const deedTypesHydrated = DeedType.hydrateArray<DeedType>(deedTypes);
|
||||
const documentTypesHydrated = DocumentType.hydrateArray<DocumentType>(documentTypes);
|
||||
|
||||
|
||||
await this.duplicateOfficeRoles(officeRolesHydrated, userOfficeHydrated);
|
||||
const documentTypesCreated = await this.duplicateDocumentTypes(documentTypesHydrated, userOfficeHydrated);
|
||||
await this.duplicateDeedTypes(deedTypesHydrated, documentTypesCreated, userOfficeHydrated);
|
||||
@ -427,37 +425,35 @@ export default class IdNotService extends BaseService {
|
||||
|
||||
public async duplicateDocumentTypes(documentTypes: DocumentType[], office: Office): Promise<DocumentType[]> {
|
||||
let newDocumentTypes: DocumentType[] = [];
|
||||
for(const documentType of documentTypes) {
|
||||
for (const documentType of documentTypes) {
|
||||
documentType.office = office;
|
||||
const documentTypeCreated = await this.documentTypesService.create(documentType);
|
||||
newDocumentTypes.push(DocumentType.hydrate<DocumentType>(documentTypeCreated));
|
||||
};
|
||||
}
|
||||
return newDocumentTypes;
|
||||
}
|
||||
|
||||
public async duplicateDeedTypes(deedTypes: DeedType[], documentTypes: DocumentType[], office: Office) {
|
||||
for (const deedType of deedTypes) {
|
||||
for (const deedType of deedTypes) {
|
||||
let newDocumentTypes: DocumentType[] = [];
|
||||
for (const document of deedType.document_types!) {
|
||||
for (const document of deedType.document_types!) {
|
||||
const newDocumentType = documentTypes.find((documentType) => documentType.name === document.name);
|
||||
if(!newDocumentType) continue;
|
||||
if (!newDocumentType) continue;
|
||||
newDocumentTypes.push(newDocumentType!);
|
||||
};
|
||||
}
|
||||
deedType.document_types = newDocumentTypes;
|
||||
deedType.office = office;
|
||||
await this.deedTypesService.create(deedType);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async duplicateOfficeRoles(officeRoles: OfficeRole[], office: Office){
|
||||
for(const officeRole of officeRoles) {
|
||||
public async duplicateOfficeRoles(officeRoles: OfficeRole[], office: Office) {
|
||||
for (const officeRole of officeRoles) {
|
||||
officeRole.office = office;
|
||||
await this.officeRolesService.create(officeRole);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async updateUsers() {
|
||||
const usersReq = await this.userService.getUsersToBeChecked();
|
||||
const users = User.hydrateArray<User>(usersReq);
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { Customers, Prisma } from "@prisma/client";
|
||||
import EmailBuilder from "@Common/emails/EmailBuilder";
|
||||
import { Customers, Documents, Prisma } from "@prisma/client";
|
||||
import CustomersRepository from "@Repositories/CustomersRepository";
|
||||
import BaseService from "@Services/BaseService";
|
||||
import { Customer } from "le-coffre-resources/dist/Notary";
|
||||
import { Customer, DocumentReminder } from "le-coffre-resources/dist/Notary";
|
||||
import { Service } from "typedi";
|
||||
import DocumentsReminderService from "../DocumentsReminder/DocumentsReminder";
|
||||
|
||||
@Service()
|
||||
export default class CustomersService extends BaseService {
|
||||
constructor(private customerRepository: CustomersRepository) {
|
||||
constructor(private customerRepository: CustomersRepository, private emailBuilder: EmailBuilder, private documentsReminderService : DocumentsReminderService) {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -49,4 +51,17 @@ export default class CustomersService extends BaseService {
|
||||
public async getByContact(contactUid: string): Promise<Customers | null> {
|
||||
return this.customerRepository.findOneByContact(contactUid);
|
||||
}
|
||||
|
||||
public async sendDocumentsReminder(customer: Customer, documents: Documents[]): Promise<void> {
|
||||
//Call email builder to send mail
|
||||
const email = this.emailBuilder.sendReminder(customer, documents);
|
||||
//Call DocumentsReminder service to create add the reminder in database
|
||||
if (!email) return;
|
||||
for (const document of documents) {
|
||||
//Create document reminder
|
||||
const documentReminder = new DocumentReminder();
|
||||
documentReminder.document = document;
|
||||
await this.documentsReminderService.create(documentReminder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
import { DocumentsNotary, Prisma } from "@prisma/client";
|
||||
import { Document, DocumentNotary } from "le-coffre-resources/dist/Notary";
|
||||
import DocumentsNotaryRepository from "@Repositories/DocumentsNotaryRepository";
|
||||
import BaseService from "@Services/BaseService";
|
||||
import { Service } from "typedi";
|
||||
|
||||
@Service()
|
||||
export default class DocumentsService extends BaseService {
|
||||
constructor(private documentsNotaryRepository: DocumentsNotaryRepository) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Get all documents
|
||||
* @throws {Error} If documents cannot be get
|
||||
*/
|
||||
public async get(query: Prisma.DocumentsNotaryFindManyArgs) {
|
||||
return this.documentsNotaryRepository.findMany(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Create a new document
|
||||
* @throws {Error} If document cannot be created
|
||||
*/
|
||||
public async create(document: DocumentNotary): Promise<DocumentsNotary> {
|
||||
return this.documentsNotaryRepository.create(document);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Delete a document
|
||||
* @throws {Error} If document cannot be deleted
|
||||
*/
|
||||
public async delete(uid: string): Promise<DocumentsNotary> {
|
||||
const documentEntity = await this.documentsNotaryRepository.findOneByUid(uid, { files: true });
|
||||
if (!documentEntity) throw new Error("document not found");
|
||||
const document = Document.hydrate<Document>(documentEntity, { strategy: "excludeAll" });
|
||||
|
||||
const isDocumentEmpty = document.files && !document!.files.find((file) => file.archived_at === null);
|
||||
|
||||
if (!isDocumentEmpty && document.document_status !== "REFUSED") {
|
||||
throw new Error("Can't delete a document with file");
|
||||
}
|
||||
return this.documentsNotaryRepository.delete(uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Get a document by uid
|
||||
* @throws {Error} If document cannot be get by uid
|
||||
*/
|
||||
public async getByUid(uid: string, query?: Prisma.DocumentsNotaryInclude): Promise<DocumentsNotary | null> {
|
||||
return this.documentsNotaryRepository.findOneByUid(uid, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Get a document by uid
|
||||
* @throws {Error} If document cannot be get by uid
|
||||
*/
|
||||
public async getByUidWithOffice(uid: string) {
|
||||
return this.documentsNotaryRepository.findOneByUidWithOffice(uid);
|
||||
}
|
||||
}
|
28
src/services/notary/DocumentsReminder/DocumentsReminder.ts
Normal file
28
src/services/notary/DocumentsReminder/DocumentsReminder.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { DocumentsReminder, Prisma } from "@prisma/client";
|
||||
import { DocumentReminder } from "le-coffre-resources/dist/Notary";
|
||||
import BaseService from "@Services/BaseService";
|
||||
import { Service } from "typedi";
|
||||
import DocumentsReminderRepository from "@Repositories/DocumentsReminderRepository";
|
||||
|
||||
@Service()
|
||||
export default class DocumentsReminderService extends BaseService {
|
||||
constructor(private documentsReminderRepository: DocumentsReminderRepository) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Get all documents
|
||||
* @throws {Error} If documents cannot be get
|
||||
*/
|
||||
public async get(query: Prisma.DocumentsReminderFindManyArgs) {
|
||||
return this.documentsReminderRepository.findMany(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : Create a new document
|
||||
* @throws {Error} If document cannot be created
|
||||
*/
|
||||
public async create(document: DocumentReminder): Promise<DocumentsReminder> {
|
||||
return this.documentsReminderRepository.create(document);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user