gadael/gadael

View on GitHub
schema/Right_Renewal.js

Summary

Maintainability
F
1 wk
Test Coverage
'use strict';

const consumptionHistory = require('../modules/consumptionHistory');
const util = require('util');


exports = module.exports = function(params) {

    var mongoose = params.mongoose;


    var rightRenewalSchema = new mongoose.Schema({

        right: { type: mongoose.Schema.Types.ObjectId, ref: 'Right', required: true, index: true },
        timeCreated: { type: Date, default: Date.now },
        lastUpdate: { type: Date, default: Date.now },
        start: { type: Date, required: true },
        finish: { type: Date, required: true },

        adjustments: [params.embeddedSchemas.RightAdjustment]
    });

    rightRenewalSchema.set('autoIndex', params.autoIndex);



    /**
     * Ensure that the renewal interval do not overlap another renewal period
     */
    rightRenewalSchema.pre('save', function() {
        return this.checkOverlap()
            .then(this.updateMonthlyAdjustment.bind(this));
    });



    /**
     * Pre remove hook
     */
    rightRenewalSchema.pre('remove', function() {
        return this.removeUserRenewalStat();
    });


    /**
     * Remove user renewal stat cache when renewal is deleted
     * @return {Promise}
     */
    rightRenewalSchema.methods.removeUserRenewalStat = function()
    {
        let renewal = this;
        let UserRenewalStat = params.db.models.UserRenewalStat;
        return UserRenewalStat.find({ renewal: renewal._id })
        .exec()
        .then(arr => {
            return Promise.all(arr.map(s => s.remove()));
        });
    };


    /**
     * @return {Promise}
     */
    rightRenewalSchema.methods.checkOverlap = function()
    {
        const gt = params.app.utility.gettext;
        const model = params.db.models.RightRenewal;

        return model.find({ right: this.right })
            .where('start').lt(this.finish)
            .where('finish').gt(this.start)
            .where('_id').ne(this._id)
            .countDocuments()
            .exec()
            .then(renewals => {
                if (renewals > 0) {
                    throw new Error(gt.gettext('The renewals periods must not overlap'));
                }

                return true;
            }
        );
    };






    /**
     * Remove auto adjustments linked to this right renewal
     * @param {User} user
     * @return {Promise}
     */
    rightRenewalSchema.methods.removeAutoAdjustement = function(user) {

        let Adjustment = params.db.models.Adjustment;

        return Adjustment.find()
        .where('rightRenewal').equals(this._id)
        .where('autoAdjustment').equals(true)
        .exec()
        .then(adjustments => {
            let promises = [];

            adjustments.forEach(a => {
                promises.push(a.remove());
            });

            return Promise.all(promises);
        });
    };


    /**
     * Add adjustement for one user
     * @param {User} user
     * @param {Date} moment
     * @param {Number} quantity
     *
     * @return {Promise}
     */
    rightRenewalSchema.methods.addAutoAdjustment = function(user, moment, quantity) {

        let renewal = this;
        let Adjustment = params.db.models.Adjustment;

        let adjust = new Adjustment();

        adjust.rightRenewal = renewal._id;
        adjust.user = user._id;
        adjust.timeCreated = moment;    // TODO: use a different field name
        adjust.quantity = quantity;
        adjust.autoAdjustment = true;

        return adjust.save();
    };



    /**
     * Update the auto adjustements list for one user
     *
     * if autoAdjustment configured on right
     * get the consumption quantity on all selected types
     * create the adjustements with timeCreated match the consumption date
     *
     * consumption is from consumedQuantity field in absence elements
     * moved quantity to time saving account is not considered as a consumption
     *
     * @param {User} user
     *
     * @return {Promise}     Promise the number of modified adjustments
     */
    rightRenewalSchema.methods.updateAutoAdjustments = function(user) {

        let renewal = this;

        return renewal.removeAutoAdjustement()
        .then(() => {
            return renewal.getRightPromise();
        })
        .then(right => {
            if (undefined !== right.autoAdjustment &&
            undefined !== right.autoAdjustment.quantity &&
            null !== right.autoAdjustment.quantity) {

                return consumptionHistory.getConsuptionHistory(user, right.autoAdjustment.types)
                .then(history => {

                    let current = 0;
                    let promises = [];

                    history.forEach(h => {
                        current += h.consumedQuantity;
                        if (current >= right.autoAdjustment.step) {
                            current = 0;
                            promises.push(renewal.addAutoAdjustment(user, h.events[0].dtstart, right.autoAdjustment.quantity));
                        }
                    });

                    return Promise.all(promises);
                });
            }

            return Promise.resolve([]);
        });
    };


    /**
     * Update the rightAdjustment object linked to this right renewal
     * Do not change ajustements in the past
     * @return {Promise}
     */
    rightRenewalSchema.methods.updateMonthlyAdjustment = function()
    {
        return this.getRightPromise()
        .then(right => {
            this.removeFutureRightAdjustments();
            this.createRightAdjustments(right);
            return true;
        });
    };


    /**
     * remove future adjustments in the monthly adjustments
     */
    rightRenewalSchema.methods.removeFutureRightAdjustments = function() {

        if (undefined === this.adjustments) {
            return;
        }

        let now = new Date();

        for (var i = this.adjustments.length - 1; i >= 0; i--) {
            if (this.adjustments[i].from >= now) {
                this.adjustments.splice(i, 1);
            }
        }
    };


    /**
     * Create adjustments from the next month 1st day to the limit
     * @return {bool}
     */
    rightRenewalSchema.methods.createRightAdjustments = function(right) {

        var renewal = this;

        if (renewal.finish <= new Date()) {
            return false;
        }


        if (undefined === right.addMonthly.quantity || 0 === right.addMonthly.quantity || null === right.addMonthly.quantity) {
            // functionality has been disabled
            return false;
        }

        function getNextMonthStart(date)
        {
            date.setDate(1);
            date.setHours(0,0,0,0);
            date.setMonth(date.getMonth()+1);

            return date;
        }

        var max = right.getMonthlyMaxQuantity();
        var loop = getNextMonthStart(new Date());


        if (loop < renewal.start) {
            loop = getNextMonthStart(new Date(renewal.start));
        }


        // start at the begining of the next month



        var inserted = 0;

        while(loop <= renewal.finish && renewal.getMonthlyAdjustmentsQuantity(right) <= max) {
            renewal.adjustments.push({
                from: new Date(loop),
                quantity: right.addMonthly.quantity
            });

            loop.setMonth(loop.getMonth()+1);

            inserted++;
        }

        return (inserted > 0);

    };



    /**
     * get the quantity in the monthly adjustments list
     * cap quantity to max because past adjustments are never removed
     * but max can be modified afterward
     * @return {Number}
     */
    rightRenewalSchema.methods.getMonthlyAdjustmentsQuantity = function(right) {
        var quantity = 0;
        this.adjustments.forEach(function(adjustment) {
            quantity += adjustment.quantity;
        });

        if (quantity>right.getMonthlyMaxQuantity()) {
            quantity = right.getMonthlyMaxQuantity();
        }

        return quantity;
    };



    /**
     * Get a user adjustement quantity, can be a negative value
     * adjustments on renewal
     *
     * @param {Document} user
     *
     * @returns {Promise} resolve to a number
     */
    rightRenewalSchema.methods.getUserAdjustmentQuantity = function(user) {

        let Adjustment = params.db.models.Adjustment;
        let renewal = this;

        return Adjustment.find({ rightRenewal: renewal._id, user: user._id }, 'quantity')
        .then(docs => {
            let adjustments = 0, i;
            for (i=0; i<docs.length; i++) {
                adjustments += docs[i].quantity;
            }

            return adjustments;
        });

    };

    /**
     * Set the right document to return in getRightPromise
     * This is a performance optimization
     *
     */
    rightRenewalSchema.methods.setRightForPromise = function(right) {
        let renewal = this;

        renewal.promiseRight = right;
    };


    /**
     * Get the right linked to the renewal
     * return a promise and resolve to a Right document
     *
     * @return {Promise}
     */
    rightRenewalSchema.methods.getRightPromise = function() {

        let renewal = this;

        if (!renewal.right) {
            throw new Error('Missing right on renewal document');
        }


        if (undefined !== renewal.promiseRight) {
            return Promise.resolve(renewal.promiseRight);
        }

        return renewal.populate('right').execPopulate()
        .then(() => {
            return renewal.right;
        });
    };


    /**
     * Get the initial quantity for a user without adjustments
     * this shoud be the quantity set by administrator on the right or a computed quantity
     * if this is a special right
     *
     * @param {User} user User document with account role
     *
     * @returns {Promise} resolve to an object, the value property is the right initial quantity
     */
    rightRenewalSchema.methods.getUserRightInitialQuantity = function(user) {

        let renewal = this;

        return renewal.getRightPromise()
        .then(right => {
            let specialright = right.getSpecialRight();

            if (null === specialright) {

                if (null === right.quantity) {
                    return {
                        value: Infinity,
                        special: false
                    };
                }

                return {
                    value: right.quantity,
                    special: false
                };
            }

            return specialright.getQuantity(renewal, user);
        });

    };


    /**
     * Get a user initial quantity
     * default right quantity + adjustments on renewal from the monthly updates + manual adjustments on renewal for the user
     * The default quantity from right is accessible only after the account arrival date
     * for renewals straddling the arrival date, the quantiy is computed using the percentage of account valid time
     *
     * @todo duplicated with accountRight object
     *
     * @param {User}    user        User document with account role
     * @param {Date}    [moment]    the adjutments will be added up to this date, default is now
     *
     * @returns {Promise} resolve to an object, the value property is the user initial quantity
     */
    rightRenewalSchema.methods.getUserQuantity = function(user, moment) {

        let renewal = this;

        if (undefined === moment) {
            moment = new Date();
        }

        return Promise.all([
            renewal.getUserRightInitialQuantity(user),
            renewal.getUserAdjustmentQuantity(user)
        ])
        .then(function(arr) {

            /**
             * Default right quantity available for the renewal
             * if the user account arrival date is > renewal.start
             * a pro rata of the quantity is computed for the default quantity
             *
             * @var {Number}
             */
            let rightQuantity = arr[0].value;

            /**
             * Manual adjustment created by administrators on the account-right page
             * @var {Number}
             */
            let userAdjustment = arr[1];


            if (rightQuantity === Infinity) {
                return {
                    value: Infinity
                };
            }


            if (user.roles.account.arrival > renewal.finish) {
                // this will not be used via the REST API because invalid renewal are disacarded before
                return {
                    value: 0
                };
            }


            if (user.roles.account.arrival > renewal.start) {
                var renewalDuration = renewal.finish.getTime() - renewal.start.getTime();
                var availableDuration = renewal.finish.getTime() - user.roles.account.arrival.getTime();

                rightQuantity = Math.round(rightQuantity * availableDuration / renewalDuration);
            }

            /**
             * If the right is configured with monthly quantity update,
             * this variable will contain adjustments in renewal from the arrival date to the current date
             * @var {Number}
             */
            var renewalAdjustment = 0;

            renewal.adjustments.forEach(function(adjustment) {
                if (adjustment.from >= user.roles.account.arrival && adjustment.from <= moment) {
                    renewalAdjustment += adjustment.quantity;
                }
            });


            return {
                value: (rightQuantity + renewalAdjustment + userAdjustment),
                details: {
                    renewalAdustment: renewalAdjustment,
                    userAdjustment: userAdjustment,
                    rtt: arr[0].rtt
                }
            };
        });
    };


    /**
     * Quantity moved to time saving accounts
     * sum of quantities in deposits for this renewal
     *
     * @param {User} user
     * @param {Date} moment
     *
     * @returns {Promise} resolve to a number
     */
    rightRenewalSchema.methods.getUserSavedQuantity = function(user, moment, addDepositQuantity) {

        let Request = this.model('Request');

        return Request.find(
            {
                'time_saving_deposit.from.renewal.id': this._id,
                'user.id': user._id
            },
            'time_saving_deposit.quantity'
        )
        .then(docs => {


            for(var i=0; i<docs.length; i++) {

                let status = docs[i].getDateStatus(moment);
                addDepositQuantity(status, docs[i].time_saving_deposit[0].quantity);


            }

            return true;
        });
    };



    /**
     * Confirmed quantity moved to time saving accounts
     * sum of quantities in deposits for this renewal
     *
     * @param {User} user
     * @param {Date} moment
     *
     * @returns {Promise} resolve to a number
     */
    rightRenewalSchema.methods.getUserSavedConfirmedQuantity = function(user, moment) {

        let savedQuantity = 0;

        return this.getUserSavedQuantity(user, moment, function(status, quantity) {
            if ('confirmed' === status.created) {
                savedQuantity += quantity;
            }
        }).then(() => {
            return savedQuantity;
        });
    };


    /**
     * Get user consumed quantity (leaves only)
     * @see rightRenewalSchema.getUserConsumedQuantity() for full consumption
     *
     * @param {User} user        Request owner
     * @param {Date} moment        Only request approved on this date
     * @param {Function} collector Get quantity and status
     *
     * @returns {Promise}         resolve to true
     */
    rightRenewalSchema.methods.getUserAbsenceQuantity = function(user, moment, collector)
    {
        let renewal = this;
        let AbsenceElem = this.model('AbsenceElem');

        return AbsenceElem.find()
        .where('user.id', user._id)
        .where('right.renewal.id', renewal._id)
        .populate('request')
        .select('consumedQuantity request')
        .exec()
        .then(elements => {



            elements.forEach(element => {

                let status = {
                    created: 'accepted',
                    deleted: null
                };

                if (element.request) {
                    status = element.request.getDateStatus(moment);
                } else {
                    console.error('Absence element '+element.id+' does not contain the link to the request');
                }

                collector(status, element.consumedQuantity);
            });

            return true;
        });


    };


    /**
     * Get user quantity in the absences requests and by approval status
     *
     * @param {User} user        Request owner
     * @param {Date} moment        Only request approved on this date
     *
     * @returns {Promise}         resolve to a number
     */
    rightRenewalSchema.methods.getUserAbsenceRequestsQuantity = function(user, moment) {
        let consumed = 0;

        let waiting = {
            created: 0,
            deleted: 0
        };

        return this.getUserAbsenceQuantity(user, moment, function(status, quantity) {
            if (status.created === 'accepted') {
                consumed += quantity;
            }

            if (status.created === 'waiting') {
                waiting.created += quantity;
            }

            if (status.deleted === 'waiting') {
                waiting.deleted += quantity;
            }
        })
        .then(() => {
            return {
                consumed: consumed,
                waiting: waiting
            };
        });
    };


    /**
     * Get user consumed quantity (leaves only)
     * @see rightRenewalSchema.getUserConsumedQuantity() for full consumption
     *
     * @param {User} user        Request owner
     * @param {Date} moment        Only request approved on this date
     *
     * @returns {Promise}         resolve to a number
     */
    rightRenewalSchema.methods.getUserAbsenceConsumedQuantity = function(user, moment) {
        return this.getUserAbsenceRequestsQuantity(user, moment)
        .then(requests => {
            return requests.consumed;
        });
    };


    /**
     * Get a user waiting quantity
     * sum of quantities in requests and saved from this renewal
     *
     * @param {User} user        Request owner
     * @param {Date} moment        Only request in waiting state on this date
     *
     * @return {Promise}     resolve to an object
     */
    rightRenewalSchema.methods.getUserWaitingQuantity = function(user, moment) {
        return this.getUserAbsenceRequestsQuantity(user, moment)
        .then(requests => {
            return requests.waiting;
        });
    };


    /**
     * Get a user consumed quantity
     * sum of quantities in requests and saved from this renewal
     *
     * @todo duplicated with accountRight object
     *
     * @param {User} user        Request owner
     * @param {Date} moment        Only request approved on this date
     *
     * @returns {Promise} resolve to an object with consumed and waiting property
     */
    rightRenewalSchema.methods.getUserConsumedQuantity = function(user, moment) {

        let renewal = this;

        return Promise.all([
            renewal.getUserAbsenceRequestsQuantity(user, moment),
            renewal.getUserSavedConfirmedQuantity(user, moment)        // time saving account only
        ])
        .then(all => {
            let requests = all[0];
            requests.consumed = requests.consumed - all[1];
            return requests;
        });
    };


    /**
     * If the associated right is a time saving account
     * sum of quantities in deposits for this renewal
     *
     * @param {User} user
     *
     * @returns {Promise} resolve to a number
     */
    rightRenewalSchema.methods.getUserTimeSavingDepositsQuantity = function(user) {

        let Request = this.model('Request');

        return Request.find({
            'time_saving_deposit.to.renewal.id': this._id,
            'user.id': user._id
        }, 'time_saving_deposit.quantity')
        .then(docs => {

            var deposits = 0;
            for(var i=0; i<docs.length; i++) {
                deposits += docs[i].time_saving_deposit[0].quantity;
            }

            return deposits;
        });
    };


    /**
     * A ratio to convert quantities in day
     * if the associated right is in days, this will always be 1
     * if the associated right is in hour, the ratio will be the one from the working times calendar associated to the user
     * on the current date if current date is in the renewal period or else the finish date of the renewal period
     *
     * @param {User} user
     *
     * @returns {Promise} Number
     */
    rightRenewalSchema.methods.getDaysRatio = function(user) {

        let renewal = this;
        let now = new Date();
        let workingTimesDate;

        if (renewal.start <= now && renewal.finish >= now) {
            workingTimesDate = now;
        } else {
            workingTimesDate = renewal.finish;
        }

        return renewal.getRightPromise()
        .then(right => {
            if ('D' === right.quantity_unit) {
                return Promise.resolve(1);
            }

            return user.getAccount()
            .then(account => {
                return account.getScheduleCalendar(workingTimesDate);
            })
            .then(calendar => {

                if (null === calendar || !calendar.hoursPerDay) {
                    // no schedule calendar on period
                    return 0;
                }

                return (1/calendar.hoursPerDay);
            });
        });
    };



    /**
     * Get a user available quantity
     * the user initial quantity (adjustments included)
     *     - the confirmed consumed quantity
     *     - waiting quantity (future consumption)
     *  + confirmed deposits quantity (if we are on a time saving deposit account)
     *
     *
     * @todo duplicated with accountRight object
     *
     * @param {User} user
     * @returns {Promise} resolve to a number
     */
    rightRenewalSchema.methods.getUserAvailableQuantity = function(user) {

        return Promise.all([
            this.getUserQuantity(user),
            this.getUserConsumedQuantity(user),
            this.getUserTimeSavingDepositsQuantity(user)
        ]).then(function(arr) {
            let requests = arr[1];
            return (arr[0].value - requests.consumed - requests.waiting.created + arr[2]);
        });
    };



    /**
     * Get a user available, consumed and initial quantity
     *
     * @param {User} user
     * @returns {Promise} resolve to an object
     */
    rightRenewalSchema.methods.getUserQuantityStats = function(user) {


        let renewal = this;

        return user.getAccount()
        .then(() => {

            return Promise.all([
                renewal.getUserQuantity(user),
                renewal.getUserConsumedQuantity(user),
                renewal.getUserTimeSavingDepositsQuantity(user),
                renewal.getDaysRatio(user)
            ]);

        })
        .then(arr => {

            let requests = arr[1];
            const initialQuantity = arr[0].value;


            let stat = {
                initial: initialQuantity,
                consumed: requests.consumed,
                deposits: arr[2],
                available: (initialQuantity - requests.consumed - requests.waiting.created + arr[2]),
                waiting: requests.waiting,
                daysratio: arr[3]
            };

            if (undefined !== arr[0].details && arr[0].details.rtt !== undefined) {
                stat.rtt = arr[0].details.rtt;
            }

            return stat;
        })
        .catch(err => {

            // set renewal on error for services/user/accountbeneficiaries/renewals
            err.renewal = renewal;
            return {};
        });
    };


    /**
     * Get users associated to the right
     * @param {Date}    moment  Optional date for collection association to users
     * @return {Promise}
     */
    rightRenewalSchema.methods.getBeneficiaryUsers = function(moment) {
        let renewal = this;
        return renewal.getRightPromise()
        .then(right => {
            return right.getBeneficiaryUsers(moment);
        });
    };


    /**
     * Force cache refresh for one user
     * @param {User} user
     * @param {beneficiary} beneficiary
     * @return {Promise}
     */
    rightRenewalSchema.methods.updateUserStat = function(user, beneficiary) {
        let renewal = this;

        return renewal.getUserQuantityStats(user)
        .then(validStat => {
            // overwrite previous error
            validStat.error = null;
            return renewal.saveUserRenewalStat(user, beneficiary, validStat);
        })
        .catch(err => {

            console.log('Error saved to stat', err.stack);

            return renewal.saveUserRenewalStat(user, beneficiary, {
                initial: 0,
                available: 0,
                error: err.message
            });
        });
    };

    /**
     * Force cache refresh for all users whith access to this renewal
     * Set only the outofdate status, chache refresh will be done afterward
     * @param {Date}    moment  Optional date for collection association to users
     * @return {Promise}
     */
    rightRenewalSchema.methods.setUsersStatOutOfDate = function(moment) {
        return this.getBeneficiaryUsers(moment)
        .then(beneficiaries => {
            const accountIds = beneficiaries.map(b => b.user.roles.account);
            return params.db.models.Account.updateMany(
                { _id: { $in: accountIds } },
                { $set: { renewalStatsOutofDate: true } }
            ).exec();
        });
    };


    /**
     * Force cache refresh for all users whith access to this renewal
     * @param {Date}    moment  Optional date for collection association to users
     * @return {Promise}
     */
    rightRenewalSchema.methods.updateUsersStat = function(moment) {
        return this.getBeneficiaryUsers(moment)
        .then(users => {
            return Promise.all(users.map(ub => {
                // this update all renewals for the user:
                return ub.user.updateRenewalsStat(moment);

                // This update only the current renewal:
                //return renewal.updateUserStat(ub.user, ub.beneficiary);
            }));
        });
    };


    /**
     * @return {Promise}
     */
    rightRenewalSchema.methods.deleteUserRenewalStat = function(user) {

        let renewal = this;
        let UserRenewalStat = renewal.model('UserRenewalStat');

        return UserRenewalStat.find({
            user: user._id,
            renewal: renewal._id
        })
        .exec()
        .then(arr => arr.map(s => s.remove()));
    };


    /**
     * Save stat object to database
     * @param {User} user
     * @param {Beneficiary} beneficiary
     * @param {Object} stat Stat object to save
     *
     * @return {Promise} resolve to the new saved document
     */
    rightRenewalSchema.methods.saveUserRenewalStat = function(user, beneficiary, stat) {
        let renewal = this;
        let UserRenewalStat = renewal.model('UserRenewalStat');

        return UserRenewalStat.find({
            user: user._id,
            renewal: renewal._id
        })
        .exec()
        .then(arr => {
            if (0 === arr.length) {
                let newStat = new UserRenewalStat();
                newStat.user = user._id;
                newStat.renewal = renewal._id;

                return renewal.getRightPromise()
                .then(right => {

                    return right.getType()
                    .then(type => {
                        let typeCache;
                        if (null !== type && undefined !== type) {
                            typeCache = {
                                name: type.name,
                                color: type.color
                            };
                        }

                        newStat.right = {
                            id: right._id,
                            name: right.name,
                            type: typeCache
                        };

                        return newStat;
                    });

                });


            }


            for (let i=1; i<arr.length; i++) {
                arr[i].remove();
            }

            return arr[0];
        })
        .then(newStat => {
            newStat.set(stat);
            newStat.beneficiary = beneficiary._id;
            return beneficiary.getAccountCollection(user)
            .then(accountCollection => {
                if (null !== accountCollection) {
                    newStat.accountCollection = accountCollection._id;
                }
                return newStat.save();
            });
        });
    };



    /**
     * get UserRenewalStat cache stat object from DB
     * @param {User} user
     *
     * @return {Promise} resolve to the saved document or NULL
     */
    rightRenewalSchema.methods.getUserRenewalStat = function(user) {

        let renewal = this;
        let UserRenewalStat = renewal.model('UserRenewalStat');

        return UserRenewalStat.findOne({
            user: user._id,
            renewal: renewal._id
        })
        .exec();
    };



    /**
     * Get the saving period for the renewal
     * With start and finish properties
     *
     * @param {Right} [right]
     * @return {Object}
     */
    rightRenewalSchema.methods.getSavingPeriod = function(right) {

        if (undefined === right || null === right) {
            if (undefined !== this.populated('right')) {
                right = this.right;
            } else {
                throw new Error('missing right as parameter or by populate');
            }
        }



        if (undefined === right.timeSavingAccount || 'timesavingaccount' !== right.special) {
            return null;
        }

        if (undefined === right.timeSavingAccount.savingInterval) {
            return null;
        }

        let savingInterval = right.timeSavingAccount.savingInterval;


        if (savingInterval.useDefault) {
            return {
                start: this.start,
                finish: this.finish
            };
        }

        let savingPeriod = {
            start: new Date(this.start),
            finish: new Date(this.finish)
        };

        savingPeriod.start.setFullYear(savingPeriod.start.getFullYear() - savingInterval.min);
        savingPeriod.finish.setFullYear(savingPeriod.finish.getFullYear() - savingInterval.max);

        return savingPeriod;
    };


    /**
     * Get worked days on renewal for one account, use the user workschedule
     * with a cache
     * OR
     * Get worked days on renewal for one account, use the collection custom schedule week
     *
     * @param   {Account}   account
     * @return {Promise}
     */
    rightRenewalSchema.methods.getWorkedDays = function(account) {
        const renewal = this;

        return account.getCollection(renewal.start)
        .then(collection => {

            if (null === collection || collection.useWorkschedule) {
                return account.getPeriodScheduleEvents(renewal.start, renewal.finish)
                .then(ScheduleEra => {
                    return ScheduleEra.getDays();
                });
            }

            const workedDays = collection.getCustomScheduleDays();

            let days = {};
            let loop = new Date(renewal.start);
            loop.setHours(0,0,0,0);

            while (loop.getTime() < renewal.finish.getTime()) {
                if (-1 !== workedDays.indexOf(loop.getDay())) {
                    days[loop.getTime()] = new Date(loop);
                }
                loop.setDate(loop.getDate() + 1);
            }

            return days;
        });
    };



    /**
     * get number of week-end days in the renewal for one user
     *
     * @param   {Account}   account
     * @returns {Promise} Int
     */
    rightRenewalSchema.methods.getWeekEndDays = function(account) {

        const renewal = this;

        return renewal.getWorkedDays(account)
        .then(days => {
            const scheduledDays = Object.keys(days).length;
            return (renewal.getDays() - scheduledDays);
        });
    };



    /**
     * Get number of days in renewal period
     * in classical cases, this will be 365
     *
     * @returns {Number} Integer
     */
    rightRenewalSchema.methods.getDays = function() {

        return (
            1+ (
                Date.UTC(this.finish.getFullYear(), this.finish.getMonth(), this.finish.getDate()) -
                 Date.UTC(this.start.getFullYear(), this.start.getMonth(), this.start.getDate())
            ) / 86400000
        );
    };


    /**
     * Get paid leave quantity
     * get number set in initial quantity on the right with adjustments only
     * if adjusted initial quantity is greater than right quantity
     * -> more annual leaves will lower number of RTT
     *
     * @return {Promise}
     */
    rightRenewalSchema.methods.getPaidLeavesQuantity = function(user) {

        const renewal = this;
        const gt = params.app.utility.gettext;
        const Type = renewal.model('Type');

        return Type.findOne({ _id: '5740adf51cf1a569643cc508'}).exec()
        .then(type => {
            if (null === type) {
                throw new Error(gt.gettext('To compute the number of planned working days, the annual leave type is required'));
            }

            return type.getInitialQuantityInPeriod(user, renewal.start, renewal.finish);
        });
    };





    rightRenewalSchema.methods.getNonWorkingDayQuantity = function(account) {
        const renewal = this;

        const promise = Promise.all([
            account.getNonWorkingDayEvents(renewal.start, renewal.finish),
            renewal.getWorkedDays(account)
        ])
        .then(all => {
            const nonWorkingDays = all[0].getDays();
            const workedDays = all[1];
            // count number of non-working days on working periods
            let count = 0;
            for (let ts in nonWorkingDays) {
                if (workedDays[ts] !== undefined) {
                    count++;
                }
            }

            return count;
        });

        return promise;
    };



    /**
     * Get number of planned working days on the period
     *
     *
     * @exemple 365 - 104 week-ends days - 25 days of annual paid leaves - 8 non working days = 228
     *
     * @param {User} user
     * @return {Promise}
     */
    rightRenewalSchema.methods.getPlannedWorkDayNumber = function(user) {

        const gt = params.app.utility.gettext;
        const renewal = this;

        return user.getAccount().then(account => {

            return Promise.all([
                renewal.getWeekEndDays(account),
                renewal.getNonWorkingDayQuantity(account),
                renewal.getPaidLeavesQuantity(user)
            ]);

        }).then(r => {

            const weekEnds = r[0];
            const nonWorkingDays = r[1];
            const paidLeaves = r[2];


            if (0 === paidLeaves) {
                throw new Error(gt.gettext('To compute the number of planned working days on a year, the annual leave initial quantity is required'));
            }

            const renewalDays = renewal.getDays();

            // Number of days on the renewal period     ~365
            // - Number of weeks-ends days                 ~104
            // - Initial quantity of annual paid leaves ~25
            // - Non working days                         ~11
            // console.log(renewal.start.getFullYear(), renewal.getDays(), weekEnds, paidLeaves, nonWorkingDays);
            return {
                value: (renewalDays - weekEnds - paidLeaves - nonWorkingDays),
                renewalDays: renewalDays,
                weekEnds: weekEnds,
                paidLeaves: paidLeaves,
                nonWorkingDays: nonWorkingDays
            };
        });

    };



    /**
     * Create a new date using day and month of the date in parameter
     * the date must match the renewal period
     *
     * @param {Date} dayMonth
     *
     * @return {Date}
     */
    rightRenewalSchema.methods.createDateFromDayMonth = function(dayMonth) {
        let renewal = this;
        const gt = params.app.utility.gettext;

        let d = new Date(dayMonth);
        d.setFullYear(renewal.start.getFullYear());

        if (d.getTime() < renewal.start.getTime()) {
            d.setFullYear(renewal.finish.getFullYear());
        }

        if (d.getTime() > renewal.finish.getTime()) {
            throw new Error(util.format(gt.gettext('Invalid renewal, the renewal is too short and does not contain the requested date: %s'), d.toString()));
        }

        return d;
    };





    params.db.model('RightRenewal', rightRenewalSchema);



};