mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-29 10:24:20 +01:00
Merge branch 'monthly-payment' into staging
This commit is contained in:
commit
3541688c03
@ -16,6 +16,7 @@ Metrics/BlockLength:
|
||||
- 'lib/tasks/**/*.rake'
|
||||
- 'config/routes.rb'
|
||||
- 'app/pdfs/pdf/*.rb'
|
||||
- 'test/**/*.rb'
|
||||
Metrics/ParameterLists:
|
||||
CountKeywordArgs: false
|
||||
Style/BracesAroundHashParameters:
|
||||
|
2
Rakefile
2
Rakefile
@ -1,6 +1,6 @@
|
||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
require_relative 'config/application'
|
||||
|
||||
Rails.application.load_tasks
|
||||
|
@ -49,7 +49,7 @@ class API::PaymentsController < API::ApiController
|
||||
if params[:cart_items][:reservation]
|
||||
res = on_reservation_success(intent, amount[:details])
|
||||
elsif params[:cart_items][:subscription]
|
||||
res = on_subscription_success(intent)
|
||||
res = on_subscription_success(intent, amount[:details])
|
||||
end
|
||||
end
|
||||
|
||||
@ -72,7 +72,7 @@ class API::PaymentsController < API::ApiController
|
||||
user = User.find(params[:user_id])
|
||||
key = Setting.get('stripe_secret_key')
|
||||
@intent = Stripe::SetupIntent.create({ customer: user.stp_customer_id }, { api_key: key })
|
||||
render json: { client_secret: @intent.client_secret }
|
||||
render json: { id: @intent.id, client_secret: @intent.client_secret }
|
||||
end
|
||||
|
||||
def confirm_payment_schedule
|
||||
@ -84,7 +84,7 @@ class API::PaymentsController < API::ApiController
|
||||
if params[:cart_items][:reservation]
|
||||
res = on_reservation_success(intent, amount[:details])
|
||||
elsif params[:cart_items][:subscription]
|
||||
res = on_subscription_success(intent)
|
||||
res = on_subscription_success(intent, amount[:details])
|
||||
end
|
||||
end
|
||||
|
||||
@ -103,7 +103,11 @@ class API::PaymentsController < API::ApiController
|
||||
current_user.id
|
||||
end
|
||||
is_reserve = Reservations::Reserve.new(user_id, current_user.invoicing_profile.id)
|
||||
.pay_and_save(@reservation, payment_details: details, payment_intent_id: intent.id)
|
||||
.pay_and_save(@reservation,
|
||||
payment_details: details,
|
||||
payment_intent_id: intent.id,
|
||||
schedule: params[:cart_items][:reservation][:payment_schedule],
|
||||
payment_method: params[:cart_items][:reservation][:payment_method])
|
||||
if intent.class == Stripe::PaymentIntent
|
||||
Stripe::PaymentIntent.update(
|
||||
intent.id,
|
||||
@ -121,7 +125,7 @@ class API::PaymentsController < API::ApiController
|
||||
end
|
||||
end
|
||||
|
||||
def on_subscription_success(intent)
|
||||
def on_subscription_success(intent, details)
|
||||
@subscription = Subscription.new(subscription_params)
|
||||
user_id = if current_user.admin? || current_user.manager?
|
||||
params[:cart_items][:subscription][:user_id]
|
||||
@ -130,8 +134,7 @@ class API::PaymentsController < API::ApiController
|
||||
end
|
||||
is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id)
|
||||
.pay_and_save(@subscription,
|
||||
coupon: coupon_params[:coupon_code],
|
||||
invoice: true,
|
||||
payment_details: details,
|
||||
payment_intent_id: intent.id,
|
||||
schedule: params[:cart_items][:subscription][:payment_schedule],
|
||||
payment_method: 'stripe')
|
||||
|
@ -35,7 +35,10 @@ class API::ReservationsController < API::ApiController
|
||||
|
||||
@reservation = Reservation.new(reservation_params)
|
||||
is_reserve = Reservations::Reserve.new(user_id, current_user.invoicing_profile.id)
|
||||
.pay_and_save(@reservation, payment_details: price[:price_details])
|
||||
.pay_and_save(@reservation,
|
||||
payment_details: price[:price_details],
|
||||
schedule: params[:reservation][:payment_schedule],
|
||||
payment_method: params[:reservation][:payment_method])
|
||||
|
||||
if is_reserve
|
||||
SubscriptionExtensionAfterReservation.new(@reservation).extend_subscription_if_eligible
|
||||
|
@ -14,14 +14,13 @@ class API::SubscriptionsController < API::ApiController
|
||||
# Managers can create subscriptions for other users
|
||||
def create
|
||||
user_id = current_user.admin? || current_user.manager? ? params[:subscription][:user_id] : current_user.id
|
||||
amount = transaction_amount(current_user.admin? || (current_user.manager? && current_user.id != user_id), user_id)
|
||||
transaction = transaction_amount(current_user.admin? || (current_user.manager? && current_user.id != user_id), user_id)
|
||||
|
||||
authorize SubscriptionContext.new(Subscription, amount, user_id)
|
||||
authorize SubscriptionContext.new(Subscription, transaction[:amount], user_id)
|
||||
|
||||
@subscription = Subscription.new(subscription_params)
|
||||
is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id)
|
||||
.pay_and_save(@subscription, coupon: coupon_params[:coupon_code],
|
||||
invoice: true,
|
||||
.pay_and_save(@subscription, payment_details: transaction[:details],
|
||||
schedule: params[:subscription][:payment_schedule],
|
||||
payment_method: params[:subscription][:payment_method])
|
||||
|
||||
@ -65,7 +64,7 @@ class API::SubscriptionsController < API::ApiController
|
||||
# Subtract wallet amount from total
|
||||
total = price_details[:total]
|
||||
wallet_debit = get_wallet_debit(user, total)
|
||||
total - wallet_debit
|
||||
{ amount: total - wallet_debit, details: price_details }
|
||||
end
|
||||
|
||||
def get_wallet_debit(user, total_amount)
|
||||
|
@ -19,6 +19,13 @@ client.interceptors.response.use(function (response) {
|
||||
});
|
||||
|
||||
function extractHumanReadableMessage(error: any): string {
|
||||
if (error.match(/^<!DOCTYPE html>/)) {
|
||||
// parse ruby error pages
|
||||
const parser = new DOMParser();
|
||||
const htmlDoc = parser.parseFromString(error, 'text/html');
|
||||
return htmlDoc.querySelector('h2').textContent;
|
||||
}
|
||||
|
||||
if (typeof error === 'string') return error;
|
||||
|
||||
let message = '';
|
||||
|
@ -37,7 +37,7 @@
|
||||
*/
|
||||
class MembersController {
|
||||
constructor ($scope, $state, Group, Training) {
|
||||
// Retrieve the profiles groups (eg. students ...)
|
||||
// Retrieve the profiles groups (e.g. students ...)
|
||||
Group.query(function (groups) { $scope.groups = groups.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; }); });
|
||||
|
||||
// Retrieve the list of available trainings
|
||||
@ -62,7 +62,7 @@ class MembersController {
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the birth day datepicker
|
||||
* Shows the birthday datepicker
|
||||
* @param $event {Object} jQuery event object
|
||||
*/
|
||||
$scope.openDatePicker = function ($event) {
|
||||
@ -85,7 +85,7 @@ class MembersController {
|
||||
* For use with ngUpload (https://github.com/twilson63/ngUpload).
|
||||
* Intended to be the callback when an upload is done: any raised error will be stacked in the
|
||||
* $scope.alerts array. If everything goes fine, the user is redirected to the members listing page.
|
||||
* @param content {Object} JSON - The upload's result
|
||||
* @param content {Object} JSON - The result of the upload
|
||||
*/
|
||||
$scope.submited = function (content) {
|
||||
if ((content.id == null)) {
|
||||
@ -110,7 +110,7 @@ class MembersController {
|
||||
|
||||
/**
|
||||
* For use with 'ng-class', returns the CSS class name for the uploads previews.
|
||||
* The preview may show a placeholder or the content of the file depending on the upload state.
|
||||
* The preview may show a placeholder, or the content of the file depending on the upload state.
|
||||
* @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
|
||||
*/
|
||||
$scope.fileinputClass = function (v) {
|
||||
@ -143,7 +143,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
searchText: '',
|
||||
// Members ordering/sorting. Default: not sorted
|
||||
order: 'id',
|
||||
// currently displayed page of members
|
||||
// the currently displayed page of members
|
||||
page: 1,
|
||||
// true when all members where loaded
|
||||
noMore: false,
|
||||
@ -158,7 +158,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
};
|
||||
|
||||
// admins list
|
||||
$scope.admins = adminsPromise.admins.filter(function(m) { return m.id != Fablab.superadminId; });
|
||||
$scope.admins = adminsPromise.admins.filter(function (m) { return m.id !== Fablab.superadminId; });
|
||||
|
||||
// Admins ordering/sorting. Default: not sorted
|
||||
$scope.orderAdmin = null;
|
||||
@ -210,7 +210,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
* @param orderPartner {string} ordering criterion
|
||||
*/
|
||||
$scope.setOrderPartner = function (orderPartner) {
|
||||
if ($scope.orderPartner === orderPartner) {
|
||||
if ($scope.orderPartner === orderPartner) {
|
||||
return $scope.orderPartner = `-${orderPartner}`;
|
||||
} else {
|
||||
return $scope.orderPartner = orderPartner;
|
||||
@ -229,7 +229,6 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Open a modal dialog allowing the admin to create a new partner user
|
||||
*/
|
||||
@ -265,12 +264,11 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Ask for confirmation then delete the specified user
|
||||
* @param memberId {number} identifier of the user to delete
|
||||
*/
|
||||
$scope.deleteMember = function(memberId) {
|
||||
$scope.deleteMember = function (memberId) {
|
||||
dialogs.confirm(
|
||||
{
|
||||
resolve: {
|
||||
@ -289,11 +287,14 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
$scope.members.splice(findItemIdxById($scope.members, memberId), 1);
|
||||
return growl.success(_t('app.admin.members.member_successfully_deleted'));
|
||||
},
|
||||
function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_member')); }
|
||||
function (error) {
|
||||
growl.error(_t('app.admin.members.unable_to_delete_the_member'));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ask for confirmation then delete the specified administrator
|
||||
@ -319,7 +320,10 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
admins.splice(findItemIdxById(admins, admin.id), 1);
|
||||
return growl.success(_t('app.admin.members.administrator_successfully_deleted'));
|
||||
},
|
||||
function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_administrator')); }
|
||||
function (error) {
|
||||
growl.error(_t('app.admin.members.unable_to_delete_the_administrator'));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
@ -349,11 +353,14 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
partners.splice(findItemIdxById(partners, partner.id), 1);
|
||||
return growl.success(_t('app.admin.members.partner_successfully_deleted'));
|
||||
},
|
||||
function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_partner')); }
|
||||
function (error) {
|
||||
growl.error(_t('app.admin.members.unable_to_delete_the_partner'));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ask for confirmation then delete the specified manager
|
||||
@ -379,11 +386,14 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
managers.splice(findItemIdxById(managers, manager.id), 1);
|
||||
return growl.success(_t('app.admin.members.manager_successfully_deleted'));
|
||||
},
|
||||
function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_manager')); }
|
||||
function (error) {
|
||||
growl.error(_t('app.admin.members.unable_to_delete_the_manager'));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for the 'load more' button.
|
||||
@ -399,7 +409,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
*/
|
||||
$scope.updateTextSearch = function () {
|
||||
if (searchTimeout) clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(function() {
|
||||
searchTimeout = setTimeout(function () {
|
||||
resetSearchMember();
|
||||
memberSearch();
|
||||
}, 300);
|
||||
@ -425,9 +435,8 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Setup the feature-tour for the admin/members page.
|
||||
* Set up the feature-tour for the admin/members page.
|
||||
* This is intended as a contextual help (when pressing F1)
|
||||
*/
|
||||
$scope.setupMembersTour = function () {
|
||||
@ -570,7 +579,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
if (settingsPromise.feature_tour_display !== 'manual' && $scope.currentUser.profile.tours.indexOf('members') < 0) {
|
||||
uitour.start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
@ -586,22 +595,22 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
/**
|
||||
* Will temporize the search query to prevent overloading the API
|
||||
*/
|
||||
var searchTimeout = null;
|
||||
let searchTimeout = null;
|
||||
|
||||
/**
|
||||
* Iterate through the provided array and return the index of the requested item
|
||||
* @param items {Array} full list of users with role 'admin'
|
||||
* @param items {Array} full list of users with the 'admin' role
|
||||
* @param id {Number} id of the item to retrieve in the list
|
||||
* @returns {Number} index of the requested item, in the provided array
|
||||
*/
|
||||
var findItemIdxById = function (items, id) {
|
||||
const findItemIdxById = function (items, id) {
|
||||
return (items.map(function (item) { return item.id; })).indexOf(id);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reinitialize the context of members's search to display new results set
|
||||
* Reinitialize the context of the search to display new results set
|
||||
*/
|
||||
var resetSearchMember = function () {
|
||||
const resetSearchMember = function () {
|
||||
$scope.member.noMore = false;
|
||||
$scope.member.page = 1;
|
||||
};
|
||||
@ -609,9 +618,9 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
/**
|
||||
* Run a search query with the current parameters set ($scope.member[searchText,order,page])
|
||||
* and affect or append the result in $scope.members, depending on the concat parameter
|
||||
* @param [concat] {boolean} if true, the result will be append to $scope.members instead of being affected
|
||||
* @param [concat] {boolean} if true, the result will be appended to $scope.members instead of being replaced
|
||||
*/
|
||||
var memberSearch = function (concat) {
|
||||
const memberSearch = function (concat) {
|
||||
Member.list({
|
||||
query: {
|
||||
search: $scope.member.searchText,
|
||||
@ -666,7 +675,6 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
// the user subscription
|
||||
if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) {
|
||||
$scope.subscription = $scope.user.subscription;
|
||||
$scope.subscription.expired_at = $scope.subscription.expired_at;
|
||||
} else {
|
||||
Plan.query({ group_id: $scope.user.group_id }, function (plans) {
|
||||
$scope.plans = plans;
|
||||
@ -696,16 +704,15 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
|
||||
/**
|
||||
* Open a modal dialog asking for confirmation to change the role of the given user
|
||||
* @param userId {number} id of the user to "promote"
|
||||
* @returns {*}
|
||||
*/
|
||||
$scope.changeUserRole = function() {
|
||||
$scope.changeUserRole = function () {
|
||||
const modalInstance = $uibModal.open({
|
||||
animation: true,
|
||||
templateUrl: '/admin/members/change_role_modal.html',
|
||||
size: 'lg',
|
||||
resolve: {
|
||||
user() { return $scope.user; }
|
||||
user () { return $scope.user; }
|
||||
},
|
||||
controller: ['$scope', '$uibModalInstance', 'Member', 'user', '_t', function ($scope, $uibModalInstance, Member, user, _t) {
|
||||
$scope.user = user;
|
||||
@ -715,7 +722,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
$scope.roles = [
|
||||
{ key: 'admin', label: _t('app.admin.members_edit.admin') },
|
||||
{ key: 'manager', label: _t('app.admin.members_edit.manager'), notAnOption: (user.role === 'admin') },
|
||||
{ key: 'member', label: _t('app.admin.members_edit.member'), notAnOption: (user.role === 'admin' || user.role === 'manager') },
|
||||
{ key: 'member', label: _t('app.admin.members_edit.member'), notAnOption: (user.role === 'admin' || user.role === 'manager') }
|
||||
];
|
||||
|
||||
$scope.ok = function () {
|
||||
@ -740,7 +747,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
return modalInstance.result.then(function (user) {
|
||||
// remove the user for the old list add to the new
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a modal dialog, allowing the admin to extend the current user's subscription (freely or not)
|
||||
@ -778,7 +785,10 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
growl.success(_t('app.admin.members_edit.you_successfully_changed_the_expiration_date_of_the_user_s_subscription'));
|
||||
return $uibModalInstance.close(_subscription);
|
||||
},
|
||||
function (error) { growl.error(_t('app.admin.members_edit.a_problem_occurred_while_saving_the_date')); }
|
||||
function (error) {
|
||||
growl.error(_t('app.admin.members_edit.a_problem_occurred_while_saving_the_date'));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@ -792,14 +802,14 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
/**
|
||||
* Open a modal dialog allowing the admin to set a subscription for the given user.
|
||||
* @param user {Object} User object, user currently reviewed, as recovered from GET /api/members/:id
|
||||
* @param plans {Array} List of plans, availables for the currently reviewed user, as recovered from GET /api/plans
|
||||
* @param plans {Array} List of plans, available for the currently reviewed user, as recovered from GET /api/plans
|
||||
*/
|
||||
$scope.createSubscriptionModal = function (user, plans) {
|
||||
const modalInstance = $uibModal.open({
|
||||
animation: true,
|
||||
templateUrl: '/admin/subscriptions/create_modal.html',
|
||||
size: 'lg',
|
||||
controller: ['$scope', '$uibModalInstance', 'Subscription', 'Group', function ($scope, $uibModalInstance, Subscription, Group) {
|
||||
controller: ['$scope', '$uibModalInstance', 'Subscription', function ($scope, $uibModalInstance, Subscription) {
|
||||
// selected user
|
||||
$scope.user = user;
|
||||
|
||||
@ -810,7 +820,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
* Generate a string identifying the given plan by literal human-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
|
||||
* @param short {boolean} If true, the generated name will contain the group slug, otherwise the group full name
|
||||
* will be included.
|
||||
* @returns {String}
|
||||
*/
|
||||
@ -902,8 +912,9 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
*/
|
||||
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
|
||||
}
|
||||
] });
|
||||
// once the form was validated succesfully ...
|
||||
]
|
||||
});
|
||||
// once the form was validated successfully...
|
||||
return modalInstance.result.then(function (wallet) {
|
||||
$scope.wallet = wallet;
|
||||
return Wallet.transactions({ id: wallet.id }, function (transactions) { $scope.transactions = transactions; });
|
||||
@ -923,13 +934,12 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
|
||||
const initialize = function () {
|
||||
CSRF.setMetaTags();
|
||||
|
||||
// init the birth date to JS object
|
||||
// init the birthdate to JS object
|
||||
$scope.user.statistic_profile.birthday = moment($scope.user.statistic_profile.birthday).toDate();
|
||||
|
||||
// the user subscription
|
||||
if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) {
|
||||
$scope.subscription = $scope.user.subscription;
|
||||
$scope.subscription.expired_at = $scope.subscription.expired_at;
|
||||
} else {
|
||||
Plan.query({ group_id: $scope.user.group_id }, function (plans) {
|
||||
$scope.plans = plans;
|
||||
@ -996,7 +1006,7 @@ Application.Controllers.controller('NewMemberController', ['$scope', '$state', '
|
||||
* Controller used in the member's import page: import from CSV (admin view)
|
||||
*/
|
||||
Application.Controllers.controller('ImportMembersController', ['$scope', '$state', 'Group', 'Training', 'CSRF', 'tags', 'growl',
|
||||
function($scope, $state, Group, Training, CSRF, tags, growl) {
|
||||
function ($scope, $state, Group, Training, CSRF, tags, growl) {
|
||||
CSRF.setMetaTags();
|
||||
|
||||
/* PUBLIC SCOPE */
|
||||
@ -1008,19 +1018,19 @@ Application.Controllers.controller('ImportMembersController', ['$scope', '$state
|
||||
$scope.method = 'post';
|
||||
|
||||
// List of all tags
|
||||
$scope.tags = tags
|
||||
$scope.tags = tags;
|
||||
|
||||
/*
|
||||
* Callback run after the form was submitted
|
||||
* @param content {*} The result provided by the server, may be an Import object or an error message
|
||||
* @param content {*} The result provided by the server, may be an Import object, or an error message
|
||||
*/
|
||||
$scope.onImportResult = function(content) {
|
||||
$scope.onImportResult = function (content) {
|
||||
if (content.id) {
|
||||
$state.go('app.admin.members_import_result', { id: content.id });
|
||||
} else {
|
||||
growl.error(JSON.stringify(content));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Using the MembersController
|
||||
return new MembersController($scope, $state, Group, Training);
|
||||
@ -1041,7 +1051,7 @@ Application.Controllers.controller('ImportMembersResultController', ['$scope', '
|
||||
$scope.results = null;
|
||||
|
||||
/**
|
||||
* Changes the admin's view to the members import page
|
||||
* Changes the view of the admin to the members import page
|
||||
*/
|
||||
$scope.cancel = function () { $state.go('app.admin.members_import'); };
|
||||
|
||||
@ -1053,8 +1063,8 @@ Application.Controllers.controller('ImportMembersResultController', ['$scope', '
|
||||
const initialize = function () {
|
||||
$scope.results = JSON.parse($scope.import.results);
|
||||
if (!$scope.results) {
|
||||
setTimeout(function() {
|
||||
Import.get({ id: $scope.import.id }, function(data) {
|
||||
setTimeout(function () {
|
||||
Import.get({ id: $scope.import.id }, function (data) {
|
||||
$scope.import = data;
|
||||
initialize();
|
||||
});
|
||||
@ -1068,69 +1078,68 @@ Application.Controllers.controller('ImportMembersResultController', ['$scope', '
|
||||
]);
|
||||
|
||||
/**
|
||||
* Controller used in the admin's creation page (admin view)
|
||||
* Controller used in the admin creation page (admin view)
|
||||
*/
|
||||
Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', 'phoneRequiredPromise',
|
||||
function ($state, $scope, Admin, growl, _t, phoneRequiredPromise) {
|
||||
// default admin profile
|
||||
let getGender;
|
||||
$scope.admin = {
|
||||
statistic_profile_attributes: {
|
||||
gender: true
|
||||
},
|
||||
profile_attributes: {},
|
||||
invoicing_profile_attributes: {}
|
||||
};
|
||||
let getGender;
|
||||
$scope.admin = {
|
||||
statistic_profile_attributes: {
|
||||
gender: true
|
||||
},
|
||||
profile_attributes: {},
|
||||
invoicing_profile_attributes: {}
|
||||
};
|
||||
|
||||
// Default parameters for AngularUI-Bootstrap datepicker
|
||||
$scope.datePicker = {
|
||||
format: Fablab.uibDateFormat,
|
||||
opened: false,
|
||||
options: {
|
||||
startingDay: Fablab.weekStartingDay
|
||||
}
|
||||
};
|
||||
// Default parameters for AngularUI-Bootstrap datepicker
|
||||
$scope.datePicker = {
|
||||
format: Fablab.uibDateFormat,
|
||||
opened: false,
|
||||
options: {
|
||||
startingDay: Fablab.weekStartingDay
|
||||
}
|
||||
};
|
||||
|
||||
// is the phone number required in _admin_form?
|
||||
$scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true');
|
||||
// is the phone number required in _admin_form?
|
||||
$scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true');
|
||||
|
||||
/**
|
||||
* Shows the birth day datepicker
|
||||
* @param $event {Object} jQuery event object
|
||||
/**
|
||||
* Shows the birthday datepicker
|
||||
*/
|
||||
$scope.openDatePicker = function ($event) { $scope.datePicker.opened = true; };
|
||||
$scope.openDatePicker = function () { $scope.datePicker.opened = true; };
|
||||
|
||||
/**
|
||||
/**
|
||||
* Send the new admin, currently stored in $scope.admin, to the server for database saving
|
||||
*/
|
||||
$scope.saveAdmin = function () {
|
||||
Admin.save(
|
||||
{},
|
||||
{ admin: $scope.admin },
|
||||
function () {
|
||||
growl.success(_t('app.admin.admins_new.administrator_successfully_created_he_will_receive_his_connection_directives_by_email', { GENDER: getGender($scope.admin) }));
|
||||
return $state.go('app.admin.members');
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.admins_new.failed_to_create_admin') + JSON.stringify(error.data ? error.data : error));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.saveAdmin = function () {
|
||||
Admin.save(
|
||||
{},
|
||||
{ admin: $scope.admin },
|
||||
function () {
|
||||
growl.success(_t('app.admin.admins_new.administrator_successfully_created_he_will_receive_his_connection_directives_by_email', { GENDER: getGender($scope.admin) }));
|
||||
return $state.go('app.admin.members');
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.admins_new.failed_to_create_admin') + JSON.stringify(error.data ? error.data : error));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
/**
|
||||
* Return an enumerable meaningful string for the gender of the provider user
|
||||
* @param user {Object} Database user record
|
||||
* @return {string} 'male' or 'female'
|
||||
*/
|
||||
return getGender = function (user) {
|
||||
if (user.statistic_profile_attributes) {
|
||||
if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; }
|
||||
} else { return 'other'; }
|
||||
};
|
||||
}
|
||||
return getGender = function (user) {
|
||||
if (user.statistic_profile_attributes) {
|
||||
if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; }
|
||||
} else { return 'other'; }
|
||||
};
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
@ -1140,65 +1149,64 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
|
||||
Application.Controllers.controller('NewManagerController', ['$state', '$scope', 'User', 'groupsPromise', 'tagsPromise', 'growl', '_t',
|
||||
function ($state, $scope, User, groupsPromise, tagsPromise, growl, _t) {
|
||||
// default admin profile
|
||||
$scope.manager = {
|
||||
statistic_profile_attributes: {
|
||||
gender: true
|
||||
},
|
||||
profile_attributes: {},
|
||||
invoicing_profile_attributes: {}
|
||||
};
|
||||
$scope.manager = {
|
||||
statistic_profile_attributes: {
|
||||
gender: true
|
||||
},
|
||||
profile_attributes: {},
|
||||
invoicing_profile_attributes: {}
|
||||
};
|
||||
|
||||
// Default parameters for AngularUI-Bootstrap datepicker
|
||||
$scope.datePicker = {
|
||||
format: Fablab.uibDateFormat,
|
||||
opened: false,
|
||||
options: {
|
||||
startingDay: Fablab.weekStartingDay
|
||||
}
|
||||
};
|
||||
// Default parameters for AngularUI-Bootstrap datepicker
|
||||
$scope.datePicker = {
|
||||
format: Fablab.uibDateFormat,
|
||||
opened: false,
|
||||
options: {
|
||||
startingDay: Fablab.weekStartingDay
|
||||
}
|
||||
};
|
||||
|
||||
// list of all groups
|
||||
$scope.groups = groupsPromise.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; });
|
||||
// list of all groups
|
||||
$scope.groups = groupsPromise.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; });
|
||||
|
||||
// list of all tags
|
||||
$scope.tags = tagsPromise;
|
||||
// list of all tags
|
||||
$scope.tags = tagsPromise;
|
||||
|
||||
/**
|
||||
* Shows the birth day datepicker
|
||||
* @param $event {Object} jQuery event object
|
||||
/**
|
||||
* Shows the birthday datepicker
|
||||
*/
|
||||
$scope.openDatePicker = function ($event) { $scope.datePicker.opened = true; };
|
||||
$scope.openDatePicker = function () { $scope.datePicker.opened = true; };
|
||||
|
||||
/**
|
||||
/**
|
||||
* Send the new manager, currently stored in $scope.manager, to the server for database saving
|
||||
*/
|
||||
$scope.saveManager = function () {
|
||||
User.save(
|
||||
{},
|
||||
{ manager: $scope.manager },
|
||||
function () {
|
||||
growl.success(_t('app.admin.manager_new.manager_successfully_created', { GENDER: getGender($scope.manager) }));
|
||||
return $state.go('app.admin.members');
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.admins_new.failed_to_create_manager') + JSON.stringify(error.data ? error.data : error));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
$scope.saveManager = function () {
|
||||
User.save(
|
||||
{},
|
||||
{ manager: $scope.manager },
|
||||
function () {
|
||||
growl.success(_t('app.admin.manager_new.manager_successfully_created', { GENDER: getGender($scope.manager) }));
|
||||
return $state.go('app.admin.members');
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.admins_new.failed_to_create_manager') + JSON.stringify(error.data ? error.data : error));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
/**
|
||||
* Return an enumerable meaningful string for the gender of the provider user
|
||||
* @param user {Object} Database user record
|
||||
* @return {string} 'male' or 'female'
|
||||
*/
|
||||
const getGender = function (user) {
|
||||
if (user.statistic_profile_attributes) {
|
||||
if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; }
|
||||
} else { return 'other'; }
|
||||
};
|
||||
}
|
||||
const getGender = function (user) {
|
||||
if (user.statistic_profile_attributes) {
|
||||
if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; }
|
||||
} else { return 'other'; }
|
||||
};
|
||||
}
|
||||
|
||||
]);
|
||||
|
@ -716,9 +716,14 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
* Open a modal window that allows the user to process a credit card payment for his current shopping cart.
|
||||
*/
|
||||
const payByStripe = function (reservation) {
|
||||
$scope.toggleStripeModal(() => {
|
||||
$scope.stripe.cartItems = mkCartItems(reservation, 'stripe');
|
||||
});
|
||||
// check that the online payment is enabled
|
||||
if ($scope.settings.online_payment_module !== 'true') {
|
||||
growl.error(_t('app.shared.cart.online_payment_disabled'));
|
||||
} else {
|
||||
$scope.toggleStripeModal(() => {
|
||||
$scope.stripe.cartItems = mkCartItems(reservation, 'stripe');
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
|
||||
@ -751,10 +756,13 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
},
|
||||
user () {
|
||||
return $scope.user;
|
||||
},
|
||||
settings () {
|
||||
return $scope.settings;
|
||||
}
|
||||
},
|
||||
controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'Subscription', 'wallet', 'helpers', '$filter', 'coupon', 'selectedPlan', 'schedule', 'cartItems', 'user',
|
||||
function ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, Subscription, wallet, helpers, $filter, coupon, selectedPlan, schedule, cartItems, user) {
|
||||
controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'Subscription', 'wallet', 'helpers', '$filter', 'coupon', 'selectedPlan', 'schedule', 'cartItems', 'user', 'settings',
|
||||
function ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, Subscription, wallet, helpers, $filter, coupon, selectedPlan, schedule, cartItems, user, settings) {
|
||||
// user wallet amount
|
||||
$scope.wallet = wallet;
|
||||
|
||||
@ -797,7 +805,12 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
*/
|
||||
$scope.ok = function () {
|
||||
if ($scope.schedule && $scope.method.payment_method === 'stripe') {
|
||||
return $scope.toggleStripeModal();
|
||||
// check that the online payment is enabled
|
||||
if (settings.online_payment_module !== 'true') {
|
||||
return growl.error(_t('app.shared.cart.online_payment_disabled'));
|
||||
} else {
|
||||
return $scope.toggleStripeModal();
|
||||
}
|
||||
}
|
||||
$scope.attempting = true;
|
||||
// save subscription (if there's only a subscription selected)
|
||||
@ -927,11 +940,7 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount);
|
||||
if ((AuthService.isAuthorized(['member']) && amountToPay > 0) ||
|
||||
(AuthService.isAuthorized('manager') && $scope.user.id === $rootScope.currentUser.id && amountToPay > 0)) {
|
||||
if ($scope.settings.online_payment_module !== 'true') {
|
||||
growl.error(_t('app.shared.cart.online_payment_disabled'));
|
||||
} else {
|
||||
return payByStripe(reservation);
|
||||
}
|
||||
return payByStripe(reservation);
|
||||
} else {
|
||||
if (AuthService.isAuthorized(['admin']) ||
|
||||
(AuthService.isAuthorized('manager') && $scope.user.id !== $rootScope.currentUser.id) ||
|
||||
|
@ -10,6 +10,7 @@
|
||||
<div ng-hide="free">
|
||||
<p translate>{{ 'app.admin.members_edit.you_intentionally_decide_to_extend_the_user_s_subscription_by_charging_him_again_for_his_current_subscription' }}</p>
|
||||
<p translate>{{ 'app.admin.members_edit.credits_will_be_reset' }}</p>
|
||||
<p translate>{{ 'app.admin.members_edit.payment_scheduled' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<form role="form" name="subscriptionForm" novalidate>
|
||||
|
@ -33,6 +33,13 @@ class PaymentSchedule < ApplicationRecord
|
||||
save
|
||||
end
|
||||
|
||||
def set_wallet_transaction(amount, transaction_id)
|
||||
raise InvalidFootprintError unless check_footprint
|
||||
|
||||
update_columns(wallet_amount: amount, wallet_transaction_id: transaction_id)
|
||||
chain_record
|
||||
end
|
||||
|
||||
def chain_record
|
||||
self.footprint = compute_footprint
|
||||
save!
|
||||
@ -46,4 +53,8 @@ class PaymentSchedule < ApplicationRecord
|
||||
def compute_footprint
|
||||
FootprintService.compute_footprint(PaymentSchedule, self)
|
||||
end
|
||||
|
||||
def check_footprint
|
||||
payment_schedule_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||
end
|
||||
end
|
||||
|
@ -4,4 +4,23 @@
|
||||
class PaymentScheduleItem < ApplicationRecord
|
||||
belongs_to :payment_schedule
|
||||
belongs_to :invoice
|
||||
after_create :chain_record
|
||||
|
||||
def chain_record
|
||||
self.footprint = compute_footprint
|
||||
save!
|
||||
FootprintDebug.create!(
|
||||
footprint: footprint,
|
||||
data: FootprintService.footprint_data(PaymentScheduleItem, self),
|
||||
klass: PaymentScheduleItem.name
|
||||
)
|
||||
end
|
||||
|
||||
def check_footprint
|
||||
footprint == compute_footprint
|
||||
end
|
||||
|
||||
def compute_footprint
|
||||
FootprintService.compute_footprint(PaymentScheduleItem, self)
|
||||
end
|
||||
end
|
||||
|
@ -30,133 +30,33 @@ class Reservation < ApplicationRecord
|
||||
after_commit :notify_member_create_reservation, on: :create
|
||||
after_commit :notify_admin_member_create_reservation, on: :create
|
||||
after_save :update_event_nb_free_places, if: proc { |reservation| reservation.reservable_type == 'Event' }
|
||||
after_create :debit_user_wallet
|
||||
|
||||
##
|
||||
# Generate an array of {Stripe::InvoiceItem} with the elements in the current reservation, price included.
|
||||
# @param payment_details {Hash} as generated by Price.compute
|
||||
# These checks will run before the invoice/payment-schedule is generated
|
||||
##
|
||||
def generate_invoice_items(payment_details = nil)
|
||||
def pre_check
|
||||
# check that none of the reserved availabilities was locked
|
||||
slots.each do |slot|
|
||||
raise LockedError if slot.availability.lock
|
||||
end
|
||||
|
||||
case reservable
|
||||
# === Event reservation ===
|
||||
when Event
|
||||
slots.each do |slot|
|
||||
description = "#{reservable.name}\n"
|
||||
description += if slot.start_at.to_date != slot.end_at.to_date
|
||||
I18n.t('events.from_STARTDATE_to_ENDDATE',
|
||||
STARTDATE: I18n.l(slot.start_at.to_date, format: :long),
|
||||
ENDDATE: I18n.l(slot.end_at.to_date, format: :long)) + ' ' +
|
||||
I18n.t('events.from_STARTTIME_to_ENDTIME',
|
||||
STARTTIME: I18n.l(slot.start_at, format: :hour_minute),
|
||||
ENDTIME: I18n.l(slot.end_at, format: :hour_minute))
|
||||
else
|
||||
"#{I18n.l slot.start_at.to_date, format: :long} #{I18n.l slot.start_at, format: :hour_minute}" \
|
||||
" - #{I18n.l slot.end_at, format: :hour_minute}"
|
||||
end
|
||||
|
||||
price_slot = payment_details[:elements][:slots].detect { |p_slot| p_slot[:start_at].to_time.in_time_zone == slot[:start_at] }
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: price_slot[:price],
|
||||
description: description
|
||||
)
|
||||
end
|
||||
# === Space|Machine|Training reservation ===
|
||||
else
|
||||
slots.each do |slot|
|
||||
description = reservable.name +
|
||||
" #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
|
||||
|
||||
price_slot = payment_details[:elements][:slots].detect { |p_slot| p_slot[:start_at].to_time.in_time_zone == slot[:start_at] }
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: price_slot[:price],
|
||||
description: description
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# === Coupon ===
|
||||
@coupon = payment_details[:coupon]
|
||||
|
||||
# === Wallet ===
|
||||
@wallet_amount_debit = wallet_amount_debit
|
||||
end
|
||||
|
||||
# check reservation amount total and strip invoice total to pay is equal
|
||||
# @param stp_invoice[Stripe::Invoice]
|
||||
# @param coupon_code[String]
|
||||
# return Boolean
|
||||
def is_equal_reservation_total_and_stp_invoice_total(stp_invoice, coupon_code = nil)
|
||||
compute_amount_total_to_pay(coupon_code) == stp_invoice.total
|
||||
## Generate the subscription associated with for the current reservation
|
||||
def generate_subscription
|
||||
return unless plan_id
|
||||
|
||||
self.subscription = Subscription.find_or_initialize_by(statistic_profile_id: statistic_profile_id)
|
||||
subscription.attributes = { plan_id: plan_id, statistic_profile_id: statistic_profile_id, expiration_date: nil }
|
||||
|
||||
subscription.init_save
|
||||
subscription
|
||||
end
|
||||
|
||||
def clear_payment_info(card, invoice)
|
||||
card&.delete
|
||||
if invoice
|
||||
invoice.closed = true
|
||||
invoice.save
|
||||
end
|
||||
rescue Stripe::InvalidRequestError => e
|
||||
logger.error e
|
||||
rescue Stripe::AuthenticationError => e
|
||||
logger.error e
|
||||
rescue Stripe::APIConnectionError => e
|
||||
logger.error e
|
||||
rescue Stripe::StripeError => e
|
||||
logger.error e
|
||||
rescue StandardError => e
|
||||
logger.error e
|
||||
end
|
||||
|
||||
def clean_pending_strip_invoice_items
|
||||
pending_invoice_items = Stripe::InvoiceItem.list(
|
||||
{ customer: user.stp_customer_id, limit: 100 },
|
||||
{ api_key: Setting.get('stripe_secret_key') }
|
||||
).data.select { |ii| ii.invoice.nil? }
|
||||
pending_invoice_items.each(&:delete)
|
||||
end
|
||||
|
||||
def save_with_payment(operator_profile_id, payment_details, payment_intent_id = nil)
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
method = operator&.admin? || (operator&.manager? && operator != user) ? nil : 'stripe'
|
||||
|
||||
build_invoice(
|
||||
invoicing_profile: user.invoicing_profile,
|
||||
statistic_profile: user.statistic_profile,
|
||||
operator_profile_id: operator_profile_id,
|
||||
stp_payment_intent_id: payment_intent_id,
|
||||
payment_method: method
|
||||
)
|
||||
generate_invoice_items(payment_details)
|
||||
|
||||
return false unless valid?
|
||||
|
||||
if plan_id
|
||||
self.subscription = Subscription.find_or_initialize_by(statistic_profile_id: statistic_profile_id)
|
||||
subscription.attributes = { plan_id: plan_id, statistic_profile_id: statistic_profile_id, expiration_date: nil }
|
||||
if subscription.save_with_payment(operator_profile_id, invoice: false)
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: payment_details[:elements][:plan],
|
||||
description: subscription.plan.name,
|
||||
subscription_id: subscription.id
|
||||
)
|
||||
set_total_and_coupon(payment_details[:coupon])
|
||||
save!
|
||||
else
|
||||
errors[:card] << subscription.errors[:card].join
|
||||
return false
|
||||
end
|
||||
else
|
||||
set_total_and_coupon(payment_details[:coupon])
|
||||
save!
|
||||
end
|
||||
|
||||
##
|
||||
# These actions will be realized after the reservation is initially saved (on creation)
|
||||
##
|
||||
def post_save
|
||||
UsersCredits::Manager.new(reservation: self).update_credits
|
||||
true
|
||||
end
|
||||
|
||||
# @param canceled if true, count the number of seats for this reservation, including canceled seats
|
||||
@ -219,61 +119,4 @@ class Reservation < ApplicationRecord
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self
|
||||
end
|
||||
|
||||
def cart_total
|
||||
total = (invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+) or 0)
|
||||
if plan_id.present?
|
||||
plan = Plan.find(plan_id)
|
||||
total += plan.amount
|
||||
end
|
||||
total
|
||||
end
|
||||
|
||||
def wallet_amount_debit
|
||||
total = cart_total
|
||||
total = CouponService.new.apply(total, @coupon, user.id) if @coupon
|
||||
|
||||
wallet_amount = (user.wallet.amount * 100).to_i
|
||||
|
||||
wallet_amount >= total ? total : wallet_amount
|
||||
end
|
||||
|
||||
def debit_user_wallet
|
||||
return unless @wallet_amount_debit.present? && @wallet_amount_debit != 0
|
||||
|
||||
amount = @wallet_amount_debit / 100.0
|
||||
wallet_transaction = WalletService.new(user: user, wallet: user.wallet).debit(amount, self)
|
||||
# wallet debit success
|
||||
raise DebitWalletError unless wallet_transaction
|
||||
|
||||
invoice.set_wallet_transaction(@wallet_amount_debit, wallet_transaction.id)
|
||||
end
|
||||
|
||||
# this function only use for compute total of reservation before save
|
||||
def compute_amount_total_to_pay(coupon_code = nil)
|
||||
total = invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+)
|
||||
unless coupon_code.nil?
|
||||
cp = Coupon.find_by(code: coupon_code)
|
||||
raise InvalidCouponError unless !cp.nil? && cp.status(user.id) == 'active'
|
||||
|
||||
total = CouponService.new.apply(total, cp, user.id)
|
||||
end
|
||||
total - wallet_amount_debit
|
||||
end
|
||||
|
||||
##
|
||||
# Set the total price to the reservation's invoice, summing its whole items.
|
||||
# Additionally a coupon may be applied to this invoice to make a discount on the total price
|
||||
# @param [coupon] {Coupon} optional coupon to apply to the invoice
|
||||
##
|
||||
def set_total_and_coupon(coupon = nil)
|
||||
total = invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+)
|
||||
|
||||
unless coupon.nil?
|
||||
total = CouponService.new.apply(total, coupon, user.id)
|
||||
invoice.coupon_id = coupon.id
|
||||
end
|
||||
|
||||
invoice.total = total
|
||||
end
|
||||
end
|
||||
|
@ -19,85 +19,20 @@ class Subscription < ApplicationRecord
|
||||
after_save :notify_admin_subscribed_plan
|
||||
after_save :notify_partner_subscribed_plan, if: :of_partner_plan?
|
||||
|
||||
# @param invoice if true then only the subscription is payed, without reservation
|
||||
# if false then the subscription is payed with reservation
|
||||
# @param payment_method is only used for schedules
|
||||
def save_with_payment(operator_profile_id, invoice: true, coupon_code: nil, payment_intent_id: nil, schedule: nil, payment_method: nil)
|
||||
##
|
||||
# Set the inner properties of the subscription, init the user's credits and save the subscription into the DB
|
||||
# @return {boolean} true, if the operation succeeded
|
||||
##
|
||||
def init_save
|
||||
return false unless valid?
|
||||
|
||||
set_expiration_date
|
||||
return false unless save
|
||||
|
||||
UsersCredits::Manager.new(user: user).reset_credits
|
||||
if invoice
|
||||
@wallet_amount_debit = get_wallet_amount_debit
|
||||
|
||||
# debit wallet
|
||||
wallet_transaction = debit_user_wallet
|
||||
|
||||
payment = if schedule
|
||||
generate_schedule(operator_profile_id, payment_method, coupon_code)
|
||||
else
|
||||
generate_invoice(operator_profile_id, coupon_code, payment_intent_id)
|
||||
end
|
||||
|
||||
if wallet_transaction
|
||||
payment.wallet_amount = @wallet_amount_debit
|
||||
payment.wallet_transaction_id = wallet_transaction.id
|
||||
end
|
||||
payment.save
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def generate_schedule(operator_profile_id, payment_method, coupon_code = nil)
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
coupon = Coupon.find_by(code: coupon_code) unless coupon_code.nil?
|
||||
|
||||
PaymentScheduleService.new.create(
|
||||
self,
|
||||
plan.amount,
|
||||
coupon: coupon,
|
||||
operator: operator,
|
||||
payment_method: payment_method,
|
||||
user: user
|
||||
)
|
||||
end
|
||||
|
||||
def generate_invoice(operator_profile_id, coupon_code = nil, payment_intent_id = nil)
|
||||
coupon_id = nil
|
||||
total = plan.amount
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
method = operator&.admin? || (operator&.manager? && operator != user) ? nil : 'stripe'
|
||||
|
||||
unless coupon_code.nil?
|
||||
@coupon = Coupon.find_by(code: coupon_code)
|
||||
|
||||
unless @coupon.nil?
|
||||
total = CouponService.new.apply(plan.amount, @coupon, user.id)
|
||||
coupon_id = @coupon.id
|
||||
end
|
||||
end
|
||||
|
||||
invoice = Invoice.new(
|
||||
invoiced_id: id,
|
||||
invoiced_type: 'Subscription',
|
||||
invoicing_profile: user.invoicing_profile,
|
||||
statistic_profile: user.statistic_profile,
|
||||
total: total,
|
||||
coupon_id: coupon_id,
|
||||
operator_profile_id: operator_profile_id,
|
||||
stp_payment_intent_id: payment_intent_id,
|
||||
payment_method: method
|
||||
)
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: plan.amount,
|
||||
description: plan.name,
|
||||
subscription_id: id
|
||||
)
|
||||
invoice
|
||||
end
|
||||
|
||||
def generate_and_save_invoice(operator_profile_id)
|
||||
generate_invoice(operator_profile_id).save
|
||||
end
|
||||
|
@ -59,4 +59,137 @@ class InvoicesService
|
||||
end
|
||||
{ direction: direction, order_key: order_key }
|
||||
end
|
||||
|
||||
##
|
||||
# Create an Invoice with an associated array of InvoiceItem matching the given parameters
|
||||
# @param payment_details {Hash} as generated by Price.compute
|
||||
# @param operator_profile_id {Number} ID of the user that operates the invoice generation (may be an admin, a manager or the customer himself)
|
||||
# @param reservation {Reservation} the booking reservation, if any
|
||||
# @param subscription {Subscription} the booking subscription, if any
|
||||
# @param payment_intent_id {String} ID of the Stripe::PaymentIntend, if the current invoice is paid by stripe
|
||||
##
|
||||
def self.create(payment_details, operator_profile_id, reservation: nil, subscription: nil, payment_intent_id: nil)
|
||||
user = reservation&.user || subscription&.user
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
method = operator&.admin? || (operator&.manager? && operator != user) ? nil : 'stripe'
|
||||
|
||||
invoice = Invoice.new(
|
||||
invoiced: subscription || reservation,
|
||||
invoicing_profile: user.invoicing_profile,
|
||||
statistic_profile: user.statistic_profile,
|
||||
operator_profile_id: operator_profile_id,
|
||||
stp_payment_intent_id: payment_intent_id,
|
||||
payment_method: method
|
||||
)
|
||||
|
||||
InvoicesService.generate_invoice_items(invoice, payment_details, reservation: reservation, subscription: subscription)
|
||||
InvoicesService.set_total_and_coupon(invoice, user, payment_details[:coupon])
|
||||
invoice
|
||||
end
|
||||
|
||||
##
|
||||
# Generate an array of {InvoiceItem} with the elements in provided reservation, price included.
|
||||
# @param invoice {Invoice} the parent invoice
|
||||
# @param payment_details {Hash} as generated by Price.compute
|
||||
##
|
||||
def self.generate_invoice_items(invoice, payment_details, reservation: nil, subscription: nil)
|
||||
if reservation
|
||||
case reservation.reservable
|
||||
# === Event reservation ===
|
||||
when Event
|
||||
InvoicesService.generate_event_item(invoice, reservation, payment_details)
|
||||
# === Space|Machine|Training reservation ===
|
||||
else
|
||||
InvoicesService.generate_generic_item(invoice, reservation, payment_details)
|
||||
end
|
||||
end
|
||||
|
||||
return unless subscription || reservation&.plan_id
|
||||
|
||||
subscription = reservation.generate_subscription if !subscription && reservation.plan_id
|
||||
InvoicesService.generate_subscription_item(invoice, subscription, payment_details)
|
||||
end
|
||||
|
||||
##
|
||||
# Generate an InvoiceItem for each slot in the given reservation and save them in invoice.invoice_items.
|
||||
# This method must be called if reservation.reservable is an Event
|
||||
##
|
||||
def self.generate_event_item(invoice, reservation, payment_details)
|
||||
raise TypeError unless reservation.reservable.class == Event
|
||||
|
||||
reservation.slots.each do |slot|
|
||||
description = "#{reservation.reservable.name}\n"
|
||||
description += if slot.start_at.to_date != slot.end_at.to_date
|
||||
I18n.t('events.from_STARTDATE_to_ENDDATE',
|
||||
STARTDATE: I18n.l(slot.start_at.to_date, format: :long),
|
||||
ENDDATE: I18n.l(slot.end_at.to_date, format: :long)) + ' ' +
|
||||
I18n.t('events.from_STARTTIME_to_ENDTIME',
|
||||
STARTTIME: I18n.l(slot.start_at, format: :hour_minute),
|
||||
ENDTIME: I18n.l(slot.end_at, format: :hour_minute))
|
||||
else
|
||||
"#{I18n.l slot.start_at.to_date, format: :long} #{I18n.l slot.start_at, format: :hour_minute}" \
|
||||
" - #{I18n.l slot.end_at, format: :hour_minute}"
|
||||
end
|
||||
|
||||
price_slot = payment_details[:elements][:slots].detect { |p_slot| p_slot[:start_at].to_time.in_time_zone == slot[:start_at] }
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: price_slot[:price],
|
||||
description: description
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Generate an InvoiceItem for each slot in the given reservation and save them in invoice.invoice_items.
|
||||
# This method must be called if reservation.reservable is a Space, a Machine or a Training
|
||||
##
|
||||
def self.generate_generic_item(invoice, reservation, payment_details)
|
||||
raise TypeError unless [Space, Machine, Training].include? reservation.reservable.class
|
||||
|
||||
reservation.slots.each do |slot|
|
||||
description = reservation.reservable.name +
|
||||
" #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
|
||||
|
||||
price_slot = payment_details[:elements][:slots].detect { |p_slot| p_slot[:start_at].to_time.in_time_zone == slot[:start_at] }
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: price_slot[:price],
|
||||
description: description
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Generate an InvoiceItem for the given subscription and save it in invoice.invoice_items.
|
||||
# This method must be called only with a valid subscription
|
||||
##
|
||||
def self.generate_subscription_item(invoice, subscription, payment_details)
|
||||
raise TypeError unless subscription
|
||||
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: payment_details[:elements][:plan],
|
||||
description: subscription.plan.name,
|
||||
subscription_id: subscription.id
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# Set the total price to the reservation's invoice, summing its whole items.
|
||||
# Additionally a coupon may be applied to this invoice to make a discount on the total price
|
||||
# @param invoice {Invoice} the invoice to fill
|
||||
# @param user {User} the customer
|
||||
# @param [coupon] {Coupon} optional coupon to apply to the invoice
|
||||
##
|
||||
def self.set_total_and_coupon(invoice, user, coupon = nil)
|
||||
return unless invoice
|
||||
|
||||
total = invoice.invoice_items.map(&:amount).map(&:to_i).reduce(:+)
|
||||
|
||||
unless coupon.nil?
|
||||
total = CouponService.new.apply(total, coupon, user.id)
|
||||
invoice.coupon_id = coupon.id
|
||||
end
|
||||
|
||||
invoice.total = total
|
||||
end
|
||||
end
|
||||
|
@ -47,11 +47,13 @@ class PaymentScheduleService
|
||||
end
|
||||
|
||||
def create(subscription, total, coupon: nil, operator: nil, payment_method: nil, reservation: nil, user: nil)
|
||||
subscription = reservation.generate_subscription if !subscription && reservation.plan_id
|
||||
|
||||
schedule = compute(subscription.plan, total, coupon)
|
||||
ps = schedule[:payment_schedule]
|
||||
items = schedule[:items]
|
||||
|
||||
ps.scheduled = subscription
|
||||
ps.scheduled = reservation || subscription
|
||||
ps.payment_method = payment_method
|
||||
ps.operator_profile = operator.invoicing_profile
|
||||
ps.invoicing_profile = user.invoicing_profile
|
||||
|
@ -9,9 +9,61 @@ class Reservations::Reserve
|
||||
@operator_profile_id = operator_profile_id
|
||||
end
|
||||
|
||||
def pay_and_save(reservation, payment_details: nil, payment_intent_id: nil, schedule: false)
|
||||
# TODO, pass the schedule payment up to subscription.save_with_payment(... schedule: schedule)
|
||||
##
|
||||
# Confirm the payment of the given reservation, generate the associated documents and save teh record into
|
||||
# the database.
|
||||
##
|
||||
def pay_and_save(reservation, payment_details: nil, payment_intent_id: nil, schedule: false, payment_method: nil)
|
||||
user = User.find(user_id)
|
||||
reservation.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id
|
||||
reservation.save_with_payment(operator_profile_id, payment_details, payment_intent_id)
|
||||
|
||||
reservation.pre_check
|
||||
payment = if schedule
|
||||
generate_schedule(reservation: reservation,
|
||||
total: payment_details[:before_coupon],
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: user,
|
||||
payment_method: payment_method,
|
||||
coupon_code: payment_details[:coupon])
|
||||
else
|
||||
generate_invoice(reservation, operator_profile_id, payment_details, payment_intent_id)
|
||||
end
|
||||
payment.save
|
||||
WalletService.debit_user_wallet(payment, user, reservation)
|
||||
reservation.post_save
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# Generate the invoice for the given reservation+subscription
|
||||
##
|
||||
def generate_schedule(reservation: nil, total: nil, operator_profile_id: nil, user: nil, payment_method: nil, coupon_code: nil)
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
coupon = Coupon.find_by(code: coupon_code) unless coupon_code.nil?
|
||||
|
||||
PaymentScheduleService.new.create(
|
||||
nil,
|
||||
total,
|
||||
coupon: coupon,
|
||||
operator: operator,
|
||||
payment_method: payment_method,
|
||||
user: user,
|
||||
reservation: reservation
|
||||
)
|
||||
end
|
||||
|
||||
##
|
||||
# Generate the invoice for the given reservation
|
||||
##
|
||||
def generate_invoice(reservation, operator_profile_id, payment_details, payment_intent_id = nil)
|
||||
InvoicesService.create(
|
||||
payment_details,
|
||||
operator_profile_id,
|
||||
reservation: reservation,
|
||||
payment_intent_id: payment_intent_id
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -11,22 +11,31 @@ class Subscriptions::Subscribe
|
||||
|
||||
##
|
||||
# @param subscription {Subscription}
|
||||
# @param coupon {String} coupon code
|
||||
# @param invoice {Boolean}
|
||||
# @param payment_details {Hash} as generated by Price.compute
|
||||
# @param payment_intent_id {String} from stripe
|
||||
# @param schedule {Boolean}
|
||||
# @param payment_method {String} only for schedules
|
||||
##
|
||||
def pay_and_save(subscription, coupon: nil, invoice: false, payment_intent_id: nil, schedule: false, payment_method: nil)
|
||||
def pay_and_save(subscription, payment_details: nil, payment_intent_id: nil, schedule: false, payment_method: nil)
|
||||
return false if user_id.nil?
|
||||
|
||||
subscription.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id
|
||||
subscription.save_with_payment(operator_profile_id,
|
||||
invoice: invoice,
|
||||
coupon_code: coupon,
|
||||
payment_intent_id: payment_intent_id,
|
||||
schedule: schedule,
|
||||
payment_method: payment_method)
|
||||
subscription.init_save
|
||||
user = User.find(user_id)
|
||||
|
||||
payment = if schedule
|
||||
generate_schedule(subscription: subscription,
|
||||
total: payment_details[:before_coupon],
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: user,
|
||||
payment_method: payment_method,
|
||||
coupon_code: payment_details[:coupon])
|
||||
else
|
||||
generate_invoice(subscription, operator_profile_id, payment_details, payment_intent_id)
|
||||
end
|
||||
payment.save
|
||||
WalletService.debit_user_wallet(payment, user, subscription)
|
||||
true
|
||||
end
|
||||
|
||||
def extend_subscription(subscription, new_expiration_date, free_days)
|
||||
@ -38,10 +47,53 @@ class Subscriptions::Subscribe
|
||||
expiration_date: new_expiration_date
|
||||
)
|
||||
if new_sub.save
|
||||
new_sub.user.generate_subscription_invoice(operator_profile_id)
|
||||
schedule = subscription.payment_schedule
|
||||
details = Price.compute(true, new_sub.user, nil, [], plan_id: subscription.plan_id)
|
||||
payment = if schedule
|
||||
generate_schedule(subscription: new_sub,
|
||||
total: details[:before_coupon],
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: new_sub.user,
|
||||
payment_method: schedule.payment_method)
|
||||
else
|
||||
generate_invoice(subscription, operator_profile_id, details)
|
||||
end
|
||||
payment.save
|
||||
UsersCredits::Manager.new(user: new_sub.user).reset_credits
|
||||
return new_sub
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# Generate the invoice for the given subscription
|
||||
##
|
||||
def generate_schedule(subscription: nil, total: nil, operator_profile_id: nil, user: nil, payment_method: nil, coupon_code: nil)
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
coupon = Coupon.find_by(code: coupon_code) unless coupon_code.nil?
|
||||
|
||||
PaymentScheduleService.new.create(
|
||||
subscription,
|
||||
total,
|
||||
coupon: coupon,
|
||||
operator: operator,
|
||||
payment_method: payment_method,
|
||||
user: user
|
||||
)
|
||||
end
|
||||
|
||||
##
|
||||
# Generate the invoice for the given subscription
|
||||
##
|
||||
def generate_invoice(subscription, operator_profile_id, payment_details, payment_intent_id = nil)
|
||||
InvoicesService.create(
|
||||
payment_details,
|
||||
operator_profile_id,
|
||||
subscription: subscription,
|
||||
payment_intent_id: payment_intent_id
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -72,4 +72,34 @@ class WalletService
|
||||
ii.invoice = avoir
|
||||
ii.save!
|
||||
end
|
||||
|
||||
##
|
||||
# Compute the amount decreased from the user's wallet, if applicable
|
||||
# @param payment {Invoice|PaymentSchedule}
|
||||
# @param user {User} the customer
|
||||
# @param coupon {Coupon|String} Coupon object or code
|
||||
##
|
||||
def self.wallet_amount_debit(payment, user, coupon = nil)
|
||||
total = payment.total
|
||||
total = CouponService.new.apply(total, coupon, user.id) if coupon
|
||||
|
||||
wallet_amount = (user.wallet.amount * 100).to_i
|
||||
|
||||
wallet_amount >= total ? total : wallet_amount
|
||||
end
|
||||
|
||||
##
|
||||
# Subtract the amount of the transactable item (Subscription|Reservation) from the customer's wallet
|
||||
##
|
||||
def self.debit_user_wallet(payment, user, transactable)
|
||||
wallet_amount = WalletService.wallet_amount_debit(payment, user)
|
||||
return unless wallet_amount.present? && wallet_amount != 0
|
||||
|
||||
amount = wallet_amount / 100.0
|
||||
wallet_transaction = WalletService.new(user: user, wallet: user.wallet).debit(amount, transactable)
|
||||
# wallet debit success
|
||||
raise DebitWalletError unless wallet_transaction
|
||||
|
||||
payment.set_wallet_transaction(wallet_amount, wallet_transaction.id)
|
||||
end
|
||||
end
|
||||
|
@ -53,7 +53,6 @@ class StripeWorker
|
||||
{ name: object.name },
|
||||
{ api_key: Setting.get('stripe_secret_key') }
|
||||
)
|
||||
p.product
|
||||
else
|
||||
product = Stripe::Product.create(
|
||||
{
|
||||
|
@ -10,7 +10,7 @@ require 'action_view/railtie'
|
||||
require 'action_mailer/railtie'
|
||||
require 'active_job/railtie'
|
||||
# require 'action_cable/engine'
|
||||
require 'rails/test_unit/railtie' if Rails.env.test?
|
||||
require 'rails/test_unit/railtie'
|
||||
# require 'sprockets/railtie'
|
||||
require 'elasticsearch/rails/instrumentation'
|
||||
require 'elasticsearch/persistence/model'
|
||||
@ -56,6 +56,7 @@ module Fablab
|
||||
|
||||
config.generators do |g|
|
||||
g.orm :active_record
|
||||
g.test_framework :mini_test
|
||||
end
|
||||
|
||||
if Rails.env.development?
|
||||
|
@ -815,6 +815,7 @@ en:
|
||||
credits_will_remain_unchanged: "The balance of free credits (training / machines / spaces) of the user will remain unchanged."
|
||||
you_intentionally_decide_to_extend_the_user_s_subscription_by_charging_him_again_for_his_current_subscription: "You intentionally decide to extend the user's subscription by charging him again for his current subscription."
|
||||
credits_will_be_reset: "The balance of free credits (training / machines / spaces) of the user will be reset, unused credits will be lost."
|
||||
payment_scheduled: "If the previous subscription was charged through a payment schedule, this one will be charged the same way."
|
||||
until_expiration_date: "Until (expiration date):"
|
||||
you_successfully_changed_the_expiration_date_of_the_user_s_subscription: "You successfully changed the expiration date of the user's subscription"
|
||||
a_problem_occurred_while_saving_the_date: "A problem occurred while saving the date."
|
||||
|
@ -815,6 +815,7 @@ fr:
|
||||
credits_will_remain_unchanged: "Le solde de crédits gratuits (formations/machines/espaces) de l'utilisateur restera inchangé."
|
||||
you_intentionally_decide_to_extend_the_user_s_subscription_by_charging_him_again_for_his_current_subscription: "Vous décidez délibérément d'étendre l'abonnement de l'utilisateur en lui faisant repayer le prix de l'abonnement qu'il possède actuellement."
|
||||
credits_will_be_reset: "Le solde de crédits gratuits (formations/machines/espaces) de l'utilisateur sera remis à zéro, ses crédits non utilisés seront perdu."
|
||||
payment_scheduled: "Si l'abonnement précédent a été facturé via un échéancier de paiement mensualisé, celui-ci sera facturé de la même façon."
|
||||
until_expiration_date: "Jusqu'à (date d'expiration) :"
|
||||
you_successfully_changed_the_expiration_date_of_the_user_s_subscription: "Vous avez bien modifié la date d'expiration de l'abonnement de l'utilisateur"
|
||||
a_problem_occurred_while_saving_the_date: "Il y a eu un problème lors de l'enregistrement de la date."
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal:true
|
||||
|
||||
# From this migration, if the current Invoice is payed with Stripe, it will be stored in database
|
||||
# using stp_payment_intent_id instead of stp_invoice_id
|
||||
class AddStpPaymentIntentIdToInvoices < ActiveRecord::Migration[4.2]
|
||||
def change
|
||||
add_column :invoices, :stp_payment_intent_id, :string
|
||||
|
@ -9,6 +9,7 @@ class CreatePaymentScheduleItems < ActiveRecord::Migration[5.2]
|
||||
t.jsonb :details, default: '{}'
|
||||
t.belongs_to :payment_schedule, foreign_key: true
|
||||
t.belongs_to :invoice, foreign_key: true
|
||||
t.string :footprint
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
@ -108,8 +108,8 @@ SET default_tablespace = '';
|
||||
|
||||
CREATE TABLE public.abuses (
|
||||
id integer NOT NULL,
|
||||
signaled_id integer,
|
||||
signaled_type character varying,
|
||||
signaled_id integer,
|
||||
first_name character varying,
|
||||
last_name character varying,
|
||||
email character varying,
|
||||
@ -187,8 +187,8 @@ CREATE TABLE public.addresses (
|
||||
locality character varying,
|
||||
country character varying,
|
||||
postal_code character varying,
|
||||
placeable_id integer,
|
||||
placeable_type character varying,
|
||||
placeable_id integer,
|
||||
created_at timestamp without time zone,
|
||||
updated_at timestamp without time zone
|
||||
);
|
||||
@ -263,8 +263,8 @@ CREATE TABLE public.ar_internal_metadata (
|
||||
|
||||
CREATE TABLE public.assets (
|
||||
id integer NOT NULL,
|
||||
viewable_id integer,
|
||||
viewable_type character varying,
|
||||
viewable_id integer,
|
||||
attachment character varying,
|
||||
type character varying,
|
||||
created_at timestamp without time zone,
|
||||
@ -504,8 +504,8 @@ ALTER SEQUENCE public.coupons_id_seq OWNED BY public.coupons.id;
|
||||
|
||||
CREATE TABLE public.credits (
|
||||
id integer NOT NULL,
|
||||
creditable_id integer,
|
||||
creditable_type character varying,
|
||||
creditable_id integer,
|
||||
plan_id integer,
|
||||
hours integer,
|
||||
created_at timestamp without time zone,
|
||||
@ -1046,8 +1046,8 @@ ALTER SEQUENCE public.invoice_items_id_seq OWNED BY public.invoice_items.id;
|
||||
|
||||
CREATE TABLE public.invoices (
|
||||
id integer NOT NULL,
|
||||
invoiced_id integer,
|
||||
invoiced_type character varying,
|
||||
invoiced_id integer,
|
||||
stp_invoice_id character varying,
|
||||
total integer,
|
||||
created_at timestamp without time zone,
|
||||
@ -1227,15 +1227,15 @@ ALTER SEQUENCE public.machines_id_seq OWNED BY public.machines.id;
|
||||
CREATE TABLE public.notifications (
|
||||
id integer NOT NULL,
|
||||
receiver_id integer,
|
||||
attached_object_id integer,
|
||||
attached_object_type character varying,
|
||||
attached_object_id integer,
|
||||
notification_type_id integer,
|
||||
is_read boolean DEFAULT false,
|
||||
created_at timestamp without time zone,
|
||||
updated_at timestamp without time zone,
|
||||
receiver_type character varying,
|
||||
is_send boolean DEFAULT false,
|
||||
meta_data jsonb DEFAULT '{}'::jsonb
|
||||
meta_data jsonb DEFAULT '"{}"'::jsonb
|
||||
);
|
||||
|
||||
|
||||
@ -1473,6 +1473,7 @@ CREATE TABLE public.payment_schedule_items (
|
||||
details jsonb DEFAULT '"{}"'::jsonb,
|
||||
payment_schedule_id bigint,
|
||||
invoice_id bigint,
|
||||
footprint character varying,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL
|
||||
);
|
||||
@ -1656,8 +1657,8 @@ CREATE TABLE public.prices (
|
||||
id integer NOT NULL,
|
||||
group_id integer,
|
||||
plan_id integer,
|
||||
priceable_id integer,
|
||||
priceable_type character varying,
|
||||
priceable_id integer,
|
||||
amount integer,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL
|
||||
@ -1972,8 +1973,8 @@ CREATE TABLE public.reservations (
|
||||
message text,
|
||||
created_at timestamp without time zone,
|
||||
updated_at timestamp without time zone,
|
||||
reservable_id integer,
|
||||
reservable_type character varying,
|
||||
reservable_id integer,
|
||||
nb_reserve_places integer,
|
||||
statistic_profile_id integer
|
||||
);
|
||||
@ -2005,8 +2006,8 @@ ALTER SEQUENCE public.reservations_id_seq OWNED BY public.reservations.id;
|
||||
CREATE TABLE public.roles (
|
||||
id integer NOT NULL,
|
||||
name character varying,
|
||||
resource_id integer,
|
||||
resource_type character varying,
|
||||
resource_id integer,
|
||||
created_at timestamp without time zone,
|
||||
updated_at timestamp without time zone
|
||||
);
|
||||
@ -2942,8 +2943,8 @@ CREATE TABLE public.users_roles (
|
||||
CREATE TABLE public.wallet_transactions (
|
||||
id integer NOT NULL,
|
||||
wallet_id integer,
|
||||
transactable_id integer,
|
||||
transactable_type character varying,
|
||||
transactable_id integer,
|
||||
transaction_type character varying,
|
||||
amount integer,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
@ -4032,6 +4033,14 @@ ALTER TABLE ONLY public.roles
|
||||
ADD CONSTRAINT roles_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.schema_migrations
|
||||
ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
|
||||
|
||||
|
||||
--
|
||||
-- Name: settings settings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@ -5096,29 +5105,6 @@ CREATE INDEX profiles_lower_unaccent_last_name_trgm_idx ON public.profiles USING
|
||||
CREATE INDEX projects_search_vector_idx ON public.projects USING gin (search_vector);
|
||||
|
||||
|
||||
--
|
||||
-- Name: unique_schema_migrations; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE UNIQUE INDEX unique_schema_migrations ON public.schema_migrations USING btree (version);
|
||||
|
||||
|
||||
--
|
||||
-- Name: accounting_periods accounting_periods_del_protect; Type: RULE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE RULE accounting_periods_del_protect AS
|
||||
ON DELETE TO public.accounting_periods DO INSTEAD NOTHING;
|
||||
|
||||
|
||||
--
|
||||
-- Name: accounting_periods accounting_periods_upd_protect; Type: RULE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE RULE accounting_periods_upd_protect AS
|
||||
ON UPDATE TO public.accounting_periods DO INSTEAD NOTHING;
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects projects_search_content_trigger; Type: TRIGGER; Schema: public; Owner: -
|
||||
--
|
||||
@ -5653,7 +5639,6 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20140605125131'),
|
||||
('20140605142133'),
|
||||
('20140605151442'),
|
||||
('20140606133116'),
|
||||
('20140609092700'),
|
||||
('20140609092827'),
|
||||
('20140610153123'),
|
||||
@ -5722,14 +5707,12 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20150507075620'),
|
||||
('20150512123546'),
|
||||
('20150520132030'),
|
||||
('20150520133409'),
|
||||
('20150526130729'),
|
||||
('20150527153312'),
|
||||
('20150529113555'),
|
||||
('20150601125944'),
|
||||
('20150603104502'),
|
||||
('20150603104658'),
|
||||
('20150603133050'),
|
||||
('20150604081757'),
|
||||
('20150604131525'),
|
||||
('20150608142234'),
|
||||
@ -5811,7 +5794,6 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20160905142700'),
|
||||
('20160906094739'),
|
||||
('20160906094847'),
|
||||
('20160906145713'),
|
||||
('20160915105234'),
|
||||
('20161123104604'),
|
||||
('20170109085345'),
|
||||
|
10
test/fixtures/history_values.yml
vendored
10
test/fixtures/history_values.yml
vendored
@ -100,7 +100,7 @@ value_history_10:
|
||||
id: 10
|
||||
setting_id: 10
|
||||
invoicing_profile_id: 1
|
||||
value: YYMMmmmX[/VL]R[/A]S[/E]
|
||||
value: YYMMmmmX[/VL]R[/A]
|
||||
created_at: 2018-12-17 11:23:01.603733000 Z
|
||||
updated_at: 2018-12-17 11:23:01.603733000 Z
|
||||
footprint: ed23a2eb1903befc977621bc3c3b19aad831fe550ebaa99e9299238b3d93c275
|
||||
@ -693,3 +693,11 @@ history_value_73:
|
||||
value: true
|
||||
created_at: 2020-06-17 10:48:19.002417000 Z
|
||||
updated_at: 2020-06-17 10:48:19.002417000 Z
|
||||
|
||||
value_history_74:
|
||||
id: 74
|
||||
setting_id: 10
|
||||
invoicing_profile_id: 1
|
||||
value: YYMMmmmX[/VL]R[/A]S[/E]
|
||||
created_at: 2020-12-14 14:37:35.615124000 Z
|
||||
updated_at: 2020-12-14 14:37:35.615124000 Z
|
||||
|
6
test/fixtures/machines.yml
vendored
6
test/fixtures/machines.yml
vendored
@ -22,6 +22,7 @@ machine_1:
|
||||
created_at: 2016-04-04 14:11:34.210242000 Z
|
||||
updated_at: 2016-04-04 14:11:34.210242000 Z
|
||||
slug: decoupeuse-laser
|
||||
stp_product_id: prod_IZPyHpMCl38iQl
|
||||
|
||||
machine_2:
|
||||
id: 2
|
||||
@ -38,6 +39,7 @@ machine_2:
|
||||
created_at: 2016-04-04 14:11:34.274025000 Z
|
||||
updated_at: 2016-04-04 14:11:34.274025000 Z
|
||||
slug: decoupeuse-vinyle
|
||||
stp_product_id: prod_IZPyPShaaRgSML
|
||||
|
||||
machine_3:
|
||||
id: 3
|
||||
@ -54,6 +56,7 @@ machine_3:
|
||||
created_at: 2016-04-04 14:11:34.304247000 Z
|
||||
updated_at: 2016-04-04 14:11:34.304247000 Z
|
||||
slug: shopbot-grande-fraiseuse
|
||||
stp_product_id: prod_IZPyEjmdfMowhY
|
||||
|
||||
machine_4:
|
||||
id: 4
|
||||
@ -67,6 +70,7 @@ machine_4:
|
||||
created_at: 2001-01-01 14:11:34.341810000 Z
|
||||
updated_at: 2001-01-01 14:11:34.341810000 Z
|
||||
slug: imprimante-3d
|
||||
stp_product_id: prod_IZPy85vZOQpAo5
|
||||
|
||||
machine_5:
|
||||
id: 5
|
||||
@ -89,6 +93,7 @@ machine_5:
|
||||
created_at: 2016-04-04 14:11:34.379481000 Z
|
||||
updated_at: 2016-04-04 14:11:34.379481000 Z
|
||||
slug: petite-fraiseuse
|
||||
stp_product_id: prod_IZPyBJEgbcpWMC
|
||||
|
||||
machine_6:
|
||||
id: 6
|
||||
@ -123,3 +128,4 @@ machine_6:
|
||||
created_at: 2016-04-04 14:11:34.424740000 Z
|
||||
updated_at: 2016-04-04 14:11:34.424740000 Z
|
||||
slug: form1-imprimante-3d
|
||||
stp_product_id: prod_IZPyjCzvLmLWAz
|
||||
|
26
test/fixtures/plans.yml
vendored
26
test/fixtures/plans.yml
vendored
@ -15,6 +15,8 @@ plan_1:
|
||||
base_name: Mensuel
|
||||
ui_weight: 1
|
||||
interval_count: 1
|
||||
slug: mensuel
|
||||
stp_product_id: prod_IZPyXhfyNiGkWR
|
||||
|
||||
plan_2:
|
||||
id: 2
|
||||
@ -32,6 +34,8 @@ plan_2:
|
||||
base_name: Sleede
|
||||
ui_weight: 5
|
||||
interval_count: 2
|
||||
slug: sleede
|
||||
stp_product_id: prod_IZPykam7a4satn
|
||||
|
||||
plan_3:
|
||||
id: 3
|
||||
@ -49,4 +53,26 @@ plan_3:
|
||||
type: Plan
|
||||
base_name: Mensuel tarif réduit
|
||||
ui_weight: 0
|
||||
interval_count: 1*
|
||||
slug: mensuel-tarif-reduit
|
||||
stp_product_id: prod_IZPyM4N36h86G0
|
||||
|
||||
plan_schedulable:
|
||||
id: 4
|
||||
name: Abonnement mensualisable - standard, association, year
|
||||
amount: 113600
|
||||
interval: year
|
||||
group_id: 1
|
||||
stp_plan_id:
|
||||
created_at: 2020-12-14 14:10:11.056241000 Z
|
||||
updated_at: 2020-12-14 14:10:11.137421000 Z
|
||||
training_credit_nb: 1
|
||||
is_rolling: true
|
||||
description:
|
||||
type: Plan
|
||||
base_name: Abonnement mensualisable
|
||||
ui_weight: 10
|
||||
interval_count: 1
|
||||
monthly_payment: true
|
||||
slug: abonnement-mensualisable
|
||||
stp_product_id: prod_IZQAhb9nLu4jfN
|
||||
|
1
test/fixtures/spaces.yml
vendored
1
test/fixtures/spaces.yml
vendored
@ -7,3 +7,4 @@ space_1:
|
||||
created_at: 2017-02-15 15:55:04.123928000 Z
|
||||
updated_at: 2017-02-15 15:55:04.123928000 Z
|
||||
characteristics: Scie à chantourner, rabot, dégauchisseuse, chanfreineuse et pyrograveur
|
||||
stp_product_id: prod_IZPyHjIb2owoB8
|
||||
|
5
test/fixtures/trainings.yml
vendored
5
test/fixtures/trainings.yml
vendored
@ -7,6 +7,7 @@ training_1:
|
||||
nb_total_places:
|
||||
slug: formation-imprimante-3d
|
||||
description:
|
||||
stp_product_id: prod_IZPyXw6BDBBFOg
|
||||
|
||||
training_2:
|
||||
id: 2
|
||||
@ -16,6 +17,7 @@ training_2:
|
||||
nb_total_places:
|
||||
slug: formation-laser-vinyle
|
||||
description:
|
||||
stp_product_id: prod_IZPytTl1wSB5jH
|
||||
|
||||
training_3:
|
||||
id: 3
|
||||
@ -25,6 +27,7 @@ training_3:
|
||||
nb_total_places:
|
||||
slug: formation-petite-fraiseuse-numerique
|
||||
description:
|
||||
stp_product_id: prod_IZPyAA1A4QfEyL
|
||||
|
||||
training_4:
|
||||
id: 4
|
||||
@ -34,6 +37,7 @@ training_4:
|
||||
nb_total_places:
|
||||
slug: formation-shopbot-grande-fraiseuse
|
||||
description:
|
||||
stp_product_id: prod_IZPyU27NjDSmqB
|
||||
|
||||
training_5:
|
||||
id: 5
|
||||
@ -43,3 +47,4 @@ training_5:
|
||||
nb_total_places:
|
||||
slug: formation-logiciel-2d
|
||||
description:
|
||||
stp_product_id: prod_IZPyvdgQHMByB3
|
||||
|
@ -1,82 +1,471 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reservations
|
||||
class CreateAsAdminTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user_without_subscription = User.members.without_subscription.first
|
||||
@user_with_subscription = User.members.with_subscription.second
|
||||
@admin = User.with_role(:admin).first
|
||||
login_as(@admin, scope: :user)
|
||||
end
|
||||
require 'test_helper'
|
||||
|
||||
test 'user without subscription reserves a machine with success' do
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user_without_subscription = User.members.without_subscription.first
|
||||
@user_with_subscription = User.members.with_subscription.second
|
||||
@admin = User.with_role(:admin).first
|
||||
login_as(@admin, scope: :user)
|
||||
end
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @user_without_subscription.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal machine.prices.find_by(group_id: @user_without_subscription.group_id, plan_id: nil).amount, invoice_item.amount
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a training with success' do
|
||||
training = Training.first
|
||||
availability = training.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
test 'user without subscription reserves a machine with success' do
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @user_without_subscription.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal machine.prices.find_by(group_id: @user_without_subscription.group_id, plan_id: nil).amount, invoice_item.amount
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a training with success' do
|
||||
training = Training.first
|
||||
availability = training.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @user_without_subscription.id,
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
# invoice_items
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal invoice_item.amount, training.amount_by_group(@user_without_subscription.group_id).amount
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user with subscription reserves a machine with success' do
|
||||
plan = @user_with_subscription.subscribed_plan
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @user_with_subscription.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
},
|
||||
{
|
||||
start_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
end_at: (availability.start_at + 2.hours).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count + 1, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_with_subscription.subscriptions.count
|
||||
assert_not_nil @user_with_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_with_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_items = InvoiceItem.last(2)
|
||||
machine_price = machine.prices.find_by(group_id: @user_with_subscription.group_id, plan_id: plan.id).amount
|
||||
|
||||
assert(invoice_items.any? { |ii| ii.amount.zero? })
|
||||
assert(invoice_items.any? { |ii| ii.amount == machine_price })
|
||||
|
||||
# users_credits assertions
|
||||
users_credit = UsersCredit.last
|
||||
|
||||
assert_equal @user_with_subscription, users_credit.user
|
||||
assert_equal [reservation.slots.count, plan.machine_credits.find_by(creditable_id: machine.id).hours].min, users_credit.hours_used
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a machine and pay by wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @vlonchamp.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal machine.prices.find_by(group_id: @vlonchamp.group_id, plan_id: nil).amount, invoice_item.amount
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# wallet
|
||||
assert_equal @vlonchamp.wallet.amount, 0
|
||||
assert_equal @vlonchamp.wallet.wallet_transactions.count, 2
|
||||
transaction = @vlonchamp.wallet.wallet_transactions.last
|
||||
assert_equal transaction.transaction_type, 'debit'
|
||||
assert_equal transaction.amount, 10
|
||||
assert_equal transaction.amount, invoice.wallet_amount / 100.0
|
||||
assert_equal transaction.id, invoice.wallet_transaction_id
|
||||
end
|
||||
|
||||
test 'user reserves a machine and a subscription pay by wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
plan = Plan.find_by(group_id: @vlonchamp.group.id, type: 'Plan', base_name: 'Mensuel tarif réduit')
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @vlonchamp.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
plan_id: plan.id,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count + 1, UsersCredit.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @vlonchamp.subscriptions.count
|
||||
assert_not_nil @vlonchamp.subscribed_plan
|
||||
assert_equal plan.id, @vlonchamp.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert_equal invoice.total, 2000
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# wallet
|
||||
assert_equal @vlonchamp.wallet.amount, 0
|
||||
assert_equal @vlonchamp.wallet.wallet_transactions.count, 2
|
||||
transaction = @vlonchamp.wallet.wallet_transactions.last
|
||||
assert_equal transaction.transaction_type, 'debit'
|
||||
assert_equal transaction.amount, 10
|
||||
assert_equal transaction.amount, invoice.wallet_amount / 100.0
|
||||
assert_equal transaction.id, invoice.wallet_transaction_id
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a machine and pay wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @vlonchamp.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @vlonchamp.subscriptions.count
|
||||
assert_nil @vlonchamp.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert_not_nil reservation.invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user reserves a training and a subscription with success' do
|
||||
training = Training.first
|
||||
availability = training.availabilities.first
|
||||
plan = Plan.where(group_id: @user_without_subscription.group.id, type: 'Plan').first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @user_without_subscription.id,
|
||||
plan_id: plan.id,
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
offered: false,
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal Mime[:json], response.content_type
|
||||
result = json_response(response.body)
|
||||
|
||||
# Check the DB objects have been created as they should
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count + 1, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_without_subscription.subscriptions.count
|
||||
assert_not_nil @user_without_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_without_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.find(result[:id])
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# credits assertions
|
||||
assert_equal 1, @user_without_subscription.credits.count
|
||||
assert_equal 'Training', @user_without_subscription.credits.last.creditable_type
|
||||
assert_equal training.id, @user_without_subscription.credits.last.creditable_id
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert_equal plan.amount, invoice.total
|
||||
|
||||
# invoice_items
|
||||
invoice_items = InvoiceItem.last(2)
|
||||
|
||||
assert(invoice_items.any? { |ii| ii.amount == plan.amount && !ii.subscription_id.nil? })
|
||||
assert(invoice_items.any? { |ii| ii.amount.zero? })
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user reserves a training and a subscription with payment schedule' do
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
subscriptions_count = Subscription.count
|
||||
users_credit_count = UsersCredit.count
|
||||
payment_schedule_count = PaymentSchedule.count
|
||||
payment_schedule_items_count = PaymentScheduleItem.count
|
||||
|
||||
training = Training.find(1)
|
||||
availability = training.availabilities.first
|
||||
plan = Plan.find_by(group_id: @user_without_subscription.group.id, type: 'Plan', base_name: 'Abonnement mensualisable')
|
||||
|
||||
VCR.use_cassette('reservations_admin_training_subscription_with_payment_schedule') do
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @user_without_subscription.id,
|
||||
payment_method: '', # pay by check
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
slots_attributes: [
|
||||
@ -85,367 +474,31 @@ module Reservations
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
# invoice_items
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal invoice_item.amount, training.amount_by_group(@user_without_subscription.group_id).amount
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user with subscription reserves a machine with success' do
|
||||
plan = @user_with_subscription.subscribed_plan
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @user_with_subscription.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
},
|
||||
{
|
||||
start_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
end_at: (availability.start_at + 2.hours).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count + 1, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_with_subscription.subscriptions.count
|
||||
assert_not_nil @user_with_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_with_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_items = InvoiceItem.last(2)
|
||||
machine_price = machine.prices.find_by(group_id: @user_with_subscription.group_id, plan_id: plan.id).amount
|
||||
|
||||
assert(invoice_items.any? { |ii| ii.amount.zero? })
|
||||
assert(invoice_items.any? { |ii| ii.amount == machine_price })
|
||||
|
||||
# users_credits assertions
|
||||
users_credit = UsersCredit.last
|
||||
|
||||
assert_equal @user_with_subscription, users_credit.user
|
||||
assert_equal [reservation.slots.count, plan.machine_credits.find_by(creditable_id: machine.id).hours].min, users_credit.hours_used
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a machine and pay by wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @vlonchamp.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal machine.prices.find_by(group_id: @vlonchamp.group_id, plan_id: nil).amount, invoice_item.amount
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# wallet
|
||||
assert_equal @vlonchamp.wallet.amount, 0
|
||||
assert_equal @vlonchamp.wallet.wallet_transactions.count, 2
|
||||
transaction = @vlonchamp.wallet.wallet_transactions.last
|
||||
assert_equal transaction.transaction_type, 'debit'
|
||||
assert_equal transaction.amount, 10
|
||||
assert_equal transaction.amount, invoice.wallet_amount / 100.0
|
||||
assert_equal transaction.id, invoice.wallet_transaction_id
|
||||
end
|
||||
|
||||
test 'user reserves a machine and a subscription pay by wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
plan = Plan.find_by(group_id: @vlonchamp.group.id, type: 'Plan', base_name: 'Mensuel tarif réduit')
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @vlonchamp.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
],
|
||||
plan_id: plan.id,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
payment_schedule: true
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count + 1, UsersCredit.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @vlonchamp.subscriptions.count
|
||||
assert_not_nil @vlonchamp.subscribed_plan
|
||||
assert_equal plan.id, @vlonchamp.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert_equal invoice.total, 2000
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# wallet
|
||||
assert_equal @vlonchamp.wallet.amount, 0
|
||||
assert_equal @vlonchamp.wallet.wallet_transactions.count, 2
|
||||
transaction = @vlonchamp.wallet.wallet_transactions.last
|
||||
assert_equal transaction.transaction_type, 'debit'
|
||||
assert_equal transaction.amount, 10
|
||||
assert_equal transaction.amount, invoice.wallet_amount / 100.0
|
||||
assert_equal transaction.id, invoice.wallet_transaction_id
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a machine and pay wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
# Check response format & status
|
||||
assert_equal 201, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
assert_equal reservations_count + 1, Reservation.count, 'missing the reservation'
|
||||
assert_equal invoice_count, Invoice.count, "an invoice was generated but it shouldn't"
|
||||
assert_equal invoice_items_count, InvoiceItem.count, "some invoice items were generated but they shouldn't"
|
||||
assert_equal users_credit_count, UsersCredit.count, "user's credits count has changed but it shouldn't"
|
||||
assert_equal subscriptions_count + 1, Subscription.count, 'missing the subscription'
|
||||
assert_equal payment_schedule_count + 1, PaymentSchedule.count, 'missing the payment schedule'
|
||||
assert_equal payment_schedule_items_count + 12, PaymentScheduleItem.count, 'missing some payment schedule items'
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_without_subscription.subscriptions.count
|
||||
assert_not_nil @user_without_subscription.subscribed_plan, "user's subscribed plan was not found"
|
||||
assert_not_nil @user_without_subscription.subscription, "user's subscription was not found"
|
||||
assert_equal plan.id, @user_without_subscription.subscribed_plan.id, "user's plan does not match"
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @vlonchamp.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @vlonchamp.subscriptions.count
|
||||
assert_nil @vlonchamp.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert_not_nil reservation.invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user reserves a training and a subscription with success' do
|
||||
training = Training.first
|
||||
availability = training.availabilities.first
|
||||
plan = Plan.where(group_id: @user_without_subscription.group.id, type: 'Plan').first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
post reservations_path, params: { reservation: {
|
||||
user_id: @user_without_subscription.id,
|
||||
plan_id: plan.id,
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
offered: false,
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
} }.to_json, headers: default_headers
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal Mime[:json], response.content_type
|
||||
result = json_response(response.body)
|
||||
|
||||
# Check the DB objects have been created as they should
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count + 1, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_without_subscription.subscriptions.count
|
||||
assert_not_nil @user_without_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_without_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.find(result[:id])
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# credits assertions
|
||||
assert_equal 1, @user_without_subscription.credits.count
|
||||
assert_equal 'Training', @user_without_subscription.credits.last.creditable_type
|
||||
assert_equal training.id, @user_without_subscription.credits.last.creditable_id
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert_equal plan.amount, invoice.total
|
||||
|
||||
# invoice_items
|
||||
invoice_items = InvoiceItem.last(2)
|
||||
|
||||
assert(invoice_items.any? { |ii| ii.amount == plan.amount && !ii.subscription_id.nil? })
|
||||
assert(invoice_items.any? { |ii| ii.amount.zero? })
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
# Check the answer
|
||||
reservation = json_response(response.body)
|
||||
assert_equal plan.id, reservation[:user][:subscribed_plan][:id], 'subscribed plan does not match'
|
||||
end
|
||||
end
|
||||
|
@ -2,299 +2,147 @@
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module Reservations
|
||||
class CreateTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user_without_subscription = User.members.without_subscription.first
|
||||
@user_with_subscription = User.members.with_subscription.second
|
||||
end
|
||||
class Reservations::CreateTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user_without_subscription = User.members.without_subscription.first
|
||||
@user_with_subscription = User.members.with_subscription.second
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a machine with success' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
test 'user without subscription reserves a machine with success' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
subscriptions_count = Subscription.count
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
subscriptions_count = Subscription.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_machine_without_subscription_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
VCR.use_cassette('reservations_create_for_machine_without_subscription_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal subscriptions_count, Subscription.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal machine.prices.find_by(group_id: @user_without_subscription.group_id, plan_id: nil).amount, invoice_item.amount
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a machine with error' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal subscriptions_count, Subscription.count
|
||||
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
notifications_count = Notification.count
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
VCR.use_cassette('reservations_create_for_machine_without_subscription_error') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method(error: :card_declined),
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal machine.prices.find_by(group_id: @user_without_subscription.group_id, plan_id: nil).amount, invoice_item.amount
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a machine with error' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
notifications_count = Notification.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_machine_without_subscription_error') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method(error: :card_declined),
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# Check response format & status
|
||||
assert_equal 200, response.status, "API does not return the expected status. #{response.body}"
|
||||
assert_equal Mime[:json], response.content_type
|
||||
|
||||
# Check the error was handled
|
||||
assert_match /Your card was declined/, response.body
|
||||
|
||||
# Check the subscription wasn't taken
|
||||
assert_equal reservations_count, Reservation.count
|
||||
assert_equal invoice_count, Invoice.count
|
||||
assert_equal invoice_items_count, InvoiceItem.count
|
||||
assert_equal notifications_count, Notification.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
test 'user without subscription reserves a training with success' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
# Check response format & status
|
||||
assert_equal 200, response.status, "API does not return the expected status. #{response.body}"
|
||||
assert_equal Mime[:json], response.content_type
|
||||
|
||||
training = Training.first
|
||||
availability = training.availabilities.first
|
||||
# Check the error was handled
|
||||
assert_match /Your card was declined/, response.body
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
# Check the subscription wasn't taken
|
||||
assert_equal reservations_count, Reservation.count
|
||||
assert_equal invoice_count, Invoice.count
|
||||
assert_equal invoice_items_count, InvoiceItem.count
|
||||
assert_equal notifications_count, Notification.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_training_without_subscription_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: availability.end_at.to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
end
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
test 'user without subscription reserves a training with success' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
training = Training.first
|
||||
availability = training.availabilities.first
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal invoice_item.amount, training.amount_by_group(@user_without_subscription.group_id).amount
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user with subscription reserves a machine with success' do
|
||||
login_as(@user_with_subscription, scope: :user)
|
||||
|
||||
plan = @user_with_subscription.subscribed_plan
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_machine_with_subscription_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
},
|
||||
{
|
||||
start_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
end_at: (availability.start_at + 2.hours).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count + 1, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_with_subscription.subscriptions.count
|
||||
assert_not_nil @user_with_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_with_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_items = InvoiceItem.last(2)
|
||||
machine_price = machine.prices.find_by(group_id: @user_with_subscription.group_id, plan_id: plan.id).amount
|
||||
|
||||
assert(invoice_items.any? { |inv| inv.amount.zero? })
|
||||
assert(invoice_items.any? { |inv| inv.amount == machine_price })
|
||||
assert(invoice_items.all?(&:check_footprint))
|
||||
|
||||
# users_credits assertions
|
||||
users_credit = UsersCredit.last
|
||||
|
||||
assert_equal @user_with_subscription, users_credit.user
|
||||
assert_equal [reservation.slots.count, plan.machine_credits.find_by(creditable_id: machine.id).hours].min, users_credit.hours_used
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user with subscription reserves the FIRST training with success' do
|
||||
login_as(@user_with_subscription, scope: :user)
|
||||
plan = @user_with_subscription.subscribed_plan
|
||||
plan.update!(is_rolling: true)
|
||||
|
||||
training = Training.joins(credits: :plan).where(credits: { plan: plan }).first
|
||||
availability = training.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_training_with_subscription_success') do
|
||||
post '/api/reservations',
|
||||
params: {
|
||||
VCR.use_cassette('reservations_create_for_training_without_subscription_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
@ -306,349 +154,584 @@ module Reservations
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_with_subscription.subscriptions.count
|
||||
assert_not_nil @user_with_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_with_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal 0, invoice_item.amount # amount is 0 because this training is a credited training with that plan
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# check that user subscription were extended
|
||||
assert_equal reservation.slots.first.start_at + plan.duration, @user_with_subscription.subscription.expired_at
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
test 'user reserves a machine and pay by wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
login_as(@vlonchamp, scope: :user)
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
VCR.use_cassette('reservations_create_for_machine_and_pay_wallet_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
user_id: @vlonchamp.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
card_token: stripe_payment_method,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal invoice_item.amount, training.amount_by_group(@user_without_subscription.group_id).amount
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user with subscription reserves a machine with success' do
|
||||
login_as(@user_with_subscription, scope: :user)
|
||||
|
||||
plan = @user_with_subscription.subscribed_plan
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_machine_with_subscription_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
},
|
||||
{
|
||||
start_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
end_at: (availability.start_at + 2.hours).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count + 1, UsersCredit.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_with_subscription.subscriptions.count
|
||||
assert_not_nil @user_with_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_with_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_items = InvoiceItem.last(2)
|
||||
machine_price = machine.prices.find_by(group_id: @user_with_subscription.group_id, plan_id: plan.id).amount
|
||||
|
||||
assert(invoice_items.any? { |inv| inv.amount.zero? })
|
||||
assert(invoice_items.any? { |inv| inv.amount == machine_price })
|
||||
assert(invoice_items.all?(&:check_footprint))
|
||||
|
||||
# users_credits assertions
|
||||
users_credit = UsersCredit.last
|
||||
|
||||
assert_equal @user_with_subscription, users_credit.user
|
||||
assert_equal [reservation.slots.count, plan.machine_credits.find_by(creditable_id: machine.id).hours].min, users_credit.hours_used
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
end
|
||||
|
||||
test 'user with subscription reserves the FIRST training with success' do
|
||||
login_as(@user_with_subscription, scope: :user)
|
||||
plan = @user_with_subscription.subscribed_plan
|
||||
plan.update!(is_rolling: true)
|
||||
|
||||
training = Training.joins(credits: :plan).where(credits: { plan: plan }).first
|
||||
availability = training.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_training_with_subscription_success') do
|
||||
post '/api/reservations',
|
||||
params: {
|
||||
reservation: {
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: availability.end_at.to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
@vlonchamp.wallet.reload
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @vlonchamp.subscriptions.count
|
||||
assert_nil @vlonchamp.subscribed_plan
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal machine.prices.find_by(group_id: @vlonchamp.group_id, plan_id: nil).amount, invoice_item.amount
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# wallet
|
||||
assert_equal 0, @vlonchamp.wallet.amount
|
||||
assert_equal 2, @vlonchamp.wallet.wallet_transactions.count
|
||||
transaction = @vlonchamp.wallet.wallet_transactions.last
|
||||
assert_equal 'debit', transaction.transaction_type
|
||||
assert_equal 10, transaction.amount
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
test 'user reserves a training and a subscription by wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
login_as(@vlonchamp, scope: :user)
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
|
||||
training = Training.first
|
||||
availability = training.availabilities.first
|
||||
plan = Plan.find_by(group_id: @vlonchamp.group.id, type: 'Plan', base_name: 'Mensuel tarif réduit')
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_with_subscription.subscriptions.count
|
||||
assert_not_nil @user_with_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_with_subscription.subscribed_plan.id
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
VCR.use_cassette('reservations_create_for_training_and_plan_by_pay_wallet_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
plan_id: plan.id,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: availability.end_at.to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
assert invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal 0, invoice_item.amount # amount is 0 because this training is a credited training with that plan
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# check that user subscription were extended
|
||||
assert_equal reservation.slots.first.start_at + plan.duration, @user_with_subscription.subscription.expired_at
|
||||
end
|
||||
|
||||
test 'user reserves a machine and pay by wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
login_as(@vlonchamp, scope: :user)
|
||||
|
||||
machine = Machine.find(6)
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_machine_and_pay_wallet_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
user_id: @vlonchamp.id,
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
card_token: stripe_payment_method,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
@vlonchamp.wallet.reload
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @vlonchamp.subscriptions.count
|
||||
assert_not_nil @vlonchamp.subscribed_plan
|
||||
assert_equal plan.id, @vlonchamp.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert_equal invoice.total, 2000
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# wallet
|
||||
assert_equal 0, @vlonchamp.wallet.amount
|
||||
assert_equal 2, @vlonchamp.wallet.wallet_transactions.count
|
||||
transaction = @vlonchamp.wallet.wallet_transactions.last
|
||||
assert_equal 'debit', transaction.transaction_type
|
||||
assert_equal 10, transaction.amount
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
test 'user reserves a machine and a subscription using a coupon with success' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
@vlonchamp.wallet.reload
|
||||
|
||||
machine = Machine.find(6)
|
||||
plan = Plan.where(group_id: @user_without_subscription.group_id).first
|
||||
availability = machine.availabilities.first
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 1, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
subscriptions_count = Subscription.count
|
||||
users_credit_count = UsersCredit.count
|
||||
# subscription assertions
|
||||
assert_equal 0, @vlonchamp.subscriptions.count
|
||||
assert_nil @vlonchamp.subscribed_plan
|
||||
|
||||
VCR.use_cassette('reservations_machine_and_plan_using_coupon_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
],
|
||||
plan_id: plan.id
|
||||
},
|
||||
coupon_code: 'SUNNYFABLAB'
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 1, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert_equal machine.prices.find_by(group_id: @vlonchamp.group_id, plan_id: nil).amount, invoice_item.amount
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# wallet
|
||||
assert_equal 0, @vlonchamp.wallet.amount
|
||||
assert_equal 2, @vlonchamp.wallet.wallet_transactions.count
|
||||
transaction = @vlonchamp.wallet.wallet_transactions.last
|
||||
assert_equal 'debit', transaction.transaction_type
|
||||
assert_equal 10, transaction.amount
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
end
|
||||
|
||||
test 'user reserves a training and a subscription by wallet with success' do
|
||||
@vlonchamp = User.find_by(username: 'vlonchamp')
|
||||
login_as(@vlonchamp, scope: :user)
|
||||
|
||||
training = Training.first
|
||||
availability = training.availabilities.first
|
||||
plan = Plan.find_by(group_id: @vlonchamp.group.id, type: 'Plan', base_name: 'Mensuel tarif réduit')
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
VCR.use_cassette('reservations_create_for_training_and_plan_by_pay_wallet_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
plan_id: plan.id,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: availability.end_at.to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal subscriptions_count + 1, Subscription.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_without_subscription.subscriptions.count
|
||||
assert_not_nil @user_without_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_without_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items assertions
|
||||
## reservation
|
||||
reservation_item = invoice.invoice_items.where(subscription_id: nil).first
|
||||
|
||||
assert_not_nil reservation_item
|
||||
assert_equal reservation_item.amount, machine.prices.find_by(group_id: @user_without_subscription.group_id, plan_id: plan.id).amount
|
||||
assert reservation_item.check_footprint
|
||||
## subscription
|
||||
subscription_item = invoice.invoice_items.where.not(subscription_id: nil).first
|
||||
|
||||
assert_not_nil subscription_item
|
||||
|
||||
subscription = Subscription.find(subscription_item.subscription_id)
|
||||
|
||||
assert_equal subscription_item.amount, plan.amount
|
||||
assert_equal subscription.plan_id, plan.id
|
||||
assert subscription_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
VCR.use_cassette('reservations_machine_and_plan_using_coupon_retrieve_invoice_from_stripe') do
|
||||
stp_intent = Stripe::PaymentIntent.retrieve(invoice.stp_payment_intent_id, api_key: Setting.get('stripe_secret_key'))
|
||||
assert_equal stp_intent.amount, invoice.total
|
||||
end
|
||||
|
||||
# notifications
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
assert_not_empty Notification.where(attached_object: subscription)
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
test 'user reserves a training with an expired coupon with error' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
@vlonchamp.wallet.reload
|
||||
|
||||
training = Training.find(1)
|
||||
availability = training.availabilities.first
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
notifications_count = Notification.count
|
||||
# subscription assertions
|
||||
assert_equal 1, @vlonchamp.subscriptions.count
|
||||
assert_not_nil @vlonchamp.subscribed_plan
|
||||
assert_equal plan.id, @vlonchamp.subscribed_plan.id
|
||||
|
||||
VCR.use_cassette('reservations_training_with_expired_coupon_error') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
user_id: @user_without_subscription.id,
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
card_token: stripe_payment_method,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
},
|
||||
coupon_code: 'XMAS10'
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
# general assertions
|
||||
assert_equal 422, response.status
|
||||
assert_equal reservations_count, Reservation.count
|
||||
assert_equal invoice_count, Invoice.count
|
||||
assert_equal invoice_items_count, InvoiceItem.count
|
||||
assert_equal notifications_count, Notification.count
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert_equal invoice.total, 2000
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
|
||||
# wallet
|
||||
assert_equal 0, @vlonchamp.wallet.amount
|
||||
assert_equal 2, @vlonchamp.wallet.wallet_transactions.count
|
||||
transaction = @vlonchamp.wallet.wallet_transactions.last
|
||||
assert_equal 'debit', transaction.transaction_type
|
||||
assert_equal 10, transaction.amount
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
end
|
||||
|
||||
test 'user reserves a machine and a subscription using a coupon with success' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
|
||||
machine = Machine.find(6)
|
||||
plan = Plan.where(group_id: @user_without_subscription.group_id).first
|
||||
availability = machine.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
subscriptions_count = Subscription.count
|
||||
users_credit_count = UsersCredit.count
|
||||
|
||||
VCR.use_cassette('reservations_machine_and_plan_using_coupon_success') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: machine.id,
|
||||
reservable_type: machine.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
],
|
||||
plan_id: plan.id
|
||||
},
|
||||
coupon_code: 'SUNNYFABLAB'
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# general assertions
|
||||
assert_equal 201, response.status
|
||||
assert_equal reservations_count + 1, Reservation.count
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal subscriptions_count + 1, Subscription.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_without_subscription.subscriptions.count
|
||||
assert_not_nil @user_without_subscription.subscribed_plan
|
||||
assert_equal plan.id, @user_without_subscription.subscribed_plan.id
|
||||
|
||||
# reservation assertions
|
||||
reservation = Reservation.last
|
||||
|
||||
assert reservation.invoice
|
||||
assert_equal 2, reservation.invoice.invoice_items.count
|
||||
|
||||
# invoice assertions
|
||||
invoice = reservation.invoice
|
||||
|
||||
refute invoice.stp_payment_intent_id.blank?
|
||||
refute invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# invoice_items assertions
|
||||
## reservation
|
||||
reservation_item = invoice.invoice_items.where(subscription_id: nil).first
|
||||
|
||||
assert_not_nil reservation_item
|
||||
assert_equal reservation_item.amount, machine.prices.find_by(group_id: @user_without_subscription.group_id, plan_id: plan.id).amount
|
||||
assert reservation_item.check_footprint
|
||||
## subscription
|
||||
subscription_item = invoice.invoice_items.where.not(subscription_id: nil).first
|
||||
|
||||
assert_not_nil subscription_item
|
||||
|
||||
subscription = Subscription.find(subscription_item.subscription_id)
|
||||
|
||||
assert_equal subscription_item.amount, plan.amount
|
||||
assert_equal subscription.plan_id, plan.id
|
||||
assert subscription_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.find_by(invoiced: reservation)
|
||||
assert_invoice_pdf invoice
|
||||
|
||||
VCR.use_cassette('reservations_machine_and_plan_using_coupon_retrieve_invoice_from_stripe') do
|
||||
stp_intent = Stripe::PaymentIntent.retrieve(invoice.stp_payment_intent_id, api_key: Setting.get('stripe_secret_key'))
|
||||
assert_equal stp_intent.amount, invoice.total
|
||||
end
|
||||
|
||||
# notifications
|
||||
assert_not_empty Notification.where(attached_object: reservation)
|
||||
assert_not_empty Notification.where(attached_object: subscription)
|
||||
end
|
||||
|
||||
test 'user reserves a training with an expired coupon with error' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
|
||||
training = Training.find(1)
|
||||
availability = training.availabilities.first
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
notifications_count = Notification.count
|
||||
|
||||
VCR.use_cassette('reservations_training_with_expired_coupon_error') do
|
||||
post '/api/payments/confirm_payment',
|
||||
params: {
|
||||
payment_method_id: stripe_payment_method,
|
||||
cart_items: {
|
||||
reservation: {
|
||||
user_id: @user_without_subscription.id,
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
card_token: stripe_payment_method,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
]
|
||||
},
|
||||
coupon_code: 'XMAS10'
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# general assertions
|
||||
assert_equal 422, response.status
|
||||
assert_equal reservations_count, Reservation.count
|
||||
assert_equal invoice_count, Invoice.count
|
||||
assert_equal invoice_items_count, InvoiceItem.count
|
||||
assert_equal notifications_count, Notification.count
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 0, @user_without_subscription.subscriptions.count
|
||||
assert_nil @user_without_subscription.subscribed_plan
|
||||
end
|
||||
|
||||
|
||||
test 'user reserves a training and a subscription with payment schedule' do
|
||||
login_as(@user_without_subscription, scope: :user)
|
||||
|
||||
reservations_count = Reservation.count
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
subscriptions_count = Subscription.count
|
||||
users_credit_count = UsersCredit.count
|
||||
payment_schedule_count = PaymentSchedule.count
|
||||
payment_schedule_items_count = PaymentScheduleItem.count
|
||||
|
||||
training = Training.find(1)
|
||||
availability = training.availabilities.first
|
||||
plan = Plan.find_by(group_id: @user_without_subscription.group.id, type: 'Plan', base_name: 'Abonnement mensualisable')
|
||||
|
||||
VCR.use_cassette('reservations_training_subscription_with_payment_schedule') do
|
||||
get "/api/payments/setup_intent/#{@user_without_subscription.id}"
|
||||
|
||||
# Check response format & status
|
||||
assert_equal 200, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
|
||||
# Check the response
|
||||
setup_intent = json_response(response.body)
|
||||
assert_not_nil setup_intent[:client_secret]
|
||||
assert_not_nil setup_intent[:id]
|
||||
assert_match /^#{setup_intent[:id]}_secret_/, setup_intent[:client_secret]
|
||||
|
||||
# Confirm the intent
|
||||
stripe_res = Stripe::SetupIntent.confirm(
|
||||
setup_intent[:id],
|
||||
{ payment_method: stripe_payment_method },
|
||||
{ api_key: Setting.get('stripe_secret_key') }
|
||||
)
|
||||
|
||||
# check the confirmation
|
||||
assert_equal setup_intent[:id], stripe_res.id
|
||||
assert_equal 'succeeded', stripe_res.status
|
||||
assert_equal 'off_session', stripe_res.usage
|
||||
|
||||
|
||||
post '/api/payments/confirm_payment_schedule',
|
||||
params: {
|
||||
setup_intent_id: setup_intent[:id],
|
||||
cart_items: {
|
||||
reservation: {
|
||||
reservable_id: training.id,
|
||||
reservable_type: training.class.name,
|
||||
slots_attributes: [
|
||||
{
|
||||
start_at: availability.start_at.to_s(:iso8601),
|
||||
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
|
||||
availability_id: availability.id
|
||||
}
|
||||
],
|
||||
plan_id: plan.id,
|
||||
payment_schedule: true
|
||||
}
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# Check response format & status
|
||||
assert_equal 201, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
assert_equal reservations_count + 1, Reservation.count, 'missing the reservation'
|
||||
assert_equal invoice_count, Invoice.count, "an invoice was generated but it shouldn't"
|
||||
assert_equal invoice_items_count, InvoiceItem.count, "some invoice items were generated but they shouldn't"
|
||||
assert_equal users_credit_count, UsersCredit.count, "user's credits count has changed but it shouldn't"
|
||||
assert_equal subscriptions_count + 1, Subscription.count, 'missing the subscription'
|
||||
assert_equal payment_schedule_count + 1, PaymentSchedule.count, 'missing the payment schedule'
|
||||
assert_equal payment_schedule_items_count + 12, PaymentScheduleItem.count, 'missing some payment schedule items'
|
||||
|
||||
# subscription assertions
|
||||
assert_equal 1, @user_without_subscription.subscriptions.count
|
||||
assert_not_nil @user_without_subscription.subscribed_plan, "user's subscribed plan was not found"
|
||||
assert_not_nil @user_without_subscription.subscription, "user's subscription was not found"
|
||||
assert_equal plan.id, @user_without_subscription.subscribed_plan.id, "user's plan does not match"
|
||||
|
||||
# Check the answer
|
||||
reservation = json_response(response.body)
|
||||
assert_equal plan.id, reservation[:user][:subscribed_plan][:id], 'subscribed plan does not match'
|
||||
end
|
||||
end
|
||||
|
@ -1,59 +1,123 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Subscriptions
|
||||
class CreateAsAdminTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = User.find_by(username: 'admin')
|
||||
login_as(@admin, scope: :user)
|
||||
require 'test_helper'
|
||||
|
||||
class Subscriptions::CreateAsAdminTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = User.find_by(username: 'admin')
|
||||
login_as(@admin, scope: :user)
|
||||
end
|
||||
|
||||
test 'admin successfully takes a subscription for a user' do
|
||||
user = User.find_by(username: 'jdupond')
|
||||
plan = Plan.find_by(group_id: user.group.id, type: 'Plan', base_name: 'Mensuel')
|
||||
|
||||
VCR.use_cassette('subscriptions_admin_create_success') do
|
||||
post '/api/subscriptions',
|
||||
params: {
|
||||
subscription: {
|
||||
plan_id: plan.id,
|
||||
user_id: user.id
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
test 'admin successfully takes a subscription for a user' do
|
||||
user = User.find_by(username: 'jdupond')
|
||||
plan = Plan.find_by(group_id: user.group.id, type: 'Plan', base_name: 'Mensuel')
|
||||
# Check response format & status
|
||||
assert_equal 201, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
|
||||
VCR.use_cassette('subscriptions_admin_create_success') do
|
||||
post '/api/subscriptions',
|
||||
params: {
|
||||
subscription: {
|
||||
plan_id: plan.id,
|
||||
user_id: user.id
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
# Check the correct plan was subscribed
|
||||
subscription = json_response(response.body)
|
||||
assert_equal plan.id, subscription[:plan_id], 'subscribed plan does not match'
|
||||
|
||||
# Check that the user has only one subscription
|
||||
assert_equal 1, user.subscriptions.count
|
||||
|
||||
# Check that the user has the correct subscription
|
||||
assert_not_nil user.subscription, "user's subscription was not found"
|
||||
assert_not_nil user.subscription.plan, "user's subscribed plan was not found"
|
||||
assert_equal plan.id, user.subscription.plan_id, "user's plan does not match"
|
||||
|
||||
# Check that the training credits were set correctly
|
||||
assert_empty user.training_credits, 'training credits were not reset'
|
||||
assert_equal user.subscription.plan.training_credit_nb, plan.training_credit_nb, 'trainings credits were not allocated'
|
||||
|
||||
# Check that the user benefit from prices of his plan
|
||||
printer = Machine.find_by(slug: 'imprimante-3d')
|
||||
assert_equal 15, (printer.prices.find_by(group_id: user.group_id, plan_id: user.subscription.plan_id).amount / 100.00), 'machine hourly price does not match'
|
||||
|
||||
# Check notification was sent to the user
|
||||
notification = Notification.find_by(notification_type_id: NotificationType.find_by_name('notify_member_subscribed_plan'), attached_object_type: 'Subscription', attached_object_id: subscription[:id])
|
||||
assert_not_nil notification, 'user notification was not created'
|
||||
assert_equal user.id, notification.receiver_id, 'wrong user notified'
|
||||
|
||||
# Check generated invoice
|
||||
invoice = Invoice.find_by(invoiced_type: 'Subscription', invoiced_id: subscription[:id])
|
||||
assert_invoice_pdf invoice
|
||||
assert_equal plan.amount, invoice.total, 'Invoice total price does not match the bought subscription'
|
||||
end
|
||||
|
||||
test 'admin takes a subscription with a payment schedule' do
|
||||
user = User.find_by(username: 'jdupond')
|
||||
plan = Plan.find_by(group_id: user.group.id, type: 'Plan', base_name: 'Abonnement mensualisable')
|
||||
invoice_count = Invoice.count
|
||||
payment_schedule_count = PaymentSchedule.count
|
||||
payment_schedule_items_count = PaymentScheduleItem.count
|
||||
|
||||
VCR.use_cassette('subscriptions_admin_create_with_payment_schedule') do
|
||||
get "/api/payments/setup_intent/#{user.id}"
|
||||
|
||||
# Check response format & status
|
||||
assert_equal 201, response.status, response.body
|
||||
assert_equal 200, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
|
||||
# Check the correct plan was subscribed
|
||||
subscription = json_response(response.body)
|
||||
assert_equal plan.id, subscription[:plan_id], 'subscribed plan does not match'
|
||||
# Check the response
|
||||
setup_intent = json_response(response.body)
|
||||
assert_not_nil setup_intent[:client_secret]
|
||||
assert_not_nil setup_intent[:id]
|
||||
assert_match /^#{setup_intent[:id]}_secret_/, setup_intent[:client_secret]
|
||||
|
||||
# Check that the user has only one subscription
|
||||
assert_equal 1, user.subscriptions.count
|
||||
# Confirm the intent
|
||||
stripe_res = Stripe::SetupIntent.confirm(
|
||||
setup_intent[:id],
|
||||
{ payment_method: stripe_payment_method },
|
||||
{ api_key: Setting.get('stripe_secret_key') }
|
||||
)
|
||||
|
||||
# Check that the user has the correct subscription
|
||||
assert_not_nil user.subscription, "user's subscription was not found"
|
||||
assert_not_nil user.subscription.plan, "user's subscribed plan was not found"
|
||||
assert_equal plan.id, user.subscription.plan_id, "user's plan does not match"
|
||||
# check the confirmation
|
||||
assert_equal setup_intent[:id], stripe_res.id
|
||||
assert_equal 'succeeded', stripe_res.status
|
||||
assert_equal 'off_session', stripe_res.usage
|
||||
|
||||
# Check that the training credits were set correctly
|
||||
assert_empty user.training_credits, 'training credits were not reset'
|
||||
assert_equal user.subscription.plan.training_credit_nb, plan.training_credit_nb, 'trainings credits were not allocated'
|
||||
|
||||
# Check that the user benefit from prices of his plan
|
||||
printer = Machine.find_by(slug: 'imprimante-3d')
|
||||
assert_equal 15, (printer.prices.find_by(group_id: user.group_id, plan_id: user.subscription.plan_id).amount / 100.00), 'machine hourly price does not match'
|
||||
|
||||
# Check notification was sent to the user
|
||||
notification = Notification.find_by(notification_type_id: NotificationType.find_by_name('notify_member_subscribed_plan'), attached_object_type: 'Subscription', attached_object_id: subscription[:id])
|
||||
assert_not_nil notification, 'user notification was not created'
|
||||
assert_equal user.id, notification.receiver_id, 'wrong user notified'
|
||||
|
||||
# Check generated invoice
|
||||
invoice = Invoice.find_by(invoiced_type: 'Subscription', invoiced_id: subscription[:id])
|
||||
assert_invoice_pdf invoice
|
||||
assert_equal plan.amount, invoice.total, 'Invoice total price does not match the bought subscription'
|
||||
post '/api/payments/confirm_payment_schedule',
|
||||
params: {
|
||||
setup_intent_id: setup_intent[:id],
|
||||
cart_items: {
|
||||
subscription: {
|
||||
plan_id: plan.id,
|
||||
payment_schedule: true,
|
||||
user_id: user.id,
|
||||
payment_method: 'stripe'
|
||||
}
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# Check generalities
|
||||
assert_equal 201, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
assert_equal invoice_count, Invoice.count, "an invoice was generated but it shouldn't"
|
||||
assert_equal payment_schedule_count + 1, PaymentSchedule.count, 'missing the payment schedule'
|
||||
assert_equal payment_schedule_items_count + 12, PaymentScheduleItem.count, 'missing some payment schedule items'
|
||||
|
||||
# Check the correct plan was subscribed
|
||||
subscription = json_response(response.body)
|
||||
assert_equal plan.id, subscription[:plan_id], 'subscribed plan does not match'
|
||||
|
||||
# Check that the user has the correct subscription
|
||||
assert_not_nil user.subscription, "user's subscription was not found"
|
||||
assert_not_nil user.subscription.plan, "user's subscribed plan was not found"
|
||||
assert_equal plan.id, user.subscription.plan_id, "user's plan does not match"
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class Subscriptions::CreateAsUserTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user = User.find_by(username: 'jdupond')
|
||||
@ -166,4 +168,63 @@ class Subscriptions::CreateAsUserTest < ActionDispatch::IntegrationTest
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
assert_equal invoice.wallet_transaction_id, transaction.id
|
||||
end
|
||||
|
||||
test 'user takes a subscription with payment schedule' do
|
||||
plan = Plan.find_by(group_id: @user.group.id, type: 'Plan', base_name: 'Abonnement mensualisable')
|
||||
payment_schedule_count = PaymentSchedule.count
|
||||
payment_schedule_items_count = PaymentScheduleItem.count
|
||||
|
||||
VCR.use_cassette('subscriptions_user_create_with_payment_schedule') do
|
||||
get "/api/payments/setup_intent/#{@user.id}"
|
||||
|
||||
# Check response format & status
|
||||
assert_equal 200, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
|
||||
# Check the response
|
||||
setup_intent = json_response(response.body)
|
||||
assert_not_nil setup_intent[:client_secret]
|
||||
assert_not_nil setup_intent[:id]
|
||||
assert_match /^#{setup_intent[:id]}_secret_/, setup_intent[:client_secret]
|
||||
|
||||
# Confirm the intent
|
||||
stripe_res = Stripe::SetupIntent.confirm(
|
||||
setup_intent[:id],
|
||||
{ payment_method: stripe_payment_method },
|
||||
{ api_key: Setting.get('stripe_secret_key') }
|
||||
)
|
||||
|
||||
# check the confirmation
|
||||
assert_equal setup_intent[:id], stripe_res.id
|
||||
assert_equal 'succeeded', stripe_res.status
|
||||
assert_equal 'off_session', stripe_res.usage
|
||||
|
||||
|
||||
post '/api/payments/confirm_payment_schedule',
|
||||
params: {
|
||||
setup_intent_id: setup_intent[:id],
|
||||
cart_items: {
|
||||
subscription: {
|
||||
plan_id: plan.id,
|
||||
payment_schedule: true
|
||||
}
|
||||
}
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
# Check generalities
|
||||
assert_equal 201, response.status, response.body
|
||||
assert_equal Mime[:json], response.content_type
|
||||
assert_equal payment_schedule_count + 1, PaymentSchedule.count, 'missing the payment schedule'
|
||||
assert_equal payment_schedule_items_count + 12, PaymentScheduleItem.count, 'missing some payment schedule items'
|
||||
|
||||
# Check the correct plan was subscribed
|
||||
subscription = json_response(response.body)
|
||||
assert_equal plan.id, subscription[:plan_id], 'subscribed plan does not match'
|
||||
|
||||
# Check that the user has the correct subscription
|
||||
assert_not_nil @user.subscription, "user's subscription was not found"
|
||||
assert_not_nil @user.subscription.plan, "user's subscribed plan was not found"
|
||||
assert_equal plan.id, @user.subscription.plan_id, "user's plan does not match"
|
||||
end
|
||||
end
|
||||
|
@ -4,7 +4,7 @@ require 'coveralls'
|
||||
Coveralls.wear!('rails')
|
||||
|
||||
ENV['RAILS_ENV'] ||= 'test'
|
||||
require File.expand_path('../config/environment', __dir__)
|
||||
require_relative '../config/environment'
|
||||
require 'action_dispatch'
|
||||
require 'rails/test_help'
|
||||
require 'vcr'
|
||||
@ -23,7 +23,7 @@ Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(color: true)]
|
||||
|
||||
class ActiveSupport::TestCase
|
||||
# Add more helper methods to be used by all tests here...
|
||||
|
||||
ActiveRecord::Migration.check_pending!
|
||||
fixtures :all
|
||||
|
||||
def json_response(body)
|
||||
|
Loading…
Reference in New Issue
Block a user