From 326e68bcef1774f0a2963c6ed094c31560a8218b Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Wed, 2 Oct 2024 20:16:23 +0100 Subject: [PATCH] feat: start v3 --- .DS_Store | Bin 0 -> 6148 bytes .github/workflows/main.yml | 10 +- README.md | 14 +- firestore.devfest.rules | 2 +- functions/src/cms4devfest-gdg.ts | 26 +-- functions/src/cms4devfest.ts | 176 ++++++++++-------- .../src/emails/template/convention-signed.ts | 12 +- .../template/relanceConventionSignee.ts | 12 +- .../relanceInformationsComplementaires.ts | 12 +- .../src/emails/template/relancePaiement.ts | 12 +- .../template/step-1-partnership-demand.ts | 22 ++- .../emails/template/step-2-partnership-ko.ts | 14 +- .../template/step-2-partnership-validation.ts | 18 +- .../template/step-3-payment-received.ts | 14 +- .../template/step-4-communcation-scheduled.ts | 10 +- .../emails/template/step-5-billet-web-url.ts | 36 ++-- .../template/step-partnership-generated.ts | 16 +- functions/src/generator/lib/generator.ts | 152 ++++++++------- .../lib/template_devfest/invoice_fr.ts | 2 +- .../template_devfest/proforma_invoice_fr.ts | 2 +- functions/src/model.ts | 22 ++- functions/src/utils/document-change.ts | 62 +++--- functions/src/utils/files.ts | 41 ++-- functions/src/utils/mail.ts | 39 +++- functions/src/utils/steps/decreasePacks.ts | 9 +- .../src/utils/steps/partnershipGenerated.ts | 16 +- .../src/utils/steps/partnershipValidated.ts | 15 +- functions/src/utils/steps/sendKoEmails.ts | 10 +- functions/src/v3/domain/email.ts | 26 +++ functions/src/v3/domain/sendToWebhooks.ts | 23 +++ functions/src/v3/domain/updateStatus.ts | 13 ++ .../src/v3/infrastructure/getConfiguration.ts | 13 ++ .../admin-validated.component.html | 2 +- .../src/app/ui/partner/partner.component.html | 2 +- .../environments/environment.devfestlille.ts | 4 +- storage.devfest.rules | 2 +- tests/companies.spec.ts | 17 +- 37 files changed, 510 insertions(+), 368 deletions(-) create mode 100644 .DS_Store create mode 100644 functions/src/v3/domain/email.ts create mode 100644 functions/src/v3/domain/sendToWebhooks.ts create mode 100644 functions/src/v3/domain/updateStatus.ts create mode 100644 functions/src/v3/infrastructure/getConfiguration.ts diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..26ee5d958d1005fe9971a6086f7c1cbd88687f0c GIT binary patch literal 6148 zcmeHK!EVz)5S>j!V^bmJ08);YxK@x>RVm`)hVTP5qKCE$cI>tmj@OEv<`6~r3_rw$ zV}GF^(-UuYH&R1ViAxbwJJRkO&(3&up5mP>5sBU;-XUrek%Ph5e2Qs}@H&qbX&4JH z3iXbJA_}RbB)eGgwg$_9W#C_9fWKXrUeP(FG^RV>?@ln3qe5@j`MKU}B>Vj+lMz;( z!^XvJ=j-+1eV*|jdKQ;=GxNEG{hiZR6uU_(E)x&ME-|Ng%mWS=YpmVrmbfN1uE zeh(#?-nvp8_gW9*GX@L$)f!a@Cex0U!mYT2p#*ClD?s01tq~T8{Ue}gu#;uruQKop Dz<;c~ literal 0 HcmV?d00001 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 009ba86..a202969 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: include: - - org: Devfest Lille + - org: DevLille secret: DEVFEST_FIREBASE_TOKEN function: "functions:cms4devfestgdg" target: "default" @@ -37,9 +37,9 @@ jobs: - name: Deploy to Firebase for ${{ matrix.org }} run: | npm --prefix public run build -- --configuration ${{ matrix.front }} - npx firebase-tools deploy -c ${{matrix.config}} -P ${{ matrix.target }} --only hosting - npx firebase-tools deploy -c ${{matrix.config}} -P ${{ matrix.target }} --only firestore:rules - npx firebase-tools deploy -c ${{matrix.config}} -P ${{ matrix.target }} --only functions:cms - npx firebase-tools deploy -c ${{matrix.config}} -P ${{ matrix.target }} --only ${{ matrix.function }} + #npx firebase-tools deploy -c ${{matrix.config}} -P ${{ matrix.target }} --only hosting + #npx firebase-tools deploy -c ${{matrix.config}} -P ${{ matrix.target }} --only firestore:rules + #npx firebase-tools deploy -c ${{matrix.config}} -P ${{ matrix.target }} --only functions:cms + #npx firebase-tools deploy -c ${{matrix.config}} -P ${{ matrix.target }} --only ${{ matrix.function }} env: FIREBASE_TOKEN: ${{ secrets[matrix.secret] }} diff --git a/README.md b/README.md index 9fee719..93a2eb9 100644 --- a/README.md +++ b/README.md @@ -22,21 +22,21 @@ Pour faire fonctionner les Firebase functions, vous devez définir cette configu "zipcode": "...", "tel": "...", "city": "...", - "event": "Devfest Lille", + "event": "DevLille", "address": "...", - "accountantemail": "...@gdglille.org", + "accountantemail": "...@devlille.fr", "website": "https://" }, "mail": { - "to": "...@gdglille.org", - "from": "...@gdglille.org", - "cc": "...@gdglille.org", + "to": "...@devlille.fr", + "from": "...@devlille.fr", + "cc": "...@devlille.fr", "enabled": "true", - "signature": "L'équipe du Devfest Lille", + "signature": "L'équipe du DevLille", "fromname": "GDG Lille" }, "hosting": { - "baseurl": "https://cms4partners.gdglille.org/" + "baseurl": "https://partenaire.devlille.fr/" }, "mailjet": { "api": "...", diff --git a/firestore.devfest.rules b/firestore.devfest.rules index 849114b..26d05a0 100644 --- a/firestore.devfest.rules +++ b/firestore.devfest.rules @@ -28,5 +28,5 @@ function isOwner(companyId) { return request.auth.token.email in resource.data.email; } function isAdministrator() { - return request.auth.token.email.matches(".*@gdglille.org") + return request.auth.token.email.matches(".*@devlille.fr") } \ No newline at end of file diff --git a/functions/src/cms4devfest-gdg.ts b/functions/src/cms4devfest-gdg.ts index 47c45c3..5b898ad 100644 --- a/functions/src/cms4devfest-gdg.ts +++ b/functions/src/cms4devfest-gdg.ts @@ -1,31 +1,13 @@ import * as admin from "firebase-admin"; import * as functions from "firebase-functions"; -import axios from "axios"; -import { Configuration } from "./model"; +import { getConfiguration } from "./v3/infrastructure/getConfiguration"; +import { sendToWebhooks } from "./v3/domain/sendToWebhooks"; const firestore = admin.firestore(); export const onSendChangesToWebHooks = functions.firestore .document("companies-2025/{companyId}") .onUpdate(async (changes) => { - const configurationFromFirestore = await firestore - .doc("editions/2025") - .get() - .then((configuration) => { - return configuration.data() as Configuration; - }); - - if (configurationFromFirestore.webhooks?.length! > 0) { - for (let webhook of configurationFromFirestore.webhooks!) { - console.log( - `Sending to webhook ${webhook} information about ${ - changes.after.data().name - }` - ); - await axios.post(webhook, { - id: changes.after.id, - data: changes.after.data(), - }); - } - } + const configuration = await getConfiguration(firestore); + sendToWebhooks(configuration, changes); }); diff --git a/functions/src/cms4devfest.ts b/functions/src/cms4devfest.ts index 493bcfe..c78c6f0 100644 --- a/functions/src/cms4devfest.ts +++ b/functions/src/cms4devfest.ts @@ -1,21 +1,21 @@ -import * as functions from "firebase-functions"; -import * as admin from "firebase-admin"; -import { sendEmail, sendEmailToAllContacts } from "./utils/mail"; -import { StatusEnum, onDocumentChange } from "./utils/document-change"; import { Timestamp } from "@google-cloud/firestore"; +import * as admin from "firebase-admin"; +import * as functions from "firebase-functions"; import relanceConventionSignee from "./emails/template/relanceConventionSignee"; +import { StatusEnum, onDocumentChange } from "./utils/document-change"; +import { sendEmailToAllContacts } from "./utils/mail"; -import WelcomeEmailFactory from "./emails/template/step-1-partnership-demand"; -import relancePaiement from "./emails/template/relancePaiement"; import relanceInformationsComplementaires from "./emails/template/relanceInformationsComplementaires"; -import { Company, Settings } from "./model"; +import relancePaiement from "./emails/template/relancePaiement"; +import { Company, Configuration } from "./model"; +import { + sendNewPartnerToOrganizationTeam, + sendWelcomeEmail, +} from "./v3/domain/email"; +import { getConfiguration } from "./v3/infrastructure/getConfiguration"; admin.initializeApp(); const firestore = admin.firestore(); -function sendWelcomeEmail(company: Company, id: string, settings: Settings) { - const emailTemplate = WelcomeEmailFactory(company, id, settings); - return sendEmailToAllContacts(company, emailTemplate, settings); -} function addCreationDate(id: string) { return firestore @@ -36,77 +36,99 @@ function updatesStatus(id: string, company: any, status: any) { .catch((err) => console.log(err)); } -export const getAllPublicSponsors = functions.https.onRequest(async (req, resp) => { - const data = await firestore.collection("companies-2025").get(); - const partners = data.docs - .map((d) => ({ - id: d.id, - ...d.data(), - })) - .filter((p: any) => p.status.paid === StatusEnum.DONE && p.public && !!p.siteUrl && !!p.logoUrl); - resp.send(partners); -}); +export const getAllPublicSponsors = functions.https.onRequest( + async (req, resp) => { + const data = await firestore.collection("companies-2025").get(); + const partners = data.docs + .map((d) => ({ + id: d.id, + ...d.data(), + })) + .filter( + (p: any) => + p.status.paid === StatusEnum.DONE && + p.public && + !!p.siteUrl && + !!p.logoUrl + ); + resp.send(partners); + } +); const relance = ( - emailFactory: (partner: Record, settings: Settings) => any, + emailFactory: ( + partner: Record, + configuration: Configuration + ) => any, partners: any[], - settings: Settings + configuration: Configuration ) => { partners.forEach((c: any) => { - const emailTemplate = emailFactory(c, settings); - sendEmailToAllContacts(c, emailTemplate, settings); + const emailTemplate = emailFactory(c, configuration); + sendEmailToAllContacts(c, emailTemplate, configuration); }); }; -export const relancePartnaireConventionASigner = functions.https.onCall(async (req, res) => { - const data = await firestore.collection("companies-2025").get(); - const partners = data.docs.map((d) => d.data()).filter((p) => p.status.sign === StatusEnum.PENDING); - relance(relanceConventionSignee, partners, functions.config() as Settings); -}); +export const relancePartnaireConventionASigner = functions.https.onCall( + async (req, res) => { + const configuration = await getConfiguration(firestore); + const data = await firestore.collection("companies-2025").get(); + const partners = data.docs + .map((d) => d.data()) + .filter((p) => p.status.sign === StatusEnum.PENDING); + relance(relanceConventionSignee, partners, configuration); + } +); -export const relancePartnaireFacture = functions.https.onCall(async (req, res) => { - const data = await firestore.collection("companies-2025").get(); - const partners = data.docs.map((d) => d.data()).filter((p) => p.status.paid === StatusEnum.PENDING); - relance(relancePaiement, partners, functions.config() as Settings); -}); +export const relancePartnaireFacture = functions.https.onCall( + async (req, res) => { + const configuration = await getConfiguration(firestore); + const data = await firestore.collection("companies-2025").get(); + const partners = data.docs + .map((d) => d.data()) + .filter((p) => p.status.paid === StatusEnum.PENDING); + relance(relancePaiement, partners, configuration); + } +); -export const relanceInformationPourGeneration = functions.https.onCall(async (req, res) => { - const data = await firestore.collection("companies-2025").get(); - const partners = data.docs.map((d) => d.data()).filter((p) => p.status.generated === StatusEnum.PENDING); - relance(relanceInformationsComplementaires, partners, functions.config() as Settings); -}); +export const relanceInformationPourGeneration = functions.https.onCall( + async (req, res) => { + const configuration = await getConfiguration(firestore); + const data = await firestore.collection("companies-2025").get(); + const partners = data.docs + .map((d) => d.data()) + .filter((p) => p.status.generated === StatusEnum.PENDING); + relance(relanceInformationsComplementaires, partners, configuration); + } +); -export const newPartner = functions.firestore.document("companies-2025/{companyId}").onCreate(async (snap) => { - const settings = functions.config() as Settings; - const company: Company = snap.data() as Company; - const id = snap.id; +export const newPartner = functions.firestore + .document("companies-2025/{companyId}") + .onCreate(async (snap) => { + const configuration = await getConfiguration(firestore); + const company: Company = snap.data() as Company; + const id = snap.id; - if (!company.name) { - return; - } - await addCreationDate(id); - await sendWelcomeEmail(company, snap.id, settings); - await sendEmail( - settings.mail.to, - "🎉 Nouveau Partenaire " + company.name, - ` -La société ${company.name} souhaite devenir partenaire ${company.sponsoring}
-`, - settings - ); + if (!company.name) { + return; + } + await addCreationDate(id); + await sendWelcomeEmail(company, snap.id, configuration); + await sendNewPartnerToOrganizationTeam(company, configuration); - return updatesStatus(id, company, { - filled: "done", - validated: "pending", + return updatesStatus(id, company, { + filled: "done", + validated: "pending", + }); }); -}); export const partnershipUpdated = functions .runWith({ memory: "1GB", }) .firestore.document("companies-2025/{companyId}") - .onUpdate((changes) => { + .onUpdate(async (changes) => { + const configuration = await getConfiguration(firestore); const before = changes.before.data() as Company; const after = changes.after.data() as Company; if (!before || !after) { @@ -114,19 +136,23 @@ export const partnershipUpdated = functions } const id = changes.after.id; - return onDocumentChange(firestore, before, after, id, functions.config() as Settings); + return onDocumentChange(firestore, before, after, id, configuration); }); -exports.updateConventionSignedUrlProperty = functions.storage.object().onFinalize(async (object) => { - const name = object.name || ""; - return admin - .storage() - .bucket() - .file(name) - .getSignedUrl({ action: "read", expires: "03-17-2025" }) - .then(([url]) => { - return firestore.doc("companies-2025/" + name.replace("signed/", "")).update({ - conventionSignedUrl: url, +exports.updateConventionSignedUrlProperty = functions.storage + .object() + .onFinalize(async (object) => { + const name = object.name || ""; + return admin + .storage() + .bucket() + .file(name) + .getSignedUrl({ action: "read", expires: "03-17-2025" }) + .then(([url]) => { + return firestore + .doc("companies-2025/" + name.replace("signed/", "")) + .update({ + conventionSignedUrl: url, + }); }); - }); -}); + }); diff --git a/functions/src/emails/template/convention-signed.ts b/functions/src/emails/template/convention-signed.ts index 621ac20..a4043e9 100644 --- a/functions/src/emails/template/convention-signed.ts +++ b/functions/src/emails/template/convention-signed.ts @@ -1,9 +1,9 @@ -import { Settings } from "../../model"; +import { Configuration } from "../../model"; -export default (id: string, settings: Settings) => { - const adress_cms4devfest = `${settings.hosting.baseurl}/partner/${id}`; +export default (id: string, configuration: Configuration) => { + const adress_cms4devfest = `${configuration.hosting.baseurl}/partner/${id}`; return { - subject: `${settings.gdg.event} ${settings.convention.edition} : Convention Signée`, + subject: `${configuration.gdg.event} ${configuration.convention.edition} : Convention Signée`, body: ` Bonjour

