const moment = require('moment')
const rerrors = require('request-promise/errors')
const { log, errors } = require('cozy-konnector-libs')
const classification = require('../classification.json')
// ====== Constants =======
const defaultMedataOperation = {
_id: '0',
_proba: 0,
_type: 'none'
}
const AccountType = {
UNKNOWN: {
id: 0,
type: 'Unknown',
scrape4Balance: null
},
CHECKINGS: {
id: 1,
type: 'Checkings',
scrape4Balance: {
sel: '#tableauConsultationHisto',
opts: { sel: 'td>strong', parse: normalizeAmount }
}
},
CREDITCARD: {
id: 2,
type: 'CreditCard',
scrape4Balance: {
sel: '#tableauConsultationHisto',
opts: { sel: 'td>a>strong', parse: normalizeAmount }
}
},
SAVINGS: {
id: 3,
type: 'Savings',
scrape4Balance: {
sel: 'div.synthese_livret_cat a',
opts: { sel: 'p.synthese_data_line_right_text', parse: normalizeAmount }
}
},
MARKET: {
id: 4,
type: 'Market',
scrape4Balance: {
sel: '#valorisation_compte>table>tbody>tr',
opts: { sel: 'td.gras', parse: normalizeAmount }
}
},
LIFEINSURANCE: {
id: 5,
type: 'LifeInsurance',
scrape4Balance: {
sel: 'div.synthese_vie>div>div.colonne_gauche>div>p>span',
opts: { sel: 'strong', parse: normalizeAmount }
}
}
}
const AbbrToAccountType = {
cco: AccountType.CHECKINGS,
esp: AccountType.CHECKINGS,
liv_a: AccountType.SAVINGS,
liv_d: AccountType.SAVINGS,
liv_p: AccountType.SAVINGS,
ord: AccountType.MARKET,
pea: AccountType.MARKET,
vie: AccountType.LIFEINSURANCE
}
// ====== Public functions =======
/**
* Convert string to {@link moment.Moment}
* @param {string} date
* @returns {moment.Moment}
*/
function parseDate(date) {
moment.locale('fr')
return moment(date, 'DD/MM/YYYY')
}
/**
* Convert an amount to float
* @param {string} amount
* @returns {Number}
*/
function normalizeAmount(amount) {
return parseFloat(
amount
.replaceAll(/\s/, '')
.replace(',', '.')
.trim()
)
}
/**
* Analyzes the CSS classes of the bank account to find its type
*
* @param {string} label The CSS classes of the bank account
* @see {@link https://docs.cozy.io/en/cozy-doctypes/docs/io.cozy.bank/#iocozybankaccounts|io.cozy.bank.accounts}
* @returns {string} The type of the bank account
*/
function getAccountTypeFromCSS(cssClasses) {
let cssClass = cssClasses.split(' ')
// Keep only the first class: e.g. 'cco compte' -> 'cco'
return AbbrToAccountType[cssClass[0]] || AccountType.UNKNOWN
}
/**
* Searches in the classification.json file, the metadata corresponding to words passed in parameters.
* This function ignores the metadata dedicated to debit operations.
*
* @param {array} words
* @returns {object}
*/
function findMetadataForCreditOperation(words) {
return _classification(words, defaultMedataOperation, (tree, metadata) =>
_readSpecificMetaData(tree, '_credit', metadata)
)
}
/**
* Searches in the classification.json file, the metadata corresponding to words passed in parameters.
* This function ignores the metadata dedicated to credit operations.
*
* @param {array} words
* @returns {object}
*/
function findMetadataForDebitOperation(words) {
return _classification(words, defaultMedataOperation, (tree, metadata) =>
_readSpecificMetaData(tree, '_debit', metadata)
)
}
/**
*
* @param err
* @returns {Promise.<*>}
*/
function handleRequestErrors(err) {
if (
err instanceof rerrors.RequestError ||
err instanceof rerrors.StatusCodeError
) {
log('error', err)
throw new Error(errors.VENDOR_DOWN)
} else {
return Promise.reject(err)
}
}
// ====== Private functions =======
function _classification(words, metadata, fnReadMetadata) {
let treeActive = classification[words[0].toUpperCase()]
if (!treeActive) {
return metadata
}
let index = words[0] === 'CARTE' ? 2 : 1
fnReadMetadata(treeActive, metadata)
for (let word of words.slice(index)) {
let tree = treeActive[word.toUpperCase()]
if (!tree) {
return metadata
}
fnReadMetadata(tree, metadata)
treeActive = tree
}
}
function _readDefaultMetaData(treeMetadata, metadata) {
Object.keys(metadata).forEach(key => {
if (treeMetadata[key]) metadata[key] = treeMetadata[key]
})
}
function _readSpecificMetaData(treeMetadata, typeOperation, metadata) {
_readDefaultMetaData(treeMetadata, metadata)
if (treeMetadata[typeOperation]) {
_readDefaultMetaData(treeMetadata[typeOperation], metadata)
}
return metadata
}
// ====== Export =======
module.exports = {
AccountType,
parseDate,
normalizeAmount,
handleRequestErrors,
getAccountTypeFromCSS,
findMetadataForCreditOperation,
findMetadataForDebitOperation
}