1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-29 18:52:22 +01:00

WIP: move backend code base to new architecture

- from stripe only to payment gateway abstract
- from reservation params style to unified cart_items

TODO: fix not passing tests,
subscriptions/renew_as_admin_test.rb fails because InvoiceItem(9).amount = nil
This commit is contained in:
Sylvain 2021-04-23 17:54:59 +02:00
parent 94cbcd3236
commit bc6ef8ef50
46 changed files with 550 additions and 500 deletions

View File

@ -55,13 +55,13 @@ class API::PaymentSchedulesController < API::ApiController
authorize @payment_schedule_item.payment_schedule
# FIXME
stripe_key = Setting.get('stripe_secret_key')
stp_invoice = Stripe::Invoice.pay(@payment_schedule_item.stp_invoice_id, {}, { api_key: stripe_key })
stp_invoice = Stripe::Invoice.pay(@payment_schedule_item.payment_gateway_object.gateway_object_id, {}, { api_key: stripe_key })
PaymentScheduleItemWorker.new.perform(@payment_schedule_item.id)
render json: { status: stp_invoice.status }, status: :ok
rescue Stripe::StripeError => e
stripe_key = Setting.get('stripe_secret_key')
stp_invoice = Stripe::Invoice.retrieve(@payment_schedule_item.stp_invoice_id, api_key: stripe_key)
stp_invoice = Stripe::Invoice.retrieve(@payment_schedule_item.payment_gateway_object.gateway_object_id, api_key: stripe_key)
PaymentScheduleItemWorker.new.perform(@payment_schedule_item.id)
render json: { status: stp_invoice.status, error: e }, status: :unprocessable_entity

View File

@ -28,8 +28,8 @@ class API::ReservationsController < API::ApiController
# otherwise, they must use payments_controller#confirm_payment.
# Managers can create reservations for other users
def create
user_id = current_user.admin? || current_user.manager? ? params[:reservation][:user_id] : current_user.id
price = transaction_amount(current_user.admin? || (current_user.manager? && current_user.id != user_id), user_id)
user_id = current_user.admin? || current_user.manager? ? params[:customer_id] : current_user.id
price = transaction_amount(user_id)
authorize ReservationContext.new(Reservation, price[:amount], user_id)
@ -62,7 +62,7 @@ class API::ReservationsController < API::ApiController
private
def transaction_amount(is_admin, user_id)
def transaction_amount(user_id)
user = User.find(user_id)
cs = CartService.new(current_user)
cart = cs.from_hash(customer_id: user_id,
@ -71,7 +71,7 @@ class API::ReservationsController < API::ApiController
},
reservation: reservation_params,
coupon_code: coupon_params[:coupon_code],
payment_schedule: !schedule.nil?)
payment_schedule: reservation_params[:payment_schedule])
price_details = cart.total
# Subtract wallet amount from total

View File

@ -30,7 +30,7 @@ class API::StripeController < API::PaymentsController
currency: Setting.get('stripe_currency'),
confirmation_method: 'manual',
confirm: true,
customer: current_user.stp_customer_id # FIXME
customer: current_user.payment_gateway_object.gateway_object_id
}, { api_key: Setting.get('stripe_secret_key') }
)
elsif params[:payment_intent_id].present?
@ -71,7 +71,7 @@ class API::StripeController < API::PaymentsController
def setup_intent
user = User.find(params[:user_id])
key = Setting.get('stripe_secret_key')
@intent = Stripe::SetupIntent.create({ customer: user.stp_customer_id }, { api_key: key })
@intent = Stripe::SetupIntent.create({ customer: user.payment_gateway_object.gateway_object_id }, { api_key: key })
render json: { id: @intent.id, client_secret: @intent.client_secret }
end
@ -96,7 +96,7 @@ class API::StripeController < API::PaymentsController
def update_card
user = User.find(params[:user_id])
key = Setting.get('stripe_secret_key')
Stripe::Customer.update(user.stp_customer_id,
Stripe::Customer.update(user.payment_gateway_object.gateway_object_id,
{ invoice_settings: { default_payment_method: params[:payment_method_id] } },
{ api_key: key })
render json: { updated: true }, status: :ok

View File

@ -57,7 +57,7 @@ class API::SubscriptionsController < API::ApiController
plan_id: subscription_params[:plan_id]
},
coupon_code: coupon_params[:coupon_code],
payment_schedule: !schedule.nil?)
payment_schedule: subscription_params[:payment_schedule])
price_details = cart.total
user = User.find(user_id)

View File

