mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-20 14:54:15 +01:00
Merge branch 'dev' for release 5.5.5
This commit is contained in:
commit
71409395e2
@ -1,5 +1,14 @@
|
||||
# Changelog Fab-manager
|
||||
|
||||
## v5.5.5 2022 November 22
|
||||
|
||||
- Soft destroy of spaces and machines
|
||||
- Fix a bug: in upgrade script, the error "the input device is not a TTY" is thrown when migrating the database
|
||||
- Fix a bug: broken display of machines pages
|
||||
- Fix a bug: some automated tests were randomly failing because ElasticSearch was not synced
|
||||
- Fix a bug: payment related objects are not synced on Stripe when enabling the online payment module
|
||||
- Fix a bug: unable set a main image of product and remove an image of product
|
||||
|
||||
## v5.5.4 2022 November 17
|
||||
|
||||
- Fix a bug: unable to download an existing export of the statistics
|
||||
|
@ -12,6 +12,8 @@ class API::MachinesController < API::ApiController
|
||||
|
||||
def show
|
||||
@machine = Machine.includes(:machine_files, :projects).friendly.find(params[:id])
|
||||
|
||||
head :not_found if @machine.deleted_at
|
||||
end
|
||||
|
||||
def create
|
||||
@ -35,7 +37,11 @@ class API::MachinesController < API::ApiController
|
||||
|
||||
def destroy
|
||||
authorize @machine
|
||||
@machine.destroy
|
||||
if @machine.destroyable?
|
||||
@machine.destroy
|
||||
else
|
||||
@machine.soft_destroy!
|
||||
end
|
||||
head :no_content
|
||||
end
|
||||
|
||||
|
@ -6,11 +6,13 @@ class API::SpacesController < API::ApiController
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@spaces = Space.includes(:space_image)
|
||||
@spaces = Space.includes(:space_image).where(deleted_at: nil)
|
||||
end
|
||||
|
||||
def show
|
||||
@space = Space.includes(:space_files, :projects).friendly.find(params[:id])
|
||||
|
||||
head :not_found if @space.deleted_at
|
||||
end
|
||||
|
||||
def create
|
||||
@ -36,7 +38,11 @@ class API::SpacesController < API::ApiController
|
||||
def destroy
|
||||
@space = get_space
|
||||
authorize @space
|
||||
@space.destroy
|
||||
if @space.destroyable?
|
||||
@space.destroy
|
||||
else
|
||||
@space.soft_destroy!
|
||||
end
|
||||
head :no_content
|
||||
end
|
||||
|
||||
|
@ -60,11 +60,12 @@ export const FormImageUpload = <TFieldValues extends FieldValues, TContext exten
|
||||
attachment_name: f.name
|
||||
});
|
||||
setValue(
|
||||
id as Path<TFieldValues>,
|
||||
{
|
||||
attachment_name: f.name,
|
||||
_destroy: false
|
||||
} as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
|
||||
`${id}.attachment_name` as Path<TFieldValues>,
|
||||
f.name as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
|
||||
);
|
||||
setValue(
|
||||
`${id}._destroy` as Path<TFieldValues>,
|
||||
false as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
|
||||
);
|
||||
if (typeof onFileChange === 'function') {
|
||||
onFileChange({ attachment_name: f.name });
|
||||
|
@ -6,9 +6,10 @@ import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||
import { FormComponent, FormControlledComponent } from '../../models/form-component';
|
||||
import { AbstractFormItemProps } from './abstract-form-item';
|
||||
import { UseFormSetValue } from 'react-hook-form/dist/types/form';
|
||||
import { ArrayPath, FieldArray, useFieldArray } from 'react-hook-form';
|
||||
import { ArrayPath, FieldArray, Path, useFieldArray, useWatch } from 'react-hook-form';
|
||||
import { FileType } from '../../models/file';
|
||||
import { UnpackNestedValue } from 'react-hook-form/dist/types';
|
||||
import { FieldPathValue } from 'react-hook-form/dist/types/path';
|
||||
|
||||
interface FormMultiFileUploadProps<TFieldValues, TContext extends object> extends FormComponent<TFieldValues>, FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
||||
setValue: UseFormSetValue<TFieldValues>,
|
||||
@ -20,13 +21,26 @@ interface FormMultiFileUploadProps<TFieldValues, TContext extends object> extend
|
||||
* This component allows to upload multiple files, in forms managed by react-hook-form.
|
||||
*/
|
||||
export const FormMultiFileUpload = <TFieldValues extends FieldValues, TContext extends object>({ id, className, register, control, setValue, formState, addButtonLabel, accept }: FormMultiFileUploadProps<TFieldValues, TContext>) => {
|
||||
const { fields, append, remove } = useFieldArray({ control, name: id as ArrayPath<TFieldValues> });
|
||||
const { append } = useFieldArray({ control, name: id as ArrayPath<TFieldValues> });
|
||||
const output = useWatch({ control, name: id as Path<TFieldValues> });
|
||||
|
||||
/**
|
||||
* Remove an file
|
||||
*/
|
||||
const handleRemoveFile = (file: FileType, index: number) => {
|
||||
return () => {
|
||||
setValue(
|
||||
`${id}.${index}._destroy` as Path<TFieldValues>,
|
||||
true as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`form-multi-file-upload ${className || ''}`}>
|
||||
<div className="list">
|
||||
{fields.map((field: FileType, index) => (
|
||||
<FormFileUpload key={field.id}
|
||||
{output.map((field: FileType, index) => (
|
||||
<FormFileUpload key={index}
|
||||
defaultFile={field}
|
||||
id={`${id}.${index}`}
|
||||
accept={accept}
|
||||
@ -34,7 +48,7 @@ export const FormMultiFileUpload = <TFieldValues extends FieldValues, TContext e
|
||||
setValue={setValue}
|
||||
formState={formState}
|
||||
className={field._destroy ? 'hidden' : ''}
|
||||
onFileRemove={() => remove(index)}/>
|
||||
onFileRemove={() => handleRemoveFile(field, index)}/>
|
||||
))}
|
||||
</div>
|
||||
<FabButton
|
||||
|
@ -20,7 +20,7 @@ interface FormMultiImageUploadProps<TFieldValues, TContext extends object> exten
|
||||
* This component allows to upload multiple images, in forms managed by react-hook-form.
|
||||
*/
|
||||
export const FormMultiImageUpload = <TFieldValues extends FieldValues, TContext extends object>({ id, className, register, control, setValue, formState, addButtonLabel }: FormMultiImageUploadProps<TFieldValues, TContext>) => {
|
||||
const { fields, append, remove } = useFieldArray({ control, name: id as ArrayPath<TFieldValues> });
|
||||
const { append } = useFieldArray({ control, name: id as ArrayPath<TFieldValues> });
|
||||
const output = useWatch({ control, name: id as Path<TFieldValues> });
|
||||
|
||||
/**
|
||||
@ -44,14 +44,10 @@ export const FormMultiImageUpload = <TFieldValues extends FieldValues, TContext
|
||||
true as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
|
||||
);
|
||||
}
|
||||
if (typeof image.id === 'string') {
|
||||
remove(index);
|
||||
} else {
|
||||
setValue(
|
||||
`${id}.${index}._destroy` as Path<TFieldValues>,
|
||||
true as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
|
||||
);
|
||||
}
|
||||
setValue(
|
||||
`${id}.${index}._destroy` as Path<TFieldValues>,
|
||||
true as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@ -74,8 +70,8 @@ export const FormMultiImageUpload = <TFieldValues extends FieldValues, TContext
|
||||
return (
|
||||
<div className={`form-multi-image-upload ${className || ''}`}>
|
||||
<div className="list">
|
||||
{fields.map((field: ImageType, index) => (
|
||||
<FormImageUpload key={field.id}
|
||||
{output.map((field: ImageType, index) => (
|
||||
<FormImageUpload key={index}
|
||||
defaultImage={field}
|
||||
id={`${id}.${index}`}
|
||||
accept="image/*"
|
||||
|
@ -44,12 +44,12 @@ const MachineCard: React.FC<MachineCardProps> = ({ user, machine, onShowMachine,
|
||||
* Return the machine's picture or a placeholder
|
||||
*/
|
||||
const machinePicture = (): ReactNode => {
|
||||
if (!machine.machine_image_attributes) {
|
||||
if (!machine.machine_image) {
|
||||
return <div className="machine-picture no-picture" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="machine-picture" style={{ backgroundImage: `url(${machine.machine_image_attributes.attachment_url}), url('/default-image.png')` }} onClick={handleShowMachine} />
|
||||
<div className="machine-picture" style={{ backgroundImage: `url(${machine.machine_image}), url('/default-image.png')` }} onClick={handleShowMachine} />
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Reservation } from './reservation';
|
||||
import { ApiFilter } from './api';
|
||||
import { FileType } from './file';
|
||||
|
||||
export interface MachineIndexFilter extends ApiFilter {
|
||||
disabled: boolean,
|
||||
@ -13,8 +12,12 @@ export interface Machine {
|
||||
spec?: string,
|
||||
disabled: boolean,
|
||||
slug: string,
|
||||
machine_image_attributes: FileType,
|
||||
machine_files_attributes?: Array<FileType>,
|
||||
machine_image: string,
|
||||
machine_files_attributes?: Array<{
|
||||
id: number,
|
||||
attachment: string,
|
||||
attachment_url: string
|
||||
}>,
|
||||
trainings?: Array<{
|
||||
id: number,
|
||||
name: string,
|
||||
|
@ -82,6 +82,10 @@ class Machine < ApplicationRecord
|
||||
reservations.empty?
|
||||
end
|
||||
|
||||
def soft_destroy!
|
||||
update(deleted_at: DateTime.current)
|
||||
end
|
||||
|
||||
def packs?(user)
|
||||
prepaid_packs.where(group_id: user.group_id)
|
||||
.where(disabled: [false, nil])
|
||||
|
@ -15,7 +15,7 @@ class Product < ApplicationRecord
|
||||
accepts_nested_attributes_for :product_files, allow_destroy: true, reject_if: :all_blank
|
||||
|
||||
has_many :product_images, as: :viewable, dependent: :destroy
|
||||
accepts_nested_attributes_for :product_images, allow_destroy: true, reject_if: ->(i) { i[:attachment].blank? }
|
||||
accepts_nested_attributes_for :product_images, allow_destroy: true, reject_if: ->(i) { i[:attachment].blank? && i[:id].blank? }
|
||||
|
||||
has_many :product_stock_movements, dependent: :destroy
|
||||
accepts_nested_attributes_for :product_stock_movements, allow_destroy: true, reject_if: :all_blank
|
||||
|
@ -66,6 +66,10 @@ class Space < ApplicationRecord
|
||||
reservations.empty?
|
||||
end
|
||||
|
||||
def soft_destroy!
|
||||
update(deleted_at: DateTime.current)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_gateway_product
|
||||
|
@ -11,6 +11,6 @@ class MachinePolicy < ApplicationPolicy
|
||||
end
|
||||
|
||||
def destroy?
|
||||
user.admin? and record.destroyable?
|
||||
user.admin?
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Check the access policies for API::SpacesController
|
||||
class SpacePolicy < ApplicationPolicy
|
||||
def create?
|
||||
user.admin?
|
||||
@ -8,6 +11,6 @@ class SpacePolicy < ApplicationPolicy
|
||||
end
|
||||
|
||||
def destroy?
|
||||
user.admin? and record.destroyable?
|
||||
user.admin?
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# module definition
|
||||
module Cart; end
|
||||
|
||||
# Provides methods for set offer to item in cart
|
||||
class Cart::SetOfferService
|
||||
def call(order, orderable, is_offered)
|
||||
|
@ -9,6 +9,9 @@ class MachineService
|
||||
else
|
||||
Machine.includes(:machine_image, :plans).order(sort_by)
|
||||
end
|
||||
# do not include soft destroyed
|
||||
machines = machines.where(deleted_at: nil)
|
||||
|
||||
if filters[:disabled].present?
|
||||
state = filters[:disabled] == 'false' ? [nil, false] : true
|
||||
machines = machines.where(disabled: state)
|
||||
|
@ -46,7 +46,7 @@ class SettingService
|
||||
|
||||
# sync all objects on stripe
|
||||
def sync_stripe_objects(setting)
|
||||
return unless setting.name == 'stripe_secret_key'
|
||||
return unless %w[stripe_secret_key online_payment_module].include?(setting.name)
|
||||
|
||||
SyncObjectsOnStripeWorker.perform_async(setting.history_values.last&.invoicing_profile&.user&.id)
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.array!(@machines) do |machine|
|
||||
json.partial! 'api/machines/machine', machine: machine
|
||||
json.extract! machine, :id, :name, :slug, :disabled
|
||||
|
||||
json.machine_image machine.machine_image.attachment.medium.url if machine.machine_image
|
||||
end
|
||||
|
@ -1,11 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.partial! 'api/machines/machine', machine: @machine
|
||||
json.extract! @machine, :description, :spec
|
||||
json.extract! @machine, :id, :name, :description, :spec, :disabled, :slug
|
||||
json.machine_image @machine.machine_image.attachment.large.url if @machine.machine_image
|
||||
|
||||
json.machine_files_attributes @machine.machine_files do |f|
|
||||
json.id f.id
|
||||
json.attachment_name f.attachment_identifier
|
||||
json.attachment f.attachment_identifier
|
||||
json.attachment_url f.attachment_url
|
||||
end
|
||||
json.trainings @machine.trainings.each, :id, :name, :disabled
|
||||
|
@ -4,12 +4,12 @@ json.extract! product, :id, :name, :slug, :sku, :is_active, :product_category_id
|
||||
:low_stock_threshold, :machine_ids, :created_at
|
||||
json.description sanitize(product.description)
|
||||
json.amount product.amount / 100.0 if product.amount.present?
|
||||
json.product_files_attributes product.product_files do |f|
|
||||
json.product_files_attributes product.product_files.order(created_at: :asc) do |f|
|
||||
json.id f.id
|
||||
json.attachment_name f.attachment_identifier
|
||||
json.attachment_url f.attachment_url
|
||||
end
|
||||
json.product_images_attributes product.product_images do |f|
|
||||
json.product_images_attributes product.product_images.order(created_at: :asc) do |f|
|
||||
json.id f.id
|
||||
json.attachment_name f.attachment_identifier
|
||||
json.attachment_url f.attachment_url
|
||||
|
@ -7,22 +7,45 @@ class SyncObjectsOnStripeWorker
|
||||
sidekiq_options lock: :until_executed, on_conflict: :reject, queue: :stripe
|
||||
|
||||
def perform(notify_user_id = nil)
|
||||
unless Stripe::Helper.enabled?
|
||||
Rails.logger.warn('A request to sync payment related objects on Stripe will not run because Stripe is not enabled')
|
||||
return false
|
||||
end
|
||||
|
||||
w = StripeWorker.new
|
||||
sync_customers(w)
|
||||
sync_coupons
|
||||
sync_objects(w)
|
||||
|
||||
Rails.logger.info 'Sync is done'
|
||||
return unless notify_user_id
|
||||
|
||||
Rails.logger.info "Notify user #{notify_user_id}"
|
||||
user = User.find(notify_user_id)
|
||||
NotificationCenter.call type: :notify_admin_objects_stripe_sync,
|
||||
receiver: user,
|
||||
attached_object: user
|
||||
end
|
||||
|
||||
def sync_customers(worker)
|
||||
Rails.logger.info 'We create all non-existing customers on stripe. This may take a while...'
|
||||
total = User.online_payers.count
|
||||
User.online_payers.each_with_index do |member, index|
|
||||
Rails.logger.info "#{index} / #{total}"
|
||||
begin
|
||||
stp_customer = member.payment_gateway_object&.gateway_object&.retrieve
|
||||
StripeWorker.new.create_stripe_customer(member.id) if stp_customer.nil? || stp_customer[:deleted]
|
||||
worker.perform(:create_stripe_customer, member.id) if stp_customer.nil? || stp_customer[:deleted]
|
||||
rescue Stripe::InvalidRequestError
|
||||
begin
|
||||
StripeWorker.new.create_stripe_customer(member.id)
|
||||
worker.perform(:create_stripe_customer, member.id)
|
||||
rescue Stripe::InvalidRequestError => e
|
||||
Rails.logger.error "Unable to create the customer #{member.id} do to a Stripe error: #{e}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sync_coupons
|
||||
Rails.logger.info 'We create all non-existing coupons on stripe. This may take a while...'
|
||||
total = Coupon.all.count
|
||||
Coupon.all.each_with_index do |coupon, index|
|
||||
@ -35,23 +58,16 @@ class SyncObjectsOnStripeWorker
|
||||
Rails.logger.error "Unable to create coupon #{coupon.code} on stripe: #{e}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
w = StripeWorker.new
|
||||
def sync_objects(worker)
|
||||
[Machine, Training, Space, Plan].each do |klass|
|
||||
Rails.logger.info "We create all non-existing #{klass} on stripe. This may take a while..."
|
||||
total = klass.all.count
|
||||
klass.all.each_with_index do |item, index|
|
||||
Rails.logger.info "#{index} / #{total}"
|
||||
w.perform(:create_or_update_stp_product, klass.name, item.id)
|
||||
worker.perform(:create_or_update_stp_product, klass.name, item.id)
|
||||
end
|
||||
end
|
||||
Rails.logger.info 'Sync is done'
|
||||
return unless notify_user_id
|
||||
|
||||
Rails.logger.info "Notify user #{notify_user_id}"
|
||||
user = User.find(notify_user_id)
|
||||
NotificationCenter.call type: :notify_admin_objects_stripe_sync,
|
||||
receiver: user,
|
||||
attached_object: user
|
||||
end
|
||||
end
|
||||
|
12
db/migrate/20221122123557_add_deleted_at_to_machine.rb
Normal file
12
db/migrate/20221122123557_add_deleted_at_to_machine.rb
Normal file
@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Allow soft destroy of machines.
|
||||
# Machines with existing reservation cannot be destroyed because we need them for rebuilding invoices, statistics, etc.
|
||||
# This attribute allows to make a "soft destroy" of a Machine, marking it as destroyed so it doesn't appear anymore in
|
||||
# the interface (as if it was destroyed) but still lives in the database so we can use it to build data.
|
||||
class AddDeletedAtToMachine < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :machines, :deleted_at, :datetime
|
||||
add_index :machines, :deleted_at
|
||||
end
|
||||
end
|
12
db/migrate/20221122123605_add_deleted_at_to_space.rb
Normal file
12
db/migrate/20221122123605_add_deleted_at_to_space.rb
Normal file
@ -0,0 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Allow soft destroy of spaces.
|
||||
# Spaces with existing reservation cannot be destroyed because we need them for rebuilding invoices, statistics, etc.
|
||||
# This attribute allows to make a "soft destroy" of a Space, marking it as destroyed so it doesn't appear anymore in
|
||||
# the interface (as if it was destroyed) but still lives in the database so we can use it to build data.
|
||||
class AddDeletedAtToSpace < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :spaces, :deleted_at, :datetime
|
||||
add_index :spaces, :deleted_at
|
||||
end
|
||||
end
|
@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2022_10_03_133019) do
|
||||
ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "fuzzystrmatch"
|
||||
@ -358,6 +358,8 @@ ActiveRecord::Schema.define(version: 2022_10_03_133019) do
|
||||
t.datetime "updated_at"
|
||||
t.string "slug"
|
||||
t.boolean "disabled"
|
||||
t.datetime "deleted_at"
|
||||
t.index ["deleted_at"], name: "index_machines_on_deleted_at"
|
||||
t.index ["slug"], name: "index_machines_on_slug", unique: true
|
||||
end
|
||||
|
||||
@ -878,6 +880,8 @@ ActiveRecord::Schema.define(version: 2022_10_03_133019) do
|
||||
t.datetime "updated_at", null: false
|
||||
t.text "characteristics"
|
||||
t.boolean "disabled"
|
||||
t.datetime "deleted_at"
|
||||
t.index ["deleted_at"], name: "index_spaces_on_deleted_at"
|
||||
end
|
||||
|
||||
create_table "spaces_availabilities", id: :serial, force: :cascade do |t|
|
||||
|
@ -21,3 +21,12 @@ to the corresponding OpenID Connect claims:
|
||||
- profile.address
|
||||
|
||||
To use the automatic mapping, add one of the fields above and click on the magic wand button near to the "Userinfo claim" input.
|
||||
|
||||
## Known issues
|
||||
|
||||
```
|
||||
Not found. Authentication passthru.
|
||||
```
|
||||
This issue may occur if you have misconfigured the environment variable `DEFAULT_HOST` and/or `DEFAULT_PROTOCOL`.
|
||||
Especially, if you have an automatic redirection (e.g. from example.org to example.com), `DEFAULT_HOST` *MUST* be configured with the redirection target (here example.com).
|
||||
Once you have reconfigured these variables, please switch back the active authentication provider to FabManager, restart the application, then delete the OIDC provider you configured and re-create a new one for the new settings to be used.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fab-manager",
|
||||
"version": "5.5.4",
|
||||
"version": "5.5.5",
|
||||
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
|
||||
"keywords": [
|
||||
"fablab",
|
||||
|
@ -296,7 +296,7 @@ upgrade()
|
||||
fi
|
||||
done
|
||||
compile_assets
|
||||
if ! docker-compose run --rm "$SERVICE" bundle exec rake db:migrate; then
|
||||
if ! docker-compose run --rm "$SERVICE" bundle exec rake db:migrate </dev/tty; then
|
||||
restore_tag
|
||||
printf "\e[91m[ ❌ ] Something went wrong while migrating the database, please check the logs above.\e[39m\nExiting...\n"
|
||||
exit 4
|
||||
|
@ -12,6 +12,7 @@ class Exports::StatisticsExportTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
test 'export machine reservations statistics to Excel' do
|
||||
Stats::Machine.refresh_index!
|
||||
# Build the stats for the June 2015, a machine reservation should have happened at the time
|
||||
::Statistics::BuilderService.generate_statistic({ start_date: '2015-06-01'.to_date.beginning_of_day,
|
||||
end_date: '2015-06-30'.to_date.end_of_day })
|
||||
@ -35,6 +36,7 @@ class Exports::StatisticsExportTest < ActionDispatch::IntegrationTest
|
||||
assert_not_nil e, 'Export was not created in database'
|
||||
|
||||
# Run the worker
|
||||
Stats::Machine.refresh_index!
|
||||
worker = StatisticsExportWorker.new
|
||||
worker.perform(e.id)
|
||||
|
||||
@ -69,6 +71,7 @@ class Exports::StatisticsExportTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
test 'export global statistics to Excel' do
|
||||
Stats::Machine.refresh_index!
|
||||
# Build the stats for the June 2015
|
||||
::Statistics::BuilderService.generate_statistic({ start_date: '2015-06-01'.to_date.beginning_of_day,
|
||||
end_date: '2015-06-30'.to_date.end_of_day })
|
||||
|
209
test/integration/store/admin_order_for_himself_test.rb
Normal file
209
test/integration/store/admin_order_for_himself_test.rb
Normal file
@ -0,0 +1,209 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module Store; end
|
||||
|
||||
class Store::AdminOrderForHimselfTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = User.find_by(username: 'admin')
|
||||
@pjproudhon = User.find_by(username: 'pjproudhon')
|
||||
@caisse_en_bois = Product.find_by(slug: 'caisse-en-bois')
|
||||
@panneaux = Product.find_by(slug: 'panneaux-de-mdf')
|
||||
@cart1 = Order.find_by(token: '0DKxbAOzSXRx-amXyhmDdg1666691976019')
|
||||
end
|
||||
|
||||
test 'admin pay himself order by card with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
|
||||
VCR.use_cassette('store_order_admin_pay_by_card_success') do
|
||||
post '/api/checkout/payment',
|
||||
params: {
|
||||
payment_id: stripe_payment_method,
|
||||
order_token: @cart1.token,
|
||||
customer_id: @admin.id
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
@cart1.reload
|
||||
|
||||
# general assertions
|
||||
assert_equal 200, response.status
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.last
|
||||
assert_invoice_pdf invoice
|
||||
assert_not_nil invoice.debug_footprint
|
||||
|
||||
assert_not @cart1.payment_gateway_object.blank?
|
||||
assert_not invoice.payment_gateway_object.blank?
|
||||
assert_not invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: invoice)
|
||||
|
||||
assert_equal @cart1.state, 'paid'
|
||||
assert_equal @cart1.payment_method, 'card'
|
||||
assert_equal @cart1.paid_total, 262_500
|
||||
|
||||
stock_movement = @caisse_en_bois.product_stock_movements.last
|
||||
assert_equal stock_movement.stock_type, 'external'
|
||||
assert_equal stock_movement.reason, 'sold'
|
||||
assert_equal stock_movement.quantity, -5
|
||||
assert_equal stock_movement.order_item_id, @cart1.order_items.first.id
|
||||
|
||||
stock_movement = @panneaux.product_stock_movements.last
|
||||
assert_equal stock_movement.stock_type, 'external'
|
||||
assert_equal stock_movement.reason, 'sold'
|
||||
assert_equal stock_movement.quantity, -2
|
||||
assert_equal stock_movement.order_item_id, @cart1.order_items.last.id
|
||||
|
||||
activity = @cart1.order_activities.last
|
||||
assert_equal activity.activity_type, 'paid'
|
||||
assert_equal activity.operator_profile_id, @admin.invoicing_profile.id
|
||||
end
|
||||
|
||||
test 'admin pay himself order by card and wallet with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
service = WalletService.new(user: @admin, wallet: @admin.wallet)
|
||||
service.credit(1000)
|
||||
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
VCR.use_cassette('store_order_admin_pay_by_cart_and_wallet_success') do
|
||||
post '/api/checkout/payment',
|
||||
params: {
|
||||
payment_id: stripe_payment_method,
|
||||
order_token: @cart1.token,
|
||||
customer_id: @admin.id
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
@admin.wallet.reload
|
||||
@cart1.reload
|
||||
|
||||
# general assertions
|
||||
assert_equal 200, response.status
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.last
|
||||
assert_invoice_pdf invoice
|
||||
assert_not_nil invoice.debug_footprint
|
||||
|
||||
assert_not @cart1.payment_gateway_object.blank?
|
||||
assert_not invoice.payment_gateway_object.blank?
|
||||
assert_not invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: invoice)
|
||||
|
||||
assert_equal @cart1.state, 'paid'
|
||||
assert_equal @cart1.payment_method, 'card'
|
||||
assert_equal @cart1.paid_total, 162_500
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
# wallet
|
||||
assert_equal 0, @admin.wallet.amount
|
||||
assert_equal 2, @admin.wallet.wallet_transactions.count
|
||||
transaction = @admin.wallet.wallet_transactions.last
|
||||
assert_equal 'debit', transaction.transaction_type
|
||||
assert_equal @cart1.wallet_amount / 100.0, transaction.amount
|
||||
assert_equal @cart1.wallet_transaction_id, transaction.id
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
end
|
||||
|
||||
test 'admin pay himself order by wallet with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
service = WalletService.new(user: @admin, wallet: @admin.wallet)
|
||||
service.credit(@cart1.total / 100)
|
||||
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
post '/api/checkout/payment',
|
||||
params: {
|
||||
order_token: @cart1.token,
|
||||
customer_id: @admin.id
|
||||
}.to_json, headers: default_headers
|
||||
|
||||
@admin.wallet.reload
|
||||
@cart1.reload
|
||||
|
||||
# general assertions
|
||||
assert_equal 200, response.status
|
||||
assert_equal @cart1.state, 'paid'
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.last
|
||||
assert_invoice_pdf invoice
|
||||
assert_not_nil invoice.debug_footprint
|
||||
|
||||
assert invoice.payment_gateway_object.blank?
|
||||
assert_not invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: invoice)
|
||||
|
||||
# wallet
|
||||
assert_equal 0, @admin.wallet.amount
|
||||
assert_equal 2, @admin.wallet.wallet_transactions.count
|
||||
transaction = @admin.wallet.wallet_transactions.last
|
||||
assert_equal 'debit', transaction.transaction_type
|
||||
assert_equal @cart1.paid_total, 0
|
||||
assert_equal @cart1.wallet_amount / 100.0, transaction.amount
|
||||
assert_equal @cart1.payment_method, 'wallet'
|
||||
assert_equal @cart1.wallet_transaction_id, transaction.id
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
end
|
||||
|
||||
test 'admin cannot offer products to himself' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
put '/api/cart/set_offer',
|
||||
params: {
|
||||
order_token: @cart1.token,
|
||||
customer_id: @admin.id,
|
||||
is_offered: true,
|
||||
orderable_id: @caisse_en_bois.id
|
||||
}.to_json, headers: default_headers
|
||||
|
||||
assert_equal 403, response.status
|
||||
end
|
||||
end
|
@ -13,129 +13,6 @@ class Store::AdminPayOrderTest < ActionDispatch::IntegrationTest
|
||||
@cart1 = Order.find_by(token: '0DKxbAOzSXRx-amXyhmDdg1666691976019')
|
||||
end
|
||||
|
||||
test 'admin pay himself order by cart with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
|
||||
VCR.use_cassette('store_order_admin_pay_by_cart_success') do
|
||||
post '/api/checkout/payment',
|
||||
params: {
|
||||
payment_id: stripe_payment_method,
|
||||
order_token: @cart1.token,
|
||||
customer_id: @admin.id
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
@cart1.reload
|
||||
|
||||
# general assertions
|
||||
assert_equal 200, response.status
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.last
|
||||
assert_invoice_pdf invoice
|
||||
assert_not_nil invoice.debug_footprint
|
||||
|
||||
assert_not @cart1.payment_gateway_object.blank?
|
||||
assert_not invoice.payment_gateway_object.blank?
|
||||
assert_not invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: invoice)
|
||||
|
||||
assert_equal @cart1.state, 'paid'
|
||||
assert_equal @cart1.payment_method, 'card'
|
||||
assert_equal @cart1.paid_total, 262_500
|
||||
|
||||
stock_movement = @caisse_en_bois.product_stock_movements.last
|
||||
assert_equal stock_movement.stock_type, 'external'
|
||||
assert_equal stock_movement.reason, 'sold'
|
||||
assert_equal stock_movement.quantity, -5
|
||||
assert_equal stock_movement.order_item_id, @cart1.order_items.first.id
|
||||
|
||||
stock_movement = @panneaux.product_stock_movements.last
|
||||
assert_equal stock_movement.stock_type, 'external'
|
||||
assert_equal stock_movement.reason, 'sold'
|
||||
assert_equal stock_movement.quantity, -2
|
||||
assert_equal stock_movement.order_item_id, @cart1.order_items.last.id
|
||||
|
||||
activity = @cart1.order_activities.last
|
||||
assert_equal activity.activity_type, 'paid'
|
||||
assert_equal activity.operator_profile_id, @admin.invoicing_profile.id
|
||||
end
|
||||
|
||||
test 'admin pay himself order by cart and wallet with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
service = WalletService.new(user: @admin, wallet: @admin.wallet)
|
||||
service.credit(1000)
|
||||
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
VCR.use_cassette('store_order_admin_pay_by_cart_and_wallet_success') do
|
||||
post '/api/checkout/payment',
|
||||
params: {
|
||||
payment_id: stripe_payment_method,
|
||||
order_token: @cart1.token,
|
||||
customer_id: @admin.id
|
||||
}.to_json, headers: default_headers
|
||||
end
|
||||
|
||||
@admin.wallet.reload
|
||||
@cart1.reload
|
||||
|
||||
# general assertions
|
||||
assert_equal 200, response.status
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.last
|
||||
assert_invoice_pdf invoice
|
||||
assert_not_nil invoice.debug_footprint
|
||||
|
||||
assert_not @cart1.payment_gateway_object.blank?
|
||||
assert_not invoice.payment_gateway_object.blank?
|
||||
assert_not invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: invoice)
|
||||
|
||||
assert_equal @cart1.state, 'paid'
|
||||
assert_equal @cart1.payment_method, 'card'
|
||||
assert_equal @cart1.paid_total, 162_500
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
# wallet
|
||||
assert_equal 0, @admin.wallet.amount
|
||||
assert_equal 2, @admin.wallet.wallet_transactions.count
|
||||
transaction = @admin.wallet.wallet_transactions.last
|
||||
assert_equal 'debit', transaction.transaction_type
|
||||
assert_equal @cart1.wallet_amount / 100.0, transaction.amount
|
||||
assert_equal @cart1.wallet_transaction_id, transaction.id
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
end
|
||||
|
||||
test 'admin pay user order by local with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
@ -254,63 +131,6 @@ class Store::AdminPayOrderTest < ActionDispatch::IntegrationTest
|
||||
assert_equal activity.operator_profile_id, @admin.invoicing_profile.id
|
||||
end
|
||||
|
||||
test 'admin pay himself order by wallet with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
service = WalletService.new(user: @admin, wallet: @admin.wallet)
|
||||
service.credit(@cart1.total / 100)
|
||||
|
||||
invoice_count = Invoice.count
|
||||
invoice_items_count = InvoiceItem.count
|
||||
users_credit_count = UsersCredit.count
|
||||
wallet_transactions_count = WalletTransaction.count
|
||||
|
||||
post '/api/checkout/payment',
|
||||
params: {
|
||||
order_token: @cart1.token,
|
||||
customer_id: @admin.id
|
||||
}.to_json, headers: default_headers
|
||||
|
||||
@admin.wallet.reload
|
||||
@cart1.reload
|
||||
|
||||
# general assertions
|
||||
assert_equal 200, response.status
|
||||
assert_equal @cart1.state, 'paid'
|
||||
assert_equal invoice_count + 1, Invoice.count
|
||||
assert_equal invoice_items_count + 2, InvoiceItem.count
|
||||
assert_equal users_credit_count, UsersCredit.count
|
||||
assert_equal wallet_transactions_count + 1, WalletTransaction.count
|
||||
|
||||
# invoice_items assertions
|
||||
invoice_item = InvoiceItem.last
|
||||
|
||||
assert invoice_item.check_footprint
|
||||
|
||||
# invoice assertions
|
||||
invoice = Invoice.last
|
||||
assert_invoice_pdf invoice
|
||||
assert_not_nil invoice.debug_footprint
|
||||
|
||||
assert invoice.payment_gateway_object.blank?
|
||||
assert_not invoice.total.blank?
|
||||
assert invoice.check_footprint
|
||||
|
||||
# notification
|
||||
assert_not_empty Notification.where(attached_object: invoice)
|
||||
|
||||
# wallet
|
||||
assert_equal 0, @admin.wallet.amount
|
||||
assert_equal 2, @admin.wallet.wallet_transactions.count
|
||||
transaction = @admin.wallet.wallet_transactions.last
|
||||
assert_equal 'debit', transaction.transaction_type
|
||||
assert_equal @cart1.paid_total, 0
|
||||
assert_equal @cart1.wallet_amount / 100.0, transaction.amount
|
||||
assert_equal @cart1.payment_method, 'wallet'
|
||||
assert_equal @cart1.wallet_transaction_id, transaction.id
|
||||
assert_equal invoice.wallet_amount / 100.0, transaction.amount
|
||||
end
|
||||
|
||||
test 'admin pay user order by wallet with success' do
|
||||
login_as(@admin, scope: :user)
|
||||
|
||||
|
@ -91,6 +91,10 @@ class ReservationSubscriptionStatisticServiceTest < ActionDispatch::IntegrationT
|
||||
]
|
||||
}.to_json, headers: default_headers
|
||||
|
||||
Stats::Machine.refresh_index!
|
||||
Stats::Training.refresh_index!
|
||||
Stats::Subscription.refresh_index!
|
||||
|
||||
# Build the stats for the last 3 days, we expect the above invoices (reservations+subscription) to appear in the resulting stats
|
||||
::Statistics::BuilderService.generate_statistic({ start_date: 2.days.ago.beginning_of_day,
|
||||
end_date: DateTime.current.end_of_day })
|
||||
@ -126,6 +130,8 @@ class ReservationSubscriptionStatisticServiceTest < ActionDispatch::IntegrationT
|
||||
check_statistics_on_user(stat_hour)
|
||||
|
||||
# training
|
||||
Stats::Training.refresh_index!
|
||||
|
||||
stat_training = Stats::Training.search(query: { bool: { must: [{ term: { date: 1.day.ago.to_date.iso8601 } },
|
||||
{ term: { type: 'booking' } }] } }).first
|
||||
assert_not_nil stat_training
|
||||
|
@ -0,0 +1,338 @@
|
||||
---
|
||||
http_interactions:
|
||||
- request:
|
||||
method: post
|
||||
uri: https://api.stripe.com/v1/payment_methods
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: type=card&card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2023&card[cvc]=314
|
||||
headers:
|
||||
User-Agent:
|
||||
- Stripe/v1 RubyBindings/5.29.0
|
||||
Authorization:
|
||||
- Bearer sk_test_testfaketestfaketestfake
|
||||
Content-Type:
|
||||
- application/x-www-form-urlencoded
|
||||
Stripe-Version:
|
||||
- '2019-08-14'
|
||||
X-Stripe-Client-User-Agent:
|
||||
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
|
||||
MacBook-Pro-Sleede-Peng 20.6.0 Darwin Kernel Version 20.6.0: Thu Sep 29 20:15:11
|
||||
PDT 2022; root:xnu-7195.141.42~1/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
|
||||
Accept-Encoding:
|
||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
||||
Accept:
|
||||
- "*/*"
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Server:
|
||||
- nginx
|
||||
Date:
|
||||
- Mon, 07 Nov 2022 17:22:23 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
- '930'
|
||||
Connection:
|
||||
- keep-alive
|
||||
Access-Control-Allow-Credentials:
|
||||
- 'true'
|
||||
Access-Control-Allow-Methods:
|
||||
- GET, POST, HEAD, OPTIONS, DELETE
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Access-Control-Expose-Headers:
|
||||
- Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required
|
||||
Access-Control-Max-Age:
|
||||
- '300'
|
||||
Cache-Control:
|
||||
- no-cache, no-store
|
||||
Idempotency-Key:
|
||||
- 3d420ca2-b79a-451b-88bc-56efb989ae3e
|
||||
Original-Request:
|
||||
- req_7tnZKRYjblvjJF
|
||||
Request-Id:
|
||||
- req_7tnZKRYjblvjJF
|
||||
Stripe-Should-Retry:
|
||||
- 'false'
|
||||
Stripe-Version:
|
||||
- '2019-08-14'
|
||||
Strict-Transport-Security:
|
||||
- max-age=63072000; includeSubDomains; preload
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: |-
|
||||
{
|
||||
"id": "pm_1M1Z1f2sOmf47Nz9y4qaYQap",
|
||||
"object": "payment_method",
|
||||
"billing_details": {
|
||||
"address": {
|
||||
"city": null,
|
||||
"country": null,
|
||||
"line1": null,
|
||||
"line2": null,
|
||||
"postal_code": null,
|
||||
"state": null
|
||||
},
|
||||
"email": null,
|
||||
"name": null,
|
||||
"phone": null
|
||||
},
|
||||
"card": {
|
||||
"brand": "visa",
|
||||
"checks": {
|
||||
"address_line1_check": null,
|
||||
"address_postal_code_check": null,
|
||||
"cvc_check": "unchecked"
|
||||
},
|
||||
"country": "US",
|
||||
"exp_month": 4,
|
||||
"exp_year": 2023,
|
||||
"fingerprint": "o52jybR7bnmNn6AT",
|
||||
"funding": "credit",
|
||||
"generated_from": null,
|
||||
"last4": "4242",
|
||||
"networks": {
|
||||
"available": [
|
||||
"visa"
|
||||
],
|
||||
"preferred": null
|
||||
},
|
||||
"three_d_secure_usage": {
|
||||
"supported": true
|
||||
},
|
||||
"wallet": null
|
||||
},
|
||||
"created": 1667841743,
|
||||
"customer": null,
|
||||
"livemode": false,
|
||||
"metadata": {},
|
||||
"type": "card"
|
||||
}
|
||||
recorded_at: Mon, 07 Nov 2022 17:22:24 GMT
|
||||
- request:
|
||||
method: post
|
||||
uri: https://api.stripe.com/v1/payment_intents
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: payment_method=pm_1M1Z1f2sOmf47Nz9y4qaYQap&amount=162500¤cy=usd&confirmation_method=manual&confirm=true&customer=cus_8CyNk3UTi8lvCc
|
||||
headers:
|
||||
User-Agent:
|
||||
- Stripe/v1 RubyBindings/5.29.0
|
||||
Authorization:
|
||||
- Bearer sk_test_testfaketestfaketestfake
|
||||
Content-Type:
|
||||
- application/x-www-form-urlencoded
|
||||
X-Stripe-Client-Telemetry:
|
||||
- '{"last_request_metrics":{"request_id":"req_7tnZKRYjblvjJF","request_duration_ms":697}}'
|
||||
Stripe-Version:
|
||||
- '2019-08-14'
|
||||
X-Stripe-Client-User-Agent:
|
||||
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
|
||||
MacBook-Pro-Sleede-Peng 20.6.0 Darwin Kernel Version 20.6.0: Thu Sep 29 20:15:11
|
||||
PDT 2022; root:xnu-7195.141.42~1/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
|
||||
Accept-Encoding:
|
||||
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
||||
Accept:
|
||||
- "*/*"
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Server:
|
||||
- nginx
|
||||
Date:
|
||||
- Mon, 07 Nov 2022 17:22:25 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
- '4476'
|
||||
Connection:
|
||||
- keep-alive
|
||||
Access-Control-Allow-Credentials:
|
||||
- 'true'
|
||||
Access-Control-Allow-Methods:
|
||||
- GET, POST, HEAD, OPTIONS, DELETE
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Access-Control-Expose-Headers:
|
||||
- Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required
|
||||
Access-Control-Max-Age:
|
||||
- '300'
|
||||
Cache-Control:
|
||||
- no-cache, no-store
|
||||
Idempotency-Key:
|
||||
- 5294d6d7-d766-4aa5-bac1-be52f491fa20
|
||||
Original-Request:
|
||||
- req_c0S6XFCR5hlcc9
|
||||
Request-Id:
|
||||
- req_c0S6XFCR5hlcc9
|
||||
Stripe-Should-Retry:
|
||||
- 'false'
|
||||
Stripe-Version:
|
||||
- '2019-08-14'
|
||||
Strict-Transport-Security:
|
||||
- max-age=63072000; includeSubDomains; preload
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: |-
|
||||
{
|
||||
"id": "pi_3M1Z1g2sOmf47Nz91KcAbrWR",
|
||||
"object": "payment_intent",
|
||||
"amount": 162500,
|
||||
"amount_capturable": 0,
|
||||
"amount_details": {
|
||||
"tip": {}
|
||||
},
|
||||
"amount_received": 162500,
|
||||
"application": null,
|
||||
"application_fee_amount": null,
|
||||
"automatic_payment_methods": null,
|
||||
"canceled_at": null,
|
||||
"cancellation_reason": null,
|
||||
"capture_method": "automatic",
|
||||
"charges": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "ch_3M1Z1g2sOmf47Nz91YJQvMPK",
|
||||
"object": "charge",
|
||||
"amount": 162500,
|
||||
"amount_captured": 162500,
|
||||
"amount_refunded": 0,
|
||||
"application": null,
|
||||
"application_fee": null,
|
||||
"application_fee_amount": null,
|
||||
"balance_transaction": "txn_3M1Z1g2sOmf47Nz913ZvXNvF",
|
||||
"billing_details": {
|
||||
"address": {
|
||||
"city": null,
|
||||
"country": null,
|
||||
"line1": null,
|
||||
"line2": null,
|
||||
"postal_code": null,
|
||||
"state": null
|
||||
},
|
||||
"email": null,
|
||||
"name": null,
|
||||
"phone": null
|
||||
},
|
||||
"calculated_statement_descriptor": "Stripe",
|
||||
"captured": true,
|
||||
"created": 1667841744,
|
||||
"currency": "usd",
|
||||
"customer": "cus_8CyNk3UTi8lvCc",
|
||||
"description": null,
|
||||
"destination": null,
|
||||
"dispute": null,
|
||||
"disputed": false,
|
||||
"failure_balance_transaction": null,
|
||||
"failure_code": null,
|
||||
"failure_message": null,
|
||||
"fraud_details": {},
|
||||
"invoice": null,
|
||||
"livemode": false,
|
||||
"metadata": {},
|
||||
"on_behalf_of": null,
|
||||
"order": null,
|
||||
"outcome": {
|
||||
"network_status": "approved_by_network",
|
||||
"reason": null,
|
||||
"risk_level": "normal",
|
||||
"risk_score": 45,
|
||||
"seller_message": "Payment complete.",
|
||||
"type": "authorized"
|
||||
},
|
||||
"paid": true,
|
||||
"payment_intent": "pi_3M1Z1g2sOmf47Nz91KcAbrWR",
|
||||
"payment_method": "pm_1M1Z1f2sOmf47Nz9y4qaYQap",
|
||||
"payment_method_details": {
|
||||
"card": {
|
||||
"brand": "visa",
|
||||
"checks": {
|
||||
"address_line1_check": null,
|
||||
"address_postal_code_check": null,
|
||||
"cvc_check": "pass"
|
||||
},
|
||||
"country": "US",
|
||||
"exp_month": 4,
|
||||
"exp_year": 2023,
|
||||
"fingerprint": "o52jybR7bnmNn6AT",
|
||||
"funding": "credit",
|
||||
"installments": null,
|
||||
"last4": "4242",
|
||||
"mandate": null,
|
||||
"network": "visa",
|
||||
"three_d_secure": null,
|
||||
"wallet": null
|
||||
},
|
||||
"type": "card"
|
||||
},
|
||||
"receipt_email": null,
|
||||
"receipt_number": null,
|
||||
"receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xMDNyRTYyc09tZjQ3Tno5KNH9pJsGMgbMAdBS9Kg6LBbMQFUWP1mmaiHjAUCgW-WYHRH5dwIHTlYhiTVjiSL5fqEMQr17GSJhWPA-",
|
||||
"refunded": false,
|
||||
"refunds": {
|
||||
"object": "list",
|
||||
"data": [],
|
||||
"has_more": false,
|
||||
"total_count": 0,
|
||||
"url": "/v1/charges/ch_3M1Z1g2sOmf47Nz91YJQvMPK/refunds"
|
||||
},
|
||||
"review": null,
|
||||
"shipping": null,
|
||||
"source": null,
|
||||
"source_transfer": null,
|
||||
"statement_descriptor": null,
|
||||
"statement_descriptor_suffix": null,
|
||||
"status": "succeeded",
|
||||
"transfer_data": null,
|
||||
"transfer_group": null
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/charges?payment_intent=pi_3M1Z1g2sOmf47Nz91KcAbrWR"
|
||||
},
|
||||
"client_secret": "pi_3M1Z1g2sOmf47Nz91KcAbrWR_secret_ocSaI8LfCIvNBzfXl5iwRB9kS",
|
||||
"confirmation_method": "manual",
|
||||
"created": 1667841744,
|
||||
"currency": "usd",
|
||||
"customer": "cus_8CyNk3UTi8lvCc",
|
||||
"description": null,
|
||||
"invoice": null,
|
||||
"last_payment_error": null,
|
||||
"livemode": false,
|
||||
"metadata": {},
|
||||
"next_action": null,
|
||||
"on_behalf_of": null,
|
||||
"payment_method": "pm_1M1Z1f2sOmf47Nz9y4qaYQap",
|
||||
"payment_method_options": {
|
||||
"card": {
|
||||
"installments": null,
|
||||
"mandate_options": null,
|
||||
"network": null,
|
||||
"request_three_d_secure": "automatic"
|
||||
}
|
||||
},
|
||||
"payment_method_types": [
|
||||
"card"
|
||||
],
|
||||
"processing": null,
|
||||
"receipt_email": null,
|
||||
"review": null,
|
||||
"setup_future_usage": null,
|
||||
"shipping": null,
|
||||
"source": null,
|
||||
"statement_descriptor": null,
|
||||
"statement_descriptor_suffix": null,
|
||||
"status": "succeeded",
|
||||
"transfer_data": null,
|
||||
"transfer_group": null
|
||||
}
|
||||
recorded_at: Mon, 07 Nov 2022 17:22:26 GMT
|
||||
recorded_with: VCR 6.0.0
|
@ -13,6 +13,8 @@ http_interactions:
|
||||
- Bearer sk_test_testfaketestfaketestfake
|
||||
Content-Type:
|
||||
- application/x-www-form-urlencoded
|
||||
X-Stripe-Client-Telemetry:
|
||||
- '{"last_request_metrics":{"request_id":"req_Xmih0ndHQjzde4","request_duration_ms":2}}'
|
||||
Stripe-Version:
|
||||
- '2019-08-14'
|
||||
X-Stripe-Client-User-Agent:
|
||||
@ -31,7 +33,7 @@ http_interactions:
|
||||
Server:
|
||||
- nginx
|
||||
Date:
|
||||
- Mon, 07 Nov 2022 17:22:23 GMT
|
||||
- Tue, 22 Nov 2022 10:23:29 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
@ -51,11 +53,11 @@ http_interactions:
|
||||
Cache-Control:
|
||||
- no-cache, no-store
|
||||
Idempotency-Key:
|
||||
- 3d420ca2-b79a-451b-88bc-56efb989ae3e
|
||||
- 524422f2-06be-4b13-a164-1d78f9221db3
|
||||
Original-Request:
|
||||
- req_7tnZKRYjblvjJF
|
||||
- req_LJ3F130BMDTCDc
|
||||
Request-Id:
|
||||
- req_7tnZKRYjblvjJF
|
||||
- req_LJ3F130BMDTCDc
|
||||
Stripe-Should-Retry:
|
||||
- 'false'
|
||||
Stripe-Version:
|
||||
@ -66,7 +68,7 @@ http_interactions:
|
||||
encoding: UTF-8
|
||||
string: |-
|
||||
{
|
||||
"id": "pm_1M1Z1f2sOmf47Nz9y4qaYQap",
|
||||
"id": "pm_1M6tdV2sOmf47Nz9ZhwXBRTL",
|
||||
"object": "payment_method",
|
||||
"billing_details": {
|
||||
"address": {
|
||||
@ -106,19 +108,19 @@ http_interactions:
|
||||
},
|
||||
"wallet": null
|
||||
},
|
||||
"created": 1667841743,
|
||||
"created": 1669112609,
|
||||
"customer": null,
|
||||
"livemode": false,
|
||||
"metadata": {},
|
||||
"type": "card"
|
||||
}
|
||||
recorded_at: Mon, 07 Nov 2022 17:22:24 GMT
|
||||
recorded_at: Tue, 22 Nov 2022 10:23:29 GMT
|
||||
- request:
|
||||
method: post
|
||||
uri: https://api.stripe.com/v1/payment_intents
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: payment_method=pm_1M1Z1f2sOmf47Nz9y4qaYQap&amount=162500¤cy=usd&confirmation_method=manual&confirm=true&customer=cus_8CyNk3UTi8lvCc
|
||||
string: payment_method=pm_1M6tdV2sOmf47Nz9ZhwXBRTL&amount=162500¤cy=usd&confirmation_method=manual&confirm=true&customer=cus_8CyNk3UTi8lvCc
|
||||
headers:
|
||||
User-Agent:
|
||||
- Stripe/v1 RubyBindings/5.29.0
|
||||
@ -127,7 +129,7 @@ http_interactions:
|
||||
Content-Type:
|
||||
- application/x-www-form-urlencoded
|
||||
X-Stripe-Client-Telemetry:
|
||||
- '{"last_request_metrics":{"request_id":"req_7tnZKRYjblvjJF","request_duration_ms":697}}'
|
||||
- '{"last_request_metrics":{"request_id":"req_LJ3F130BMDTCDc","request_duration_ms":737}}'
|
||||
Stripe-Version:
|
||||
- '2019-08-14'
|
||||
X-Stripe-Client-User-Agent:
|
||||
@ -146,7 +148,7 @@ http_interactions:
|
||||
Server:
|
||||
- nginx
|
||||
Date:
|
||||
- Mon, 07 Nov 2022 17:22:25 GMT
|
||||
- Tue, 22 Nov 2022 10:23:31 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
@ -166,11 +168,11 @@ http_interactions:
|
||||
Cache-Control:
|
||||
- no-cache, no-store
|
||||
Idempotency-Key:
|
||||
- 5294d6d7-d766-4aa5-bac1-be52f491fa20
|
||||
- 3c835ee8-86ea-4896-811f-0d3e0785cd09
|
||||
Original-Request:
|
||||
- req_c0S6XFCR5hlcc9
|
||||
- req_ybBnHONjaSyx1X
|
||||
Request-Id:
|
||||
- req_c0S6XFCR5hlcc9
|
||||
- req_ybBnHONjaSyx1X
|
||||
Stripe-Should-Retry:
|
||||
- 'false'
|
||||
Stripe-Version:
|
||||
@ -181,7 +183,7 @@ http_interactions:
|
||||
encoding: UTF-8
|
||||
string: |-
|
||||
{
|
||||
"id": "pi_3M1Z1g2sOmf47Nz91KcAbrWR",
|
||||
"id": "pi_3M6tdW2sOmf47Nz90KZ43vXZ",
|
||||
"object": "payment_intent",
|
||||
"amount": 162500,
|
||||
"amount_capturable": 0,
|
||||
@ -199,7 +201,7 @@ http_interactions:
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "ch_3M1Z1g2sOmf47Nz91YJQvMPK",
|
||||
"id": "ch_3M6tdW2sOmf47Nz90CWztIp1",
|
||||
"object": "charge",
|
||||
"amount": 162500,
|
||||
"amount_captured": 162500,
|
||||
@ -207,7 +209,7 @@ http_interactions:
|
||||
"application": null,
|
||||
"application_fee": null,
|
||||
"application_fee_amount": null,
|
||||
"balance_transaction": "txn_3M1Z1g2sOmf47Nz913ZvXNvF",
|
||||
"balance_transaction": "txn_3M6tdW2sOmf47Nz90xHPB7ge",
|
||||
"billing_details": {
|
||||
"address": {
|
||||
"city": null,
|
||||
@ -223,7 +225,7 @@ http_interactions:
|
||||
},
|
||||
"calculated_statement_descriptor": "Stripe",
|
||||
"captured": true,
|
||||
"created": 1667841744,
|
||||
"created": 1669112610,
|
||||
"currency": "usd",
|
||||
"customer": "cus_8CyNk3UTi8lvCc",
|
||||
"description": null,
|
||||
@ -243,13 +245,13 @@ http_interactions:
|
||||
"network_status": "approved_by_network",
|
||||
"reason": null,
|
||||
"risk_level": "normal",
|
||||
"risk_score": 45,
|
||||
"risk_score": 19,
|
||||
"seller_message": "Payment complete.",
|
||||
"type": "authorized"
|
||||
},
|
||||
"paid": true,
|
||||
"payment_intent": "pi_3M1Z1g2sOmf47Nz91KcAbrWR",
|
||||
"payment_method": "pm_1M1Z1f2sOmf47Nz9y4qaYQap",
|
||||
"payment_intent": "pi_3M6tdW2sOmf47Nz90KZ43vXZ",
|
||||
"payment_method": "pm_1M6tdV2sOmf47Nz9ZhwXBRTL",
|
||||
"payment_method_details": {
|
||||
"card": {
|
||||
"brand": "visa",
|
||||
@ -274,14 +276,14 @@ http_interactions:
|
||||
},
|
||||
"receipt_email": null,
|
||||
"receipt_number": null,
|
||||
"receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xMDNyRTYyc09tZjQ3Tno5KNH9pJsGMgbMAdBS9Kg6LBbMQFUWP1mmaiHjAUCgW-WYHRH5dwIHTlYhiTVjiSL5fqEMQr17GSJhWPA-",
|
||||
"receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xMDNyRTYyc09tZjQ3Tno5KKPG8psGMgbPi1sBzwo6LBYq6qekkFXHe51z6Ei_GniUXrfPnNwAP9pxTHhMG3SSyEbSD-_jgxqNwc0o",
|
||||
"refunded": false,
|
||||
"refunds": {
|
||||
"object": "list",
|
||||
"data": [],
|
||||
"has_more": false,
|
||||
"total_count": 0,
|
||||
"url": "/v1/charges/ch_3M1Z1g2sOmf47Nz91YJQvMPK/refunds"
|
||||
"url": "/v1/charges/ch_3M6tdW2sOmf47Nz90CWztIp1/refunds"
|
||||
},
|
||||
"review": null,
|
||||
"shipping": null,
|
||||
@ -296,11 +298,11 @@ http_interactions:
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/charges?payment_intent=pi_3M1Z1g2sOmf47Nz91KcAbrWR"
|
||||
"url": "/v1/charges?payment_intent=pi_3M6tdW2sOmf47Nz90KZ43vXZ"
|
||||
},
|
||||
"client_secret": "pi_3M1Z1g2sOmf47Nz91KcAbrWR_secret_ocSaI8LfCIvNBzfXl5iwRB9kS",
|
||||
"client_secret": "pi_3M6tdW2sOmf47Nz90KZ43vXZ_secret_EZPT1VWG8jNZgqaW0iqXN0LjY",
|
||||
"confirmation_method": "manual",
|
||||
"created": 1667841744,
|
||||
"created": 1669112610,
|
||||
"currency": "usd",
|
||||
"customer": "cus_8CyNk3UTi8lvCc",
|
||||
"description": null,
|
||||
@ -310,7 +312,7 @@ http_interactions:
|
||||
"metadata": {},
|
||||
"next_action": null,
|
||||
"on_behalf_of": null,
|
||||
"payment_method": "pm_1M1Z1f2sOmf47Nz9y4qaYQap",
|
||||
"payment_method": "pm_1M6tdV2sOmf47Nz9ZhwXBRTL",
|
||||
"payment_method_options": {
|
||||
"card": {
|
||||
"installments": null,
|
||||
@ -334,5 +336,5 @@ http_interactions:
|
||||
"transfer_data": null,
|
||||
"transfer_group": null
|
||||
}
|
||||
recorded_at: Mon, 07 Nov 2022 17:22:26 GMT
|
||||
recorded_at: Tue, 22 Nov 2022 10:23:31 GMT
|
||||
recorded_with: VCR 6.0.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user