/** * Contains program Quote class * * Copyright (C) 2017 R-T Specialty, LLC. * * This file is part of the Liza Data Collection Framework. * * liza is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * @todo Use ``document'' terminology in place of ``quote'' */ var Class = require( 'easejs' ).Class, Quote = require( './Quote' ), Program = require( '../program/Program' ).Program, EventEmitter = require( 'events' ).EventEmitter; /** * Creates a new quote * * TODO: This also has a bit of server-side logic; extend/decorate the class * for use server-side and remove all the server-only logic */ module.exports = Class( 'BaseQuote' ) .implement( Quote ) .extend( EventEmitter, { /** * Raised when current step id changes * @type {string} */ 'const EVENT_STEP_CHANGE': 'stepChange', /** * Quote id * @type {number} */ 'private _id': 0, /** * Data bucket * @type {QuoteDataBucket} */ 'private _bucket': null, /** * Id of the current step * @type {number} */ 'private _currentStepId': 1, /** * Id of the highest step that the user has reached * @type {number} */ 'private _topVisitedStepId': 1, /** * Program to which the quote belongs * @type {Program} */ 'private _program': null, /** * Date (UNIX timestamp) that the quote was started * @type {number} */ 'private _startDate': 0, /** * Id of agent that owns the quote * @type {number} */ 'private _agentId': 0, /** * Agency name * @type {string} */ 'private _agentName': '', /** * Whether the quote has been imported * @type {boolean} */ 'private _imported': false, /** * Whether the quote has been bound * @type {boolean} */ 'private _bound': false, /** * Quote-wide error (should invalidate quote until cleared) * @type {string} */ 'private _error': '', /** * Quick save data (diff format) * @type {Object} */ 'private _quickSaveData': {}, /** * Allows quote to be locked for any reason * @type {string} */ 'private _explicitLock': '', /** * Optional step associated with explicit lock * @type {number} */ 'private _explicitLockStep': 0, /** * Initializes quote with the given id and bucket * * @return {undefined} */ 'public __construct': function( id, bucket ) { this._id = id; this._bucket = bucket; }, /** * Returns the quote id * * The quote id is immutable. A different quote id would represent a * different quote, therefore a new object should be created with the * desired quote id. * * @return {number} quote id */ 'public getId': function() { return this._id; }, /** * Returns the bucket used to store the quote form data * * @return {QuoteDataBucket} */ 'public getBucket': function() { return this._bucket; }, /** * Sets the program id to associate with the quote * * @param {string} id program id * * @return {Quote} self */ 'public setProgram': function( program ) { if ( !Class.isA( Program, program ) ) { throw Error( 'Program expected; given ' + program ); } this._program = program; return this; }, /** * Returns the program id associated with the quote * * @return {string} program id */ 'public getProgramId': function() { return ( this._program !== null ) ? this._program.getId() : ''; }, /** * Retrieve Program associated with quote * * @return {Program} quote program */ 'public getProgram': function() { return this._program; }, /** * Sets the quote start date * * @param {number} time start date as a UNIX timestamp * * @return {Quote} self */ 'public setStartDate': function( time ) { this._startDate = +( time ); return this; }, /** * Returns the quote start date * * @return {number} quote start date */ 'public getStartDate': function() { return this._startDate; }, /** * Sets id of agent that owns the quote * * @param {number} id agent id * * @return {Quote} self */ 'public setAgentId': function( id ) { this._agentId = +( id ); return this; }, /** * Returns the id of the agent that owns the quote * * @return {number} agent id */ 'public getAgentId': function() { return this._agentId; }, /** * Sets name of agent that owns the quote * * @param {string} name agent name * * @return {Quote} self */ 'public setAgentName': function( name ) { this._agentName = ''+( name ); return this; }, /** * Returns the name of the agent that owns the quote * * @return {string} agent name */ 'public getAgentName': function() { return this._agentName; }, /** * Sets quote imported status * * Represents whether the quote has been imported into our agency management * system. * * @param {boolean} value true if imported, otherwise false * * @return {Quote} self */ 'public setImported': function( value ) { this._imported = !!value; this._needsImport = false; return this; }, /** * Returns whether the quote has been imported * * @return {boolean} true if imported, otherwise false */ 'public isImported': function() { return this._imported; }, /** * Sets quote bound status * * Represents whether the quote has been bound * * @param {boolean} value true if bound, otherwise false * * @return {Quote} self */ 'public setBound': function( value ) { this._bound = !!value; return this; }, /** * Returns whether the quote has been bound * * @return {boolean} true if bound, otherwise false */ 'public isBound': function() { return this._bound; }, /** * Returns the id of the current step * * @return {number} id of current step */ 'public getCurrentStepId': function() { return this._currentStepId; }, /** * Sets the top visited step id * * If the provided step id is less than the current step, then the current * step id is used instead. * * @return {Quote} self */ 'public setTopVisitedStepId': function( step_id ) { step_id = +step_id; this._topVisitedStepId = ( step_id < this._currentStepId ) ? this._currentStepId : step_id; return this; }, /** * Returns the id of the highest step the quote has reached * * @return {number} top visited step id */ 'public getTopVisitedStepId': function() { return this._topVisitedStepId; }, /** * Sets the current step id * * @param {number} step_id id of the step to set * * @return {Quote} self */ 'public setCurrentStepId': function( step_id ) { step_id = +step_id; this._currentStepId = step_id; // if this step is higher than the highest step this quote has reached, // then update it if ( step_id > this._topVisitedStepId ) { this._topVisitedStepId = step_id; } this.emit( this.__self.$('EVENT_STEP_CHANGE'), this._currentStepId ); return this; }, 'public getTopSavedStepId': function() { return this._topSavedStepId; }, 'public setTopSavedStepId': function( id ) { this._topSavedStepId = +id; return this; }, /** * Returns whether the step has been previously visited * * @param {number} step_id id of the step to check * * @return {boolean} true if visited, otherwise false */ 'public hasVisitedStep': function( step_id ) { if ( step_id <= 0 ) { return false; } return ( step_id <= this.getTopVisitedStepId() ) ? true : false; }, /** * Sets quote data * * The data will be merged, not overwritten. * * @param {Object.} data data to set on the quote * * @return {Quote} self */ 'public setData': function( data ) { this._bucket.setValues( data ); return this; }, /** * Returns whether the quote should be locked from modifications * * If an explicit lock is set, then we shall only consider the quote to be * in a locked state if there is no explicit step restriction on the lock * (since otherwise the user may access the unlocked steps). * * If a quote is imported, it will not be considered locked if there is an * explicit step restriction set; this permits users to modify imported * quotes, if such an ability should be granted. * * If the quote is bound, then it is locked, full stop. * * @return {boolean} true if locked, otherwise false */ 'public isLocked': function() { var exlock = ( this._explicitLock !== '' ), slock = ( this._explicitLockStep !== 0 ), ilock = ( ( this._imported && !slock ) || this._bound ); // we are locked if we (a) have the import/bind lock or (b) have an // exclusive lock without a step constraint return ilock || ( exlock && !slock ); }, /** * Returns quote data * * @param {string} name name of data to retrieve * * @return {Array} quote data */ 'public getDataByName': function( name ) { return this._bucket.getDataByName( name ); }, /** * Calls visitor callback with the data bucket * * todo: this pretty much breaks encapsulation, so ultimately we won't want * to send in the actual bucket * * @param {function( QuoteDataBucket )} callback visitor * * @return {Quote} self */ 'public visitData': function( callback ) { callback.call( this, this._bucket ); return this; }, /** * Sets a quote-wide error * * This error should invalidate the entire quote until it is cleared. * * @param {string} error error string * * @return {Quote} self */ 'public setError': function( error ) { this._error = ''+( error ); return this; }, /** * Retrieve quote-wide error * * @return {string} quote-wide error, or empty string */ 'public getError': function() { return this._error; }, /** * Determine whether or not a quote-wide error exists * * Use getError() to retrieve the actual error string. * * @return {boolean} true if error exists, otherwise false */ 'public hasError': function() { return ( this._error !== '' ); }, /** * Set quicksave data * * @param {Object} data quicksave data, diff format * * @return {Quote} self */ 'public setQuickSaveData': function( data ) { this._quickSaveData = data; return this; }, /** * Retrieve quicksave data * * @return {Object} quicksave data */ 'public getQuickSaveData': function() { return this._quickSaveData; }, /** * Sets an explicit lock, providing a reason for doing so * * @param {string} reason lock reason * @param {number} step step that user may not navigate prior * * @return {Quote} self */ 'public setExplicitLock': function( reason, step ) { step = +step || 0; this._explicitLock = ''+( reason ); this._explicitLockStep = step; return this; }, /** * Clears an explicit lock * * @return {Quote} self */ 'public clearExplicitLock': function() { this._explicitLock = ''; this._explicitLockStep = 0; return this; }, /** * Retrieves the reason for an explicit lock * * @return {string} lock reason */ 'public getExplicitLockReason': function() { return ( this.isBound() ) ? 'Quote has been bound' : this._explicitLock; }, /** * Returns the maximum step to which the explicit lock applies * * If no step restriction is set, then 0 will be returned. * * @return {number} locked max step or 0 if not applicable */ 'public getExplicitLockStep': function() { return ( this.isBound() ) ? 0 : this._explicitLockStep; }, /** * Determine whether quote needs to be imported * * Bound quotes will never need importing. * * @param {boolean=} set flag value */ 'public needsImport': function( set ) { if ( set !== undefined ) { this._importDirty = !!set; return this; } return ( this.isBound() ) ? false : this._importDirty; } } );