@@ -13,11 +13,11 @@ export default (id: string, settings: Settings) => {

Une fois cette étape terminée, nous pourrons commencer la communication de notre partenariat sur les réseaux sociaux.

- Nous restons à votre disposition pour tout complément via l'adresse ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; diff --git a/functions/src/emails/template/relanceConventionSignee.ts b/functions/src/emails/template/relanceConventionSignee.ts index ea67237..fe2a261 100644 --- a/functions/src/emails/template/relanceConventionSignee.ts +++ b/functions/src/emails/template/relanceConventionSignee.ts @@ -1,20 +1,20 @@ -import { Settings } from "../../model"; +import { Configuration } from "../../model"; -export default (company: Record, settings: Settings) => { +export default (company: Record, configuration: Configuration) => { return { - subject: `Partenariat ${settings.gdg.event} ${settings.convention.edition}: Relance Convention à Signer`, + subject: `Partenariat ${configuration.gdg.event} ${configuration.convention.edition}: Relance Convention à Signer`, body: ` Bonjour

- Nous vous envoyons cet email afin de vous annoncer que nous sommes toujours dans l'attente de votre signature pour la convention de partenariat pour ${settings.gdg.event}. + Nous vous envoyons cet email afin de vous annoncer que nous sommes toujours dans l'attente de votre signature pour la convention de partenariat pour ${configuration.gdg.event}.

Une fois signée, vous pouvez nous la retourner par email, ou la sauvegarde sur votre espace dédié.

- Nous restons à votre disposition pour tout complément via l'adresse email ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse email ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; diff --git a/functions/src/emails/template/relanceInformationsComplementaires.ts b/functions/src/emails/template/relanceInformationsComplementaires.ts index 46c82d9..976ac46 100644 --- a/functions/src/emails/template/relanceInformationsComplementaires.ts +++ b/functions/src/emails/template/relanceInformationsComplementaires.ts @@ -1,20 +1,20 @@ -import { Settings } from "../../model"; +import { Configuration } from "../../model"; -export default (partner: Record, settings: Settings) => { +export default (partner: Record, configuration: Configuration) => { return { - subject: `Partenariat ${settings.gdg.event} ${settings.convention.edition}: Relance Informations Complémentaires`, + subject: `Partenariat ${configuration.gdg.event} ${configuration.convention.edition}: Relance Informations Complémentaires`, body: ` Bonjour

- Nous vous envoyons cet email afin de vous annoncer que nous sommes toujours dans l'attente d'informations complémentaires afin d'établir la convention et la facture pour ${settings.gdg.event}. + Nous vous envoyons cet email afin de vous annoncer que nous sommes toujours dans l'attente d'informations complémentaires afin d'établir la convention et la facture pour ${configuration.gdg.event}.

Vous pouvez renseigner ces informations sur votre espace dédié.

- Nous restons à votre disposition pour tout complément via l'adresse email ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse email ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; diff --git a/functions/src/emails/template/relancePaiement.ts b/functions/src/emails/template/relancePaiement.ts index 9f86222..9ee4043 100644 --- a/functions/src/emails/template/relancePaiement.ts +++ b/functions/src/emails/template/relancePaiement.ts @@ -1,18 +1,18 @@ -import { Settings } from "../../model"; +import { Configuration } from "../../model"; -export default (partner: Record, settings: Settings) => { +export default (partner: Record, configuration: Configuration) => { return { - subject: `Partenariat ${settings.gdg.event} ${settings.convention.edition}: Relance Paiement`, + subject: `Partenariat ${configuration.gdg.event} ${configuration.convention.edition}: Relance Paiement`, body: ` Bonjour

- Nous vous envoyons cet email afin de vous annoncer que nous sommes toujours dans l'attente du paiement de la facture relative à notre partenariat pour ${settings.gdg.event}. + Nous vous envoyons cet email afin de vous annoncer que nous sommes toujours dans l'attente du paiement de la facture relative à notre partenariat pour ${configuration.gdg.event}.

- Nous restons à votre disposition pour tout complément via l'adresse email ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse email ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; diff --git a/functions/src/emails/template/step-1-partnership-demand.ts b/functions/src/emails/template/step-1-partnership-demand.ts index ae63abf..f42b13d 100644 --- a/functions/src/emails/template/step-1-partnership-demand.ts +++ b/functions/src/emails/template/step-1-partnership-demand.ts @@ -1,21 +1,27 @@ -import { Company, Settings } from "../../model"; +import { Company, Configuration } from "../../model"; -export default ({ sponsoring, sponsoringOptions }: Company, id: string, settings: Settings) => { +export default ( + { sponsoring, sponsoringOptions }: Company, + id: string, + configuration: Configuration +) => { let options = ""; if (sponsoringOptions?.length! > 0) { options = ` A ce partenariat, les options suivantes seront ajoutées.
- ${sponsoringOptions?.map((option) => `- ${option.label} (${option.price} euros)`).join("
")} + ${sponsoringOptions + ?.map((option) => `- ${option.label} (${option.price} euros)`) + .join("
")} `; } options += "

"; - const adress_cms4devfest = `${settings.hosting.baseurl}/partner/${id}`; + const adress_cms4devfest = `${configuration.hosting.baseurl}/partner/${id}`; return { - subject: `${settings.gdg.event} ${settings.convention.edition} : Demande de partenariat bien reçue`, + subject: `${configuration.gdg.event} ${configuration.convention.edition} : Demande de partenariat bien reçue`, body: ` Bonjour

- Nous avons bien pris connaissance de votre souhait de devenir partenaire ${settings.gdg.event} ${settings.convention.edition} et nous vous en remercions. + Nous avons bien pris connaissance de votre souhait de devenir partenaire ${configuration.gdg.event} ${configuration.convention.edition} et nous vous en remercions.

${options}

@@ -23,11 +29,11 @@ export default ({ sponsoring, sponsoringOptions }: Company, id: string, settings

Vous pouvez suivre l'avancement de votre demande sur votre espace dédié : ${adress_cms4devfest}

- Nous restons à votre disposition pour tout complément via l'adresse email ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse email ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; diff --git a/functions/src/emails/template/step-2-partnership-ko.ts b/functions/src/emails/template/step-2-partnership-ko.ts index 3481738..59805e2 100644 --- a/functions/src/emails/template/step-2-partnership-ko.ts +++ b/functions/src/emails/template/step-2-partnership-ko.ts @@ -1,18 +1,18 @@ -import { Settings } from "../../model"; +import { Configuration } from "../../model"; -export default (settings: Settings) => ({ - subject: `Partenariat ${settings.gdg.event} ${settings.convention.edition}: RDV l'année prochaine ?`, +export default (configuration: Configuration) => ({ + subject: `Partenariat ${configuration.gdg.event} ${configuration.convention.edition}: RDV l'année prochaine ?`, body: ` Bonjour

-Nous sommes dans le regret de vous annoncer que votre demande de partenariat pour ${settings.gdg.event} ${settings.convention.edition} n'a malheureusement pas pu être retenu. +Nous sommes dans le regret de vous annoncer que votre demande de partenariat pour ${configuration.gdg.event} ${configuration.convention.edition} n'a malheureusement pas pu être retenu.

-En effet, nous avons reçu énormément de demandes, et comme depuis le début de l'aventure ${settings.gdg.event}, les premiers partenaires ayant rempli le formulaire ont été les premiers validés. +En effet, nous avons reçu énormément de demandes, et comme depuis le début de l'aventure ${configuration.gdg.event}, les premiers partenaires ayant rempli le formulaire ont été les premiers validés.

-Nous restons à votre disposition pour tout complément via l'adresse email ${settings.mail.from}. +Nous restons à votre disposition pour tout complément via l'adresse email ${configuration.mail.from}.

Cordialement

-${settings.mail.signature} ${settings.convention.edition} +${configuration.mail.signature} ${configuration.convention.edition} `, }); diff --git a/functions/src/emails/template/step-2-partnership-validation.ts b/functions/src/emails/template/step-2-partnership-validation.ts index c77a47c..6188c72 100644 --- a/functions/src/emails/template/step-2-partnership-validation.ts +++ b/functions/src/emails/template/step-2-partnership-validation.ts @@ -1,10 +1,14 @@ -import { Settings } from "../../model"; +import { Configuration } from "../../model"; -export default ({ sponsoring }: any, id: string, settings: Settings) => { - const edition = settings.convention.edition; - const address_cms4devfest = `${settings.hosting.baseurl}/partner/${id}`; +export default ( + { sponsoring }: any, + id: string, + configuration: Configuration +) => { + const edition = configuration.convention.edition; + const address_cms4devfest = `${configuration.hosting.baseurl}/partner/${id}`; return { - subject: `Partenariat ${settings.gdg.event} ${edition}: Informations Complémentaires à renseigner`, + subject: `Partenariat ${configuration.gdg.event} ${edition}: Informations Complémentaires à renseigner`, body: ` Bonjour

@@ -14,11 +18,11 @@ export default ({ sponsoring }: any, id: string, settings: Settings) => {

Une fois les informations fournies, nous pourrons générer la convention et la facture.

- Nous restons à votre disposition pour tout complément via l'adresse email ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse email ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${edition} + ${configuration.mail.signature} ${edition} `, }; }; diff --git a/functions/src/emails/template/step-3-payment-received.ts b/functions/src/emails/template/step-3-payment-received.ts index e0d6c7b..14e224d 100644 --- a/functions/src/emails/template/step-3-payment-received.ts +++ b/functions/src/emails/template/step-3-payment-received.ts @@ -1,16 +1,16 @@ -import { Settings } from "../../model"; +import { Configuration } from "../../model"; -export default (company: any, id: string, settings: Settings) => { - const adress_cms4devfest = `${settings.hosting.baseurl}/partner/${id}`; +export default (company: any, id: string, configuration: Configuration) => { + const adress_cms4devfest = `${configuration.hosting.baseurl}/partner/${id}`; return { - subject: `${settings.gdg.event} ${settings.convention.edition} : Place à la communication de notre partenariat`, + subject: `${configuration.gdg.event} ${configuration.convention.edition} : Place à la communication de notre partenariat`, body: ` Bonjour

Nous avons bien reçu votre paiement et nous vous remercions pour votre réactivité.

- L'étape suivante consiste à annoncer notre partenariat sur le site ${settings.gdg.event} et sur les réseaux sociaux. Pour cela nous vous invitons à compléter les informations suivantes sur votre espace dédié : ${adress_cms4devfest} + L'étape suivante consiste à annoncer notre partenariat sur le site ${configuration.gdg.event} et sur les réseaux sociaux. Pour cela nous vous invitons à compléter les informations suivantes sur votre espace dédié : ${adress_cms4devfest}
* Votre logo au format SVG.
@@ -18,11 +18,11 @@ export default (company: any, id: string, settings: Settings) => {

Une fois cette étape terminée, nous reviendrons vers vous pour vous tenir informé de l'avancement global de l'organisation de l'évènement et surtout pour vous accompagner dans votre préparation pour l'évènement.

- Nous restons à votre disposition pour tout complément via l'adresse ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; diff --git a/functions/src/emails/template/step-4-communcation-scheduled.ts b/functions/src/emails/template/step-4-communcation-scheduled.ts index 51fce50..1273542 100644 --- a/functions/src/emails/template/step-4-communcation-scheduled.ts +++ b/functions/src/emails/template/step-4-communcation-scheduled.ts @@ -1,18 +1,18 @@ -import { Settings } from "../../model"; +import { Configuration } from "../../model"; -export default (date: string, settings: Settings) => { +export default (date: string, configuration: Configuration) => { return { - subject: `${settings.gdg.event} ${settings.convention.edition} : Votre programmation a été programmée`, + subject: `${configuration.gdg.event} ${configuration.convention.edition} : Votre programmation a été programmée`, body: ` Bonjour

Nous vous envoyons ce mail pour vous annoncer que la communication liée à notre partenariat a été prévue pour le ${date}.

- Nous restons à votre disposition pour tout complément via l'adresse ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; diff --git a/functions/src/emails/template/step-5-billet-web-url.ts b/functions/src/emails/template/step-5-billet-web-url.ts index c21b2f6..7512d6d 100644 --- a/functions/src/emails/template/step-5-billet-web-url.ts +++ b/functions/src/emails/template/step-5-billet-web-url.ts @@ -1,23 +1,29 @@ -import { Company, Settings } from "../../model"; +import { Company, Configuration } from "../../model"; -const generateEmailForSponsorWithoutStand = (billetWebUrl: string, settings: Settings) => { +const generateEmailForSponsorWithoutStand = ( + billetWebUrl: string, + configuration: Configuration +) => { return { - subject: `${settings.gdg.event} ${settings.convention.edition} : Lien pour récupérer vos billets`, + subject: `${configuration.gdg.event} ${configuration.convention.edition} : Lien pour récupérer vos billets`, body: ` Bonjour

Nous vous envoyons ce mail pour vous annoncer que vous pouvez à présent récupérer vos billets à cette adresse: ${billetWebUrl}.

- Nous restons à votre disposition pour tout complément via l'adresse ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; -const generateEmailForSponsorWithStand = (billetWebUrl: string, settings: Settings) => ({ - subject: `${settings.gdg.event} ${settings.convention.edition} : Lien pour récupérer vos billets`, +const generateEmailForSponsorWithStand = ( + billetWebUrl: string, + configuration: Configuration +) => ({ + subject: `${configuration.gdg.event} ${configuration.convention.edition} : Lien pour récupérer vos billets`, body: ` Bonjour

@@ -25,18 +31,22 @@ Nous vous envoyons ce mail pour vous annoncer que vous pouvez à présent récup

Vous pouvez également indiquer les activités (jeux, lots à gagner, ...) que vous avez prévu sur votre stand.

-Nous restons à votre disposition pour tout complément via l'adresse ${settings.mail.from}. +Nous restons à votre disposition pour tout complément via l'adresse ${configuration.mail.from}.

Cordialement

-${settings.mail.signature} ${settings.convention.edition} +${configuration.mail.signature} ${configuration.convention.edition} `, }); -export default (company: Company, settings: Settings) => { +export default (company: Company, configuration: Configuration) => { const billetWebUrl = company.billetWebUrl!; - if (company.sponsoring === "Platinium" || company.sponsoring === "Gold" || company.sponsoring === "Silver") { - return generateEmailForSponsorWithStand(billetWebUrl, settings); + if ( + company.sponsoring === "Platinium" || + company.sponsoring === "Gold" || + company.sponsoring === "Silver" + ) { + return generateEmailForSponsorWithStand(billetWebUrl, configuration); } - return generateEmailForSponsorWithoutStand(billetWebUrl, settings); + return generateEmailForSponsorWithoutStand(billetWebUrl, configuration); }; diff --git a/functions/src/emails/template/step-partnership-generated.ts b/functions/src/emails/template/step-partnership-generated.ts index 986f9a1..fce90a6 100644 --- a/functions/src/emails/template/step-partnership-generated.ts +++ b/functions/src/emails/template/step-partnership-generated.ts @@ -1,8 +1,12 @@ import { addDays } from "date-fns"; import { Timestamp } from "@google-cloud/firestore"; -import { Company, Settings } from "../../model"; +import { Company, Configuration } from "../../model"; -export default ({ sponsoring, creationDate }: Company, id: string, settings: Settings) => { +export default ( + { sponsoring, creationDate }: Company, + id: string, + configuration: Configuration +) => { const options = { weekday: "long", year: "numeric", @@ -14,10 +18,10 @@ export default ({ sponsoring, creationDate }: Company, id: string, settings: Set const sponsor_reservation_end_date = dateTimeFormat.format(date); - const address_cms4devfest = `${settings.hosting.baseurl}/partner/${id}`; + const address_cms4devfest = `${configuration.hosting.baseurl}/partner/${id}`; return { - subject: `Partenariat ${settings.gdg.event} ${settings.convention.edition}: Contrat et facture à acquitter`, + subject: `Partenariat ${configuration.gdg.event} ${configuration.convention.edition}: Contrat et facture à acquitter`, body: ` Bonjour

@@ -29,11 +33,11 @@ export default ({ sponsoring, creationDate }: Company, id: string, settings: Set

Une fois la convention signée et le paiement reçu, nous passerons à la prochaine étape du partenariat.

- Nous restons à votre disposition pour tout complément via l'adresse email ${settings.mail.from}. + Nous restons à votre disposition pour tout complément via l'adresse email ${configuration.mail.from}.

Cordialement

- ${settings.mail.signature} ${settings.convention.edition} + ${configuration.mail.signature} ${configuration.convention.edition} `, }; }; diff --git a/functions/src/generator/lib/generator.ts b/functions/src/generator/lib/generator.ts index c4ffb7a..a525c8b 100644 --- a/functions/src/generator/lib/generator.ts +++ b/functions/src/generator/lib/generator.ts @@ -2,22 +2,31 @@ import * as ejs from "ejs"; import * as markdownToPDf from "markdown-pdf"; import * as os from "os"; -import { Configuration, Settings, SponsoringOption, SponsorshipConfiguration } from "../../model"; - -function getSponsoringFees(sponsoringConfiguration: SponsorshipConfiguration): [string, number, number] { +import { + Configuration, + SponsoringOption, + SponsorshipConfiguration, +} from "../../model"; + +function getSponsoringFees( + sponsoringConfiguration: SponsorshipConfiguration +): [string, number, number] { if (!sponsoringConfiguration) { return ["", 0, 0]; } - return [sponsoringConfiguration.priceString, sponsoringConfiguration.price, sponsoringConfiguration.freeTickets]; + return [ + sponsoringConfiguration.priceString, + sponsoringConfiguration.price, + sponsoringConfiguration.freeTickets, + ]; } function generateFile( config: any, fileName: string, fileModule: any, - settings: Settings, - invoiceType: any, - configurationFromFirestore: Configuration + configuration: Configuration, + invoiceType: any ) { const file = fileModule.default; const getOfficialName = () => { @@ -32,22 +41,28 @@ function generateFile( year: "numeric", }).format(new Date()); - const sponsoringConfiguration: SponsorshipConfiguration | undefined = configurationFromFirestore.sponsorships.find( - (s) => s.name.toLowerCase() === config.sponsoring.toLowerCase() - ); + const sponsoringConfiguration: SponsorshipConfiguration | undefined = + configuration.sponsorships.find( + (s) => s.name.toLowerCase() === config.sponsoring.toLowerCase() + ); if (!sponsoringConfiguration) { return; } - const [SPONSORING_TEXT, SPONSORING_NUMBER, NUMBER_PLACE] = getSponsoringFees(sponsoringConfiguration); + const [SPONSORING_TEXT, SPONSORING_NUMBER, NUMBER_PLACE] = getSponsoringFees( + sponsoringConfiguration + ); const LINES: { label: string; price: number }[] = []; let total = 0; if (SPONSORING_NUMBER > 0) { total += SPONSORING_NUMBER; - LINES.push({ label: `Partenariat ${settings.gdg.event}`, price: SPONSORING_NUMBER }); + LINES.push({ + label: `Partenariat ${configuration.gdg.event}`, + price: SPONSORING_NUMBER, + }); } (config.sponsoringOptions ?? []).forEach((option: SponsoringOption) => { @@ -59,13 +74,16 @@ function generateFile( const considerations = config.lang === "fr" ? sponsoringConfiguration?.considerations - : sponsoringConfiguration?.considerationsEn ?? sponsoringConfiguration?.considerations; + : sponsoringConfiguration?.considerationsEn ?? + sponsoringConfiguration?.considerations; const data = { LINES, CONSIDERATIONS: [ ...considerations, - ...(config.sponsoringOptions ?? []).map((option: SponsoringOption) => option.label), + ...(config.sponsoringOptions ?? []).map( + (option: SponsoringOption) => option.label + ), ], HAS_BOOTH: sponsoringConfiguration?.hasBooth?.toString(), COMPANY: getOfficialName().trim(), @@ -76,23 +94,23 @@ function generateFile( COMPANY_PERSON: config.representant, CONTACT: config.representant.trim(), ROLE: config.role.trim(), - EVENT_EDITION: settings.convention.edition, - EVENT_NAME: settings.gdg.event, + EVENT_EDITION: configuration.convention.edition, + EVENT_NAME: configuration.gdg.event, NUMBER_PLACE, SPONSORING: config.sponsoring, PO: config.PO, SPONSORING_TEXT, SPONSORING_NUMBER: total, - START_DATE: settings.convention.startdate, - END_DATE: settings.convention.enddate, + START_DATE: configuration.convention.startdate, + END_DATE: configuration.convention.enddate, DATE, - GDG_CP: settings.gdg.zipcode, - GDG_ADDRESS: settings.gdg.address, - GDG_CITY: settings.gdg.city, - GDG_EMAIL: settings.mail.from, - GDG_TEL: settings.gdg.tel, - GDG_ACCOUNTANT_EMAIL: settings.gdg.accountantemail, - GDG_WEBSITE: settings.gdg.website, + GDG_CP: configuration.gdg.zipcode, + GDG_ADDRESS: configuration.gdg.address, + GDG_CITY: configuration.gdg.city, + GDG_EMAIL: configuration.mail.from, + GDG_TEL: configuration.gdg.tel, + GDG_ACCOUNTANT_EMAIL: configuration.gdg.accountantemail, + GDG_WEBSITE: configuration.gdg.website, INVOICE_NUMBER: config.invoiceNumber, INVOICE_TYPE: invoiceType, }; @@ -117,74 +135,72 @@ function generateFile( }); } -export function generateProformaInvoice(config: any, settings: Settings, configurationFromFirestore: Configuration) { - const ProformaInvoiceFr = - settings.gdg.event === "Devfest Lille" - ? require("./template_devfest/proforma_invoice_fr") - : require("./template_cloudnord/proforma_invoice_fr"); +export function generateProformaInvoice( + config: any, + configuration: Configuration +) { + const ProformaInvoiceFr = require( + `./${configuration.template_folder}/proforma_invoice_fr` + ); return generateFile( config, `proforma_invoice_${config.id}.pdf`, ProformaInvoiceFr, - settings, - "FACTURE PRO FORMA", - configurationFromFirestore + configuration, + "FACTURE PRO FORMA" ); } -export function generateDevis(config: any, settings: Settings, configurationFromFirestore: Configuration) { - const ProformaInvoiceFr = - settings.gdg.event === "Devfest Lille" - ? require("./template_devfest/proforma_invoice_fr") - : require("./template_cloudnord/proforma_invoice_fr"); +export function generateDevis(config: any, configuration: Configuration) { + const ProformaInvoiceFr = require( + `./${configuration.template_folder}/proforma_invoice_fr` + ); return generateFile( config, `devis_${config.id}.pdf`, ProformaInvoiceFr, - settings, - "DEVIS", - configurationFromFirestore + configuration, + "DEVIS" ); } -export function generateDepositInvoice(config: any, settings: Settings, configurationFromFirestore: Configuration) { - const ProformaInvoiceFr = - settings.gdg.event === "Devfest Lille" - ? require("./template_devfest/proforma_invoice_fr") - : require("./template_cloudnord/proforma_invoice_fr"); +export function generateDepositInvoice( + config: any, + configuration: Configuration +) { + const ProformaInvoiceFr = require( + `./${configuration.template_folder}/proforma_invoice_fr` + ); return generateFile( config, `deposit_invoice_${config.id}.pdf`, ProformaInvoiceFr, - settings, - "FACTURE ACCOMPTE 100%", - configurationFromFirestore + configuration, + "FACTURE ACCOMPTE 100%" ); } -export function generateInvoice(config: any, settings: Settings, configurationFromFirestore: Configuration) { - const InvoiceFr = - settings.gdg.event === "Devfest Lille" - ? require("./template_devfest/invoice_fr") - : require("./template_cloudnord/invoice_fr"); - return generateFile(config, `invoice_${config.id}.pdf`, InvoiceFr, settings, "", configurationFromFirestore); +export function generateInvoice(config: any, configuration: Configuration) { + const InvoiceFr = require(`./${configuration.template_folder}/invoice_fr`); + + return generateFile( + config, + `invoice_${config.id}.pdf`, + InvoiceFr, + configuration, + "" + ); } -export function generateConvention(config: any, settings: Settings, configurationFromFirestore: Configuration) { - const ConventionFr = - settings.gdg.event === "Devfest Lille" - ? require("./template_devfest/convention_fr") - : require("./template_cloudnord/convention_fr"); - const ConventionEn = - settings.gdg.event === "Devfest Lille" - ? require("./template_devfest/convention_en") - : require("./template_cloudnord/convention_fr"); +export function generateConvention(config: any, configuration: Configuration) { + const Convention = require( + `./${configuration.template_folder}/convention_${config.lang}` + ); return generateFile( config, `convention_${config.id}.pdf`, - config.lang === "fr" ? ConventionFr : ConventionEn, - settings, - "", - configurationFromFirestore + Convention, + configuration, + "" ); } diff --git a/functions/src/generator/lib/template_devfest/invoice_fr.ts b/functions/src/generator/lib/template_devfest/invoice_fr.ts index 0ab4d3f..cf27a02 100644 --- a/functions/src/generator/lib/template_devfest/invoice_fr.ts +++ b/functions/src/generator/lib/template_devfest/invoice_fr.ts @@ -1,5 +1,5 @@ export default ` - +
Date : <%= DATE %>
diff --git a/functions/src/generator/lib/template_devfest/proforma_invoice_fr.ts b/functions/src/generator/lib/template_devfest/proforma_invoice_fr.ts index bce3285..d1dbada 100644 --- a/functions/src/generator/lib/template_devfest/proforma_invoice_fr.ts +++ b/functions/src/generator/lib/template_devfest/proforma_invoice_fr.ts @@ -1,5 +1,5 @@ export default ` - +
Date : <%= DATE %>
diff --git a/functions/src/model.ts b/functions/src/model.ts index cf2b4b5..65073d1 100644 --- a/functions/src/model.ts +++ b/functions/src/model.ts @@ -31,13 +31,6 @@ export type Mailjet = { api: string; private: string; }; -export type Settings = { - convention: Convention; - hosting: Hosting; - gdg: Association; - mail: Email; - mailjet: Mailjet; -}; export type SponsoringType = Record; export type SponsorshipConfiguration = { @@ -49,11 +42,18 @@ export type SponsorshipConfiguration = { considerationsEn: string[]; hasBooth: boolean; }; + export type Configuration = SponsoringType & { next_value: string; enabled: boolean; sponsorships: SponsorshipConfiguration[]; webhooks?: string[]; + convention: Convention; + hosting: Hosting; + gdg: Association; + mail: Email; + mailjet: Mailjet; + template_folder: string; }; export type SponsoringOption = { label: string; price: number }; @@ -70,7 +70,13 @@ export interface WorkflowStatus { code?: State; } -export type State = "disabled" | "enabled" | "pending" | "done" | "refused" | "retry"; +export type State = + | "disabled" + | "enabled" + | "pending" + | "done" + | "refused" + | "retry"; export interface Company { archived?: boolean; diff --git a/functions/src/utils/document-change.ts b/functions/src/utils/document-change.ts index d2acc1b..7b6ab38 100644 --- a/functions/src/utils/document-change.ts +++ b/functions/src/utils/document-change.ts @@ -1,18 +1,19 @@ -import partnershipValidated from "./steps/partnershipValidated"; -import partnershipGenerated from "./steps/partnershipGenerated"; -import { sendEmailToAllContacts } from "./mail"; +import ConventionSignedFactory from "../emails/template/convention-signed"; +import PaymentReceivedFactory from "../emails/template/step-3-payment-received"; import { generateAndStoreInvoice, generateAndStoreProformaInvoiceAndConvention, } from "./files"; -import PaymentReceivedFactory from "../emails/template/step-3-payment-received"; -import ConventionSignedFactory from "../emails/template/convention-signed"; +import { sendEmailToAllContacts } from "./mail"; +import partnershipGenerated from "./steps/partnershipGenerated"; +import partnershipValidated from "./steps/partnershipValidated"; import CommunicationScheduledFactory from "../emails/template/step-4-communcation-scheduled"; import BilletWebUrlFactory from "../emails/template/step-5-billet-web-url"; +import { Company, Configuration } from "../model"; import decreasePacks from "./steps/decreasePacks"; import sendKoEmails from "./steps/sendKoEmails"; -import { Company, Settings } from "../model"; +import { makeTicketStepAsDone } from "../v3/domain/updateStatus"; export enum StatusEnum { PENDING = "pending", @@ -26,7 +27,7 @@ export async function onDocumentChange( before: Company, after: Company, id: string, - settings: Settings + configuration: Configuration ) { const document = firestore.doc("companies-2025/" + id); console.log( @@ -58,32 +59,18 @@ export async function onDocumentChange( beforeStatus.generated !== StatusEnum.RETRY) || status.generated === StatusEnum.RETRY) ) { - const configurationFromFirestore = await firestore - .doc("editions/2025") - .get() - .then((invoice) => { - return invoice.data() as any; - }); - await generateAndStoreProformaInvoiceAndConvention( after, id, - settings, - configurationFromFirestore - ); - await generateAndStoreInvoice( - firestore, - after, - id, - settings, - configurationFromFirestore + configuration ); + await generateAndStoreInvoice(firestore, after, id, configuration); return document.update({ ...partnershipGenerated( after, id, - settings, + configuration, status.generated === StatusEnum.DONE ), }); @@ -98,7 +85,7 @@ export async function onDocumentChange( ...partnershipValidated( after, id, - settings, + configuration, status.validated === StatusEnum.DONE ), }); @@ -106,13 +93,13 @@ export async function onDocumentChange( beforeStatus.validated !== status.validated && status.validated === StatusEnum.REFUSED ) { - await sendKoEmails(after, settings); + await sendKoEmails(after, configuration); } else if ( beforeStatus.sign !== status.sign && status.sign === StatusEnum.DONE ) { - const emailTemplate = ConventionSignedFactory(id, settings); - sendEmailToAllContacts(after, emailTemplate, settings); + const emailTemplate = ConventionSignedFactory(id, configuration); + sendEmailToAllContacts(after, emailTemplate, configuration); return document.update({ status: { @@ -124,8 +111,8 @@ export async function onDocumentChange( beforeStatus.paid !== status.paid && status.paid === StatusEnum.DONE ) { - const emailTemplate = PaymentReceivedFactory(after, id, settings); - sendEmailToAllContacts(after, emailTemplate, settings); + const emailTemplate = PaymentReceivedFactory(after, id, configuration); + sendEmailToAllContacts(after, emailTemplate, configuration); return document.update({ public: true, status: { @@ -154,9 +141,9 @@ export async function onDocumentChange( if (!!after.publicationDate && (after.publicationDate as any) !== "") { const emailTemplate = CommunicationScheduledFactory( Intl.DateTimeFormat("fr").format(new Date(after.publicationDate)), - settings + configuration ); - sendEmailToAllContacts(after, emailTemplate, settings); + sendEmailToAllContacts(after, emailTemplate, configuration); return document.update({ status: { @@ -169,17 +156,12 @@ export async function onDocumentChange( status.code === StatusEnum.PENDING && before.billetWebUrl !== after.billetWebUrl ) { - const emailTemplate = BilletWebUrlFactory(after, settings); - sendEmailToAllContacts(after, emailTemplate, settings); + const emailTemplate = BilletWebUrlFactory(after, configuration); + sendEmailToAllContacts(after, emailTemplate, configuration); } else if (status.code === StatusEnum.PENDING) { const billetWebDone = after.billetWebDone; if (billetWebDone) { - return document.update({ - status: { - ...status, - code: StatusEnum.DONE, - }, - }); + return document.update(makeTicketStepAsDone(status)); } } diff --git a/functions/src/utils/files.ts b/functions/src/utils/files.ts index 33a746b..011a6b4 100644 --- a/functions/src/utils/files.ts +++ b/functions/src/utils/files.ts @@ -7,7 +7,8 @@ import { } from "../generator/lib/generator"; import * as os from "os"; import * as admin from "firebase-admin"; -import { Company, Configuration, Settings } from "../model"; +import { Company, Configuration } from "../model"; +import { getConfiguration } from "../v3/infrastructure/getConfiguration"; export async function storeFile(cloudStorageDest: string, tempPath: string) { await admin @@ -26,29 +27,16 @@ export async function storeFile(cloudStorageDest: string, tempPath: string) { export async function generateAndStoreProformaInvoiceAndConvention( company: Company, id: string, - settings: Settings, - configurationFromFirestore: Configuration + configuration: Configuration ) { console.log("Generate Proforma invoice and convention for " + id); const [convention, proformaInvoice, depositInvoice, devis] = await Promise.all([ - generateConvention( - { ...company, id }, - settings, - configurationFromFirestore - ), - generateProformaInvoice( - { ...company, id }, - settings, - configurationFromFirestore - ), - generateDepositInvoice( - { ...company, id }, - settings, - configurationFromFirestore - ), - generateDevis({ ...company, id }, settings, configurationFromFirestore), + generateConvention({ ...company, id }, configuration), + generateProformaInvoice({ ...company, id }, configuration), + generateDepositInvoice({ ...company, id }, configuration), + generateDevis({ ...company, id }, configuration), ]); await Promise.all([ @@ -62,8 +50,7 @@ export async function generateAndStoreInvoice( firestore: FirebaseFirestore.Firestore, company: Company, id: string, - settings: any, - configurationFromFirestore: Configuration + configuration: Configuration ) { let invoiceNumber = company.invoiceNumber; @@ -77,8 +64,7 @@ export async function generateAndStoreInvoice( id, invoiceNumber, }, - settings, - configurationFromFirestore + configuration ); const publicInvoiceUrl = await storeFile("facture/", invoice as any); @@ -95,12 +81,9 @@ export async function generateInvoiceNumber( firestore: FirebaseFirestore.Firestore ) { console.log("Generate Invoice Number"); - const invoiceNumber = await firestore - .doc("editions/2025") - .get() - .then((invoice) => { - return (invoice.data() as any).next_value; - }); + const invoiceNumber = await getConfiguration(firestore).then((invoice) => { + return invoice.next_value; + }); const formattedNumber = "2025_" + invoiceNumber.padStart(3, "0"); diff --git a/functions/src/utils/mail.ts b/functions/src/utils/mail.ts index 553d954..a99c9fc 100644 --- a/functions/src/utils/mail.ts +++ b/functions/src/utils/mail.ts @@ -1,6 +1,8 @@ -import { Company, Email, Settings } from "../model"; +import { Company, Configuration, Email } from "../model"; -export function getFrom(mail: Email) { +export function getFrom(mail: Email): { + From: { Email: string; Name: string }; +} { return { From: { Email: mail.from, @@ -9,24 +11,41 @@ export function getFrom(mail: Email) { }; } -export function sendEmailToAllContacts(company: Company, emailFactory: any, settings: Settings) { - let emails = [settings.mail.cc]; - if (settings.mail.enabled === "true") { +export function sendEmailToAllContacts( + company: Company, + emailFactory: any, + configuration: Configuration +) { + let emails = [configuration.mail.cc]; + if (configuration.mail.enabled === "true") { emails = [...emails, ...company.email]; } return Promise.all( emails.map((email: string) => { - return sendEmail(email.trim(), `${emailFactory.subject} (${company.name})`, emailFactory.body, settings); + return sendEmail( + email.trim(), + `${emailFactory.subject} (${company.name})`, + emailFactory.body, + configuration + ); }) ); } -export function sendEmail(to: string, subject: string, body: string, settings: Settings) { - const mailjet = settings.mailjet; - const mailjetClient = require("node-mailjet").connect(mailjet.api, mailjet.private); +export function sendEmail( + to: string, + subject: string, + body: string, + configuration: Configuration +) { + const mailjet = configuration.mailjet; + const mailjetClient = require("node-mailjet").connect( + mailjet.api, + mailjet.private + ); const request = mailjetClient.post("send", { version: "v3.1" }).request({ Messages: [ { - ...getFrom(settings.mail), + ...getFrom(configuration.mail), To: [ { Email: to, diff --git a/functions/src/utils/steps/decreasePacks.ts b/functions/src/utils/steps/decreasePacks.ts index 536d6e7..6be6d04 100644 --- a/functions/src/utils/steps/decreasePacks.ts +++ b/functions/src/utils/steps/decreasePacks.ts @@ -1,14 +1,11 @@ +import { getConfiguration } from "../../v3/infrastructure/getConfiguration"; + export default async ( firestore: FirebaseFirestore.Firestore, sponsoringType: string ) => { if (sponsoringType !== "newsletter") { - const configuration = await firestore - .doc("editions/2025") - .get() - .then((invoice) => { - return invoice.data(); - }); + const configuration = await getConfiguration(firestore); if (configuration) { await firestore diff --git a/functions/src/utils/steps/partnershipGenerated.ts b/functions/src/utils/steps/partnershipGenerated.ts index 81977bd..371ee3b 100644 --- a/functions/src/utils/steps/partnershipGenerated.ts +++ b/functions/src/utils/steps/partnershipGenerated.ts @@ -2,10 +2,20 @@ import PartnerhipGeneratedFactory from "../../emails/template/step-partnership-g import { sendEmailToAllContacts } from "../mail"; import { StatusEnum } from "../document-change"; -import { Company, Settings } from "../../model"; -export default (company: Company, id: string, settings: Settings, shouldSendEmail: boolean) => { +import { Company, Configuration } from "../../model"; + +export default ( + company: Company, + id: string, + configuration: Configuration, + shouldSendEmail: boolean +) => { if (shouldSendEmail) { - sendEmailToAllContacts(company, PartnerhipGeneratedFactory(company, id, settings), settings); + sendEmailToAllContacts( + company, + PartnerhipGeneratedFactory(company, id, configuration), + configuration + ); } return { diff --git a/functions/src/utils/steps/partnershipValidated.ts b/functions/src/utils/steps/partnershipValidated.ts index c61526c..eb8d777 100644 --- a/functions/src/utils/steps/partnershipValidated.ts +++ b/functions/src/utils/steps/partnershipValidated.ts @@ -1,12 +1,21 @@ import PartnerhipValidatedFactory from "../../emails/template/step-2-partnership-validation"; import { sendEmailToAllContacts } from "../mail"; +import { Company, Configuration } from "../../model"; import { StatusEnum } from "../document-change"; -import { Company, Settings } from "../../model"; -export default (company: Company, id: string, settings: Settings, shouldSendEmail: boolean) => { +export default ( + company: Company, + id: string, + configuration: Configuration, + shouldSendEmail: boolean +) => { if (shouldSendEmail) { - sendEmailToAllContacts(company, PartnerhipValidatedFactory(company, id, settings), settings); + sendEmailToAllContacts( + company, + PartnerhipValidatedFactory(company, id, configuration), + configuration + ); } return { diff --git a/functions/src/utils/steps/sendKoEmails.ts b/functions/src/utils/steps/sendKoEmails.ts index 740caaa..c53198d 100644 --- a/functions/src/utils/steps/sendKoEmails.ts +++ b/functions/src/utils/steps/sendKoEmails.ts @@ -1,7 +1,11 @@ import { sendEmailToAllContacts } from "../mail"; import PartnerhipKoFactory from "../../emails/template/step-2-partnership-ko"; -import { Company } from "../../model"; +import { Company, Configuration } from "../../model"; -export default (company: Company, settings: any) => { - sendEmailToAllContacts(company, PartnerhipKoFactory(settings), settings); +export default (company: Company, configuration: Configuration) => { + sendEmailToAllContacts( + company, + PartnerhipKoFactory(configuration), + configuration + ); }; diff --git a/functions/src/v3/domain/email.ts b/functions/src/v3/domain/email.ts new file mode 100644 index 0000000..a9aadc6 --- /dev/null +++ b/functions/src/v3/domain/email.ts @@ -0,0 +1,26 @@ +import { Company, Configuration } from "../../model"; +import { sendEmail, sendEmailToAllContacts } from "../../utils/mail"; +import WelcomeEmailFactory from "../../emails/template/step-1-partnership-demand"; + +export const sendWelcomeEmail = ( + company: Company, + id: string, + configuration: Configuration +) => { + const emailTemplate = WelcomeEmailFactory(company, id, configuration); + return sendEmailToAllContacts(company, emailTemplate, configuration); +}; + +export const sendNewPartnerToOrganizationTeam = ( + company: Company, + configuration: Configuration +) => { + return sendEmail( + configuration.mail.to, + "🎉 Nouveau Partenaire " + company.name, + ` + La société ${company.name} souhaite devenir partenaire ${company.sponsoring}
+ `, + configuration + ); +}; diff --git a/functions/src/v3/domain/sendToWebhooks.ts b/functions/src/v3/domain/sendToWebhooks.ts new file mode 100644 index 0000000..83b56fb --- /dev/null +++ b/functions/src/v3/domain/sendToWebhooks.ts @@ -0,0 +1,23 @@ +import { Change } from "firebase-functions/v1"; +import { Configuration } from "../../model"; +import axios from "axios"; +import { QueryDocumentSnapshot } from "firebase-admin/firestore"; + +export const sendToWebhooks = async ( + configuration: Configuration, + changes: Change +) => { + if (configuration.webhooks?.length! > 0) { + for (let webhook of configuration.webhooks!) { + console.log( + `Sending to webhook ${webhook} information about ${ + changes.after.data().name + }` + ); + await axios.post(webhook, { + id: changes.after.id, + data: changes.after.data(), + }); + } + } +}; diff --git a/functions/src/v3/domain/updateStatus.ts b/functions/src/v3/domain/updateStatus.ts new file mode 100644 index 0000000..4b0debf --- /dev/null +++ b/functions/src/v3/domain/updateStatus.ts @@ -0,0 +1,13 @@ +import { WorkflowStatus } from "../../model"; +import { StatusEnum } from "../../utils/document-change"; + +export const makeTicketStepAsDone = ( + status: WorkflowStatus +): { status: WorkflowStatus } => { + return { + status: { + ...status, + code: StatusEnum.DONE, + }, + }; +}; diff --git a/functions/src/v3/infrastructure/getConfiguration.ts b/functions/src/v3/infrastructure/getConfiguration.ts new file mode 100644 index 0000000..3b16d5d --- /dev/null +++ b/functions/src/v3/infrastructure/getConfiguration.ts @@ -0,0 +1,13 @@ +import { Firestore } from "firebase-admin/firestore"; +import { Configuration } from "../../model"; + +export const getConfiguration = ( + firestore: Firestore +): Promise => { + return firestore + .doc("editions/2025") + .get() + .then((invoice) => { + return invoice.data() as Configuration; + }); +}; diff --git a/public/src/app/ui/admin-validated/admin-validated.component.html b/public/src/app/ui/admin-validated/admin-validated.component.html index 7717b4d..bbc6cb5 100644 --- a/public/src/app/ui/admin-validated/admin-validated.component.html +++ b/public/src/app/ui/admin-validated/admin-validated.component.html @@ -11,7 +11,7 @@ @if (company.status?.validated === "done") {

Bonne nouvelle ! Votre demande de pack - {{ company.sponsoring }} pour le Devfest Lille 2025 a été + {{ company.sponsoring }} pour le DevLille 2025 a été validé.

diff --git a/public/src/app/ui/partner/partner.component.html b/public/src/app/ui/partner/partner.component.html index c774669..a902ebc 100644 --- a/public/src/app/ui/partner/partner.component.html +++ b/public/src/app/ui/partner/partner.component.html @@ -21,7 +21,7 @@ > Code de conduite { await testEnv.withSecurityRulesDisabled(async (context) => { const firestoreWithoutRule = context.firestore(); - await firestoreWithoutRule.collection("companies-2025").doc("1").set({ name: "initial user name" }); + await firestoreWithoutRule + .collection("companies-2025") + .doc("1") + .set({ name: "initial user name" }); }); }); it("should not be able to access to the collection", async () => { const user = testEnv.unauthenticatedContext(); - await testing.assertFails(getDocs(collection(user.firestore(), "companies-2025"))); + await testing.assertFails( + getDocs(collection(user.firestore(), "companies-2025")) + ); }); it("should be able to access to the collection", async () => { - const user = testEnv.authenticatedContext("manu", { email: "manu@gdglille.org" }); - await testing.assertSucceeds(getDocs(collection(user.firestore(), "companies-2025"))); + const user = testEnv.authenticatedContext("manu", { + email: "manu@devlille.fr", + }); + await testing.assertSucceeds( + getDocs(collection(user.firestore(), "companies-2025")) + ); }); });