diff --git a/README.md b/README.md index 3c50a82e8d..9b77fc77fa 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ curl TODO ## Backoffice screenshots -![mockSolarisPerson](https://user-images.githubusercontent.com/6367201/179481833-0d7087cc-1682-435d-94f5-ed316140eaa2.png) +![mockSolarisPerson](https://user-images.githubusercontent.com/47757191/189340823-2b200e6f-5068-4a32-8936-9b7b4d7ae38e.png) ### Persons and accounts @@ -61,6 +61,16 @@ You can set screening values from the "Person data" section ([More info](https:/ Screen Shot 2022-07-18 at 11 38 55 AM +### Seizure Protection +When querying the balance for P-konto accounts, you will get a `seizure_protection` object as stated in this [API](https://docs.solarisgroup.com/api-reference/digital-banking/account-management/#tag/Accounts/paths/~1v1~1accounts~1{account_id}~1balance/get). Since solaris does not support changing an account to P-konto; we mocked the existence of seizure protection. + +You can modify the values of seizure protection in this section: + +![seizure-protection](https://user-images.githubusercontent.com/47757191/189342102-a69a6a30-bc25-4fc1-bf9a-ba6ee45a76ff.png) + +You can also delete the object afterwards: + +![allow-delete-seizure-protection](https://user-images.githubusercontent.com/47757191/189342305-284ebf13-af56-4166-b71a-8591eea0f82a.png) ## Configuration diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index f81843c802..665dd0fb0a 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,12 +1,12 @@ { "name": "@kontist/mock-solaris", - "version": "1.0.39", + "version": "1.0.54", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@kontist/mock-solaris", - "version": "1.0.39", + "version": "1.0.54", "license": "Apache-2.0", "dependencies": { "bluebird": "^3.4.7", @@ -1753,9 +1753,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "engines": [ - "node >=0.6.0" - ] + "engines": ["node >=0.6.0"] }, "node_modules/eyes": { "version": "0.1.8", @@ -1929,9 +1927,7 @@ "dev": true, "hasInstallScript": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -2936,9 +2932,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "engines": [ - "node >=0.6.0" - ], + "engines": ["node >=0.6.0"], "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -5251,9 +5245,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "engines": [ - "node >=0.6.0" - ], + "engines": ["node >=0.6.0"], "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/package.json b/package.json index c72d11659b..fc83d0a33b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kontist/mock-solaris", - "version": "1.0.49", + "version": "1.0.54", "description": "Mock Service for Solaris API", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", diff --git a/src/app.ts b/src/app.ts index 2cc36f6fba..496dabae30 100644 --- a/src/app.ts +++ b/src/app.ts @@ -605,6 +605,16 @@ app.post( safeRequestHandler(standingOrdersAPI.triggerStandingOrderRequestHandler) ); +// BACKOFFICE - SEIZURES PROTECTION +app.post( + "/__BACKOFFICE__/addAccountSeizureProtection/:email", + safeRequestHandler(backofficeAPI.addAccountSeizureProtectionHandler) +); +app.post( + "/__BACKOFFICE__/deleteAccountSeizureProtection/:email", + safeRequestHandler(backofficeAPI.deleteAccountSeizureProtectionHandler) +); + // BACKOFFICE - SEIZURES app.post( "/__BACKOFFICE__/createSeizure/:person_id", diff --git a/src/db.ts b/src/db.ts index 00cb1c36fa..72d9c6b5ad 100644 --- a/src/db.ts +++ b/src/db.ts @@ -3,7 +3,11 @@ import Promise from "bluebird"; import * as log from "./logger"; import { calculateOverdraftInterest } from "./helpers/overdraft"; -import { CustomerVettingStatus, RiskClarificationStatus, ScreeningProgress } from "./helpers/types"; +import { + CustomerVettingStatus, + RiskClarificationStatus, + ScreeningProgress, +} from "./helpers/types"; let redis; @@ -111,6 +115,7 @@ export const migrate = async () => { available_balance: { value: 100, }, + seizure_protection: null, }, billing_account: { id: process.env.SOLARIS_KONTIST_BILLING_ACCOUNT_ID, diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 3c286b064d..35e978b94e 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -30,6 +30,7 @@ const DEFAULT_ACCOUNT = { person_id: "66a692fdddc32c05ebe1c1f1c3145a3bcper", status: "ACTIVE", closure_reasons: null, + seizure_protection: null, }; const requestAccountFields = [ @@ -73,17 +74,14 @@ export const showAccountBookings = async (req, res) => { export const showAccountReservations = async (req, res) => { const { page: { size, number }, - filter: { - reservation_type: reservationType, - }, + filter: { reservation_type: reservationType }, } = req.query; const { account_id: accountId } = req.params; const person = await findPersonByAccountId(accountId); - const reservations = _.get(person.account, "reservations", []) - .filter(reservation => reservation.reservation_type === reservationType) + .filter((reservation) => reservation.reservation_type === reservationType) .slice((number - 1) * size, number * size); res.status(200).send(reservations); @@ -206,7 +204,11 @@ export const createAccountSnapshot = async (req, res) => { export const showAccountBalance = async (req, res) => { const { account_id: accountId } = req.params; const person = await findPersonByAccountId(accountId); - const balance = _.pick(person.account, ["balance", "available_balance"]); + const balance = _.pick(person.account, [ + "balance", + "available_balance", + "seizure_protection", + ]); res.status(200).send(balance); }; diff --git a/src/routes/backoffice.ts b/src/routes/backoffice.ts index 32f57ac8ef..e693ad232e 100644 --- a/src/routes/backoffice.ts +++ b/src/routes/backoffice.ts @@ -13,11 +13,11 @@ import { deleteMobileNumber, saveSepaDirectDebitReturn, getDevicesByPersonId, - saveTaxIdentifications + saveTaxIdentifications, } from "../db"; import { createSepaDirectDebitReturn, - triggerSepaDirectDebitReturnWebhook + triggerSepaDirectDebitReturnWebhook, } from "../helpers/sepaDirectDebitReturn"; import { shouldReturnJSON } from "../helpers"; import { triggerWebhook } from "../helpers/webhooks"; @@ -37,11 +37,11 @@ import { IdentificationStatus, ScreeningProgress, RiskClarificationStatus, - CustomerVettingStatus + CustomerVettingStatus, } from "../helpers/types"; import { changeOverdraftApplicationStatus, - issueInterestAccruedBooking + issueInterestAccruedBooking, } from "../helpers/overdraft"; const triggerIdentificationWebhook = (payload) => @@ -56,7 +56,7 @@ const triggerAccountBlockWebhook = async (person) => { business_id: null, locking_status: lockingStatus, updated_at: new Date().toISOString(), - iban + iban, }; await triggerWebhook(AccountWebhookEvent.ACCOUNT_BLOCK, payload); @@ -67,6 +67,74 @@ export const triggerBookingsWebhook = async (solarisAccountId) => { await triggerWebhook(TransactionWebhookEvent.BOOKING, payload); }; +export const addAccountSeizureProtectionHandler = async (req, res) => { + const { email } = req.params; + + const { + currentBlockedAmount, + protectedAmount, + protectedAmountExpiring, + protectedAmountExpiringDate, + } = req.body; + + const persons = await getAllPersons(); + const person = persons.find((item) => item.email === email); + + if (!person?.account) return null; + + person.account = { + ...person.account, + seizure_protection: { + current_blocked_amount: { + value: currentBlockedAmount, + currency: "EUR", + unit: "cents", + }, + protected_amount: { + value: protectedAmount, + currency: "EUR", + unit: "cents", + }, + protected_amount_expiring: { + value: protectedAmountExpiring, + currency: "EUR", + unit: "cents", + }, + protected_amount_expiring_date: protectedAmountExpiringDate, + }, + }; + + await savePerson(person); + + if (shouldReturnJSON(req)) { + res.status(200).send(person.account); + } else { + res.redirect("back"); + } +}; + +export const deleteAccountSeizureProtectionHandler = async (req, res) => { + const { email } = req.params; + + const persons = await getAllPersons(); + const person = persons.find((item) => item.email === email); + + if (!person?.account) return null; + + person.account = { + ...person.account, + seizure_protection: null, + }; + + await savePerson(person); + + if (shouldReturnJSON(req)) { + res.status(204).send(); + } else { + res.redirect("back"); + } +}; + /** * Handles changes on the provisioning token and redirects back to refresh data. * Reads the personId and cardId from the url params and the status (if sent) from the body. @@ -126,7 +194,7 @@ export const getPersonHandler = async (req, res) => { if (!person) { return res.status(HttpStatusCodes.NOT_FOUND).send({ message: "Couldn't find person", - details: req.params + details: req.params, }); } @@ -143,7 +211,7 @@ export const getPersonHandler = async (req, res) => { taxIdentifications, devices, identifications: person.identifications, - SEIZURE_STATUSES + SEIZURE_STATUSES, }); } }; @@ -191,7 +259,7 @@ export const updatePersonHandler = async (req, res) => { const shouldMarkMobileNumberAsVerified = (identification) => [ IdentificationStatus.PENDING_SUCCESSFUL, - IdentificationStatus.SUCCESSFUL + IdentificationStatus.SUCCESSFUL, ].includes(identification.status) && identification.method === "idnow"; export const setIdentification = async (req, res) => { @@ -206,7 +274,7 @@ export const setIdentification = async (req, res) => { !(skipSettingScreeningValues === "true") && [ IdentificationStatus.SUCCESSFUL, - IdentificationStatus.PENDING_SUCCESSFUL + IdentificationStatus.PENDING_SUCCESSFUL, ].includes(identification.status) ) { person.screening_progress = ScreeningProgress.SCREENED_ACCEPTED; @@ -231,7 +299,7 @@ export const setIdentification = async (req, res) => { completed_at: identification.completed_at, reference: identification.reference, status: identification.status, - method: "idnow" + method: "idnow", }); res.status(204).send(); @@ -245,7 +313,7 @@ export const setScreening = async (req, res) => { const { screening_progress, risk_classification_status, - customer_vetting_status + customer_vetting_status, } = req.body; const person = (await getAllPersons()).find( @@ -292,7 +360,7 @@ export const setIdentificationState = async (req, res) => { !(skipSettingScreeningValues === "true") && [ IdentificationStatus.SUCCESSFUL, - IdentificationStatus.PENDING_SUCCESSFUL + IdentificationStatus.PENDING_SUCCESSFUL, ].includes(identification.status) ) { // TODO: assign these values manually from the backend tests and remove this @@ -318,7 +386,7 @@ export const setIdentificationState = async (req, res) => { completed_at: identification.completed_at, reference: identification.reference, method, - status + status, }); res.redirect(`/__BACKOFFICE__/person/${person.id}#identifications`); @@ -345,8 +413,8 @@ const generateBookingFromStandingOrder = (standingOrder) => { booking_date: moment().format("YYYY-MM-DD"), booking_type: BookingType.SEPA_CREDIT_TRANSFER, amount: { - value: -Math.abs(standingOrder.amount.value) - } + value: -Math.abs(standingOrder.amount.value), + }, }; }; @@ -392,7 +460,7 @@ export const processQueuedBooking = async ( const isDirectDebit = [ BookingType.DIRECT_DEBIT, - BookingType.SEPA_DIRECT_DEBIT + BookingType.SEPA_DIRECT_DEBIT, ].includes(booking.booking_type); const wouldOverdraw = @@ -418,8 +486,8 @@ export const processQueuedBooking = async ( amount: { value: booking.amount.value, unit: "cents", - currency: "EUR" - } + currency: "EUR", + }, }; } @@ -468,7 +536,7 @@ export const generateBookingForPerson = (bookingData) => { transactionId, bookingDate, valutaDate, - status + status, } = bookingData; const recipientName = `${person.salutation} ${person.first_name} ${person.last_name}`; @@ -498,7 +566,7 @@ export const generateBookingForPerson = (bookingData) => { booking_type: bookingType, transaction_id: transactionId || uuid.v4(), return_transaction_id: null, - status + status, }; }; @@ -522,7 +590,7 @@ export const queueBookingRequestHandler = async (req, res) => { transactionId, bookingDate, valutaDate, - status + status, } = req.body; senderName = senderName || "mocksolaris"; @@ -542,7 +610,7 @@ export const queueBookingRequestHandler = async (req, res) => { transactionId, bookingDate, valutaDate, - status + status, }); person.queuedBookings.push(queuedBooking); @@ -597,10 +665,10 @@ export const createDirectDebitReturn = async (personId, id) => { amount: { value: -directDebit.amount.value, unit: "cents", - currency: "EUR" + currency: "EUR", }, booking_date: today, - valuta_date: today + valuta_date: today, }; person.transactions.push(directDebitReturn); @@ -624,7 +692,7 @@ export const updateAccountLockingStatus = async (personId, lockingStatus) => { person.account = { ...person.account, - locking_status: lockingStatus + locking_status: lockingStatus, }; await savePerson(person); @@ -644,7 +712,7 @@ const changeCardStatusAllowed = async (personId, cardId, newCardStatus) => { const cardData = person.account.cards.find(({ card }) => card.id === cardId); const { - card: { status: currentCardStatus, type } + card: { status: currentCardStatus, type }, } = cardData; if ( @@ -668,7 +736,7 @@ const changeCardStatusAllowed = async (personId, cardId, newCardStatus) => { CardStatus.INACTIVE, CardStatus.PROCESSING, CardStatus.CLOSED, - CardStatus.CLOSED_BY_SOLARIS + CardStatus.CLOSED_BY_SOLARIS, ].includes(cardData.card.status) ) { throw new Error( @@ -695,7 +763,7 @@ export const createReservationHandler = async (req, res) => { type, recipient, declineReason, - posEntryMode + posEntryMode, } = req.body; if (!personId) { @@ -714,7 +782,7 @@ export const createReservationHandler = async (req, res) => { type, recipient, declineReason, - posEntryMode + posEntryMode, }; const reservation = await (type === TransactionType.CREDIT_PRESENTMENT @@ -744,7 +812,7 @@ export const updateReservationHandler = async (req, res) => { personId, reservationId, action, - increaseAmount + increaseAmount, }); res.redirect("back"); diff --git a/src/templates/person.html b/src/templates/person.html index cbd5f2935c..be70f027b3 100644 --- a/src/templates/person.html +++ b/src/templates/person.html @@ -193,6 +193,64 @@

{{ person.email }}

This person does not have an account. {% endif %} + +
+

Seizure Protection

+
+ {% if person.account.seizure_protection %} +
+
+ Account is seizure protected! +
+ +
+
+
+
+ +
+
+
{{ JSON.stringify(person.account.seizure_protection, undefined, 2) }}
+
+
+
+
+ {% else %} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ {% endif %} +
+
+

Seizures