UNPKG

synt_backend

Version:

Synt light-weight node backend service

819 lines (717 loc) 19.5 kB
import { Router } from "express"; const router = Router(); const db = require("./../mysql/models/index"); import * as ValidationHelper from "./../helpers/validations"; const userHelper = require("./../helpers/user"); import { formatUser, formatVME } from "./../helpers/format"; import { setupRemindersForVME } from "../database/reminders"; const notifier = require("./../helpers/notifier"); const { Op } = require("sequelize"); router.get("/", getVMEs); router.get("/:VMEId", getVME); router.post("/", postVME); router.get("/user/:UserId", getUser); router.post("/user", attachUser); router.post("/:VMEId/freeze", freeze); // only for admin router.delete("/:VMEId/delete", deleteVME); // only for admin router.post("/:VMEId/select", selectVME); async function getUser(req, res) { const { t } = req; try { let user = await userHelper.getAuthUser(req); let { UserId } = req.params; if (user) { // verify vme and get vme let VmeValidation = await ValidationHelper.validateVme(t, user.VMEId); //TODO: Validate user roles if (!VmeValidation.success) { return res.json(VmeValidation); } const { VME } = VmeValidation; // id is used const Users = await VME.getUsers({ where: { id: UserId } }); if (Users.length) { let User = Users[0]; if (User.CompanyId) { let Company = await User.getCompany(); User.setDataValue("Company", Company); } let vmes = []; if (User.UserVME?.type === "bookkeeper") { vmes = await db.VME.findAll({ include: [{ model: db.User, where: { id: UserId }, as: "Users" }], }); console.log(`vmes`, vmes); } return res.json({ success: true, User: formatUser(User, req.t), vmes, }); } return res.json({ success: false, error: t( "api.vmes.errors.notYourVme", "This user is not a member of your VME." ), }); } } catch (error) { return res.json({ success: false, error: error.message }); } } async function selectVME(req, res) { const { t } = req; let User = await userHelper.getAuthUser(req); if (User) { const { VMEId } = req.params; if (!Number.isInteger(parseInt(VMEId))) { return res.json({ success: false, error: t("api.vmes.errors.firstSelectVme", "First select a VME."), }); } if (!User.is_admin) { if (!User.hasVME(Number(VMEId))) { return res.json({ success: false, error: t( "api.vmes.errors.noPermissions", "You don't have the right role." ), }); } } const VME = await db.VME.findOne({ where: { id: VMEId }, include: [{ model: db.Company }, { model: db.User, as: "Users" }], }); const UserVME = await db.UserVME.findOne({ where: { UserId: User.id, VMEId: VME.id }, }); if (UserVME) { User.setDataValue("UserVME", UserVME); User = formatUser(User, req.t); } return res.json({ success: true, User: userHelper.addJwtToken(User, VMEId), VME: formatVME(VME, req.t), }); } } async function deleteVME(req, res) { const { t } = req; let user = await userHelper.getAuthUser(req); if (user) { if (!user.is_admin) { return res.json({ success: false, error: t( "api.vmes.errors.noPermissions", "You don't have the right role." ), }); } const { VMEId } = req.params; const VME = await db.VME.findOne({ where: { id: VMEId } }); await VME.destroy(); return res.json({ success: true }); } } async function freeze(req, res) { const { t } = req; let user = await userHelper.getAuthUser(req); if (user) { if (!user.is_admin) { return res.json({ success: false, error: t( "api.vmes.errors.noPermissions", "You don't have the right role." ), }); } const { VMEId } = req.params; /* db.VME.update( { is_frozen: Sequelize.literal("NOT is_frozen") }, { where: { id: VMEId } } ); */ const VME = await db.VME.findOne({ where: { id: VMEId } }); VME.is_frozen = !VME.is_frozen; await VME.save(); return res.json({ success: true, VME, }); } } async function createUser(id, email, userData) { let newUser = null; // if you have id, return by id if (!newUser && id) { newUser = await db.User.findOne({ where: { id } }); } // email, CompanyId should be unique if (!newUser && email) { newUser = await db.User.findOne({ where: { email, CompanyId: userData["CompanyId"] }, }); } if (!newUser) { newUser = await db.User.create({ ...userData }); } else { // FIXME: change email for known user... await newUser.update({ ...userData }); } return newUser; } const notifyCreateOrUpdateUser = (type, User, VME, Synt) => { if (type === "synt_authoriser" || type === "synt_viewer") { // send welcome mail synt user notifier.notify(User, "new_synt", { VME, Synt, }); } else if (type === "commissioner") { // send welcome mail commissioner user notifier.notify(User, "new_commissioner", { VME, Synt, }); } else { // send welcome mail normal user notifier.notify(User, "new_user", { VME, Synt, }); } }; const depromoteUsers = async (type, VME) => { // depromote existing synt if (type === "synt_authoriser") { await db.UserVME.update( { type: "synt_viewer" }, { where: { type: "synt_authoriser", VMEId: VME.id, }, } ); } if (type === "commissioner") { await db.UserVME.update( { type: "regular_user" }, { where: { type: "commissioner", VMEId: VME.id, }, } ); } }; const getSynt = (type, User, VME) => { let Synt = {}; if (type === "synt_authoriser") { Synt = User; } else { VME.Users.forEach((User) => { if (User.UserVME.type === "synt_authoriser") { Synt = User; } }); } return Synt; }; const validateUserCharacter = async ({ t, id, email, UserAsCompany, VME }) => { let existsAsNatural = false; if (!UserAsCompany) { existsAsNatural = await db.User.findOne({ where: { email, CompanyId: null, ...(id !== null && { id: { [Op.not]: id } }), }, include: { model: db.VME, where: { id: VME.id }, as: "VMEs" }, }); } if (!UserAsCompany && existsAsNatural) { // cannot create new user because of exisiting email return { success: false, errors: { email: t( "api.vmes.errors.emailAlreadyLinked", "Email is already linked to a natural user in this VME." ), }, }; } let existsAsCompany = false; if (UserAsCompany) { existsAsCompany = await db.User.findOne({ where: { email, CompanyId: UserAsCompany.id, ...(id !== null && { id: { [Op.not]: id } }), }, include: { model: db.VME, where: { id: VME.id }, as: "VMEs" }, }); } if (UserAsCompany && existsAsCompany) { // cannot create new user because of exisiting email return { success: false, errors: { email: t( "api.vmes.errors.emailAlreadyLinkedCompany", "Email and company already exist in this VME." ), }, }; } return { success: true }; }; async function attachUser(req, res) { const { t } = req; const { id, email, phone, first_name, last_name, vat_number, // vat_number of the VME (admin path) Company, // user as company character, // natural or legal address, UserVME, type = "regular_user", // type user within vme vmes, } = req.body; // verify user let validation = ValidationHelper.validateNewUser(req.body, t); if (!validation.success) { return res.json(validation); } let errors = {}; if (!first_name) { errors["first_name"] = t( "api.vmes.errors.firstNameRequired", "First name is required." ); } if (!last_name) { errors["last_name"] = t( "api.vmes.errors.lastNameRequired", "Last name is rquired" ); } if (!phone) { errors["phone"] = t("api.vmes.errors.phoneRequired", "Phone is required"); } if (!address && character === "natural") { errors["address"] = t( "api.vmes.errors.addressRequired", "Address is required" ); } if (character === "legal" && !Company) { errors["vat_number"] = t( "api.vmes.errors.companyRequired", "Company must be known." ); } if (Object.keys(errors).length) { return res.json({ success: false, errors, }); } let UserAsCompany; if (Company) { // verify and format vat number const vatValidation = ValidationHelper.formatVatNumber( Company?.vat_number, req.t ); if (!vatValidation.success) { return res.json(vatValidation); } // define const const company_number = vatValidation.company_number; const country_code = vatValidation.country_code; UserAsCompany = await db.Company.findOne({ where: { company_number, country_code }, }); } const userData = { email, phone, first_name, last_name, character, type, CompanyId: UserAsCompany?.id || null, address: address ? address : Company?.address, }; try { let User = await userHelper.getAuthUser(req); if (User) { //CHECK: IS ADMIN PATH USED??? if (vat_number) { // admin path to add users to any VME if (!User.is_admin) { return res.json({ success: false, error: t( "api.vmes.errors.noPermissions", "You don't have the right role." ), }); } // verify and format vat number const vatValidation = ValidationHelper.formatVatNumber( vat_number, req.t ); if (!vatValidation.success) { return res.json(vatValidation); } // define const const { company_number, country_code } = vatValidation; const validation = await validateUserCharacter({ id, email, UserAsCompany, VME, t, }); if (!validation.success) { return res.json(validation); } let newUser = await createUser(id, email, userData); const VMECompany = await db.Company.findOne({ where: { company_number, country_code }, }); if (!VMECompany) { return res.json({ success: false, error: t( "api.vmes.errors.vmeNotExist", "The VME with accompanying company number does not yet exist. First save the new VME and then link a user." ), }); } const VME = await db.VME.findOne({ where: { CompanyId: VMECompany.id }, }); if (!VME) { return res.json({ success: false, error: t( "api.vmes.errors.vmeNotExist", "The VME with accompanying company number does not yet exist. First save the new VME and then link a user." ), }); } // depromote depromoteUsers(type, VME); // get synt const Synt = getSynt(type, newUser, VME); if (type === "bookkeeper" && vmes) { await syncUserVmes(type, newUser, vmes); } try { await VME.addUser(newUser, { through: { type, }, }); notifyCreateOrUpdateUser(type, newUser, VME, Synt); } catch (e) { console.log(e); } return res.json({ success: true }); } else { // user logged in to specific VME // verify vme and get vme let VmeValidation = await ValidationHelper.validateVme( t, User.VMEId, User, true ); //TODO: Validate user roles if (!VmeValidation.success) { return res.json(VmeValidation); } const { VME } = VmeValidation; const validation = await validateUserCharacter({ id, email, UserAsCompany, VME, t, }); if (!validation.success) { return res.json(validation); } /* if (VME.is_frozen) { return res.json({ success: false, error: "VME is bevrozen. Je kan geen wijzigen aanbrengen.", }); } */ let newUser = await createUser(id, email, userData); // depromote depromoteUsers(type, VME); if (type === "bookkeeper" && vmes) { await syncUserVmes(type, newUser, vmes); } if (UserVME) { newUser.UserVME = UserVME; newUser.UserVME.type = type; } const has = await VME.hasUser(newUser); // get synt const Synt = getSynt(type, newUser, VME); if (!has) { await VME.addUser(newUser, { through: { type, }, }); try { notifyCreateOrUpdateUser(type, newUser, VME, Synt); // update disabled if (id && typeof UserVME !== "undefined") { await db.UserVME.update( { is_disabled: UserVME.is_disabled }, { where: { VMEId: VME.id, UserId: id, }, } ); } } catch (e) { console.log(e); } return res.json({ success: true }); } else { console.log("update"); // send update mail normal user notifier.notify(newUser, "update_user", { VME, Synt, }); // update disabled if (id && typeof UserVME !== "undefined") { await db.UserVME.update( { is_disabled: UserVME.is_disabled, type }, { where: { VMEId: VME.id, UserId: id, }, } ); } } return res.json({ success: true }); } } } catch (error) { console.log(error); return res.json({ success: false, error }); } } async function syncUserVmes(type, user, vmes) { const existingVmes = await db.UserVME.findAll({ include: [{ model: db.User, where: { id: user.id }, as: "User" }], }); const toDelete = existingVmes.filter( (ev) => !vmes.find((v) => v.value === ev.id) ); if (toDelete.length > 0) { await Promise.all(toDelete.map((td) => td.destroy())); } const toCreate = vmes.filter( (v) => !existingVmes.find((ev) => v.value === ev.id) ); if (toCreate.length > 0) { await Promise.all( toCreate.map(async (v) => { const VME = await db.VME.findOne({ where: { id: v.value } }); await VME.addUser(user, { through: { type, }, }); }) ); } } async function getVME(req, res) { const { t } = req; let user = await userHelper.getAuthUser(req); const { VMEId } = req.params; if (user) { const VME = await db.VME.findOne({ where: { id: VMEId }, include: [{ model: db.Company }, { model: db.User, as: "Users" }], }); if (VME) { if (!user.is_admin) { if (VME.hasUser(user)) { return res.json({ success: true, VME: formatVME(VME, req.t), }); } return res.json({ success: false, error: t( "api.vmes.errors.noAccountOrPermissions", "You do not yet have an account or do not have the appropriate role." ), }); } // admin return res.json({ success: true, VME: formatVME(VME, req.t), }); } return res.json({ success: false, error: t("api.vmes.errors.unknownVme", "VME is unknown."), }); } } async function getVMEs(req, res) { const { t } = req; let User = await userHelper.getAuthUser(req); if (User) { if (!User.is_admin) { // get specific VMEs linked to the user const VMEs = await User.getVMEs({ include: [{ model: db.Company }] }); if (VMEs) { return res.json({ success: true, VMEs: VMEs.map((VME) => formatVME(VME, req.t)), }); } return res.json({ success: false, error: t( "api.vmes.errors.noAccountOrPermissions", "You do not yet have an account or do not have the appropriate role." ), }); } // admin can get all const VMEs = await db.VME.findAll({ include: [{ model: db.Company }, { model: db.User, as: "Users" }], }); return res.json({ success: true, VMEs: VMEs.map((VME) => formatVME(VME, req.t)), }); } return res.json({ success: false }); } async function postVME(req, res) { const { t } = req; // verify and format vat number const vatValidation = ValidationHelper.formatVatNumber( req.body.vat_number, req.t ); if (!vatValidation.success) { return res.json(vatValidation); } // define const const { company_number, country_code } = vatValidation; const { alias, meeting_period_starts_at, meeting_period_ends_at, insurance_company, insurance_number, division_id, division_name, total_shares, phone, email, current_account, savings_account, financial_year_starts_at, has_banking, create_reminder = true, } = req.body; if (!alias) { return res.json({ success: false, errors: { alias: t("api.vmes.post.errors.aliasRequired", "An alias is required."), }, }); } // find existing company const company = await db.Company.findOne({ where: { company_number, country_code }, }); if (!company) { return res.json({ success: false, error: t( "api.vmes.post.errors.companyNumberIncorrect", "Company number incorrect." ), }); } company.phone = phone; company.email = email; // TODO: Change current account only after approval company.current_account = current_account; company.savings_account = savings_account; await company.save(); // save vme const VMEData = { CompanyId: company.id, alias, meeting_period_ends_at, meeting_period_starts_at, insurance_company, insurance_number, division_id, division_name, total_shares, financial_year_starts_at, has_banking, }; const VME = await db.VME.findOne({ where: { CompanyId: company.id } }); try { if (VME) { await VME.update(VMEData); console.log("VME updated"); } else { const vme = await db.VME.create(VMEData); console.log("VME created"); if (create_reminder) { await setupRemindersForVME(t, vme.id); } } } catch (e) { console.log(e); return res.json({ success: false }); } return res.json({ success: true }); } module.exports = router;