1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-12-04 15:24:23 +01:00
fab-manager/app/assets/javascripts/controllers/admin/pricing.coffee.erb
2017-02-14 15:25:28 +01:00

454 lines
16 KiB
Plaintext

'use strict'
##
# Controller used in the prices edition page
##
Application.Controllers.controller "EditPricingController", ["$scope", "$state", '$uibModal', '$filter', 'TrainingsPricing', 'Credit', 'Pricing', 'Plan', 'Coupon', 'plans', 'groups', 'growl', 'machinesPricesPromise', 'Price', 'dialogs', 'trainingsPricingsPromise', 'trainingsPromise', 'machineCreditsPromise', 'machinesPromise', 'trainingCreditsPromise', 'couponsPromise', 'spacesPromise', 'spacesPricesPromise', '_t'
, ($scope, $state, $uibModal, $filter, TrainingsPricing, Credit, Pricing, Plan, Coupon, plans, groups, growl, machinesPricesPromise, Price, dialogs, trainingsPricingsPromise, trainingsPromise, machineCreditsPromise, machinesPromise, trainingCreditsPromise, couponsPromise, spacesPromise, spacesPricesPromise, _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
## List of spaces
$scope.spaces = spacesPromise
## List of spaces prices (not considering any plan)
$scope.spacesPrices = spacesPricesPromise
## The plans list ordering. Default: by group
$scope.orderPlans = 'group_id'
## Status of the drop-down menu in Credits tab
$scope.status =
isopen: false
$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('pricing.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<number>} trainings IDs array
##
$scope.showTrainings = (trainings) ->
unless angular.isArray(trainings) and trainings.length > 0
return _t('pricing.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('pricing.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('pricing.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('pricing.an_error_occurred_while_deleting_credit_with_the_TRAINING', {TRAINING:tc.creditable.name}))
else
growl.error(_t('pricing.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('pricing.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('pricing.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('pricing.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('pricing.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('pricing.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('pricing.partner')
else return _t('pricing.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('pricing.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('pricing.confirmation_required')
msg: _t('pricing.do_you_really_want_to_delete_this_subscription_plan')
, ->
# the admin has confirmed, delete the plan
Plan.delete {id: id}, (res) ->
growl.success(_t('pricing.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('pricing.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<Object>} 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('pricing.confirmation_required')
msg: _t('pricing.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('pricing.unable_to_delete_the_specified_coupon_already_in_use'))
else
growl.error(_t('pricing.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('pricing.coupon_successfully_sent_to_USER', {USER: $scope.ctrl.member.name}))
$uibModalInstance.close({user_id: $scope.ctrl.member.id})
, (err) ->
growl.error(_t('pricing.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()
]