mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-17 06:52:27 +01:00
refactored Invoice & PaymentSchedule to use inheritance
This commit is contained in:
parent
6fa9780ad5
commit
bbf88846dd
18
app/controllers/api/payment_schedules_controller.rb
Normal file
18
app/controllers/api/payment_schedules_controller.rb
Normal file
@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# API Controller for resources of PaymentSchedule
|
||||
class API::PaymentSchedulesController < API::ApiController
|
||||
before_action :authenticate_user!
|
||||
before_action :set_payment_schedule, only: %i[download]
|
||||
|
||||
def download
|
||||
authorize @payment_schedule
|
||||
# TODO, send_file File.join(Rails.root, @payment_schedule.file), type: 'application/pdf', disposition: 'attachment'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_payment_schedule
|
||||
@payment_schedule = PaymentSchedule.find(params[:id])
|
||||
end
|
||||
end
|
6
app/exceptions/cannot_refund_error.rb
Normal file
6
app/exceptions/cannot_refund_error.rb
Normal file
@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Raised when the Avoir cannot be generated from an existing Invoice
|
||||
class CannotRefundError < StandardError
|
||||
end
|
||||
|
@ -28,6 +28,7 @@ function extractHumanReadableMessage(error: any): string {
|
||||
|
||||
if (typeof error === 'string') return error;
|
||||
|
||||
// parse Rails errors (as JSON)
|
||||
let message = '';
|
||||
if (error instanceof Object) {
|
||||
// iterate through all the keys to build the message
|
||||
|
@ -12,7 +12,7 @@ class Avoir < Invoice
|
||||
attr_accessor :invoice_items_ids
|
||||
|
||||
def generate_reference
|
||||
self.reference = InvoiceReferenceService.generate_reference(self, date: created_at, avoir: true)
|
||||
super(created_at)
|
||||
end
|
||||
|
||||
def expire_subscription
|
||||
|
@ -4,7 +4,7 @@ require 'checksum'
|
||||
|
||||
# Invoice correspond to a single purchase made by an user. This purchase may
|
||||
# include reservation(s) and/or a subscription
|
||||
class Invoice < Footprintable
|
||||
class Invoice < PaymentDocument
|
||||
include NotifyWith::NotificationAttachedObject
|
||||
require 'fileutils'
|
||||
scope :only_invoice, -> { where(type: nil) }
|
||||
@ -41,30 +41,21 @@ class Invoice < Footprintable
|
||||
end
|
||||
|
||||
def filename
|
||||
prefix = Setting.find_by(name: 'invoice_prefix').history_values.order(created_at: :desc).where('created_at <= ?', created_at).limit(1).first
|
||||
prefix ||= if created_at < Setting.find_by(name: 'invoice_prefix').history_values.order(created_at: :asc).limit(1).first.created_at
|
||||
Setting.find_by(name: 'invoice_prefix').history_values.order(created_at: :asc).limit(1).first
|
||||
prefix = Setting.find_by(name: 'invoice_prefix').value_at(created_at)
|
||||
prefix ||= if created_at < Setting.find_by(name: 'invoice_prefix').first_update
|
||||
Setting.find_by(name: 'invoice_prefix').first_value
|
||||
else
|
||||
Setting.find_by(name: 'invoice_prefix')..history_values.order(created_at: :desc).limit(1).first
|
||||
Setting.get('invoice_prefix')
|
||||
end
|
||||
"#{prefix.value}-#{id}_#{created_at.strftime('%d%m%Y')}.pdf"
|
||||
"#{prefix}-#{id}_#{created_at.strftime('%d%m%Y')}.pdf"
|
||||
end
|
||||
|
||||
def user
|
||||
invoicing_profile.user
|
||||
end
|
||||
|
||||
def generate_reference
|
||||
self.reference = InvoiceReferenceService.generate_reference(self)
|
||||
end
|
||||
|
||||
def update_reference
|
||||
generate_reference
|
||||
save
|
||||
end
|
||||
|
||||
def order_number
|
||||
InvoiceReferenceService.generate_order_number(self)
|
||||
PaymentDocumentService.generate_order_number(self)
|
||||
end
|
||||
|
||||
# for debug & used by rake task "fablab:maintenance:regenerate_invoices"
|
||||
@ -74,7 +65,7 @@ class Invoice < Footprintable
|
||||
end
|
||||
|
||||
def build_avoir(attrs = {})
|
||||
raise Exception if refunded? == true || prevent_refund?
|
||||
raise CannotRefundError if refunded? == true || prevent_refund?
|
||||
|
||||
avoir = Avoir.new(dup.attributes)
|
||||
avoir.type = 'Avoir'
|
||||
@ -168,21 +159,10 @@ class Invoice < Footprintable
|
||||
res
|
||||
end
|
||||
|
||||
def add_environment
|
||||
self.environment = Rails.env
|
||||
end
|
||||
|
||||
def check_footprint
|
||||
invoice_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||
end
|
||||
|
||||
def set_wallet_transaction(amount, transaction_id)
|
||||
raise InvalidFootprintError unless check_footprint
|
||||
|
||||
update_columns(wallet_amount: amount, wallet_transaction_id: transaction_id)
|
||||
chain_record
|
||||
end
|
||||
|
||||
def paid_with_stripe?
|
||||
stp_payment_intent_id? || stp_invoice_id? || payment_method == 'stripe'
|
||||
end
|
||||
|
26
app/models/payment_document.rb
Normal file
26
app/models/payment_document.rb
Normal file
@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# SuperClass for models that provides legal PDF documents concerning sales
|
||||
class PaymentDocument < Footprintable
|
||||
self.abstract_class = true
|
||||
|
||||
def generate_reference(date = DateTime.current)
|
||||
self.reference = PaymentDocumentService.generate_reference(self, date: date)
|
||||
end
|
||||
|
||||
def update_reference
|
||||
generate_reference
|
||||
save
|
||||
end
|
||||
|
||||
def add_environment
|
||||
self.environment = Rails.env
|
||||
end
|
||||
|
||||
def set_wallet_transaction(amount, transaction_id)
|
||||
raise InvalidFootprintError unless check_footprint
|
||||
|
||||
update_columns(wallet_amount: amount, wallet_transaction_id: transaction_id)
|
||||
chain_record
|
||||
end
|
||||
end
|
@ -2,7 +2,9 @@
|
||||
|
||||
# PaymentSchedule is a way for members to pay something (especially a Subscription) with multiple payment,
|
||||
# staged on a long period rather than with a single payment
|
||||
class PaymentSchedule < Footprintable
|
||||
class PaymentSchedule < PaymentDocument
|
||||
require 'fileutils'
|
||||
|
||||
belongs_to :scheduled, polymorphic: true
|
||||
belongs_to :wallet_transaction
|
||||
belongs_to :coupon
|
||||
@ -17,6 +19,24 @@ class PaymentSchedule < Footprintable
|
||||
before_create :add_environment
|
||||
after_create :update_reference, :chain_record
|
||||
|
||||
def file
|
||||
dir = "payment_schedules/#{invoicing_profile.id}"
|
||||
|
||||
# create directories if they doesn't exists (payment_schedules & invoicing_profile_id)
|
||||
FileUtils.mkdir_p dir
|
||||
"#{dir}/#{filename}"
|
||||
end
|
||||
|
||||
def filename
|
||||
prefix = Setting.find_by(name: 'invoice_prefix').value_at(created_at)
|
||||
prefix ||= if created_at < Setting.find_by(name: 'invoice_prefix').history_values.order(created_at: :asc).limit(1).first.created_at
|
||||
Setting.find_by(name: 'invoice_prefix').history_values.order(created_at: :asc).limit(1).first
|
||||
else
|
||||
Setting.find_by(name: 'invoice_prefix')..history_values.order(created_at: :desc).limit(1).first
|
||||
end
|
||||
"#{prefix.value}-#{id}_#{created_at.strftime('%d%m%Y')}.pdf"
|
||||
end
|
||||
|
||||
##
|
||||
# This is useful to check the first item because its amount may be different from the others
|
||||
##
|
||||
@ -24,22 +44,6 @@ class PaymentSchedule < Footprintable
|
||||
payment_schedule_items.order(due_date: :asc)
|
||||
end
|
||||
|
||||
def add_environment
|
||||
self.environment = Rails.env
|
||||
end
|
||||
|
||||
def update_reference
|
||||
self.reference = InvoiceReferenceService.generate_reference(self, payment_schedule: true)
|
||||
save
|
||||
end
|
||||
|
||||
def set_wallet_transaction(amount, transaction_id)
|
||||
raise InvalidFootprintError unless check_footprint
|
||||
|
||||
update_columns(wallet_amount: amount, wallet_transaction_id: transaction_id)
|
||||
chain_record
|
||||
end
|
||||
|
||||
def check_footprint
|
||||
payment_schedule_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||
end
|
||||
|
@ -110,12 +110,27 @@ class Setting < ApplicationRecord
|
||||
# WARNING: when adding a new key, you may also want to add it in app/policies/setting_policy.rb#public_whitelist
|
||||
|
||||
def value
|
||||
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).first
|
||||
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(1).first
|
||||
last_value&.value
|
||||
end
|
||||
|
||||
def value_at(date)
|
||||
val = history_values.order(HistoryValue.arel_table['created_at'].desc).where('created_at <= ?', date).limit(1).first
|
||||
val&.value
|
||||
end
|
||||
|
||||
def first_update
|
||||
first_value = history_values.order(HistoryValue.arel_table['created_at'].asc).limit(1).first
|
||||
first_value&.created_at
|
||||
end
|
||||
|
||||
def first_value
|
||||
first_value = history_values.order(HistoryValue.arel_table['created_at'].asc).limit(1).first
|
||||
first_value&.value
|
||||
end
|
||||
|
||||
def last_update
|
||||
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).first
|
||||
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(1).first
|
||||
last_value&.created_at
|
||||
end
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Provides methods to generate Invoice or PaymentSchedule references
|
||||
class InvoiceReferenceService
|
||||
# Provides methods to generate Invoice, Avoir or PaymentSchedule references
|
||||
class PaymentDocumentService
|
||||
class << self
|
||||
def generate_reference(invoice, date: DateTime.current, avoir: false, payment_schedule: false)
|
||||
def generate_reference(document, date: DateTime.current)
|
||||
pattern = Setting.get('invoice_reference')
|
||||
|
||||
reference = replace_invoice_number_pattern(pattern)
|
||||
reference = replace_date_pattern(reference, date)
|
||||
|
||||
if avoir
|
||||
if document.class == Avoir
|
||||
# information about refund/avoir (R[text])
|
||||
reference.gsub!(/R\[([^\]]+)\]/, '\1')
|
||||
|
||||
@ -17,16 +17,16 @@ class InvoiceReferenceService
|
||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||
# remove information about payment schedule (S[text])
|
||||
reference.gsub!(/S\[([^\]]+)\]/, ''.to_s)
|
||||
elsif payment_schedule
|
||||
elsif document.class == PaymentSchedule
|
||||
# information about payment schedule
|
||||
reference.gsub!(/S\[([^\]]+)\]/, '\1')
|
||||
# remove information about online selling (X[text])
|
||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||
# remove information about refunds (R[text])
|
||||
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
||||
else
|
||||
elsif document.class == Invoice
|
||||
# information about online selling (X[text])
|
||||
if invoice.paid_with_stripe?
|
||||
if document.paid_with_stripe?
|
||||
reference.gsub!(/X\[([^\]]+)\]/, '\1')
|
||||
else
|
||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||
@ -36,6 +36,8 @@ class InvoiceReferenceService
|
||||
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
||||
# remove information about payment schedule (S[text])
|
||||
reference.gsub!(/S\[([^\]]+)\]/, ''.to_s)
|
||||
else
|
||||
raise TypeError
|
||||
end
|
||||
|
||||
reference
|
||||
@ -44,7 +46,7 @@ class InvoiceReferenceService
|
||||
def generate_order_number(invoice)
|
||||
pattern = Setting.get('invoice_order-nb')
|
||||
|
||||
# global invoice number (nn..nn)
|
||||
# global document number (nn..nn)
|
||||
reference = pattern.gsub(/n+(?![^\[]*\])/) do |match|
|
||||
pad_and_truncate(number_of_invoices('global'), match.to_s.length)
|
||||
end
|
||||
@ -119,21 +121,21 @@ class InvoiceReferenceService
|
||||
end
|
||||
|
||||
##
|
||||
# Replace the invoice number elements in the provided pattern with counts from the database
|
||||
# Replace the document number elements in the provided pattern with counts from the database
|
||||
# @param reference {string}
|
||||
##
|
||||
def replace_invoice_number_pattern(reference)
|
||||
copy = reference.dup
|
||||
|
||||
# invoice number per year (yy..yy)
|
||||
# document number per year (yy..yy)
|
||||
copy.gsub!(/y+(?![^\[]*\])/) do |match|
|
||||
pad_and_truncate(number_of_invoices('year'), match.to_s.length)
|
||||
end
|
||||
# invoice number per month (mm..mm)
|
||||
# document number per month (mm..mm)
|
||||
copy.gsub!(/m+(?![^\[]*\])/) do |match|
|
||||
pad_and_truncate(number_of_invoices('month'), match.to_s.length)
|
||||
end
|
||||
# invoice number per day (dd..dd)
|
||||
# document number per day (dd..dd)
|
||||
copy.gsub!(/d+(?![^\[]*\])/) do |match|
|
||||
pad_and_truncate(number_of_invoices('day'), match.to_s.length)
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user