1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-19 13:54:25 +01:00

db model, api and interface for cash coupons

This commit is contained in:
Sylvain 2016-11-23 12:43:42 +01:00
parent b7737be6f3
commit ad2911529c
15 changed files with 100 additions and 17 deletions

View File

@ -8,12 +8,17 @@ userValidities = ['once', 'forever']
##
# Controller used in the coupon creation page
##
Application.Controllers.controller "NewCouponController", ["$scope", "$state",'Coupon', 'growl', '_t'
, ($scope, $state, Coupon, growl, _t) ->
Application.Controllers.controller "NewCouponController", ["$scope", "$state", '$locale', 'Coupon', 'growl', '_t'
, ($scope, $state, $locale, Coupon, growl, _t) ->
## Values for the coupon currently created
$scope.coupon =
active: true
type: 'percent_off'
## currency symbol for the current locale (cf. angular-i18n)
$scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM;
## Options for the validity per user
$scope.validities = userValidities
@ -57,8 +62,8 @@ Application.Controllers.controller "NewCouponController", ["$scope", "$state",'C
##
# Controller used in the coupon edition page
##
Application.Controllers.controller "EditCouponController", ["$scope", "$state", 'Coupon', 'couponPromise', '_t'
, ($scope, $state, Coupon, couponPromise, _t) ->
Application.Controllers.controller "EditCouponController", ["$scope", "$state", '$locale', 'Coupon', 'couponPromise', '_t'
, ($scope, $state, $locale, Coupon, couponPromise, _t) ->
### PUBLIC SCOPE ###
@ -72,6 +77,9 @@ Application.Controllers.controller "EditCouponController", ["$scope", "$state",
## Options for the validity per user
$scope.validities = userValidities
## currency symbol for the current locale (cf. angular-i18n)
$scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM;
## Default parameters for AngularUI-Bootstrap datepicker (used for coupon validity limit selection)
$scope.datePicker =
format: Fablab.uibDateFormat

View File

@ -3,8 +3,8 @@
##
# Controller used in the prices edition page
##
Application.Controllers.controller "EditPricingController", ["$scope", "$state", '$uibModal', 'TrainingsPricing', '$filter', 'Credit', 'Pricing', 'Plan', 'Coupon', 'plans', 'groups', 'growl', 'machinesPricesPromise', 'Price', 'dialogs', 'trainingsPricingsPromise', 'trainingsPromise', 'machineCreditsPromise', 'machinesPromise', 'trainingCreditsPromise', 'couponsPromise', '_t'
, ($scope, $state, $uibModal, TrainingsPricing, $filter, Credit, Pricing, Plan, Coupon, plans, groups, growl, machinesPricesPromise, Price, dialogs, trainingsPricingsPromise, trainingsPromise, machineCreditsPromise, machinesPromise, trainingCreditsPromise, couponsPromise, _t) ->
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)
@ -44,6 +44,9 @@ Application.Controllers.controller "EditPricingController", ["$scope", "$state",
$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)->

View File

@ -21,7 +21,20 @@
<span class="help-block error" ng-show="couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$error.pattern" translate>{{ 'code_must_be_composed_of_capital_letters_digits_and_or_dashes' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$invalid}">
<div class="form-group">
<label for="coupon[type]">{{ 'kind_of_coupon' | translate }} *</label>
<select id="coupon[type]"
name="coupon[type]"
class="form-control"
ng-model="coupon.type"
ng-disabled="mode == 'EDIT'"
required="required">
<option value="percent_off" translate>{{ 'percentage' }}</option>
<option value="amount_off" translate>{{ 'amount' }}</option>
</select>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$invalid}" ng-show="coupon.type == 'percent_off'">
<label for="coupon[percent_off]">{{ 'percent_off' | translate }} *</label>
<div class="input-group">
<input type="number" id="coupon[percent_off]"
@ -31,13 +44,30 @@
min="0"
max="100"
ng-disabled="mode == 'EDIT'"
required="required"/>
ng-required="coupon.type == 'percent_off'"/>
<span class="input-group-addon"><i class="fa fa-percent"></i></span>
</div>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$error.required" translate>{{ 'percent_off_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && (couponForm['coupon[percent_off]'].$error.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'percentage_must_be_between_0_and_100' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[amount_off]'].$dirty && couponForm['coupon[amount_off]'].$invalid}" ng-show="coupon.type == 'amount_off'">
<label for="coupon[amount_off]">{{ 'amount_off' | translate }} *</label>
<div class="input-group">
<span class="input-group-addon">{{currencySymbol}}</span>
<input type="number" id="coupon[amount_off]"
name="coupon[amount_off]"
class="form-control"
ng-model="coupon.amount_off"
min="0"
ng-disabled="mode == 'EDIT'"
ng-required="coupon.type == 'amount_off'"/>
</div>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$error.required" translate>{{ 'percent_off_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && (couponForm['coupon[percent_off]'].$error.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'percentage_must_be_between_0_and_100' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[validity_per_user]'].$dirty && couponForm['coupon[validity_per_user]'].$invalid}">
<label for="coupon[validity_per_user]">{{ 'validity_per_user' | translate }} *</label>
<select id="coupon[validity_per_user]"

View File

@ -5,7 +5,7 @@
<thead>
<tr>
<th translate>{{ 'name' }}</th>
<th translate>{{ 'percentage_off' }}</th>
<th translate>{{ 'discount' }}</th>
<th translate>{{ 'nb_of_usages' }}</th>
<th translate>{{ 'status' }}</th>
<th></th>
@ -14,7 +14,10 @@
<tbody>
<tr ng-repeat="coupon in coupons">
<td>{{coupon.name}}</td>
<td>{{coupon.percent_off}} %</td>
<td>
<span ng-show="coupon.type == 'percent_off'">{{coupon.percent_off}} %</span>
<span ng-show="coupon.type == 'amount_off'">{{coupon.amount_off}} {{currencySymbol}}</span>
</td>
<td>{{coupon.usages}}</td>
<td translate>{{coupon.status}}</td>
<td>

View File

@ -78,7 +78,7 @@ class API::CouponsController < API::ApiController
end
def coupon_params
params.require(:coupon).permit(:name, :code, :percent_off, :validity_per_user, :valid_until, :max_usages, :active)
params.require(:coupon).permit(:name, :code, :percent_off, :amount_off, :validity_per_user, :valid_until, :max_usages, :active)
end
def coupon_editable_params

View File

@ -8,10 +8,9 @@ class Coupon < ActiveRecord::Base
validates :code, presence: true
validates :code, format: { with: /\A[A-Z0-9\-]+\z/ ,message: 'only caps letters, numbers, and dashes'}
validates :code, uniqueness: true
validates :percent_off, presence: true
validates :percent_off, :inclusion => 0..100
validates :validity_per_user, presence: true
validates :validity_per_user, inclusion: { in: %w(once forever) }
validates_with CouponDiscountValidator
def safe_destroy
if self.invoices.size == 0
@ -46,6 +45,14 @@ class Coupon < ActiveRecord::Base
end
end
def type
if amount_off.nil? and !percent_off.nil?
'percent_off'
elsif percent_off.nil? and !amount_off.nil?
'amount_off'
end
end
def users
self.invoices.map do |i|
i.user

View File

@ -0,0 +1,15 @@
class CouponDiscountValidator < ActiveModel::Validator
def validate(record)
if !record.percent_off.nil?
unless [0..100].include? record.percent_off
record.errors[:percent_off] << 'Percentage must be included between 0 and 100'
end
elsif !record.amount_off.nil?
unless record.amount_off > 0
record.errors[:amount_off] << I18n.t('errors.messages.greater_than_or_equal_to', count: 0)
end
else
record.errors[:percent_off] << 'cannot be blank when amount_off is blank too'
end
end
end

View File

@ -1,3 +1,3 @@
json.extract! coupon, :id, :name, :code, :percent_off, :valid_until, :validity_per_user, :max_usages, :active, :created_at
json.extract! coupon, :id, :name, :code, :type, :percent_off, :amount_off, :valid_until, :validity_per_user, :max_usages, :active, :created_at
json.usages coupon.invoices.count
json.status coupon.status

View File

@ -21,6 +21,8 @@ class StripeWorker
id: coupon.code,
duration: coupon.validity_per_user,
percent_off: coupon.percent_off,
amount_off: coupon.amount_off,
currency: Rails.application.secrets.stripe_currency,
}
unless coupon.valid_until.nil?
stp_coupon[:redeem_by] = coupon.valid_until.to_i

View File

@ -164,7 +164,7 @@ en:
unable_to_delete_the_specified_subscription_an_error_occurred: "Unable to delete the specified subscription, an error occurred."
coupons: "Coupons"
list_of_the_coupons: "List of the coupons"
percentage_off: "Percentage off"
discount: "Discount"
nb_of_usages: "Number of usages"
status: "Status"
add_a_new_coupon: "Add a new coupon"

View File

@ -164,7 +164,7 @@ fr:
unable_to_delete_the_specified_subscription_an_error_occurred: "Impossible de supprimer l'abonnement spécifié, une erreur s'est produite."
coupons: "Codes promotionnels"
list_of_the_coupons: "Liste des codes promotionnels"
percentage_off: "Pourcentage de réduction"
discount: "Réduction"
nb_of_usages: "Nombre d'utilisations"
status: "Statut"
add_a_new_coupon: "Ajouter un code promotionnel"

View File

@ -346,6 +346,10 @@ en:
code: "Code"
code_is_required: "Code is required."
code_must_be_composed_of_capital_letters_digits_and_or_dashes: "The code must be composed of capital letters, digits and/or dashes."
kind_of_coupon: "Kind of coupon"
percentage: "Percentage"
amount: "Amount"
amount_off: "Amount off"
percent_off: "Percentage off"
percent_off_is_required: "Percentage off is required."
percentage_must_be_between_0_and_100: "Percentage must be between 0 and 100."

View File

@ -346,6 +346,10 @@ fr:
code: "Code"
code_is_required: "Le code est requis."
code_must_be_composed_of_capital_letters_digits_and_or_dashes: "Le code doit être composé de lettres majuscules, de chiffres et/ou de tirets."
kind_of_coupon: "Type de réduction"
percentage: "Pourcentage"
amount: "Montant"
amount_off: "Montant de la réduction"
percent_off: "Pourcentage de réduction"
percent_off_is_required: "Le pourcentage de réduction est requis."
percentage_must_be_between_0_and_100: "Le pourcentage doit être compris entre 0 et 100."

View File

@ -0,0 +1,5 @@
class AddAmountOffToCoupons < ActiveRecord::Migration
def change
add_column :coupons, :amount_off, :integer
end
end

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160915105234) do
ActiveRecord::Schema.define(version: 20161123104604) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -114,6 +114,7 @@ ActiveRecord::Schema.define(version: 20160915105234) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "validity_per_user"
t.integer "amount_off"
end
create_table "credits", force: :cascade do |t|
@ -319,6 +320,7 @@ ActiveRecord::Schema.define(version: 20160915105234) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "profile_url"
t.string "logout_endpoint"
end
create_table "offer_days", force: :cascade do |t|