diff --git a/logfile b/logfile new file mode 100644 index 00000000..6aab8728 --- /dev/null +++ b/logfile @@ -0,0 +1,39 @@ +2024-07-16 10:29:09.983 CEST [21605] LOG: starting PostgreSQL 14.11 (Homebrew) on aarch64-apple-darwin22.6.0, compiled by Apple clang version 15.0.0 (clang-1500.1.0.2.5), 64-bit +2024-07-16 10:29:09.985 CEST [21605] LOG: listening on IPv6 address "::1", port 5432 +2024-07-16 10:29:09.985 CEST [21605] LOG: listening on IPv4 address "127.0.0.1", port 5432 +2024-07-16 10:29:09.986 CEST [21605] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432" +2024-07-16 10:29:09.996 CEST [21606] LOG: database system was shut down at 2024-05-04 13:45:41 CEST +2024-07-16 10:29:10.002 CEST [21605] LOG: database system is ready to accept connections +2024-07-16 10:29:13.986 CEST [21782] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:29:13.986 CEST [21782] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:29:14.132 CEST [21798] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:29:14.132 CEST [21798] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:29:30.826 CEST [22049] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:29:30.826 CEST [22049] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:29:49.261 CEST [22461] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:29:49.261 CEST [22461] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:29:49.463 CEST [22463] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:29:49.463 CEST [22463] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:32:09.636 CEST [24972] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:32:09.636 CEST [24972] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:32:09.903 CEST [24974] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:32:09.903 CEST [24974] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:32:13.659 CEST [25042] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:32:13.659 CEST [25042] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:32:13.960 CEST [25044] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:32:13.960 CEST [25044] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:32:26.772 CEST [25252] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:32:26.772 CEST [25252] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:32:45.488 CEST [25549] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:32:45.488 CEST [25549] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE 1=1 ORDER BY "public"."customers"."uid" ASC LIMIT $1 OFFSET $2 +2024-07-16 10:33:22.589 CEST [26127] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:33:22.589 CEST [26127] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:42:51.648 CEST [37340] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:42:51.648 CEST [37340] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:42:51.859 CEST [37342] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:42:51.859 CEST [37342] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21) OFFSET $22 +2024-07-16 10:42:56.305 CEST [37406] ERROR: column customers.office_uid does not exist at character 36 +2024-07-16 10:42:56.305 CEST [37406] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."office_uid" IN ($1,$2) OFFSET $3 +2024-07-16 10:45:03.049 CEST [39643] LOG: could not receive data from client: Connection reset by peer +2024-07-16 10:45:03.062 CEST [39645] ERROR: relation "_prisma_migrations" does not exist at character 126 +2024-07-16 10:45:03.062 CEST [39645] STATEMENT: SELECT "id", "checksum", "finished_at", "migration_name", "logs", "rolled_back_at", "started_at", "applied_steps_count" FROM "_prisma_migrations" ORDER BY "started_at" ASC diff --git a/package.json b/package.json index ba8a4fdc..0483be4f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/api/customer/DocumentsNotaryController.ts b/src/app/api/customer/DocumentsNotaryController.ts new file mode 100644 index 00000000..4fd90e4e --- /dev/null +++ b/src/app/api/customer/DocumentsNotaryController.ts @@ -0,0 +1,50 @@ +import { Response, Request } from "express"; +import { Controller, Get } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Service } from "typedi"; +import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; +import { Prisma } from "@prisma/client"; +import { DocumentNotary } from "le-coffre-resources/dist/Notary"; +import authHandler from "@App/middlewares/AuthHandler"; +// import NotificationBuilder from "@Common/notifications/NotificationBuilder"; + +@Controller() +@Service() +export default class DocumentsNotaryController extends ApiController { + constructor( + private documentsNotaryService: DocumentsNotaryService, + ) { + super(); + } + + /** + * @description Get all documents + * @returns IDocument[] list of documents + */ + @Get("/api/v1/customer/documents_notary", [authHandler]) + 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; + } + } + + //call service to get prisma entity + const documentEntities = await this.documentsNotaryService.get(query); + + //Hydrate ressource with prisma entity + const documents = DocumentNotary.hydrateArray(documentEntities, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, documents); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/api/notary/CustomersController.ts b/src/app/api/notary/CustomersController.ts index 83d3d1c9..ebb1b541 100644 --- a/src/app/api/notary/CustomersController.ts +++ b/src/app/api/notary/CustomersController.ts @@ -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,55 @@ 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) { + const documentEntity = await this.documentsService.getByUid(documentUid, { document_type: true, folder: true }); + + if (!documentEntity) { + this.httpBadRequest(response, "Document not found"); + return; + } + documentEntities.push(documentEntity); + } + + const customerEntity = await this.customersService.getByUid(uid, { contact: true, office: true }); + + if (!customerEntity) { + this.httpNotFoundRequest(response, "customer not found"); + return; + } + + //Hydrate ressource with prisma entity + const customer = Customer.hydrate(customerEntity, { strategy: "excludeAll" }); + + // Call service to send reminder with documents + await this.customersService.sendDocumentsReminder(customer, documentEntities); + + // Success + this.httpSuccess(response, customer); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } } diff --git a/src/app/api/notary/DocumentsController.ts b/src/app/api/notary/DocumentsController.ts index 7e866c8e..f4af409c 100644 --- a/src/app/api/notary/DocumentsController.ts +++ b/src/app/api/notary/DocumentsController.ts @@ -35,15 +35,15 @@ export default class DocumentsController extends ApiController { let query: Prisma.DocumentsFindManyArgs = {}; if (req.query["q"]) { query = JSON.parse(req.query["q"] as string); - if(query.where?.uid) { + 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 = { document_type: { office: officeWhereInput } }; - query.where.document_type!.office = officeWhereInput; + + query.where = { ...query.where, document_type: { office: officeWhereInput } }; //call service to get prisma entity const documentEntities = await this.documentsService.get(query); @@ -124,7 +124,7 @@ export default class DocumentsController extends ApiController { return; } - if(documentFound.document_status === EDocumentStatus.REFUSED || documentFound.document_status === EDocumentStatus.VALIDATED) { + if (documentFound.document_status === EDocumentStatus.REFUSED || documentFound.document_status === EDocumentStatus.VALIDATED) { this.httpForbidden(response, "You are not allowed to update a VALIDATED or REFUSED document"); return; } @@ -186,11 +186,9 @@ export default class DocumentsController extends ApiController { //call service to get prisma entity const documentEntityUpdated: Documents = await this.documentsService.refuse(uid, documentEntity, req.body.refused_reason); - //create email for asked document await this.emailBuilder.sendDocumentEmails(documentEntityUpdated); - //Hydrate ressource with prisma entity const document = Document.hydrate(documentEntityUpdated, { strategy: "excludeAll" }); diff --git a/src/app/api/notary/DocumentsNotaryController.ts b/src/app/api/notary/DocumentsNotaryController.ts new file mode 100644 index 00000000..900a54d5 --- /dev/null +++ b/src/app/api/notary/DocumentsNotaryController.ts @@ -0,0 +1,137 @@ +import authHandler from "@App/middlewares/AuthHandler"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; +import { DocumentsNotary, Prisma } from "@prisma/client"; +import CustomersService from "@Services/admin/CustomersService/CustomersService"; +import FilesNotaryService from "@Services/common/FilesNotaryService/FilesNotaryService"; +import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; +import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; +import UsersService from "@Services/notary/UsersService/UsersService"; +import { Request, Response } from "express"; +import { DocumentNotary, FileNotary } from "le-coffre-resources/dist/Notary"; +import { Service } from "typedi"; + +@Controller() +@Service() +export default class DocumentsNotaryController extends ApiController { + constructor( + private documentsNotaryService: DocumentsNotaryService, + private officeFoldersService: OfficeFoldersService, + private customerService: CustomersService, + private userService: UsersService, + private filesNotaryService: FilesNotaryService, + ) { + super(); + } + + /** + * @description Get all documents + * @returns IDocument[] list of documents + */ + @Get("/api/v1/notary/documents_notary", [authHandler]) + 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; + } + } + + //call service to get prisma entity + const documentEntities = await this.documentsNotaryService.get(query); + + //Hydrate ressource with prisma entity + const documents = DocumentNotary.hydrateArray(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; + + const folder = await this.officeFoldersService.getByUid(req.body.folderUid); + if (!folder) return; + + const user = await this.userService.getByUid(req.body.user.userId); + if (!user) return; + + const documentNotaryEntity = DocumentNotary.hydrate({ + customer: customer, + folder: folder, + depositor: user, + }); + + const documentNotaryEntityCreated = await this.documentsNotaryService.create(documentNotaryEntity); + + const query = JSON.stringify({ document: { uid: documentNotaryEntityCreated.uid } }); + + const fileEntity = FileNotary.hydrate(JSON.parse(query)); + + const fileEntityCreated = await this.filesNotaryService.create(fileEntity, req.file!); + if (!fileEntityCreated) { + this.httpBadRequest(response, "File could not be created"); + return; + } + + const documentNotary = await this.documentsNotaryService.getByUid(documentNotaryEntityCreated.uid); + + const document = DocumentNotary.hydrate(documentNotary!); + //success + this.httpCreated(response, document); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Delete a specific document + */ + @Delete("/api/v1/notary/documents_notary/:uid", [authHandler]) + protected async delete(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const documentNotaryFound = await this.documentsNotaryService.getByUid(uid); + + if (!documentNotaryFound) { + this.httpNotFoundRequest(response, "document not found"); + return; + } + + //call service to get prisma entity + const documentNotaryEntity: DocumentsNotary = await this.documentsNotaryService.delete(uid); + + //Hydrate ressource with prisma entity + const documentNotary = DocumentNotary.hydrate(documentNotaryEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, documentNotary); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/api/notary/DocumentsReminderController.ts b/src/app/api/notary/DocumentsReminderController.ts new file mode 100644 index 00000000..27829d6d --- /dev/null +++ b/src/app/api/notary/DocumentsReminderController.ts @@ -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(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(documentEntity, { strategy: "excludeAll" }); + + // //success + // this.httpSuccess(response, document); + // } catch (error) { + // this.httpInternalError(response, error); + // return; + // } + // } +} diff --git a/src/app/api/notary/FilesController.ts b/src/app/api/notary/FilesController.ts index 46a4969b..a630218c 100644 --- a/src/app/api/notary/FilesController.ts +++ b/src/app/api/notary/FilesController.ts @@ -27,14 +27,14 @@ export default class FilesController extends ApiController { let query: Prisma.FilesFindManyArgs = {}; if (req.query["q"]) { query = JSON.parse(req.query["q"] as string); - if(query.where?.uid) { + 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 = { document: { folder: {office: officeWhereInput}}}; + const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId }; + if (!query.where) query.where = { document: { folder: { office: officeWhereInput } } }; query.where.document!.folder!.office = officeWhereInput; //call service to get prisma entity const fileEntities = await this.filesService.get(query); diff --git a/src/app/api/notary/FilesNotaryController.ts b/src/app/api/notary/FilesNotaryController.ts new file mode 100644 index 00000000..acda6176 --- /dev/null +++ b/src/app/api/notary/FilesNotaryController.ts @@ -0,0 +1,109 @@ +import authHandler from "@App/middlewares/AuthHandler"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Controller, Get } from "@ControllerPattern/index"; +import { Prisma } from "@prisma/client"; +import FilesNotaryService from "@Services/common/FilesNotaryService/FilesNotaryService"; + +import { Request, Response } from "express"; +import { File } from "le-coffre-resources/dist/Notary"; +import { Service } from "typedi"; + +@Controller() +@Service() +export default class FilesNotaryController extends ApiController { + constructor(private filesNotaryService: FilesNotaryService) { + super(); + } + + /** + * @description Get all Files + * @returns File[] list of Files + */ + @Get("/api/v1/notary/files-notary", [authHandler]) + protected async get(req: Request, response: Response) { + try { + //get query + let query: Prisma.FilesFindManyArgs = {}; + 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 = { document: { folder: { office: officeWhereInput } } }; + query.where.document!.folder!.office = officeWhereInput; + //call service to get prisma entity + const fileEntities = await this.filesNotaryService.get(query); + + //Hydrate ressource with prisma entity + const files = File.hydrateArray(fileEntities, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, files); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } + + /** + * @description Get a specific File by uid + */ + @Get("/api/v1/notary/files-notary/download/:uid", [authHandler]) + protected async download(req: Request, response: Response) { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "uid not found"); + return; + } + try { + const fileInfo = await this.filesNotaryService.download(uid); + + if (!fileInfo) { + this.httpNotFoundRequest(response, "file not found"); + return; + } + + response.setHeader("Content-Type", fileInfo.file.mimetype); + response.setHeader("Content-Disposition", `inline; filename=${encodeURIComponent(fileInfo.file.file_name)}`); + + this.httpSuccess(response, fileInfo.buffer); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Get a specific File by uid + */ + @Get("/api/v1/notary/files-notary/:uid", [authHandler]) + protected async getOneByUid(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const fileEntity = await this.filesNotaryService.getByUid(uid); + + if (!fileEntity) { + this.httpNotFoundRequest(response, "file not found"); + return; + } + + //Hydrate ressource with prisma entity + const file = File.hydrate(fileEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, file); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/api/notary/OfficeFolderAnchorsController.ts b/src/app/api/notary/OfficeFolderAnchorsController.ts index 96bba906..da4cc236 100644 --- a/src/app/api/notary/OfficeFolderAnchorsController.ts +++ b/src/app/api/notary/OfficeFolderAnchorsController.ts @@ -90,20 +90,20 @@ export default class OfficeFoldersController extends ApiController { const sortedHashes = [...folderHashes].sort(); const anchoringProof = await this.secureService.download(sortedHashes, officeFolder.office!.name); - const addFileToZip = (zip: Zip) => (uid: string): Promise => - (async () => { - const data = await this.filesService.download(uid); - if (!data?.buffer) return; - zip.addFile(`Documents du client/${uid}-${data.file.file_name}`, data.buffer); - })() + const addFileToZip = + (zip: Zip) => + (uid: string): Promise => + (async () => { + const data = await this.filesService.download(uid); + if (!data?.buffer) return; + zip.addFile(`Documents du client/${uid}-${data.file.file_name}`, data.buffer); + })(); const uids: string[] = folderFilesUid.filter((uid): uid is string => uid !== undefined); const zip = new Zip(); zip.addFile("Certificat de dépôt du dossier.pdf", anchoringProof); - await Promise.allSettled( - uids.map(addFileToZip(zip)) - ) + await Promise.allSettled(uids.map(addFileToZip(zip))); response.setHeader("Content-Type", "application/zip"); this.httpSuccess(response, zip.toBuffer()); @@ -132,6 +132,11 @@ export default class OfficeFoldersController extends ApiController { files: true, }, }, + documents_notary: { + include: { + files: true, + }, + }, folder_anchor: true, }; @@ -276,7 +281,7 @@ export default class OfficeFoldersController extends ApiController { let query: Prisma.OfficeFolderAnchorsFindManyArgs = {}; if (req.query["q"]) { query = JSON.parse(req.query["q"] as string); - if(query.where?.uid) { + if (query.where?.uid) { this.httpBadRequest(response, "You can't filter by uid"); return; } diff --git a/src/app/index.ts b/src/app/index.ts index 3b747d8a..f2bcaf1c 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -55,6 +55,9 @@ 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"; +import FilesNotaryController from "./api/notary/FilesNotaryController"; /** * @description This allow to declare all controllers used in the application @@ -118,5 +121,8 @@ export default { Container.get(RulesGroupsController); Container.get(NotesController); Container.get(MailchimpController); + Container.get(DocumentsReminderController); + Container.get(DocumentsNotaryController); + Container.get(FilesNotaryController); }, }; diff --git a/src/common/databases/migrations/20240830075809_notary_documents/migration.sql b/src/common/databases/migrations/20240830075809_notary_documents/migration.sql new file mode 100644 index 00000000..c0136620 --- /dev/null +++ b/src/common/databases/migrations/20240830075809_notary_documents/migration.sql @@ -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; diff --git a/src/common/databases/migrations/20240906073736_email_reminder/migration.sql b/src/common/databases/migrations/20240906073736_email_reminder/migration.sql new file mode 100644 index 00000000..04034ca4 --- /dev/null +++ b/src/common/databases/migrations/20240906073736_email_reminder/migration.sql @@ -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; diff --git a/src/common/databases/migrations/20240909032319_document_notary_customer/migration.sql b/src/common/databases/migrations/20240909032319_document_notary_customer/migration.sql new file mode 100644 index 00000000..9b636a65 --- /dev/null +++ b/src/common/databases/migrations/20240909032319_document_notary_customer/migration.sql @@ -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; diff --git a/src/common/databases/migrations/20240910081445_document_name/migration.sql b/src/common/databases/migrations/20240910081445_document_name/migration.sql new file mode 100644 index 00000000..97e5f5c0 --- /dev/null +++ b/src/common/databases/migrations/20240910081445_document_name/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "documents_notary" ADD COLUMN "document_name" VARCHAR(255) NOT NULL DEFAULT ''; diff --git a/src/common/databases/migrations/20240910081547_name/migration.sql b/src/common/databases/migrations/20240910081547_name/migration.sql new file mode 100644 index 00000000..18e8d895 --- /dev/null +++ b/src/common/databases/migrations/20240910081547_name/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `document_name` on the `documents_notary` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "documents_notary" DROP COLUMN "document_name", +ADD COLUMN "document" VARCHAR(255) NOT NULL DEFAULT ''; diff --git a/src/common/databases/migrations/20240912095804_add_key_for_files_notary/migration.sql b/src/common/databases/migrations/20240912095804_add_key_for_files_notary/migration.sql new file mode 100644 index 00000000..c44e5fda --- /dev/null +++ b/src/common/databases/migrations/20240912095804_add_key_for_files_notary/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "files_notary" ADD COLUMN "key" VARCHAR(255); diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 259c59f1..8e58f283 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -37,7 +37,7 @@ model Contacts { last_name String @db.VarChar(255) email String @db.VarChar(255) phone_number String? @db.VarChar(50) - cell_phone_number String? @db.VarChar(50) + cell_phone_number String? @db.VarChar(50) civility ECivility @default(MALE) address Addresses? @relation(fields: [address_uid], references: [uid], onDelete: Cascade) address_uid String? @unique @db.VarChar(255) @@ -51,24 +51,25 @@ model Contacts { } model Users { - uid String @id @unique @default(uuid()) @map("uid") - idNot String @unique @db.VarChar(255) - contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade) - contact_uid String @unique @db.VarChar(255) - role Roles @relation(fields: [roles_uid], references: [uid], onDelete: Cascade) - roles_uid String @db.VarChar(255) - office_role OfficeRoles? @relation(fields: [office_role_uid], references: [uid], onDelete: Cascade) - office_role_uid String? @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - checked_at DateTime? - office_membership Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) - office_uid String @db.VarChar(255) - office_folders OfficeFolders[] @relation("OfficeFolderHasStakeholders") - appointment Appointments[] - votes Votes[] + uid String @id @unique @default(uuid()) @map("uid") + idNot String @unique @db.VarChar(255) + contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade) + contact_uid String @unique @db.VarChar(255) + role Roles @relation(fields: [roles_uid], references: [uid], onDelete: Cascade) + roles_uid String @db.VarChar(255) + office_role OfficeRoles? @relation(fields: [office_role_uid], references: [uid], onDelete: Cascade) + office_role_uid String? @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + checked_at DateTime? + office_membership Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) + office_uid String @db.VarChar(255) + office_folders OfficeFolders[] @relation("OfficeFolderHasStakeholders") + appointment Appointments[] + votes Votes[] user_notifications UserNotifications[] - seats Seats[] + seats Seats[] + documents_notary DocumentsNotary[] @@map("users") } @@ -79,6 +80,7 @@ model Whitelist { active Boolean @default(true) created_at DateTime? @default(now()) updated_at DateTime? @updatedAt + @@map("whitelist") } @@ -88,6 +90,7 @@ model UserWhitelist { active Boolean @default(true) created_at DateTime? @default(now()) updated_at DateTime? @updatedAt + @@map("user_whitelist") } @@ -116,109 +119,140 @@ model Offices { } model Customers { - uid String @id @unique @default(uuid()) - status ECustomerStatus @default(PENDING) - contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade) - contact_uid String @unique @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - office_folders OfficeFolders[] @relation("OfficeFolderHasCustomers") - documents Documents[] - password String? @db.VarChar(255) - totpCodes TotpCodes[] - office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) - office_uid String @db.VarChar(255) - notes Notes[] + uid String @id @unique @default(uuid()) + status ECustomerStatus @default(PENDING) + contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade) + contact_uid String @unique @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + office_folders OfficeFolders[] @relation("OfficeFolderHasCustomers") + documents Documents[] + password String? @db.VarChar(255) + totpCodes TotpCodes[] + office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) + office_uid String @db.VarChar(255) + notes Notes[] + documents_notary DocumentsNotary[] + @@map("customers") } model Notifications { - uid String @id @unique @default(uuid()) - message String @db.VarChar(1000) - redirection_url String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - userNotifications UserNotifications[] + uid String @id @unique @default(uuid()) + message String @db.VarChar(1000) + redirection_url String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + userNotifications UserNotifications[] @@map("notifications") } model UserNotifications { - uid String @id @unique @default(uuid()) - user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) - user_uid String @db.VarChar(255) - read Boolean @default(false) - notification Notifications @relation(fields: [notification_uid], references: [uid], onDelete: Cascade) - notification_uid String @db.VarChar(255) + uid String @id @unique @default(uuid()) + user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) + user_uid String @db.VarChar(255) + read Boolean @default(false) + notification Notifications @relation(fields: [notification_uid], references: [uid], onDelete: Cascade) + notification_uid String @db.VarChar(255) @@map("user_notifications") } model OfficeFolders { - uid String @id @unique @default(uuid()) - folder_number String @db.VarChar(255) - name String @db.VarChar(255) - description String? @db.VarChar(1000) - archived_description String? @db.VarChar(255) - status EFolderStatus @default(LIVE) - deed Deeds @relation(fields: [deed_uid], references: [uid], onDelete: Cascade) - deed_uid String @unique @db.VarChar(255) - office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) - office_uid String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - stakeholders Users[] @relation("OfficeFolderHasStakeholders") - customers Customers[] @relation("OfficeFolderHasCustomers") + uid String @id @unique @default(uuid()) + folder_number String @db.VarChar(255) + name String @db.VarChar(255) + description String? @db.VarChar(1000) + archived_description String? @db.VarChar(255) + status EFolderStatus @default(LIVE) + deed Deeds @relation(fields: [deed_uid], references: [uid], onDelete: Cascade) + deed_uid String @unique @db.VarChar(255) + office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) + office_uid String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + 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) - notes Notes[] + folder_anchor OfficeFolderAnchors? @relation(fields: [folder_anchor_uid], references: [uid]) + folder_anchor_uid String? @unique @db.VarChar(255) + notes Notes[] @@unique([folder_number, office_uid]) @@map("office_folders") } model OfficeFolderAnchors { - uid String @id @unique @default(uuid()) + uid String @id @unique @default(uuid()) - hash_sources String[] - root_hash String @db.VarChar(255) + hash_sources String[] + root_hash String @db.VarChar(255) - blockchain EBlockchainName @default(TEZOS) - status EAnchoringStatus @default(QUEUED) - anchor_nb_try Int @default(0) + blockchain EBlockchainName @default(TEZOS) + status EAnchoringStatus @default(QUEUED) + anchor_nb_try Int @default(0) - anchored_at DateTime? - tx_id String? @db.VarChar(255) - tx_link String? @db.VarChar(255) - tx_hash String? @db.VarChar(255) + anchored_at DateTime? + tx_id String? @db.VarChar(255) + tx_link String? @db.VarChar(255) + tx_hash String? @db.VarChar(255) - folder OfficeFolders? + folder OfficeFolders? - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt @@map("office_folder_anchors") } model Documents { - uid String @id @unique @default(uuid()) - document_status EDocumentStatus @default(ASKED) - document_type DocumentTypes @relation(fields: [document_type_uid], references: [uid]) - document_type_uid String @db.VarChar(255) - folder OfficeFolders @relation(fields: [folder_uid], references: [uid]) - folder_uid String @db.VarChar(255) - depositor Customers @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade) - depositor_uid String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - files Files[] - document_history DocumentHistory[] + uid String @id @unique @default(uuid()) + document_status EDocumentStatus @default(ASKED) + document_type DocumentTypes @relation(fields: [document_type_uid], references: [uid]) + document_type_uid String @db.VarChar(255) + folder OfficeFolders @relation(fields: [folder_uid], references: [uid]) + folder_uid String @db.VarChar(255) + depositor Customers @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade) + depositor_uid String @db.VarChar(255) + created_at DateTime? @default(now()) + 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) + document String @default("") @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 +282,22 @@ 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 + key String? @db.VarChar(255) + 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) @@ -327,8 +377,9 @@ model Rules { updated_at DateTime? @updatedAt role Roles[] @relation("RolesHasRules") office_roles OfficeRoles[] @relation("OfficeRolesHasRules") - namespace String @db.VarChar(255) @default("notary") + namespace String @default("notary") @db.VarChar(255) groups RulesGroups[] @relation("RulesGroupsHasRules") + @@map("rules") } @@ -338,8 +389,9 @@ model RulesGroups { created_at DateTime? @default(now()) updated_at DateTime? @updatedAt rules Rules[] @relation("RulesGroupsHasRules") + @@map("rules_groups") -} +} model Emails { uid String @id @unique @default(uuid()) @@ -380,54 +432,58 @@ model Votes { } model TotpCodes { - uid String @id @unique @default(uuid()) - customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) - customer_uid String @db.VarChar(255) - code String @db.VarChar(255) - reason TotpCodesReasons @default(LOGIN) - resent Boolean @default(false) - expire_at DateTime? @default(now()) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + uid String @id @unique @default(uuid()) + customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) + customer_uid String @db.VarChar(255) + code String @db.VarChar(255) + reason TotpCodesReasons @default(LOGIN) + resent Boolean @default(false) + expire_at DateTime? @default(now()) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + @@map("totp_codes") } model Subscriptions { - uid String @id @unique @default(uuid()) - type ESubscriptionType - status ESubscriptionStatus @default(ACTIVE) - stripe_subscription_id String @db.VarChar(255) - start_date DateTime @default(now()) - end_date DateTime - nb_seats Int - office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) - office_uid String @db.VarChar(255) - seats Seats[] - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + uid String @id @unique @default(uuid()) + type ESubscriptionType + status ESubscriptionStatus @default(ACTIVE) + stripe_subscription_id String @db.VarChar(255) + start_date DateTime @default(now()) + end_date DateTime + nb_seats Int + office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) + office_uid String @db.VarChar(255) + seats Seats[] + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + @@map("subscriptions") } model Seats { - uid String @id @unique @default(uuid()) - subscription Subscriptions @relation(fields: [subscription_uid], references: [uid], onDelete: Cascade) - subscription_uid String @db.VarChar(255) - user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) - user_uid String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + uid String @id @unique @default(uuid()) + subscription Subscriptions @relation(fields: [subscription_uid], references: [uid], onDelete: Cascade) + subscription_uid String @db.VarChar(255) + user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) + user_uid String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + @@map("seats") } model Notes { - uid String @id @unique @default(uuid()) - content String @db.VarChar(1000) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) - customer_uid String @db.VarChar(255) + uid String @id @unique @default(uuid()) + content String @db.VarChar(1000) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) + customer_uid String @db.VarChar(255) folder OfficeFolders @relation(fields: [folder_uid], references: [uid], onDelete: Cascade) - folder_uid String @db.VarChar(255) + folder_uid String @db.VarChar(255) + @@map("notes") } diff --git a/src/common/emails/EmailBuilder.ts b/src/common/emails/EmailBuilder.ts index f3296d13..7d795ba9 100644 --- a/src/common/emails/EmailBuilder.ts +++ b/src/common/emails/EmailBuilder.ts @@ -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, + }); + } } diff --git a/src/common/emails/Templates/EmailTemplates.ts b/src/common/emails/Templates/EmailTemplates.ts index 520098e2..4943b885 100644 --- a/src/common/emails/Templates/EmailTemplates.ts +++ b/src/common/emails/Templates/EmailTemplates.ts @@ -3,4 +3,5 @@ export const ETemplates = { DOCUMENT_REFUSED: "DOCUMENT_REFUSED", DOCUMENT_RECAP: "DOCUMENT_RECAP", SUBSCRIPTION_INVITATION: "SUBSCRIPTION_INVITATION", + DOCUMENT_REMINDER: "DOCUMENT_REMINDER", }; \ No newline at end of file diff --git a/src/common/repositories/DocumentsNotaryRepository.ts b/src/common/repositories/DocumentsNotaryRepository.ts new file mode 100644 index 00000000..4d785adb --- /dev/null +++ b/src/common/repositories/DocumentsNotaryRepository.ts @@ -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 { + 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 { + return this.model.delete({ + where: { + uid: uid, + }, + }); + } + + /** + * @description : Find unique document + */ + public async findOneByUid(uid: string, query?: Prisma.DocumentsNotaryInclude): Promise { + 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 }, + }); + } +} diff --git a/src/common/repositories/DocumentsReminderRepository.ts b/src/common/repositories/DocumentsReminderRepository.ts new file mode 100644 index 00000000..1b5b1ac2 --- /dev/null +++ b/src/common/repositories/DocumentsReminderRepository.ts @@ -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 { + const createArgs: Prisma.DocumentsReminderCreateArgs = { + data: { + document: { + connect: { + uid: documentReminder.document!.uid, + }, + }, + reminder_date: new Date(), + }, + }; + + const documentReminderCreated = await this.model.create({ ...createArgs }); + + return documentReminderCreated; + } +} diff --git a/src/common/repositories/FilesNotaryRepository.ts b/src/common/repositories/FilesNotaryRepository.ts new file mode 100644 index 00000000..3319b372 --- /dev/null +++ b/src/common/repositories/FilesNotaryRepository.ts @@ -0,0 +1,84 @@ +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 { + 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, + key: key, + }, + }; + 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 }, + }); + } +} diff --git a/src/services/common/FilesNotaryService/FilesNotaryService.ts b/src/services/common/FilesNotaryService/FilesNotaryService.ts new file mode 100644 index 00000000..cd508b4a --- /dev/null +++ b/src/services/common/FilesNotaryService/FilesNotaryService.ts @@ -0,0 +1,90 @@ +import { BackendVariables } from "@Common/config/variables/Variables"; +import { Prisma } from "@prisma/client"; +import FilesNotaryRepository from "@Repositories/FilesNotaryRepository"; +import FilesRepository from "@Repositories/FilesRepository"; +import BaseService from "@Services/BaseService"; +import { FileNotary } from "le-coffre-resources/dist/Notary"; +import { Readable } from "stream"; +import { Service } from "typedi"; +import { v4 } from "uuid"; + +import CryptoService from "../CryptoService/CryptoService"; +import IpfsService from "../IpfsService/IpfsService"; + +@Service() +export default class FilesNotaryService extends BaseService { + constructor( + private filesRepository: FilesRepository, + private ipfsService: IpfsService, + private variables: BackendVariables, + private cryptoService: CryptoService, + private filesNotaryRepository: FilesNotaryRepository, + ) { + super(); + } + + /** + * @description : Get all files + * @throws {Error} If files cannot be ge + */ + public async get(query: Prisma.FilesFindManyArgs) { + return this.filesRepository.findMany(query); + } + + /** + * @description : Get a file by uid + * @throws {Error} If project cannot be created + */ + public async getByUid(uid: string) { + return this.filesNotaryRepository.findOneByUid(uid); + } + + /** + * @description : Get a file by uid with office + * @throws {Error} If project cannot be created + */ + public async getByUidWithOffice(uid: string) { + return this.filesNotaryRepository.findOneByUidWithOffice(uid); + } + + /** + * @description : Get a file by uid with document + * @throws {Error} If project cannot be created + */ + public async getByUidWithDocument(uid: string) { + return this.filesNotaryRepository.findOneByUidWithDocument(uid); + } + + /** + * @description : view a file + * @throws {Error} If file cannot be deleted + */ + public async download(uid: string) { + const file = await this.filesNotaryRepository.findOneByUid(uid); + if (!file?.key) return null; + const fileResult = await fetch(file.file_path.concat("?pinataGatewayToken=").concat(this.variables.PINATA_GATEWAY_TOKEN)); + const fileArrayBuffer = await fileResult.arrayBuffer(); + return { file: file, buffer: await this.cryptoService.decrypt(Buffer.from(fileArrayBuffer), file.key) }; + } + + /** + * @description : Create a new file + * @throws {Error} If file cannot be created + */ + public async create(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); + } +} diff --git a/src/services/common/FilesService/FilesService.ts b/src/services/common/FilesService/FilesService.ts index ef02f19b..a05b2ae1 100644 --- a/src/services/common/FilesService/FilesService.ts +++ b/src/services/common/FilesService/FilesService.ts @@ -1,13 +1,14 @@ +import { BackendVariables } from "@Common/config/variables/Variables"; +import { Files, Prisma } from "@prisma/client"; import FilesRepository from "@Repositories/FilesRepository"; import BaseService from "@Services/BaseService"; -import { Service } from "typedi"; import { File } from "le-coffre-resources/dist/SuperAdmin"; +import { Readable } from "stream"; +import { Service } from "typedi"; +import { v4 } from "uuid"; + import CryptoService from "../CryptoService/CryptoService"; import IpfsService from "../IpfsService/IpfsService"; -import { BackendVariables } from "@Common/config/variables/Variables"; -import { Readable } from "stream"; -import { v4 } from "uuid"; -import { Files, Prisma } from "@prisma/client"; @Service() export default class FilesService extends BaseService { diff --git a/src/services/common/IdNotService/IdNotService.ts b/src/services/common/IdNotService/IdNotService.ts index f18721ad..b108aed5 100644 --- a/src/services/common/IdNotService/IdNotService.ts +++ b/src/services/common/IdNotService/IdNotService.ts @@ -113,24 +113,22 @@ export default class IdNotService extends BaseService { super(); } - public async getIdNotToken(code: string) { + public async getIdNotToken(code: string) { const query = new URLSearchParams({ client_id: this.variables.IDNOT_CLIENT_ID, client_secret: this.variables.IDNOT_CLIENT_SECRET, redirect_uri: this.variables.IDNOT_REDIRECT_URL, code: code, grant_type: "authorization_code", - }); - + }); + 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; - + + const decodedIdToken = jwt.decode(decodedToken.id_token) as IdNotJwtPayload; return decodedIdToken; } @@ -178,7 +176,7 @@ export default class IdNotService extends BaseService { } public async getOfficeMemberships(officeId: string) { - const officeInfos = await this.officeService.getByUid(officeId); + const officeInfos = await this.officeService.getByUid(officeId); const office = Office.hydrate(officeInfos!); const searchParams = new URLSearchParams({ key: this.variables.IDNOT_API_KEY, @@ -187,7 +185,7 @@ export default class IdNotService extends BaseService { await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entites/${office.idNot}/personnes?` + searchParams, { method: "GET", }) - ).json()) as any; + ).json()) as any; } public getOfficeStatus(statusName: string) { @@ -291,13 +289,13 @@ export default class IdNotService extends BaseService { await this.userService.updateCheckedAt(user.uid!); } - public async updateOffice(officeId: string) { + public async updateOffice(officeId: string) { const officeInfos = await this.officeService.getByUid(officeId); const office = Office.hydrate(officeInfos!); 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) { @@ -339,7 +337,7 @@ export default class IdNotService extends BaseService { const officeLocationData = (await ( await fetch(`${this.variables.IDNOT_API_BASE_URL + userData.entite.locationsUrl}?` + searchParams, { method: "GET" }) ).json()) as IOfficeLocation; - + const office = await this.officeService.get({ where: { idNot: decodedToken.entity_idn } }); // if(officeLocationData.result[0]!.adrGeoCodePostal.slice(0,2) !== "35") { @@ -378,17 +376,17 @@ export default class IdNotService extends BaseService { }, }; - if(!userToAdd.contact.email) { + if (!userToAdd.contact.email) { return null; } - + let userHydrated = User.hydrate(userToAdd); const user = await this.userService.create(userHydrated); const userOffice = await this.officeService.getByUid(user.office_uid); userHydrated = User.hydrate(user); const userOfficeHydrated = Office.hydrate(userOffice!); - if(office.length === 0) { + if (office.length === 0) { const officeRoles = await this.officeRolesService.get({ where: { office: { idNot: "0000" } }, include: { office: true, rules: true }, @@ -401,17 +399,16 @@ export default class IdNotService extends BaseService { where: { office: { idNot: "0000" } }, include: { office: true }, }); - + const officeRolesHydrated = OfficeRole.hydrateArray(officeRoles); const deedTypesHydrated = DeedType.hydrateArray(deedTypes); const documentTypesHydrated = DocumentType.hydrateArray(documentTypes); - - + await this.duplicateOfficeRoles(officeRolesHydrated, userOfficeHydrated); const documentTypesCreated = await this.duplicateDocumentTypes(documentTypesHydrated, userOfficeHydrated); await this.duplicateDeedTypes(deedTypesHydrated, documentTypesCreated, userOfficeHydrated); } - + const officeRole = await this.getOfficeRole(userData.typeLien.name, user.office_uid); if (officeRole) { @@ -427,37 +424,35 @@ export default class IdNotService extends BaseService { public async duplicateDocumentTypes(documentTypes: DocumentType[], office: Office): Promise { 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(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(usersReq); diff --git a/src/services/notary/CustomersService/CustomersService.ts b/src/services/notary/CustomersService/CustomersService.ts index 72e6923d..880d7c8d 100644 --- a/src/services/notary/CustomersService/CustomersService.ts +++ b/src/services/notary/CustomersService/CustomersService.ts @@ -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 { return this.customerRepository.findOneByContact(contactUid); } + + public async sendDocumentsReminder(customer: Customer, documents: Documents[]): Promise { + //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); + } + } } diff --git a/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts b/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts new file mode 100644 index 00000000..865752ca --- /dev/null +++ b/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts @@ -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 { + return this.documentsNotaryRepository.create(document); + } + + /** + * @description : Delete a document + * @throws {Error} If document cannot be deleted + */ + public async delete(uid: string): Promise { + const documentEntity = await this.documentsNotaryRepository.findOneByUid(uid, { files: true }); + if (!documentEntity) throw new Error("document not found"); + const document = Document.hydrate(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 { + 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); + } +} diff --git a/src/services/notary/DocumentsReminder/DocumentsReminder.ts b/src/services/notary/DocumentsReminder/DocumentsReminder.ts new file mode 100644 index 00000000..3ee1f823 --- /dev/null +++ b/src/services/notary/DocumentsReminder/DocumentsReminder.ts @@ -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 { + return this.documentsReminderRepository.create(document); + } +}