'use strict' ## # Controller used in the prices edition page ## Application.Controllers.controller "EditPricingController", ["$scope", "$state", '$uibModal', '$locale', '$filter', 'TrainingsPricing', 'Credit', 'Pricing', 'Plan', 'Coupon', 'plans', 'groups', 'growl', 'machinesPricesPromise', 'Price', 'dialogs', 'trainingsPricingsPromise', 'trainingsPromise', 'machineCreditsPromise', 'machinesPromise', 'trainingCreditsPromise', 'couponsPromise', '_t' , ($scope, $state, $uibModal, $locale, $filter, TrainingsPricing, Credit, Pricing, Plan, Coupon, plans, groups, growl, machinesPricesPromise, Price, dialogs, trainingsPricingsPromise, trainingsPromise, machineCreditsPromise, machinesPromise, trainingCreditsPromise, couponsPromise, _t) -> ### PUBLIC SCOPE ### ## List of machines prices (not considering any plan) $scope.machinesPrices = machinesPricesPromise ## List of trainings pricing $scope.trainingsPricings = trainingsPricingsPromise ## List of available subscriptions plans (eg. student/month, PME/year ...) $scope.plans = plans ## List of groups (eg. normal, student ...) $scope.groups = groups ## Associate free machine hours with subscriptions $scope.machineCredits = machineCreditsPromise ## Array of associations (plan <-> training) $scope.trainingCredits = trainingCreditsPromise ## Associate a plan with all its trainings ids $scope.trainingCreditsGroups = {} ## List of trainings $scope.trainings = trainingsPromise ## List of machines $scope.machines = machinesPromise ## List of coupons $scope.coupons = couponsPromise ## The plans list ordering. Default: by group $scope.orderPlans = 'group_id' ## Status of the drop-down menu in Credits tab $scope.status = isopen: false ## currency symbol for the current locale (cf. angular-i18n) $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM; $scope.findTrainingsPricing = (trainingsPricings, trainingId, groupId)-> for trainingsPricing in trainingsPricings if trainingsPricing.training_id == trainingId and trainingsPricing.group_id == groupId return trainingsPricing $scope.updateTrainingsPricing = (data, trainingsPricing)-> if data? TrainingsPricing.update({ id: trainingsPricing.id }, { trainings_pricing: { amount: data } }).$promise else _t('please_specify_a_number') ## # Retrieve a plan from its given identifier and returns it # @param id {number} plan ID # @returns {Object} Plan, inherits from $resource ## $scope.getPlanFromId = (id) -> for plan in $scope.plans if plan.id == parseInt(id) return plan ## # Retrieve a group from its given identifier and returns it # @param id {number} group ID # @returns {Object} Group, inherits from $resource ## $scope.getGroupFromId = (groups, id) -> for group in groups if group.id == parseInt(id) return group ## # Returns a human readable string of named trainings, according to the provided array. # $scope.trainings may contains the full list of training. The returned string will only contains the trainings # whom ID are given in the provided parameter # @param trainings {Array} trainings IDs array ## $scope.showTrainings = (trainings) -> unless angular.isArray(trainings) and trainings.length > 0 return _t('none') selected = [] angular.forEach $scope.trainings, (t) -> if trainings.indexOf(t.id) >= 0 selected.push t.name return if selected.length then selected.join(' | ') else _t('none') ## # Validation callback when editing training's credits. Save the changes. # @param newdata {Object} training and associated plans # @param planId {number|string} plan id ## $scope.saveTrainingCredits = (newdata, planId) -> # save the number of credits Plan.update {id: planId}, training_credit_nb: newdata.training_credits , angular.noop() # do nothing in case of success , (error) -> growl.error(_t('an_error_occurred_while_saving_the_number_of_credits')) # save the associated trainings angular.forEach $scope.trainingCreditsGroups, (original, key) -> if parseInt(key) == parseInt(planId) # we've got the original data if original.join('_') != newdata.training_ids.join('_') # if any changes # iterate through the previous credits to remove angular.forEach original, (oldTrainingId) -> if newdata.training_ids.indexOf(oldTrainingId) == -1 tc = findTrainingCredit(oldTrainingId, planId) if tc tc.$delete {} , -> $scope.trainingCredits.splice($scope.trainingCredits.indexOf(tc), 1) $scope.trainingCreditsGroups[planId].splice($scope.trainingCreditsGroups[planId].indexOf(tc.id), 1) , (error) -> growl.error(_t('an_error_occurred_while_deleting_credit_with_the_TRAINING', {TRAINING:tc.creditable.name})) else growl.error(_t('an_error_occurred_unable_to_find_the_credit_to_revoke')) # iterate through the new credits to add angular.forEach newdata.training_ids, (newTrainingId) -> if original.indexOf(newTrainingId) == -1 Credit.save credit: creditable_id: newTrainingId creditable_type: 'Training' plan_id: planId , (newTc) -> # success $scope.trainingCredits.push(newTc) $scope.trainingCreditsGroups[newTc.plan_id].push(newTc.creditable_id) , (error) -> # failed training = getTrainingFromId(newTrainingId) growl.error(_t('an_error_occurred_while_creating_credit_with_the_TRAINING', {TRAINING: training.name})) console.error(error) ## # Cancel the current training credit modification # @param rowform {Object} see http://vitalets.github.io/angular-xeditable/ ## $scope.cancelTrainingCredit = (rowform) -> rowform.$cancel() ## # Create a new empty entry in the $scope.machineCredits array # @param e {Object} see https://docs.angularjs.org/guide/expression#-event- ## $scope.addMachineCredit = (e)-> e.preventDefault() e.stopPropagation() $scope.inserted = creditable_type: 'Machine' $scope.machineCredits.push($scope.inserted) $scope.status.isopen = !$scope.status.isopen ## # In the Credits tab, while editing a machine credit row, select the current machine from the # drop-down list of machines as the current item. # @param credit {Object} credit object, inherited from $resource ## $scope.showCreditableName = (credit) -> selected = _t('not_set') if credit and credit.creditable_id angular.forEach $scope.machines, (m)-> if m.id == credit.creditable_id selected = m.name+' ( id. '+m.id+' )' return selected ## # Validation callback when editing machine's credits. Save the changes. # This will prevent the creation of two credits associating the same machine and plan. # @param data {Object} machine, associated plan and number of credit hours. # @param [id] {number} credit id for edition, create a new credit object if not provided ## $scope.saveMachineCredit = (data, id) -> for mc in $scope.machineCredits if mc.plan_id == data.plan_id and mc.creditable_id == data.creditable_id and (id == null or mc.id != id) growl.error(_t('error_a_credit_linking_this_machine_with_that_subscription_already_exists')) unless id $scope.machineCredits.pop() return false if id? Credit.update {id: id}, credit: data, -> growl.success(_t('changes_have_been_successfully_saved')) else data.creditable_type = 'Machine' Credit.save credit: data , (resp) -> $scope.machineCredits[$scope.machineCredits.length-1].id = resp.id growl.success(_t('credit_was_successfully_saved')) ## # Removes the newly inserted but not saved machine credit / Cancel the current machine credit modification # @param rowform {Object} see http://vitalets.github.io/angular-xeditable/ # @param index {number} theme index in the $scope.machineCredits array ## $scope.cancelMachineCredit = (rowform, index) -> if $scope.machineCredits[index].id? rowform.$cancel() else $scope.machineCredits.splice(index, 1) ## # Deletes the machine credit at the specified index # @param index {number} machine credit index in the $scope.machineCredits array ## $scope.removeMachineCredit = (index) -> Credit.delete $scope.machineCredits[index] $scope.machineCredits.splice(index, 1) ## # If the plan does not have a type, return a default value for display purposes # @param type {string|undefined|null} plan's type (eg. 'partner') # @returns {string} ## $scope.getPlanType = (type) -> if type == 'PartnerPlan' return _t('partner') else return _t('standard') ## # Change the plans ordering criterion to the one provided # @param orderBy {string} ordering criterion ## $scope.setOrderPlans = (orderBy) -> if $scope.orderPlans == orderBy $scope.orderPlans = '-'+orderBy else $scope.orderPlans = orderBy ## # Retrieve a price from prices array by a machineId and a groupId ## $scope.findPriceBy = (prices, machineId, groupId)-> for price in prices if price.priceable_id == machineId and price.group_id == groupId return price ## # update a price for a machine and a group, not considering any plan ## $scope.updatePrice = (data, price)-> if data? Price.update({ id: price.id }, { price: { amount: data } }).$promise else _t('please_specify_a_number') ## # Delete the specified subcription plan # @param id {number} plan id ## $scope.deletePlan = (plans, id) -> if typeof id != 'number' console.error('[EditPricingController::deletePlan] Error: invalid id parameter') else # open a confirmation dialog dialogs.confirm resolve: object: -> title: _t('confirmation_required') msg: _t('do_you_really_want_to_delete_this_subscription_plan') , -> # the admin has confirmed, delete the plan Plan.delete {id: id}, (res) -> growl.success(_t('subscription_plan_was_successfully_deleted')) $scope.plans.splice(findItemIdxById(plans, id), 1) , (error) -> console.error('[EditPricingController::deletePlan] Error: '+error.statusText) if error.statusText growl.error(_t('unable_to_delete_the_specified_subscription_an_error_occurred')) ## # Generate a string identifying the given plan by literal humain-readable name # @param plan {Object} Plan object, as recovered from GET /api/plan/:id # @param groups {Array} List of Groups objects, as recovered from GET /api/groups # @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name # will be included. # @returns {String} ## $scope.humanReadablePlanName = (plan, groups, short)-> "#{$filter('humanReadablePlanName')(plan, groups, short)}" ## # Delete a coupon from the server's database and, in case of success, from the list in memory # @param coupons {Array} should be called with $scope.coupons # @param id {number} ID of the coupon to delete ## $scope.deleteCoupon = (coupons, id) -> if typeof id != 'number' console.error('[EditPricingController::deleteCoupon] Error: invalid id parameter') else # open a confirmation dialog dialogs.confirm resolve: object: -> title: _t('confirmation_required') msg: _t('do_you_really_want_to_delete_this_coupon') , -> # the admin has confirmed, delete the coupon Coupon.delete {id: id}, (res) -> growl.success(_t('coupon_was_successfully_deleted')) $scope.coupons.splice(findItemIdxById(coupons, id), 1) , (error) -> console.error('[EditPricingController::deleteCoupon] Error: '+error.statusText) if error.statusText if error.status == 422 growl.error(_t('unable_to_delete_the_specified_coupon_already_in_use')) else growl.error(_t('unable_to_delete_the_specified_coupon_an_unexpected_error_occurred')) ## # Open a modal allowing to select an user and send him the details of the provided coupon # @param coupon {Object} The coupon to send ## $scope.sendCouponToUser = (coupon) -> $uibModal.open templateUrl: '<%= asset_path "admin/pricing/sendCoupon.html" %>' resolve: coupon: -> coupon size: 'md' controller: ['$scope', '$uibModalInstance', 'Coupon', 'coupon', '_t', ($scope, $uibModalInstance, Coupon, coupon, _t) -> ## Member, receiver of the coupon $scope.ctrl = member: null ## Details of the coupon to send $scope.coupon = coupon ## Callback to validate sending of the coupon $scope.ok = -> Coupon.send {coupon_code: coupon.code, user_id: $scope.ctrl.member.id}, (res) -> growl.success(_t('coupon_successfully_sent_to_USER', {USER: $scope.ctrl.member.name})) $uibModalInstance.close({user_id: $scope.ctrl.member.id}) , (err) -> growl.error(_t('an_error_occurred_unable_to_send_the_coupon')) ## Callback to close the modal and cancel the sending process $scope.cancel = -> $uibModalInstance.dismiss('cancel') ] ### PRIVATE SCOPE ### ## # Kind of constructor: these actions will be realized first when the controller is loaded ## initialize = -> $scope.trainingCreditsGroups = groupCreditsByPlan($scope.trainingCredits) ## adds empty array for plan which hasn't any credits yet for plan in $scope.plans unless $scope.trainingCreditsGroups[plan.id]? $scope.trainingCreditsGroups[plan.id] = [] ## # Retrieve an item index by its ID from the given array of objects # @param items {Array<{id:number}>} # @param id {number} # @returns {number} item index in the provided array ## findItemIdxById = (items, id)-> (items.map (item)-> item.id ).indexOf(id) ## # Group the given credits array into a map associating the plan ID with its associated trainings/machines # @return {Object} the association map ## groupCreditsByPlan = (credits) -> creditsMap = {} angular.forEach credits, (c) -> unless creditsMap[c.plan_id] creditsMap[c.plan_id] = [] creditsMap[c.plan_id].push(c.creditable_id) creditsMap ## # Iterate through $scope.traininfCredits to find the credit matching the given criterion # @param trainingId {number|string} training ID # @param planId {number|string} plan ID ## findTrainingCredit = (trainingId, planId) -> trainingId = parseInt(trainingId) planId = parseInt(planId) for credit in $scope.trainingCredits if credit.plan_id == planId and credit.creditable_id == trainingId return credit ## # Retrieve a training from its given identifier and returns it # @param id {number} training ID # @returns {Object} Training inherited from $resource ## getTrainingFromId = (id) -> for training in $scope.trainings if training.id == parseInt(id) return training ## !!! MUST BE CALLED AT THE END of the controller initialize() ]