1
0
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:
Sylvain 2021-04-16 16:03:10 +02:00
parent 4ffd357841
commit a897d37b2d
17 changed files with 104 additions and 42 deletions

View File

@ -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)

View File

@ -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?

View File

@ -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
{ {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
) )

View File

@ -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

View File

@ -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

View File

@ -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