@ -16,7 +16,6 @@ class OpenAPI::V1::InvoicesDoc < OpenAPI::V1::BaseDoc
description "Index of users' invoices, with optional pagination. Order by *created_at* descendant."
param_group :pagination
param :user_id, [Integer, Array], optional: true, desc: 'Scope the request to one or various users.'
# FIXME
example <<-INVOICES
# /open_api/v1/invoices?user_id=211&page=1&per_page=3
{

View File

@ -81,7 +81,7 @@ export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOp
*/
useEffect(() => {
if (!cartItems) return;
WalletAPI.getByUser(cartItems.reservation?.user_id || cartItems.subscription?.user_id).then((wallet) => {
WalletAPI.getByUser(cartItems.customer_id).then((wallet) => {
setWallet(wallet);
PriceAPI.compute(cartItems).then((res) => {
setPrice(res);

View File

@ -632,12 +632,11 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
* @return {{reservation:Object, coupon_code:string}}
*/
const mkRequestParams = function (reservation, coupon) {
const params = {
return {
customer_id: reservation.user_id,
reservation,
coupon_code: ((coupon ? coupon.code : undefined))
};
return params;
};
/**

View File

@ -680,18 +680,12 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
/**
* Create a hash map implementing the Subscription specs
* @param planId {number}
* @param userId {number}
* @param schedule {boolean}
* @param method {String} 'stripe' | 'payzen' | ''
* @return {{subscription: {payment_schedule: boolean, user_id: number, plan_id: number}}}
* @return {{subscription: {plan_id: number}}}
*/
const mkSubscription = function (planId, userId, schedule, method) {
const mkSubscription = function (planId) {
return {
subscription: {
plan_id: planId,
user_id: userId,
payment_schedule: schedule,
payment_method: method
plan_id: planId
}
};
};
@ -703,11 +697,15 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
* @return {CartItems}
*/
const mkCartItems = function (reservation, paymentMethod) {
let request = { reservation };
const request = {
customer_id: reservation.user_id,
payment_schedule: $scope.schedule.requested_schedule,
payment_method: paymentMethod
};
if (reservation.slots_attributes.length === 0 && reservation.plan_id) {
request = mkSubscription($scope.selectedPlan.id, reservation.user_id, $scope.schedule.requested_schedule, paymentMethod);
Object.assign(request, mkSubscription($scope.selectedPlan.id));
} else {
request.reservation.payment_method = paymentMethod;
Object.assign(request, { reservation });
}
return mkRequestParams(request, $scope.coupon.applied);
};

View File

@ -1,102 +0,0 @@
/* global Stripe */
/**
* This directive allows to extend a form with the Stripe payment input and error handling area.
* Strong-customer authentication is supported.
* --
* https://stripe.com/docs/payments/payment-intents/web-manual
*/
Application.Directives.directive('stripeForm', ['Payment', 'growl', '_t',
function (Payment, growl, _t) {
return ({
restrict: 'A',
scope: {
cartItems: '=',
onPaymentSuccess: '=',
stripeKey: '@'
},
link: function ($scope, element, attributes) {
const stripe = Stripe($scope.stripeKey);
const elements = stripe.elements();
const style = {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
const card = elements.create('card', { style, hidePostalCode: true });
card.addEventListener('change', function ({ error }) {
const displayError = document.getElementById('card-errors');
if (error) {
displayError.textContent = error.message;
} else {
displayError.textContent = '';
}
});
// Add an instance of the card Element into the `card-element` <div>.
const form = angular.element(element);
const cardElement = form.find('#card-element');
card.mount(cardElement[0]);
form.bind('submit', function () {
const button = form.find('button');
button.prop('disabled', true);
stripe.createPaymentMethod('card', card).then(function ({ paymentMethod, error }) {
if (error) {
growl.error(error.message);
button.prop('disabled', false);
} else {
// Send paymentMethod.id to your server (see Step 2)
Payment.confirm({ payment_method_id: paymentMethod.id, cart_items: $scope.cartItems }, function (response) {
// Handle server response (see Step 3)
handleServerResponse(response, button);
}, function (error) { handleServerResponse({ error }, button); });
}
});
});
function handleServerResponse (response, confirmButton) {
if (response.error) {
if (response.error.statusText) {
growl.error(response.error.statusText);
} else {
growl.error(`${_t('app.shared.messages.payment_card_error')} ${response.error}`);
}
confirmButton.prop('disabled', false);
} else if (response.requires_action) {
// Use Stripe.js to handle required card action
stripe.handleCardAction(
response.payment_intent_client_secret
).then(function (result) {
if (result.error) {
growl.error(result.error.message);
confirmButton.prop('disabled', false);
} else {
// The card action has been handled
// The PaymentIntent can be confirmed again on the server
Payment.confirm({ payment_intent_id: result.paymentIntent.id, cart_items: $scope.cartItems }, function (confirmResult) {
handleServerResponse(confirmResult, confirmButton);
}, function (error) { handleServerResponse({ error }, confirmButton); });
}
});
} else {
$scope.onPaymentSuccess(response);
}
}
}
});
}]);

View File

@ -5,8 +5,6 @@ module CartItem; end
# This is an abstract class implemented by classes that can be added to the shopping cart
class CartItem::BaseItem
self.abstract_class = true
def price
{ elements: {}, amount: 0 }
end

View File

@ -9,7 +9,7 @@ class CartItem::EventReservation < CartItem::Reservation
super(customer, operator, event, slots)
@normal_tickets = normal_tickets
@other_tickets = other_tickets
@other_tickets = other_tickets || []
end
def price

View File

@ -75,6 +75,8 @@ class CartItem::Reservation < CartItem::BaseItem
# Compute the number of remaining hours in the users current credits (for machine or space)
##
def credits_hours(credits, new_plan_being_bought = false)
return 0 unless credits
hours_available = credits.hours
unless new_plan_being_bought
user_credit = @customer.users_credits.find_by(credit_id: credits.id)

View File

@ -13,7 +13,7 @@ class CartItem::TrainingReservation < CartItem::Reservation
end
def price
base_amount = @reservable.amount_by_group(@customer.group_id, plan_id: @plan.try(:id)).amount
base_amount = @reservable.amount_by_group(@customer.group_id).amount
is_privileged = @operator.admin? || (@operator.manager? && @operator.id != @customer.id)
elements = { slots: [] }
@ -25,7 +25,7 @@ class CartItem::TrainingReservation < CartItem::Reservation
slot,
is_privileged,
elements: elements,
has_credits: (@user.training_credits.size < hours_available),
has_credits: (@customer.training_credits.size < hours_available),
is_division: false)
end

View File

@ -26,6 +26,8 @@ class Machine < ApplicationRecord
has_many :credits, as: :creditable, dependent: :destroy
has_many :plans, through: :credits
has_one :payment_gateway_object, as: :item
after_create :create_statistic_subtype
after_create :create_machine_prices

View File

@ -10,6 +10,11 @@ class PaymentGatewayObject < ApplicationRecord
belongs_to :subscription, foreign_type: 'Subscription', foreign_key: 'item_id'
belongs_to :payment_schedule, foreign_type: 'PaymentSchedule', foreign_key: 'item_id'
belongs_to :payment_schedule_item, foreign_type: 'PaymentScheduleItem', foreign_key: 'item_id'
belongs_to :user, foreign_type: 'User', foreign_key: 'item_id'
belongs_to :plan, foreign_type: 'Plan', foreign_key: 'item_id'
belongs_to :machine, foreign_type: 'Machine', foreign_key: 'item_id'
belongs_to :space, foreign_type: 'Space', foreign_key: 'item_id'
belongs_to :training, foreign_type: 'Training', foreign_key: 'item_id'
def gateway_object
Payment::ItemBuilder.build(gateway_object_type, gateway_object_id)

View File

@ -52,6 +52,10 @@ class PaymentSchedule < PaymentDocument
payment_gateway_objects.map(&:gateway_object).find(&:payment_mean?)
end
def gateway_subscription
payment_gateway_objects.map(&:gateway_object).find { |item| !item.payment_mean? }
end
def user
invoicing_profile.user
end

View File

@ -14,10 +14,9 @@ class PaymentScheduleItem < Footprintable
def payment_intent
return unless payment_gateway_object
# FIXME
key = Setting.get('stripe_secret_key')
stp_invoice = Stripe::Invoice.retrieve(stp_invoice_id, api_key: key)
Stripe::PaymentIntent.retrieve(stp_invoice.payment_intent, api_key: key)
stp_invoice = payment_gateway_object.gateway_object.retrieve
Stripe::PaymentIntent.retrieve(stp_invoice.payment_intent, api_key: Setting.get('stripe_secret_key'))
end
def self.columns_out_of_footprint

View File

@ -12,6 +12,7 @@ class Plan < ApplicationRecord
has_many :subscriptions
has_one :plan_file, as: :viewable, dependent: :destroy
has_many :prices, dependent: :destroy
has_one :payment_gateway_object, as: :item
extend FriendlyId
friendly_id :base_name, use: :slugged

View File

@ -25,7 +25,7 @@ class ShoppingCart
@items.map(&:price).each do |price|
total_amount += price[:amount]
all_elements.merge(price[:elements]) do |_key, old_val, new_val|
all_elements = all_elements.merge(price[:elements]) do |_key, old_val, new_val|
old_val | new_val
end
end

View File

@ -24,6 +24,8 @@ class Space < ApplicationRecord
has_many :prices, as: :priceable, dependent: :destroy
has_many :credits, as: :creditable, dependent: :destroy
has_one :payment_gateway_object, as: :item
after_create :create_statistic_subtype
after_create :create_space_prices
after_create :update_stripe_product

View File

@ -26,6 +26,8 @@ class Training < ApplicationRecord
has_many :credits, as: :creditable, dependent: :destroy
has_many :plans, through: :credits
has_one :payment_gateway_object, as: :item
after_create :create_statistic_subtype
after_create :create_trainings_pricings
after_create :update_stripe_product

View File

@ -45,6 +45,8 @@ class User < ApplicationRecord
has_many :exports, dependent: :destroy
has_many :imports, dependent: :nullify
has_one :payment_gateway_object, as: :item
# fix for create admin user
before_save do
email&.downcase!
@ -177,11 +179,6 @@ class User < ApplicationRecord
subscription.generate_and_save_invoice(operator_profile_id)
end
def stripe_customer
# FIXME
Stripe::Customer.retrieve(stp_customer_id, api_key: Setting.get('stripe_secret_key'))
end
def active_for_authentication?
super && is_active?
end
@ -315,7 +312,7 @@ class User < ApplicationRecord
blacklist = %w[id encrypted_password reset_password_token reset_password_sent_at remember_created_at
sign_in_count current_sign_in_at last_sign_in_at current_sign_in_ip last_sign_in_ip confirmation_token
confirmed_at confirmation_sent_at unconfirmed_email failed_attempts unlock_token locked_at created_at
updated_at stp_customer_id slug provider auth_token merged_at]
updated_at slug provider auth_token merged_at]
User.columns_hash
.map { |k, v| [k, v.type.to_s] }
.delete_if { |col| blacklist.include?(col[0]) }

View File

@ -11,12 +11,11 @@ class CartService
# @see app/frontend/src/javascript/models/payment.ts > interface CartItems
##
def from_hash(cart_items)
items = []
@customer = customer(cart_items)
plan_info = plan(cart_items)
@customer = User.find(cart_items[:customer_id])
items.push(CartItem::Subscription.new(plan_info[:plan])) if cart_items[:subscription]
items = []
items.push(CartItem::Subscription.new(plan_info[:plan])) if plan_info[:new_subscription]
items.push(reservable_from_hash(cart_items[:reservation], plan_info)) if cart_items[:reservation]
coupon = CartItem::Coupon.new(@customer, @operator, cart_items[:coupon_code])
@ -37,7 +36,7 @@ class CartService
plan = if @customer.subscribed_plan
new_plan_being_bought = false
@customer.subscribed_plan
elsif cart_items[:subscription]
elsif cart_items[:subscription] && cart_items[:subscription][:plan_id]
new_plan_being_bought = true
Plan.find(cart_items[:subscription][:plan_id])
else
@ -47,6 +46,14 @@ class CartService
{ plan: plan, new_subscription: new_plan_being_bought }
end
def customer(cart_items)
if @operator.admin? || (@operator.manager? && @operator.id != cart_items[:customer_id])
User.find(cart_items[:customer_id])
else
@operator
end
end
def reservable_from_hash(cart_item, plan_info)
return nil if cart_item[:reservable_id].blank?

View File

@ -78,12 +78,7 @@ class InvoicesService
operator&.admin? || (operator&.manager? && operator != user) ? nil : Setting.get('payment_gateway')
end
pgo = unless payment_id.nil?
{
gateway_object_id: payment_id,
gateway_object_type: payment_type
}
end
pgo = payment_id.nil? ? {} : { gateway_object_id: payment_id, gateway_object_type: payment_type }
invoice = Invoice.new(
invoiced: subscription || reservation,
invoicing_profile: user.invoicing_profile,

View File

@ -61,10 +61,12 @@ class PaymentScheduleService
ps.scheduled = reservation || subscription
ps.payment_method = payment_method
if !payment_id.nil? && !payment_type.nil?
ps.payment_gateway_object = {
pgo = PaymentGatewayObject.new(
gateway_object_id: payment_id,
gateway_object_type: payment_type
}
gateway_object_type: payment_type,
item: ps
)
ps.payment_gateway_objects.push(pgo)
end
ps.operator_profile = operator.invoicing_profile
ps.invoicing_profile = user.invoicing_profile

View File

@ -12,7 +12,7 @@ class StripeService
case payment_schedule.scheduled_type
when Reservation.name
subscription = payment_schedule.scheduled.subscription
reservable_stp_id = payment_schedule.scheduled.reservable&.stp_product_id
reservable_stp_id = payment_schedule.scheduled.reservable&.payment_gateway_object&.gateway_object_id
when Subscription.name
subscription = payment_schedule.scheduled
reservable_stp_id = nil
@ -26,13 +26,13 @@ class StripeService
intent = Stripe::SetupIntent.retrieve(setup_intent_id, api_key: stripe_key)
# subscription (recurring price)
price = create_price(first_item.details['recurring'],
subscription.plan.stp_product_id,
subscription.plan.payment_gateway_object.gateway_object_id,
nil, monthly: true)
# other items (not recurring)
items = subscription_invoice_items(payment_schedule, subscription, first_item, reservable_stp_id)
stp_subscription = Stripe::Subscription.create({
customer: payment_schedule.invoicing_profile.user.stp_customer_id,
customer: payment_schedule.invoicing_profile.user.payment_gateway_object.gateway_object_id,
cancel_at: (payment_schedule.ordered_items.last.due_date + 3.day).to_i,
add_invoice_items: items,
coupon: payment_schedule.coupon&.code,
@ -41,7 +41,9 @@ class StripeService
],
default_payment_method: intent[:payment_method]
}, { api_key: stripe_key })
payment_schedule.update_attributes(stp_subscription_id: stp_subscription.id)
pgo = PaymentGatewayObject.new(item: payment_schedule)
pgo.gateway_object = stp_subscription
pgo.save!
end
def create_stripe_coupon(coupon_id)
@ -72,7 +74,7 @@ class StripeService
# adjustment: when dividing the price of the plan / months, sometimes it forces us to round the amount per month.
# The difference is invoiced here
p1 = create_price(first_item.details['adjustment'],
subscription.plan.stp_product_id,
subscription.plan.payment_gateway_object.gateway_object_id,
"Price adjustment for payment schedule #{payment_schedule.id}")
items.push(price: p1[:id])
end
@ -104,7 +106,7 @@ class StripeService
def handle_wallet_transaction(payment_schedule)
return unless payment_schedule.wallet_amount
customer_id = payment_schedule.invoicing_profile.user.stp_customer_id
customer_id = payment_schedule.invoicing_profile.user.payment_gateway_object.gateway_object_id
Stripe::Customer.update(customer_id, { balance: -payment_schedule.wallet_amount }, { api_key: Setting.get('stripe_secret_key') })
end
end

View File

@ -62,8 +62,9 @@ class Subscriptions::Subscribe
if new_sub.save
schedule = subscription.original_payment_schedule
cs = CartService.new(current_user)
cart = cs.from_hash(customer_id: @user_id,
operator = InvoicingProfile.find(@operator_profile_id).user
cs = CartService.new(operator)
cart = cs.from_hash(customer_id: subscription.user.id,
subscription: {
plan_id: subscription.plan_id
},
@ -84,7 +85,7 @@ class Subscriptions::Subscribe
details)
end
payment.save
payment.post_save(schedule&.stp_setup_intent_id)
payment.post_save(schedule&.gateway_payment_mean&.id)
UsersCredits::Manager.new(user: new_sub.user).reset_credits
return new_sub
end

View File

@ -21,7 +21,7 @@ class PaymentScheduleItemWorker
if psi.payment_schedule.payment_method == 'card'
### Stripe
stripe_key = Setting.get('stripe_secret_key')
stp_subscription = Stripe::Subscription.retrieve(psi.payment_schedule.stp_subscription_id, api_key: stripe_key)
stp_subscription = psi.payment_schedule.gateway_subscription.retrieve
stp_invoice = Stripe::Invoice.retrieve(stp_subscription.latest_invoice, api_key: stripe_key)
if stp_invoice.status == 'paid'
##### Stripe / Successfully paid

View File

@ -33,9 +33,9 @@ class StripeWorker
def create_or_update_stp_product(class_name, id)
object = class_name.constantize.find(id)
if !object.stp_product_id.nil?
if !object.payment_gateway_object.nil?
Stripe::Product.update(
object.stp_product_id,
object.payment_gateway_object.gateway_object_id,
{ name: object.name },
{ api_key: Setting.get('stripe_secret_key') }
)
@ -49,12 +49,15 @@ class StripeWorker
}
}, { api_key: Setting.get('stripe_secret_key') }
)
object.update_attributes(stp_product_id: product.id)
pgo = PaymentGatewayObject.new(item: object)
pgo.gateway_object = product
pgo.save!
puts "Stripe product was created for the #{class_name} \##{id}"
end
rescue Stripe::InvalidRequestError
STDERR.puts "WARNING: saved stp_product_id (#{object.stp_product_id}) does not match on Stripe, recreating..."
obj_id = object.payment_gateway_object.gateway_object_id
STDERR.puts "WARNING: saved payment_gateway_object#id (#{obj_id}) does not match on Stripe, recreating..."
product = Stripe::Product.create(
{
name: object.name,
@ -64,7 +67,9 @@ class StripeWorker
}
}, { api_key: Setting.get('stripe_secret_key') }
)
object.update_attributes(stp_product_id: product.id)
pgo = PaymentGatewayObject.new(item: object)
pgo.gateway_object = product
pgo.save!
puts "Stripe product was created for the #{class_name} \##{id}"
end
end

View File

@ -11,7 +11,7 @@ class SyncMembersOnStripeWorker
User.online_payers.each_with_index do |member, index|
logger.debug "#{index} / #{total}"
begin
stp_customer = Stripe::Customer.retrieve(member.stp_customer_id, api_key: Setting.get('stripe_secret_key'))
stp_customer = member.payment_gateway_objects.gateway_object.retrieve
StripeWorker.new.create_stripe_customer(member.id) if stp_customer.nil? || stp_customer[:deleted]
rescue Stripe::InvalidRequestError
StripeWorker.new.create_stripe_customer(member.id)

View File

@ -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,
@ -1045,8 +1045,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,
total integer,
created_at timestamp without time zone,
updated_at timestamp without time zone,
@ -1223,15 +1223,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
);
@ -1687,8 +1687,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
@ -2003,8 +2003,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
);
@ -2036,8 +2036,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
);
@ -2969,8 +2969,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,
@ -4074,6 +4074,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: -
--
@ -5152,29 +5160,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: -
--
@ -5717,7 +5702,6 @@ INSERT INTO "schema_migrations" (version) VALUES
('20140605125131'),
('20140605142133'),
('20140605151442'),
('20140606133116'),
('20140609092700'),
('20140609092827'),
('20140610153123'),
@ -5786,14 +5770,12 @@ INSERT INTO "schema_migrations" (version) VALUES
('20150507075620'),
('20150512123546'),
('20150520132030'),
('20150520133409'),
('20150526130729'),
('20150527153312'),
('20150529113555'),
('20150601125944'),
('20150603104502'),
('20150603104658'),
('20150603133050'),
('20150604081757'),
('20150604131525'),
('20150608142234'),
@ -5875,7 +5857,6 @@ INSERT INTO "schema_migrations" (version) VALUES
('20160905142700'),
('20160906094739'),
('20160906094847'),
('20160906145713'),
('20160915105234'),
('20161123104604'),
('20170109085345'),

View File

@ -18,4 +18,5 @@ fi
RAILS_ENV='test' bin/rails db:drop
RAILS_ENV='test' bin/rails db:create
RAILS_ENV='test' bin/rails db:migrate
clear
STRIPE_PUBLISHABLE_KEY="$stripe_public_key" STRIPE_API_KEY="$stripe_secret_key" RAILS_ENV='test' bundle exec bin/rails test "$@"

View File

@ -3,7 +3,6 @@ invoice_1:
id: 1
invoiced_id: 1
invoiced_type: Subscription
stp_invoice_id: in_17wpf92sOmf47Nz9itj6vmJw
total: 10000
created_at: 2012-03-12 11:03:31.651441000 Z
updated_at: 2012-03-12 11:03:31.651441000 Z
@ -24,7 +23,6 @@ invoice_2:
id: 2
invoiced_id: 2
invoiced_type: Subscription
stp_invoice_id:
total: 2000
created_at: 2012-03-12 13:40:22.342717000 Z
updated_at: 2012-03-12 13:40:22.342717000 Z
@ -45,7 +43,6 @@ invoice_3:
id: 3
invoiced_id: 3
invoiced_type: Subscription
stp_invoice_id:
total: 3000
created_at: 2015-06-10 11:20:01.341130000 Z
updated_at: 2015-06-10 11:20:01.341130000 Z
@ -67,7 +64,6 @@ invoice_4:
id: 4
invoiced_id: 1
invoiced_type: Reservation
stp_invoice_id:
total: 0
created_at: 2016-04-05 08:35:52.931187000 Z
updated_at: 2016-04-05 08:35:52.931187000 Z
@ -88,7 +84,6 @@ invoice_5:
id: 5
invoiced_id: 2
invoiced_type: Reservation
stp_invoice_id:
total: 1500
created_at: 2016-04-05 08:36:46.853368000 Z
updated_at: 2016-04-05 08:36:46.853368000 Z
@ -109,7 +104,6 @@ invoice_6:
id: 6
invoiced_id: 4
invoiced_type: Subscription
stp_invoice_id:
total: 3000
created_at: 2021-01-04 14:51:21.616153182 Z
updated_at: 2021-01-04 14:51:21.616153182 Z

View File

@ -22,7 +22,6 @@ 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
@ -39,7 +38,6 @@ 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
@ -56,7 +54,6 @@ 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
@ -70,7 +67,6 @@ 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
@ -93,7 +89,6 @@ 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
@ -128,4 +123,3 @@ 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

View File

@ -6,6 +6,156 @@ pgo_1:
pgo2:
item_type: 'Subscription'
item_id: 1
gateway_object_type: 'Stripe::Subscription'
gateway_object_id: 'sub_8DGB4ErIc2asOv'
item_id: 1
gateway_object_type: 'Stripe::Subscription'
gateway_object_id: 'sub_8DGB4ErIc2asOv'
pgo3:
item_type: 'Invoice'
item_id: 1
gateway_object_type: 'Stripe::Invoice'
gateway_object_id: 'in_17wpf92sOmf47Nz9itj6vmJw'
pgo4:
item_type: 'Plan'
item_id: 1
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyXhfyNiGkWR'
pgo5:
item_type: 'Plan'
item_id: 2
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPykam7a4satn'
pgo6:
item_type: 'Plan'
item_id: 3
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyM4N36h86G0'
pgo7:
item_type: 'Plan'
item_id: 4
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZQAhb9nLu4jfN'
pgo8:
item_type: 'Machine'
item_id: 1
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyHpMCl38iQl'
pgo9:
item_type: 'Machine'
item_id: 2
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyPShaaRgSML'
pgo10:
item_type: 'Machine'
item_id: 3
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyEjmdfMowhY'
pgo11:
item_type: 'Machine'
item_id: 4
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPy85vZOQpAo5'
pgo12:
item_type: 'Machine'
item_id: 5
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyBJEgbcpWMC'
pgo13:
item_type: 'Machine'
item_id: 6
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyjCzvLmLWAz'
pgo14:
item_type: 'Space'
item_id: 1
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyHjIb2owoB8'
pgo15:
item_type: 'Training'
item_id: 1
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyXw6BDBBFOg'
pgo16:
item_type: 'Training'
item_id: 2
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPytTl1wSB5jH'
pgo17:
item_type: 'Training'
item_id: 3
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyAA1A4QfEyL'
pgo18:
item_type: 'Training'
item_id: 4
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyU27NjDSmqB'
pgo19:
item_type: 'Training'
item_id: 5
gateway_object_type: 'Stripe::Product'
gateway_object_id: 'prod_IZPyvdgQHMByB3'
pgo20:
item_type: 'User'
item_id: 1
gateway_object_type: 'Stripe::Customer'
gateway_object_id: 'cus_8CyNk3UTi8lvCc'
pgo21:
item_type: 'User'
item_id: 2
gateway_object_type: 'Stripe::Customer'
gateway_object_id: 'cus_8Di1wjdVktv5kt'
pgo22:
item_type: 'User'
item_id: 3
gateway_object_type: 'Stripe::Customer'
gateway_object_id: 'cus_8CzHcwBJtlA3IL'
pgo23:
item_type: 'User'
item_id: 4
gateway_object_type: 'Stripe::Customer'
gateway_object_id: 'cus_8CzKe50I0J1gaI'
pgo24:
item_type: 'User'
item_id: 5
gateway_object_type: 'Stripe::Customer'
gateway_object_id: 'cus_8CzNtM08NVlSGN'
pgo25:
item_type: 'User'
item_id: 6
gateway_object_type: 'Stripe::Customer'
gateway_object_id: 'cus_8CzQK5uXPeyh4K'
pgo26:
item_type: 'User'
item_id: 7
gateway_object_type: 'Stripe::Customer'
gateway_object_id: 'cus_8E2ys9zDZgetWX'
pgo27:
item_type: 'User'
item_id: 8
gateway_object_type: 'Stripe::Customer'
gateway_object_id: 'cus_IhIynmoJbzLpwX'

View File

@ -16,7 +16,6 @@ plan_1:
ui_weight: 1
interval_count: 1
slug: mensuel
stp_product_id: prod_IZPyXhfyNiGkWR
plan_2:
id: 2
@ -35,7 +34,6 @@ plan_2:
ui_weight: 5
interval_count: 2
slug: sleede
stp_product_id: prod_IZPykam7a4satn
plan_3:
id: 3
@ -55,7 +53,6 @@ plan_3:
ui_weight: 0
interval_count: 1*
slug: mensuel-tarif-reduit
stp_product_id: prod_IZPyM4N36h86G0
plan_schedulable:
id: 4
@ -75,4 +72,3 @@ plan_schedulable:
interval_count: 1
monthly_payment: true
slug: abonnement-mensualisable
stp_product_id: prod_IZQAhb9nLu4jfN

View File

@ -7,4 +7,3 @@ 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

View File

@ -7,7 +7,6 @@ training_1:
nb_total_places:
slug: formation-imprimante-3d
description:
stp_product_id: prod_IZPyXw6BDBBFOg
training_2:
id: 2
@ -17,7 +16,6 @@ training_2:
nb_total_places:
slug: formation-laser-vinyle
description:
stp_product_id: prod_IZPytTl1wSB5jH
training_3:
id: 3
@ -27,7 +25,6 @@ training_3:
nb_total_places:
slug: formation-petite-fraiseuse-numerique
description:
stp_product_id: prod_IZPyAA1A4QfEyL
training_4:
id: 4
@ -37,7 +34,6 @@ training_4:
nb_total_places:
slug: formation-shopbot-grande-fraiseuse
description:
stp_product_id: prod_IZPyU27NjDSmqB
training_5:
id: 5
@ -47,4 +43,3 @@ training_5:
nb_total_places:
slug: formation-logiciel-2d
description:
stp_product_id: prod_IZPyvdgQHMByB3

View File

@ -1,3 +1,34 @@
user_1:
id: 1
username: admin
email: admin@fab-manager.com
encrypted_password: "$2a$10$LtQ2OggropcXpmcsPTEPruuGZrwuFYGOo8qjh.po91YDna/K4/Bbe"
reset_password_token:
reset_password_sent_at:
remember_created_at:
sign_in_count: 8
current_sign_in_at: 2016-04-05 08:36:08.361260000 Z
last_sign_in_at: 2016-04-05 08:34:28.227054000 Z
current_sign_in_ip: 127.0.0.1
last_sign_in_ip: 127.0.0.1
confirmation_token:
confirmed_at: 2016-04-05 08:40:21.514533000 Z
confirmation_sent_at: 2001-01-01 14:11:33.852719000 Z
unconfirmed_email:
failed_attempts: 0
unlock_token:
locked_at:
created_at: 2001-01-01 14:11:33.852719000 Z
updated_at: 2016-04-05 08:36:08.362215000 Z
is_allow_contact: true
group_id: 3
slug: admin
is_active: true
provider:
uid:
auth_token:
merged_at:
is_allow_newsletter: true
user_2:
id: 2
@ -23,7 +54,6 @@ user_2:
updated_at: 2016-04-04 15:00:43.556308000 Z
is_allow_contact: true
group_id: 1
stp_customer_id: cus_8Di1wjdVktv5kt
slug: jdupond
is_active: true
provider:
@ -32,105 +62,6 @@ user_2:
merged_at:
is_allow_newsletter: true
user_4:
id: 4
username: kdumas
email: kevin.dumas@orange.fr
encrypted_password: "$2a$10$N6Q4SHkkUtwlnMNFvN6nF.rkmLFTIPsARk7xEuR1Dws7Dy2sSrSOW"
reset_password_token:
reset_password_sent_at:
remember_created_at:
sign_in_count: 0
current_sign_in_at:
last_sign_in_at:
current_sign_in_ip:
last_sign_in_ip:
confirmation_token:
confirmed_at: 2016-04-04 15:11:02.968494000 Z
confirmation_sent_at: 2016-04-04 15:10:42.376191000 Z
unconfirmed_email:
failed_attempts: 0
unlock_token:
locked_at:
created_at: 2016-04-04 15:10:42.340868000 Z
updated_at: 2016-04-04 15:11:02.969182000 Z
is_allow_contact: true
group_id: 2
stp_customer_id: cus_8CzKe50I0J1gaI
slug: kdumas
is_active: true
provider:
uid:
auth_token:
merged_at:
is_allow_newsletter: false
user_6:
id: 6
username: GilbertPartenaire
email: gilbert.partenaire@nicolas.com
encrypted_password: "$2a$10$UtWcIYMd2aOtQjw6OFL4cuMV1MB20Bfs/opzJDqWXqu8woUo.2cLu"
reset_password_token:
reset_password_sent_at:
remember_created_at:
sign_in_count: 0
current_sign_in_at:
last_sign_in_at:
current_sign_in_ip:
last_sign_in_ip:
confirmation_token:
confirmed_at: 2016-04-04 15:18:14.124111000 Z
confirmation_sent_at: 2016-04-04 15:17:12.522028000 Z
unconfirmed_email:
failed_attempts: 0
unlock_token:
locked_at:
created_at: 2016-04-04 15:17:12.522028000 Z
updated_at: 2016-04-04 15:17:12.522028000 Z
is_allow_contact: true
group_id: 1
stp_customer_id: cus_8CzQK5uXPeyh4K
slug: gilbertpartenaire
is_active: true
provider:
uid:
auth_token:
merged_at:
is_allow_newsletter: true
user_5:
id: 5
username: vlonchamp
email: vanessa.lonchamp@sfr.fr
encrypted_password: "$2a$10$HOEk.QyGwpd9nsk2gVXGSuAxig9jMrNFHVqYMW1.Sp41EtN7rdBbG"
reset_password_token:
reset_password_sent_at:
remember_created_at:
sign_in_count: 1
current_sign_in_at: 2016-04-04 15:34:23.343596000 Z
last_sign_in_at: 2016-04-04 15:34:23.343596000 Z
current_sign_in_ip: 37.64.230.6
last_sign_in_ip: 37.67.211.2
confirmation_token: 59cfd407491c4e1d95a9f91f81e710a62a83a6fd7171ad22823b4c4395312c4d
confirmed_at: 2016-04-04 15:18:12.122101000 Z
confirmation_sent_at: 2016-04-04 15:14:08.599603000 Z
unconfirmed_email:
failed_attempts: 0
unlock_token:
locked_at:
created_at: 2016-04-04 15:14:08.563152000 Z
updated_at: 2016-04-04 15:34:23.344613000 Z
is_allow_contact: true
group_id: 2
stp_customer_id: cus_8CzNtM08NVlSGN
slug: vlonchamp
is_active: true
provider:
uid:
auth_token:
merged_at:
is_allow_newsletter: true
user_3:
id: 3
username: pdurand
@ -155,7 +86,6 @@ user_3:
updated_at: 2016-04-05 08:35:05.678582000 Z
is_allow_contact: false
group_id: 1
stp_customer_id: cus_8CzHcwBJtlA3IL
slug: pdurand
is_active: true
provider:
@ -164,32 +94,63 @@ user_3:
merged_at:
is_allow_newsletter: false
user_1:
id: 1
username: admin
email: admin@fab-manager.com
encrypted_password: "$2a$10$LtQ2OggropcXpmcsPTEPruuGZrwuFYGOo8qjh.po91YDna/K4/Bbe"
user_4:
id: 4
username: kdumas
email: kevin.dumas@orange.fr
encrypted_password: "$2a$10$N6Q4SHkkUtwlnMNFvN6nF.rkmLFTIPsARk7xEuR1Dws7Dy2sSrSOW"
reset_password_token:
reset_password_sent_at:
remember_created_at:
sign_in_count: 8
current_sign_in_at: 2016-04-05 08:36:08.361260000 Z
last_sign_in_at: 2016-04-05 08:34:28.227054000 Z
current_sign_in_ip: 127.0.0.1
last_sign_in_ip: 127.0.0.1
sign_in_count: 0
current_sign_in_at:
last_sign_in_at:
current_sign_in_ip:
last_sign_in_ip:
confirmation_token:
confirmed_at: 2016-04-05 08:40:21.514533000 Z
confirmation_sent_at: 2001-01-01 14:11:33.852719000 Z
confirmed_at: 2016-04-04 15:11:02.968494000 Z
confirmation_sent_at: 2016-04-04 15:10:42.376191000 Z
unconfirmed_email:
failed_attempts: 0
unlock_token:
locked_at:
created_at: 2001-01-01 14:11:33.852719000 Z
updated_at: 2016-04-05 08:36:08.362215000 Z
created_at: 2016-04-04 15:10:42.340868000 Z
updated_at: 2016-04-04 15:11:02.969182000 Z
is_allow_contact: true
group_id: 3
stp_customer_id: cus_8CyNk3UTi8lvCc
slug: admin
group_id: 2
slug: kdumas
is_active: true
provider:
uid:
auth_token:
merged_at:
is_allow_newsletter: false
user_5:
id: 5
username: vlonchamp
email: vanessa.lonchamp@sfr.fr
encrypted_password: "$2a$10$HOEk.QyGwpd9nsk2gVXGSuAxig9jMrNFHVqYMW1.Sp41EtN7rdBbG"
reset_password_token:
reset_password_sent_at:
remember_created_at:
sign_in_count: 1
current_sign_in_at: 2016-04-04 15:34:23.343596000 Z
last_sign_in_at: 2016-04-04 15:34:23.343596000 Z
current_sign_in_ip: 37.64.230.6
last_sign_in_ip: 37.67.211.2
confirmation_token: 59cfd407491c4e1d95a9f91f81e710a62a83a6fd7171ad22823b4c4395312c4d
confirmed_at: 2016-04-04 15:18:12.122101000 Z
confirmation_sent_at: 2016-04-04 15:14:08.599603000 Z
unconfirmed_email:
failed_attempts: 0
unlock_token:
locked_at:
created_at: 2016-04-04 15:14:08.563152000 Z
updated_at: 2016-04-04 15:34:23.344613000 Z
is_allow_contact: true
group_id: 2
slug: vlonchamp
is_active: true
provider:
uid:
@ -197,6 +158,37 @@ user_1:
merged_at:
is_allow_newsletter: true
user_6:
id: 6
username: GilbertPartenaire
email: gilbert.partenaire@nicolas.com
encrypted_password: "$2a$10$UtWcIYMd2aOtQjw6OFL4cuMV1MB20Bfs/opzJDqWXqu8woUo.2cLu"
reset_password_token:
reset_password_sent_at:
remember_created_at:
sign_in_count: 0
current_sign_in_at:
last_sign_in_at:
current_sign_in_ip:
last_sign_in_ip:
confirmation_token:
confirmed_at: 2016-04-04 15:18:14.124111000 Z
confirmation_sent_at: 2016-04-04 15:17:12.522028000 Z
unconfirmed_email:
failed_attempts: 0
unlock_token:
locked_at:
created_at: 2016-04-04 15:17:12.522028000 Z
updated_at: 2016-04-04 15:17:12.522028000 Z
is_allow_contact: true
group_id: 1
slug: gilbertpartenaire
is_active: true
provider:
uid:
auth_token:
merged_at:
is_allow_newsletter: true
user_7:
id: 7
@ -222,7 +214,6 @@ user_7:
updated_at: 2016-04-07 11:00:37.643112000 Z
is_allow_contact: true
group_id: 1
stp_customer_id: cus_8E2ys9zDZgetWX
slug: lseguin
is_active: true
provider:
@ -255,7 +246,6 @@ user_8:
updated_at: 2021-01-04 14:36:39.729052932 Z
is_allow_contact: true
group_id: 1
stp_customer_id: cus_IhIynmoJbzLpwX
slug: atiermoulin
is_active: true
provider:

View File

@ -64,8 +64,8 @@ module Events
# Now, let's make a reservation on this event
post '/api/reservations',
params: {
customer_id: User.find_by(username: 'pdurand').id,
reservation: {
user_id: User.find_by(username: 'pdurand').id,
reservable_id: e.id,
reservable_type: 'Event',
nb_reserve_places: 2,
@ -158,8 +158,8 @@ module Events
# Now, let's make a reservation on this event
post '/api/reservations',
params: {
customer_id: User.find_by(username: 'lseguin').id,
reservation: {
user_id: User.find_by(username: 'lseguin').id,
reservable_id: e.id,
reservable_type: 'Event',
nb_reserve_places: 4,

View File

@ -29,9 +29,9 @@ module Events
post '/api/stripe/confirm_payment',
params: {
payment_method_id: stripe_payment_method,
customer_id: User.find_by(username: 'vlonchamp').id,
cart_items: {
reservation: {
user_id: User.find_by(username: 'vlonchamp').id,
reservable_id: radio.id,
reservable_type: 'Event',
nb_reserve_places: 2,
@ -78,7 +78,7 @@ module Events
# invoice assertions
invoice = reservation.invoice
refute invoice.stp_payment_intent_id.blank?
refute invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert_equal 43_350, invoice.total # total minus coupon
@ -94,7 +94,7 @@ module Events
assert_invoice_pdf invoice
VCR.use_cassette('reserve_event_with_many_prices_and_payment_means_retrieve_invoice_from_stripe') do
stp_intent = Stripe::PaymentIntent.retrieve(invoice.stp_payment_intent_id, api_key: Setting.get('stripe_secret_key'))
stp_intent = invoice.payment_gateway_object.gateway_object.retrieve
assert_equal stp_intent.amount, (invoice.total - invoice.wallet_amount) # total minus coupon minus wallet = amount really payed by the user
end

View File

@ -14,8 +14,8 @@ module Prices
post '/api/prices/compute',
params: {
customer_id: user.id,
reservation: {
user_id: user.id,
reservable_id: printer_training.id,
reservable_type: printer_training.class.name,
slots_attributes: [
@ -49,11 +49,10 @@ module Prices
post '/api/prices/compute',
params: {
customer_id: user.id,
reservation: {
user_id: user.id,
reservable_id: laser.id,
reservable_type: laser.class.name,
plan_id: plan.id,
slots_attributes: [
{
availability_id: availability.id,
@ -68,6 +67,9 @@ module Prices
start_at: (availability.start_at + 1.hour).strftime('%Y-%m-%d %H:%M:%S.%9N Z')
}
]
},
subscription: {
plan_id: plan.id
}
}.to_json,
headers: default_headers

View File

@ -19,18 +19,20 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
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
post reservations_path, params: {
customer_id: @user_without_subscription.id,
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
# general assertions
assert_equal 201, response.status
@ -52,7 +54,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
assert invoice.stp_payment_intent_id.blank?
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
# invoice_items assertions
@ -76,18 +78,20 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
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
post reservations_path, params: {
customer_id: @user_without_subscription.id,
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
}
]
}
}.to_json, headers: default_headers
# general assertions
assert_equal 201, response.status
@ -108,7 +112,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
assert invoice.stp_payment_intent_id.blank?
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
# invoice_items
invoice_item = InvoiceItem.last
@ -133,23 +137,25 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
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
post reservations_path, params: {
customer_id: @user_with_subscription.id,
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
# general assertions
assert_equal 201, response.status
@ -172,7 +178,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
assert invoice.stp_payment_intent_id.blank?
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
# invoice_items assertions
@ -206,18 +212,20 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
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
post reservations_path, params: {
customer_id: @vlonchamp.id,
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
# general assertions
assert_equal 201, response.status
@ -239,7 +247,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
assert invoice.stp_payment_intent_id.blank?
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
# invoice_items assertions
@ -276,19 +284,23 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
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
post reservations_path, params: {
customer_id: @vlonchamp.id,
subscription: {
plan_id: plan.id,
},
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
# general assertions
assert_equal 201, response.status
@ -312,7 +324,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
assert invoice.stp_payment_intent_id.blank?
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert_equal invoice.total, 2000
@ -343,18 +355,20 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
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
post reservations_path, params: {
customer_id: @vlonchamp.id,
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
# general assertions
assert_equal 201, response.status
@ -386,20 +400,24 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
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
post reservations_path, params: {
customer_id: @user_without_subscription.id,
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),
offered: false,
availability_id: availability.id
}
]
},
subscription: {
plan_id: plan.id,
}
}.to_json, headers: default_headers
# general assertions
assert_equal 201, response.status
@ -431,7 +449,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
assert invoice.stp_payment_intent_id.blank?
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert_equal plan.amount, invoice.total
@ -463,21 +481,25 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
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,
post reservations_path, params: {
payment_method: 'check',
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
payment_schedule: true,
customer_id: @user_without_subscription.id,
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
}
]
},
subscription: {
plan_id: plan.id
}
}.to_json, headers: default_headers
end
# get the objects
@ -513,8 +535,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# payment schedule assertions
assert_not_nil payment_schedule.reference
assert_equal 'check', payment_schedule.payment_method
assert_nil payment_schedule.stp_subscription_id
assert_nil payment_schedule.stp_setup_intent_id # FIXME
assert_empty payment_schedule.payment_gateway_objects
assert_nil payment_schedule.wallet_transaction
assert_nil payment_schedule.wallet_amount
assert_nil payment_schedule.coupon_id

View File

@ -61,7 +61,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
refute invoice.stp_payment_intent_id.blank?
refute invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert invoice.check_footprint
@ -177,7 +177,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
refute invoice.stp_payment_intent_id.blank?
refute invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert invoice.check_footprint
@ -253,7 +253,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
refute invoice.stp_payment_intent_id.blank?
refute invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert invoice.check_footprint
@ -328,7 +328,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
assert invoice.stp_payment_intent_id.blank?
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert invoice.check_footprint
@ -367,11 +367,10 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
params: {
payment_method_id: stripe_payment_method,
cart_items: {
customer_id: @vlonchamp.id,
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),
@ -407,7 +406,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
refute invoice.stp_payment_intent_id.blank?
refute invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert invoice.check_footprint
@ -454,7 +453,6 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
reservation: {
reservable_id: training.id,
reservable_type: training.class.name,
plan_id: plan.id,
slots_attributes: [
{
start_at: availability.start_at.to_s(:iso8601),
@ -462,6 +460,9 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
availability_id: availability.id
}
]
},
subscription: {
plan_id: plan.id
}
}
}.to_json, headers: default_headers
@ -490,7 +491,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
refute invoice.stp_payment_intent_id.blank?
refute invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert_equal invoice.total, 2000
assert invoice.check_footprint
@ -538,7 +539,9 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
end_at: (availability.start_at + 1.hour).to_s(:iso8601),
availability_id: availability.id
}
],
]
},
subscription: {
plan_id: plan.id
},
coupon_code: 'SUNNYFABLAB'
@ -568,7 +571,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# invoice assertions
invoice = reservation.invoice
refute invoice.stp_payment_intent_id.blank?
refute invoice.payment_gateway_object.blank?
refute invoice.total.blank?
assert invoice.check_footprint
@ -595,7 +598,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
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'))
stp_intent = invoice.payment_gateway_object.gateway_object.retrieve
assert_equal stp_intent.amount, invoice.total
end
@ -620,8 +623,8 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
params: {
payment_method_id: stripe_payment_method,
cart_items: {
customer_id: @user_without_subscription.id,
reservation: {
user_id: @user_without_subscription.id,
reservable_id: training.id,
reservable_type: training.class.name,
card_token: stripe_payment_method,
@ -696,6 +699,7 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
params: {
setup_intent_id: setup_intent[:id],
cart_items: {
payment_schedule: true,
reservation: {
reservable_id: training.id,
reservable_type: training.class.name,
@ -706,8 +710,9 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
availability_id: availability.id
}
],
},
subscription: {
plan_id: plan.id,
payment_schedule: true
}
}
}.to_json, headers: default_headers
@ -791,9 +796,11 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
setup_intent_id: setup_intent[:id],
cart_items: {
coupon_code: 'GIME3EUR',
reservation: {
payment_schedule: true,
subscription: {
plan_id: plan.id,
payment_schedule: true,
},
reservation: {
reservable_id: machine.id,
reservable_type: machine.class.name,
slots_attributes: [
@ -838,9 +845,9 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# payment schedule assertions
assert_not_nil payment_schedule.reference
assert_equal 'stripe', payment_schedule.payment_method
assert_not_nil payment_schedule.stp_subscription_id
assert_not_nil payment_schedule.stp_setup_intent_id # FIXME
assert_equal 'card', payment_schedule.payment_method
assert_equal 2, payment_schedule.payment_gateway_objects.count
assert_not_nil payment_schedule.gateway_payment_mean
assert_not_nil payment_schedule.wallet_transaction
assert_equal payment_schedule.ordered_items.first.amount, payment_schedule.wallet_amount
assert_equal Coupon.find_by(code: 'GIME3EUR').id, payment_schedule.coupon_id

View File

@ -94,10 +94,10 @@ class Subscriptions::CreateAsAdminTest < ActionDispatch::IntegrationTest
params: {
setup_intent_id: setup_intent[:id],
cart_items: {
customer_id: user.id,
subscription: {
plan_id: plan.id,
payment_schedule: true,
user_id: user.id,
payment_method: 'stripe'
}
}

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
require 'test_helper'
class Subscriptions::RenewAsAdminTest < ActionDispatch::IntegrationTest
setup do
@admin = User.find_by(username: 'admin')