mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-29 10:24:20 +01:00
improved footprint debug process
This commit is contained in:
parent
4ffd357841
commit
a897d37b2d
@ -53,7 +53,7 @@ class API::PaymentSchedulesController < API::ApiController
|
|||||||
|
|
||||||
def pay_item
|
def pay_item
|
||||||
authorize @payment_schedule_item.payment_schedule
|
authorize @payment_schedule_item.payment_schedule
|
||||||
|
# FIXME
|
||||||
stripe_key = Setting.get('stripe_secret_key')
|
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.stp_invoice_id, {}, { api_key: stripe_key })
|
||||||
PaymentScheduleItemWorker.new.perform(@payment_schedule_item.id)
|
PaymentScheduleItemWorker.new.perform(@payment_schedule_item.id)
|
||||||
|
@ -30,7 +30,7 @@ class API::StripeController < API::PaymentsController
|
|||||||
currency: Setting.get('stripe_currency'),
|
currency: Setting.get('stripe_currency'),
|
||||||
confirmation_method: 'manual',
|
confirmation_method: 'manual',
|
||||||
confirm: true,
|
confirm: true,
|
||||||
customer: current_user.stp_customer_id
|
customer: current_user.stp_customer_id # FIXME
|
||||||
}, { api_key: Setting.get('stripe_secret_key') }
|
}, { api_key: Setting.get('stripe_secret_key') }
|
||||||
)
|
)
|
||||||
elsif params[:payment_intent_id].present?
|
elsif params[:payment_intent_id].present?
|
||||||
|
@ -16,6 +16,7 @@ class OpenAPI::V1::InvoicesDoc < OpenAPI::V1::BaseDoc
|
|||||||
description "Index of users' invoices, with optional pagination. Order by *created_at* descendant."
|
description "Index of users' invoices, with optional pagination. Order by *created_at* descendant."
|
||||||
param_group :pagination
|
param_group :pagination
|
||||||
param :user_id, [Integer, Array], optional: true, desc: 'Scope the request to one or various users.'
|
param :user_id, [Integer, Array], optional: true, desc: 'Scope the request to one or various users.'
|
||||||
|
# FIXME
|
||||||
example <<-INVOICES
|
example <<-INVOICES
|
||||||
# /open_api/v1/invoices?user_id=211&page=1&per_page=3
|
# /open_api/v1/invoices?user_id=211&page=1&per_page=3
|
||||||
{
|
{
|
||||||
|
@ -2,4 +2,32 @@
|
|||||||
|
|
||||||
# When a footprint is generated, the associated data is kept to allow further verifications
|
# When a footprint is generated, the associated data is kept to allow further verifications
|
||||||
class FootprintDebug < ApplicationRecord
|
class FootprintDebug < ApplicationRecord
|
||||||
|
# We try to rebuild the data, column by column, from the db object
|
||||||
|
# If any datum oes not match, we print it as ERROR
|
||||||
|
def format_data(item_id)
|
||||||
|
item = klass.constantize.find(item_id)
|
||||||
|
columns = FootprintService.footprint_columns(klass.constantize)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
index = 0
|
||||||
|
columns.each do |column|
|
||||||
|
col_data = item[column]
|
||||||
|
end_idx = index + col_data.to_s.length - 1
|
||||||
|
|
||||||
|
if data[index..end_idx] == col_data.to_s
|
||||||
|
# if the item data for the current column matches, save it into the results and move forward teh cursor
|
||||||
|
result.push(col_data.to_s)
|
||||||
|
index = end_idx + 1
|
||||||
|
else
|
||||||
|
# if the item data for the current column does not matches, mark it as an error, display the next chars, but do not move the cursor
|
||||||
|
datum = data[index..end_idx]
|
||||||
|
datum = data[index..index + 5] if datum&.empty?
|
||||||
|
result.push "ERROR (#{datum}...)"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# the remaining data is the previous record checksum
|
||||||
|
result.push(data[index..-1])
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -8,16 +8,24 @@ class Footprintable < ApplicationRecord
|
|||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_footprint
|
def footprint_children
|
||||||
footprint == compute_footprint
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
def chain_record(sort_on = 'id')
|
def sort_on_field
|
||||||
|
'id'
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_footprint
|
||||||
|
footprint_children.map(&:check_footprint).all? && footprint == compute_footprint
|
||||||
|
end
|
||||||
|
|
||||||
|
def chain_record
|
||||||
self.footprint = compute_footprint
|
self.footprint = compute_footprint
|
||||||
save!
|
save!
|
||||||
FootprintDebug.create!(
|
FootprintDebug.create!(
|
||||||
footprint: footprint,
|
footprint: footprint,
|
||||||
data: FootprintService.footprint_data(self.class, self, sort_on),
|
data: FootprintService.footprint_data(self.class, self),
|
||||||
klass: self.class.name
|
klass: self.class.name
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
@ -28,7 +36,7 @@ class Footprintable < ApplicationRecord
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def compute_footprint(sort_on = 'id')
|
def compute_footprint
|
||||||
FootprintService.compute_footprint(self.class, self, sort_on)
|
FootprintService.compute_footprint(self.class, self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -7,17 +7,11 @@ class HistoryValue < Footprintable
|
|||||||
|
|
||||||
after_create :chain_record
|
after_create :chain_record
|
||||||
|
|
||||||
def chain_record
|
def sort_on_field
|
||||||
super('created_at')
|
'created_at'
|
||||||
end
|
end
|
||||||
|
|
||||||
def user
|
def user
|
||||||
invoicing_profile.user
|
invoicing_profile.user
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def compute_footprint
|
|
||||||
super('created_at')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -21,6 +21,7 @@ class Invoice < PaymentDocument
|
|||||||
|
|
||||||
has_one :avoir, class_name: 'Invoice', foreign_key: :invoice_id, dependent: :destroy
|
has_one :avoir, class_name: 'Invoice', foreign_key: :invoice_id, dependent: :destroy
|
||||||
has_one :payment_schedule_item
|
has_one :payment_schedule_item
|
||||||
|
has_one :payment_gateway_object
|
||||||
belongs_to :operator_profile, foreign_key: :operator_profile_id, class_name: 'InvoicingProfile'
|
belongs_to :operator_profile, foreign_key: :operator_profile_id, class_name: 'InvoicingProfile'
|
||||||
|
|
||||||
before_create :add_environment
|
before_create :add_environment
|
||||||
@ -157,11 +158,12 @@ class Invoice < PaymentDocument
|
|||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_footprint
|
def footprint_children
|
||||||
invoice_items.map(&:check_footprint).all? && footprint == compute_footprint
|
invoice_items
|
||||||
end
|
end
|
||||||
|
|
||||||
def paid_with_stripe?
|
def paid_with_stripe?
|
||||||
|
# FIXME
|
||||||
stp_payment_intent_id? || stp_invoice_id? || payment_method == 'stripe'
|
stp_payment_intent_id? || stp_invoice_id? || payment_method == 'stripe'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ class InvoiceItem < Footprintable
|
|||||||
belongs_to :subscription
|
belongs_to :subscription
|
||||||
|
|
||||||
has_one :invoice_item # associates invoice_items of an invoice to invoice_items of an Avoir
|
has_one :invoice_item # associates invoice_items of an invoice to invoice_items of an Avoir
|
||||||
|
has_one :payment_gateway_object
|
||||||
|
|
||||||
after_create :chain_record
|
after_create :chain_record
|
||||||
after_update :log_changes
|
after_update :log_changes
|
||||||
|
@ -16,6 +16,7 @@ class PaymentSchedule < PaymentDocument
|
|||||||
belongs_to :reservation, foreign_type: 'Reservation', foreign_key: 'scheduled_id'
|
belongs_to :reservation, foreign_type: 'Reservation', foreign_key: 'scheduled_id'
|
||||||
|
|
||||||
has_many :payment_schedule_items
|
has_many :payment_schedule_items
|
||||||
|
has_many :payment_gateway_object
|
||||||
|
|
||||||
before_create :add_environment
|
before_create :add_environment
|
||||||
after_create :update_reference, :chain_record
|
after_create :update_reference, :chain_record
|
||||||
@ -57,8 +58,8 @@ class PaymentSchedule < PaymentDocument
|
|||||||
File.binwrite(file, pdf)
|
File.binwrite(file, pdf)
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_footprint
|
def footprint_children
|
||||||
payment_schedule_items.map(&:check_footprint).all? && footprint == compute_footprint
|
payment_schedule_items
|
||||||
end
|
end
|
||||||
|
|
||||||
def post_save(setup_intent_id)
|
def post_save(setup_intent_id)
|
||||||
|
@ -14,7 +14,7 @@ class PaymentScheduleItem < Footprintable
|
|||||||
|
|
||||||
def payment_intent
|
def payment_intent
|
||||||
return unless payment_gateway_object
|
return unless payment_gateway_object
|
||||||
|
# FIXME
|
||||||
key = Setting.get('stripe_secret_key')
|
key = Setting.get('stripe_secret_key')
|
||||||
stp_invoice = Stripe::Invoice.retrieve(stp_invoice_id, api_key: key)
|
stp_invoice = Stripe::Invoice.retrieve(stp_invoice_id, api_key: key)
|
||||||
Stripe::PaymentIntent.retrieve(stp_invoice.payment_intent, api_key: key)
|
Stripe::PaymentIntent.retrieve(stp_invoice.payment_intent, api_key: key)
|
||||||
|
@ -8,6 +8,7 @@ class Subscription < ApplicationRecord
|
|||||||
belongs_to :statistic_profile
|
belongs_to :statistic_profile
|
||||||
|
|
||||||
has_one :payment_schedule, as: :scheduled, dependent: :destroy
|
has_one :payment_schedule, as: :scheduled, dependent: :destroy
|
||||||
|
has_one :payment_gateway_object
|
||||||
has_many :invoices, as: :invoiced, dependent: :destroy
|
has_many :invoices, as: :invoiced, dependent: :destroy
|
||||||
has_many :offer_days, dependent: :destroy
|
has_many :offer_days, dependent: :destroy
|
||||||
|
|
||||||
|
@ -178,6 +178,7 @@ class User < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def stripe_customer
|
def stripe_customer
|
||||||
|
# FIXME
|
||||||
Stripe::Customer.retrieve(stp_customer_id, api_key: Setting.get('stripe_secret_key'))
|
Stripe::Customer.retrieve(stp_customer_id, api_key: Setting.get('stripe_secret_key'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,50 +6,53 @@ require 'integrity/checksum'
|
|||||||
class FootprintService
|
class FootprintService
|
||||||
class << self
|
class << self
|
||||||
# Compute the footprint
|
# Compute the footprint
|
||||||
# @param klass a class inheriting from Footprintable
|
# @param klass {Class} a class inheriting from Footprintable
|
||||||
# @param item an instance of the provided class
|
# @param item {Footprintable} an instance of the provided class
|
||||||
# @param sort_on the items in database by the provided criterion, to find the previous one
|
def compute_footprint(klass, item)
|
||||||
def compute_footprint(klass, item, sort_on = 'id')
|
Integrity::Checksum.text(FootprintService.footprint_data(klass, item))
|
||||||
Integrity::Checksum.text(FootprintService.footprint_data(klass, item, sort_on))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return the original data string used to compute the footprint
|
# Return the original data string used to compute the footprint
|
||||||
# @param klass a class inheriting from Footprintable
|
# @param klass {Class} a class inheriting from Footprintable
|
||||||
# @param item an instance of the provided class
|
# @param item {Footprintable} an instance of the provided class
|
||||||
# @param sort_on the items in database by the provided criterion, to find the previous one
|
# @param array {Boolean} if true, the result is return on the form of an array, otherwise a concatenated string is returned
|
||||||
def footprint_data(klass, item, sort_on = 'id')
|
def footprint_data(klass, item, array: false)
|
||||||
raise TypeError unless item.is_a? klass
|
raise TypeError unless item.is_a? klass
|
||||||
|
|
||||||
|
sort_on = item.sort_on_field
|
||||||
previous = klass.where("#{sort_on} < ?", item[sort_on])
|
previous = klass.where("#{sort_on} < ?", item[sort_on])
|
||||||
.order("#{sort_on} DESC")
|
.order("#{sort_on} DESC")
|
||||||
.limit(1)
|
.limit(1)
|
||||||
|
|
||||||
columns = FootprintService.footprint_columns(klass)
|
columns = FootprintService.footprint_columns(klass)
|
||||||
|
|
||||||
"#{columns.map { |c| comparable(item[c]) }.join}#{previous.first ? previous.first.footprint : ''}"
|
res = columns.map { |c| comparable(item[c]) }.push(previous.first ? previous.first.footprint : '')
|
||||||
|
array ? res.map(&:to_s) : res.join.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return an ordered array of the columns used in the footprint computation
|
# Return an ordered array of the columns used in the footprint computation
|
||||||
# @param klass a class inheriting from Footprintable
|
# @param klass {Class} a class inheriting from Footprintable
|
||||||
def footprint_columns(klass)
|
def footprint_columns(klass)
|
||||||
klass.columns.map(&:name).delete_if { |c| %w[footprint updated_at].concat(klass.columns_out_of_footprint).include? c }
|
klass.columns.map(&:name).delete_if { |c| %w[footprint updated_at].concat(klass.columns_out_of_footprint).include? c }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Logs a debugging message to help finding why a footprint is invalid
|
# Logs a debugging message to help finding why a footprint is invalid
|
||||||
# @param klass a class inheriting from Footprintable
|
# @param klass {Class} a class inheriting from Footprintable
|
||||||
# @param item an instance of the provided class
|
# @param item {Footprintable} an instance of the provided class
|
||||||
def debug_footprint(klass, item)
|
def debug_footprint(klass, item)
|
||||||
columns = FootprintService.footprint_columns(klass)
|
columns = FootprintService.footprint_columns(klass)
|
||||||
current = FootprintService.footprint_data(klass, item)
|
current = FootprintService.footprint_data(klass, item, array: true)
|
||||||
saved = FootprintDebug.find_by(footprint: item.footprint, klass: klass.name)
|
saved = FootprintDebug.find_by(footprint: item.footprint, klass: klass.name)
|
||||||
others = FootprintDebug.where('klass = ? AND data LIKE ?', klass, "#{item.id}%")
|
others = FootprintDebug.where('klass = ? AND data LIKE ? AND id != ?', klass, "#{item.id}%", saved.id)
|
||||||
puts "Debug footprint for #{klass} [ id: #{item.id} ]"
|
puts "Debug footprint for #{klass} [ id: #{item.id} ]"
|
||||||
puts '-----------------------------------------'
|
puts '-----------------------------------------'
|
||||||
puts "columns: [ #{columns.join(', ')} ]"
|
puts "columns: [ #{columns.join(', ')} ]"
|
||||||
puts "current: #{current}"
|
puts "current: #{current}"
|
||||||
puts " saved: #{saved&.data}"
|
puts " saved: #{saved.format_data(item.id)}"
|
||||||
puts "others possible matches: #{others.map(&:id)}"
|
|
||||||
puts '-----------------------------------------'
|
puts '-----------------------------------------'
|
||||||
|
puts "other possible matches IDs: #{others.map(&:id)}"
|
||||||
|
puts '-----------------------------------------'
|
||||||
|
item.footprint_children.map(&:debug_footprint)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -83,7 +83,7 @@ class InvoicesService
|
|||||||
invoicing_profile: user.invoicing_profile,
|
invoicing_profile: user.invoicing_profile,
|
||||||
statistic_profile: user.statistic_profile,
|
statistic_profile: user.statistic_profile,
|
||||||
operator_profile_id: operator_profile_id,
|
operator_profile_id: operator_profile_id,
|
||||||
stp_payment_intent_id: payment_id,
|
stp_payment_intent_id: payment_id, # FIXME
|
||||||
payment_method: method
|
payment_method: method
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -114,6 +114,19 @@ class MigrateStripeIdsToPaymentGatewayObjects < ActiveRecord::Migration[5.2]
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
## USERS
|
||||||
|
puts 'Migrating users. This may take a while...'
|
||||||
|
User.order(:id).all.each do |u|
|
||||||
|
next unless u.stp_customer_id
|
||||||
|
|
||||||
|
PaymentGatewayObject.create!(
|
||||||
|
item: u,
|
||||||
|
gateway_object_id: u.stp_customer_id,
|
||||||
|
gateway_object_type: 'Stripe::Customer'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
remove_column :users, :stp_customer_id
|
||||||
|
|
||||||
# chain all records
|
# chain all records
|
||||||
InvoiceItem.order(:id).all.each(&:chain_record)
|
InvoiceItem.order(:id).all.each(&:chain_record)
|
||||||
Invoice.order(:id).all.each(&:chain_record)
|
Invoice.order(:id).all.each(&:chain_record)
|
||||||
@ -138,6 +151,7 @@ class MigrateStripeIdsToPaymentGatewayObjects < ActiveRecord::Migration[5.2]
|
|||||||
add_column :payment_schedules, :stp_subscription_id, :string
|
add_column :payment_schedules, :stp_subscription_id, :string
|
||||||
add_column :payment_schedules, :stp_setup_intent_id, :string
|
add_column :payment_schedules, :stp_setup_intent_id, :string
|
||||||
add_column :payment_schedule_items, :stp_invoice_id, :string
|
add_column :payment_schedule_items, :stp_invoice_id, :string
|
||||||
|
add_column :users, :stp_customer_id, :string
|
||||||
[Plan, Machine, Space, Training].each do |klass|
|
[Plan, Machine, Space, Training].each do |klass|
|
||||||
add_column klass.arel_table.name, :stp_product_id, :string
|
add_column klass.arel_table.name, :stp_product_id, :string
|
||||||
end
|
end
|
||||||
@ -155,6 +169,8 @@ class MigrateStripeIdsToPaymentGatewayObjects < ActiveRecord::Migration[5.2]
|
|||||||
'stp_invoice_item_id'
|
'stp_invoice_item_id'
|
||||||
when 'Stripe::PaymentIntent'
|
when 'Stripe::PaymentIntent'
|
||||||
'stp_payment_intent_id'
|
'stp_payment_intent_id'
|
||||||
|
when 'Stripe::Customer'
|
||||||
|
'stp_customer_id'
|
||||||
else
|
else
|
||||||
raise "Unknown gateway_object_type #{pgo.gateway_object_type}"
|
raise "Unknown gateway_object_type #{pgo.gateway_object_type}"
|
||||||
end
|
end
|
||||||
|
@ -13,12 +13,18 @@ class Integrity::ArchiveHelper
|
|||||||
last_period = AccountingPeriod.order(start_at: :desc).first
|
last_period = AccountingPeriod.order(start_at: :desc).first
|
||||||
puts "Checking invoices footprints from #{last_period.end_at}. This may take a while..."
|
puts "Checking invoices footprints from #{last_period.end_at}. This may take a while..."
|
||||||
Invoice.where('created_at > ?', last_period.end_at).order(:id).each do |i|
|
Invoice.where('created_at > ?', last_period.end_at).order(:id).each do |i|
|
||||||
raise "Invalid footprint for invoice #{i.id}" unless i.check_footprint
|
next if i.check_footprint
|
||||||
|
|
||||||
|
puts i.debug_footprint
|
||||||
|
raise "Invalid footprint for invoice #{i.id}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
puts 'Checking all invoices footprints. This may take a while...'
|
puts 'Checking all invoices footprints. This may take a while...'
|
||||||
Invoice.order(:id).all.each do |i|
|
Invoice.order(:id).all.each do |i|
|
||||||
raise "Invalid footprint for invoice #{i.id}" unless i.check_footprint
|
next if i.check_footprint
|
||||||
|
|
||||||
|
puts i.debug_footprint
|
||||||
|
raise "Invalid footprint for invoice #{i.id}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -102,7 +102,7 @@ namespace :fablab do
|
|||||||
klass.all.each do |item|
|
klass.all.each do |item|
|
||||||
FootprintDebug.create!(
|
FootprintDebug.create!(
|
||||||
footprint: item.footprint,
|
footprint: item.footprint,
|
||||||
data: FootprintService.footprint_data(klass, item, klass == 'HistoryValue' ? 'created_at' : 'id'),
|
data: FootprintService.footprint_data(klass, item),
|
||||||
klass: klass
|
klass: klass
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user