mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-29 18:52:22 +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;
|
if (typeof error === 'string') return error;
|
||||||
|
|
||||||
|
// parse Rails errors (as JSON)
|
||||||
let message = '';
|
let message = '';
|
||||||
if (error instanceof Object) {
|
if (error instanceof Object) {
|
||||||
// iterate through all the keys to build the message
|
// iterate through all the keys to build the message
|
||||||
|
@ -12,7 +12,7 @@ class Avoir < Invoice
|
|||||||
attr_accessor :invoice_items_ids
|
attr_accessor :invoice_items_ids
|
||||||
|
|
||||||
def generate_reference
|
def generate_reference
|
||||||
self.reference = InvoiceReferenceService.generate_reference(self, date: created_at, avoir: true)
|
super(created_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
def expire_subscription
|
def expire_subscription
|
||||||
|
@ -4,7 +4,7 @@ require 'checksum'
|
|||||||
|
|
||||||
# Invoice correspond to a single purchase made by an user. This purchase may
|
# Invoice correspond to a single purchase made by an user. This purchase may
|
||||||
# include reservation(s) and/or a subscription
|
# include reservation(s) and/or a subscription
|
||||||
class Invoice < Footprintable
|
class Invoice < PaymentDocument
|
||||||
include NotifyWith::NotificationAttachedObject
|
include NotifyWith::NotificationAttachedObject
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
scope :only_invoice, -> { where(type: nil) }
|
scope :only_invoice, -> { where(type: nil) }
|
||||||
@ -41,30 +41,21 @@ class Invoice < Footprintable
|
|||||||
end
|
end
|
||||||
|
|
||||||
def filename
|
def filename
|
||||||
prefix = Setting.find_by(name: 'invoice_prefix').history_values.order(created_at: :desc).where('created_at <= ?', created_at).limit(1).first
|
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
|
prefix ||= if created_at < Setting.find_by(name: 'invoice_prefix').first_update
|
||||||
Setting.find_by(name: 'invoice_prefix').history_values.order(created_at: :asc).limit(1).first
|
Setting.find_by(name: 'invoice_prefix').first_value
|
||||||
else
|
else
|
||||||
Setting.find_by(name: 'invoice_prefix')..history_values.order(created_at: :desc).limit(1).first
|
Setting.get('invoice_prefix')
|
||||||
end
|
end
|
||||||
"#{prefix.value}-#{id}_#{created_at.strftime('%d%m%Y')}.pdf"
|
"#{prefix}-#{id}_#{created_at.strftime('%d%m%Y')}.pdf"
|
||||||
end
|
end
|
||||||
|
|
||||||
def user
|
def user
|
||||||
invoicing_profile.user
|
invoicing_profile.user
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_reference
|
|
||||||
self.reference = InvoiceReferenceService.generate_reference(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_reference
|
|
||||||
generate_reference
|
|
||||||
save
|
|
||||||
end
|
|
||||||
|
|
||||||
def order_number
|
def order_number
|
||||||
InvoiceReferenceService.generate_order_number(self)
|
PaymentDocumentService.generate_order_number(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
# for debug & used by rake task "fablab:maintenance:regenerate_invoices"
|
# for debug & used by rake task "fablab:maintenance:regenerate_invoices"
|
||||||
@ -74,7 +65,7 @@ class Invoice < Footprintable
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_avoir(attrs = {})
|
def build_avoir(attrs = {})
|
||||||
raise Exception if refunded? == true || prevent_refund?
|
raise CannotRefundError if refunded? == true || prevent_refund?
|
||||||
|
|
||||||
avoir = Avoir.new(dup.attributes)
|
avoir = Avoir.new(dup.attributes)
|
||||||
avoir.type = 'Avoir'
|
avoir.type = 'Avoir'
|
||||||
@ -168,21 +159,10 @@ class Invoice < Footprintable
|
|||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_environment
|
|
||||||
self.environment = Rails.env
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_footprint
|
def check_footprint
|
||||||
invoice_items.map(&:check_footprint).all? && footprint == compute_footprint
|
invoice_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||||
end
|
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?
|
def paid_with_stripe?
|
||||||
stp_payment_intent_id? || stp_invoice_id? || payment_method == 'stripe'
|
stp_payment_intent_id? || stp_invoice_id? || payment_method == 'stripe'
|
||||||
end
|
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,
|
# 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
|
# 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 :scheduled, polymorphic: true
|
||||||
belongs_to :wallet_transaction
|
belongs_to :wallet_transaction
|
||||||
belongs_to :coupon
|
belongs_to :coupon
|
||||||
@ -17,6 +19,24 @@ class PaymentSchedule < Footprintable
|
|||||||
before_create :add_environment
|
before_create :add_environment
|
||||||
after_create :update_reference, :chain_record
|
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
|
# 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)
|
payment_schedule_items.order(due_date: :asc)
|
||||||
end
|
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
|
def check_footprint
|
||||||
payment_schedule_items.map(&:check_footprint).all? && footprint == compute_footprint
|
payment_schedule_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||||
end
|
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
|
# WARNING: when adding a new key, you may also want to add it in app/policies/setting_policy.rb#public_whitelist
|
||||||
|
|
||||||
def value
|
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
|
last_value&.value
|
||||||
end
|
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
|
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
|
last_value&.created_at
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Provides methods to generate Invoice or PaymentSchedule references
|
# Provides methods to generate Invoice, Avoir or PaymentSchedule references
|
||||||
class InvoiceReferenceService
|
class PaymentDocumentService
|
||||||
class << self
|
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')
|
pattern = Setting.get('invoice_reference')
|
||||||
|
|
||||||
reference = replace_invoice_number_pattern(pattern)
|
reference = replace_invoice_number_pattern(pattern)
|
||||||
reference = replace_date_pattern(reference, date)
|
reference = replace_date_pattern(reference, date)
|
||||||
|
|
||||||
if avoir
|
if document.class == Avoir
|
||||||
# information about refund/avoir (R[text])
|
# information about refund/avoir (R[text])
|
||||||
reference.gsub!(/R\[([^\]]+)\]/, '\1')
|
reference.gsub!(/R\[([^\]]+)\]/, '\1')
|
||||||
|
|
||||||
@ -17,16 +17,16 @@ class InvoiceReferenceService
|
|||||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||||
# remove information about payment schedule (S[text])
|
# remove information about payment schedule (S[text])
|
||||||
reference.gsub!(/S\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/S\[([^\]]+)\]/, ''.to_s)
|
||||||
elsif payment_schedule
|
elsif document.class == PaymentSchedule
|
||||||
# information about payment schedule
|
# information about payment schedule
|
||||||
reference.gsub!(/S\[([^\]]+)\]/, '\1')
|
reference.gsub!(/S\[([^\]]+)\]/, '\1')
|
||||||
# remove information about online selling (X[text])
|
# remove information about online selling (X[text])
|
||||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||||
# remove information about refunds (R[text])
|
# remove information about refunds (R[text])
|
||||||
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
||||||
else
|
elsif document.class == Invoice
|
||||||
# information about online selling (X[text])
|
# information about online selling (X[text])
|
||||||
if invoice.paid_with_stripe?
|
if document.paid_with_stripe?
|
||||||
reference.gsub!(/X\[([^\]]+)\]/, '\1')
|
reference.gsub!(/X\[([^\]]+)\]/, '\1')
|
||||||
else
|
else
|
||||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||||
@ -36,6 +36,8 @@ class InvoiceReferenceService
|
|||||||
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
||||||
# remove information about payment schedule (S[text])
|
# remove information about payment schedule (S[text])
|
||||||
reference.gsub!(/S\[([^\]]+)\]/, ''.to_s)
|
reference.gsub!(/S\[([^\]]+)\]/, ''.to_s)
|
||||||
|
else
|
||||||
|
raise TypeError
|
||||||
end
|
end
|
||||||
|
|
||||||
reference
|
reference
|
||||||
@ -44,7 +46,7 @@ class InvoiceReferenceService
|
|||||||
def generate_order_number(invoice)
|
def generate_order_number(invoice)
|
||||||
pattern = Setting.get('invoice_order-nb')
|
pattern = Setting.get('invoice_order-nb')
|
||||||
|
|
||||||
# global invoice number (nn..nn)
|
# global document number (nn..nn)
|
||||||
reference = pattern.gsub(/n+(?![^\[]*\])/) do |match|
|
reference = pattern.gsub(/n+(?![^\[]*\])/) do |match|
|
||||||
pad_and_truncate(number_of_invoices('global'), match.to_s.length)
|
pad_and_truncate(number_of_invoices('global'), match.to_s.length)
|
||||||
end
|
end
|
||||||
@ -119,21 +121,21 @@ class InvoiceReferenceService
|
|||||||
end
|
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}
|
# @param reference {string}
|
||||||
##
|
##
|
||||||
def replace_invoice_number_pattern(reference)
|
def replace_invoice_number_pattern(reference)
|
||||||
copy = reference.dup
|
copy = reference.dup
|
||||||
|
|
||||||
# invoice number per year (yy..yy)
|
# document number per year (yy..yy)
|
||||||
copy.gsub!(/y+(?![^\[]*\])/) do |match|
|
copy.gsub!(/y+(?![^\[]*\])/) do |match|
|
||||||
pad_and_truncate(number_of_invoices('year'), match.to_s.length)
|
pad_and_truncate(number_of_invoices('year'), match.to_s.length)
|
||||||
end
|
end
|
||||||
# invoice number per month (mm..mm)
|
# document number per month (mm..mm)
|
||||||
copy.gsub!(/m+(?![^\[]*\])/) do |match|
|
copy.gsub!(/m+(?![^\[]*\])/) do |match|
|
||||||
pad_and_truncate(number_of_invoices('month'), match.to_s.length)
|
pad_and_truncate(number_of_invoices('month'), match.to_s.length)
|
||||||
end
|
end
|
||||||
# invoice number per day (dd..dd)
|
# document number per day (dd..dd)
|
||||||
copy.gsub!(/d+(?![^\[]*\])/) do |match|
|
copy.gsub!(/d+(?![^\[]*\])/) do |match|
|
||||||
pad_and_truncate(number_of_invoices('day'), match.to_s.length)
|
pad_and_truncate(number_of_invoices('day'), match.to_s.length)
|
||||||
end
|
end
|
Loading…
x
Reference in New Issue
Block a user