mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-28 09:24:24 +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
|
||||
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 })
|
||||
PaymentScheduleItemWorker.new.perform(@payment_schedule_item.id)
|
||||
|
@ -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
|
||||
customer: current_user.stp_customer_id # FIXME
|
||||
}, { api_key: Setting.get('stripe_secret_key') }
|
||||
)
|
||||
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."
|
||||
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
|
||||
{
|
||||
|
@ -2,4 +2,32 @@
|
||||
|
||||
# When a footprint is generated, the associated data is kept to allow further verifications
|
||||
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
|
||||
|
@ -8,16 +8,24 @@ class Footprintable < ApplicationRecord
|
||||
[]
|
||||
end
|
||||
|
||||
def check_footprint
|
||||
footprint == compute_footprint
|
||||
def footprint_children
|
||||
[]
|
||||
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
|
||||
save!
|
||||
FootprintDebug.create!(
|
||||
footprint: footprint,
|
||||
data: FootprintService.footprint_data(self.class, self, sort_on),
|
||||
data: FootprintService.footprint_data(self.class, self),
|
||||
klass: self.class.name
|
||||
)
|
||||
end
|
||||
@ -28,7 +36,7 @@ class Footprintable < ApplicationRecord
|
||||
|
||||
protected
|
||||
|
||||
def compute_footprint(sort_on = 'id')
|
||||
FootprintService.compute_footprint(self.class, self, sort_on)
|
||||
def compute_footprint
|
||||
FootprintService.compute_footprint(self.class, self)
|
||||
end
|
||||
end
|
||||
|
@ -7,17 +7,11 @@ class HistoryValue < Footprintable
|
||||
|
||||
after_create :chain_record
|
||||
|
||||
def chain_record
|
||||
super('created_at')
|
||||
def sort_on_field
|
||||
'created_at'
|
||||
end
|
||||
|
||||
def user
|
||||
invoicing_profile.user
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compute_footprint
|
||||
super('created_at')
|
||||
end
|
||||
end
|
||||
|
@ -21,6 +21,7 @@ class Invoice < PaymentDocument
|
||||
|
||||
has_one :avoir, class_name: 'Invoice', foreign_key: :invoice_id, dependent: :destroy
|
||||
has_one :payment_schedule_item
|
||||
has_one :payment_gateway_object
|
||||
belongs_to :operator_profile, foreign_key: :operator_profile_id, class_name: 'InvoicingProfile'
|
||||
|
||||
before_create :add_environment
|
||||
@ -157,11 +158,12 @@ class Invoice < PaymentDocument
|
||||
res
|
||||
end
|
||||
|
||||
def check_footprint
|
||||
invoice_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||
def footprint_children
|
||||
invoice_items
|
||||
end
|
||||
|
||||
def paid_with_stripe?
|
||||
# FIXME
|
||||
stp_payment_intent_id? || stp_invoice_id? || payment_method == 'stripe'
|
||||
end
|
||||
|
||||
|
@ -6,6 +6,7 @@ class InvoiceItem < Footprintable
|
||||
belongs_to :subscription
|
||||
|
||||
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_update :log_changes
|
||||
|
@ -16,6 +16,7 @@ class PaymentSchedule < PaymentDocument
|
||||
belongs_to :reservation, foreign_type: 'Reservation', foreign_key: 'scheduled_id'
|
||||
|
||||
has_many :payment_schedule_items
|
||||
has_many :payment_gateway_object
|
||||
|
||||
before_create :add_environment
|
||||
after_create :update_reference, :chain_record
|
||||
@ -57,8 +58,8 @@ class PaymentSchedule < PaymentDocument
|
||||
File.binwrite(file, pdf)
|
||||
end
|
||||
|
||||
def check_footprint
|
||||
payment_schedule_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||
def footprint_children
|
||||
payment_schedule_items
|
||||
end
|
||||
|
||||
def post_save(setup_intent_id)
|
||||
|
@ -14,7 +14,7 @@ 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)
|
||||
|
@ -8,6 +8,7 @@ class Subscription < ApplicationRecord
|
||||
belongs_to :statistic_profile
|
||||
|
||||
has_one :payment_schedule, as: :scheduled, dependent: :destroy
|
||||
has_one :payment_gateway_object
|
||||
has_many :invoices, as: :invoiced, dependent: :destroy
|
||||
has_many :offer_days, dependent: :destroy
|
||||
|
||||
|
@ -178,6 +178,7 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
def stripe_customer
|
||||
# FIXME
|
||||
Stripe::Customer.retrieve(stp_customer_id, api_key: Setting.get('stripe_secret_key'))
|
||||
end
|
||||
|
||||
|
@ -6,50 +6,53 @@ require 'integrity/checksum'
|
||||
class FootprintService
|
||||
class << self
|
||||
# Compute the footprint
|
||||
# @param klass a class inheriting from Footprintable
|
||||
# @param item 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, sort_on = 'id')
|
||||
Integrity::Checksum.text(FootprintService.footprint_data(klass, item, sort_on))
|
||||
# @param klass {Class} a class inheriting from Footprintable
|
||||
# @param item {Footprintable} an instance of the provided class
|
||||
def compute_footprint(klass, item)
|
||||
Integrity::Checksum.text(FootprintService.footprint_data(klass, item))
|
||||
end
|
||||
|
||||
# Return the original data string used to compute the footprint
|
||||
# @param klass a class inheriting from Footprintable
|
||||
# @param item an instance of the provided class
|
||||
# @param sort_on the items in database by the provided criterion, to find the previous one
|
||||
def footprint_data(klass, item, sort_on = 'id')
|
||||
# @param klass {Class} a class inheriting from Footprintable
|
||||
# @param item {Footprintable} an instance of the provided class
|
||||
# @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, array: false)
|
||||
raise TypeError unless item.is_a? klass
|
||||
|
||||
sort_on = item.sort_on_field
|
||||
previous = klass.where("#{sort_on} < ?", item[sort_on])
|
||||
.order("#{sort_on} DESC")
|
||||
.limit(1)
|
||||
|
||||
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
|
||||
|
||||
# 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)
|
||||
klass.columns.map(&:name).delete_if { |c| %w[footprint updated_at].concat(klass.columns_out_of_footprint).include? c }
|
||||
end
|
||||
|
||||
# Logs a debugging message to help finding why a footprint is invalid
|
||||
# @param klass a class inheriting from Footprintable
|
||||
# @param item an instance of the provided class
|
||||
# @param klass {Class} a class inheriting from Footprintable
|
||||
# @param item {Footprintable} an instance of the provided class
|
||||
def debug_footprint(klass, item)
|
||||
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)
|
||||
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 '-----------------------------------------'
|
||||
puts "columns: [ #{columns.join(', ')} ]"
|
||||
puts "current: #{current}"
|
||||
puts " saved: #{saved&.data}"
|
||||
puts "others possible matches: #{others.map(&:id)}"
|
||||
puts " saved: #{saved.format_data(item.id)}"
|
||||
puts '-----------------------------------------'
|
||||
puts "other possible matches IDs: #{others.map(&:id)}"
|
||||
puts '-----------------------------------------'
|
||||
item.footprint_children.map(&:debug_footprint)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -83,7 +83,7 @@ class InvoicesService
|
||||
invoicing_profile: user.invoicing_profile,
|
||||
statistic_profile: user.statistic_profile,
|
||||
operator_profile_id: operator_profile_id,
|
||||
stp_payment_intent_id: payment_id,
|
||||
stp_payment_intent_id: payment_id, # FIXME
|
||||
payment_method: method
|
||||
)
|
||||
|
||||
|
@ -114,6 +114,19 @@ class MigrateStripeIdsToPaymentGatewayObjects < ActiveRecord::Migration[5.2]
|
||||
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
|
||||
InvoiceItem.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_setup_intent_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|
|
||||
add_column klass.arel_table.name, :stp_product_id, :string
|
||||
end
|
||||
@ -155,6 +169,8 @@ class MigrateStripeIdsToPaymentGatewayObjects < ActiveRecord::Migration[5.2]
|
||||
'stp_invoice_item_id'
|
||||
when 'Stripe::PaymentIntent'
|
||||
'stp_payment_intent_id'
|
||||
when 'Stripe::Customer'
|
||||
'stp_customer_id'
|
||||
else
|
||||
raise "Unknown gateway_object_type #{pgo.gateway_object_type}"
|
||||
end
|
||||
|
@ -13,12 +13,18 @@ class Integrity::ArchiveHelper
|
||||
last_period = AccountingPeriod.order(start_at: :desc).first
|
||||
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|
|
||||
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
|
||||
else
|
||||
puts 'Checking all invoices footprints. This may take a while...'
|
||||
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
|
||||
|
@ -102,7 +102,7 @@ namespace :fablab do
|
||||
klass.all.each do |item|
|
||||
FootprintDebug.create!(
|
||||
footprint: item.footprint,
|
||||
data: FootprintService.footprint_data(klass, item, klass == 'HistoryValue' ? 'created_at' : 'id'),
|
||||
data: FootprintService.footprint_data(klass, item),
|
||||
klass: klass
|
||||
)
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user