1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-11-28 09:24:24 +01:00

(feat) order invoice

This commit is contained in:
Du Peng 2022-09-09 16:35:49 +02:00
parent 2840c93823
commit 185589407f
11 changed files with 63 additions and 24 deletions

View File

@ -83,7 +83,7 @@ class Coupon < ApplicationRecord
end
def users
invoices.map(&:user).concat(orders.map(&:user)).uniq(&:id)
invoices.map(&:user).uniq(&:id)
end
def users_ids

View File

@ -31,7 +31,7 @@ class InvoiceItem < Footprintable
amount_after_coupon - net_amount
end
# return invoice item type (Machine/Training/Space/Event/Subscription/Order) used to determine the VAT rate
# return invoice item type (Machine/Training/Space/Event/Subscription/OrderItem) used to determine the VAT rate
def invoice_item_type
if object_type == Reservation.name
object.try(:reservable_type) || ''
@ -39,7 +39,7 @@ class InvoiceItem < Footprintable
Subscription.name
elsif object_type == StatisticProfilePrepaidPack.name
object.prepaid_pack.priceable_type
elsif object_type == Order.name
elsif object_type == OrderItem.name
Product.name
else
''

View File

@ -5,6 +5,7 @@ class Order < PaymentDocument
belongs_to :statistic_profile
belongs_to :operator_profile, class_name: 'InvoicingProfile'
belongs_to :coupon
belongs_to :invoice
has_many :order_items, dependent: :destroy
has_one :payment_gateway_object, as: :item

View File

@ -41,14 +41,13 @@ class PDF::Invoice < Prawn::Document
else
text I18n.t('invoices.invoice_reference', REF: invoice.reference), leading: 3
end
if Setting.get('invoice_code-active')
text I18n.t('invoices.code', CODE: Setting.get('invoice_code-value')), leading: 3
end
text I18n.t('invoices.code', CODE: Setting.get('invoice_code-value')), leading: 3 if Setting.get('invoice_code-active')
if invoice.main_item.object_type != WalletTransaction.name
if invoice.is_a?(Avoir)
text I18n.t('invoices.order_number', NUMBER: invoice.invoice.order_number), leading: 3
else
text I18n.t('invoices.order_number', NUMBER: invoice.order_number), leading: 3
order_number = invoice.main_item.object_type == OrderItem.name ? invoice.main_item.object.order.reference : invoice.order_number
text I18n.t('invoices.order_number', NUMBER: order_number), leading: 3
end
end
if invoice.is_a?(Avoir)
@ -119,6 +118,8 @@ class PDF::Invoice < Prawn::Document
object = I18n.t('invoices.error_invoice')
when 'StatisticProfilePrepaidPack'
object = I18n.t('invoices.prepaid_pack')
when 'OrderItem'
object = I18n.t('invoices.order')
else
Rails.logger.error "specified main_item.object_type type (#{invoice.main_item.object_type}) is unknown"
end
@ -136,7 +137,6 @@ class PDF::Invoice < Prawn::Document
total_vat = 0
# going through invoice_items
invoice.invoice_items.each do |item|
price = item.amount.to_i / 100.00
details = invoice.is_a?(Avoir) ? I18n.t('invoices.cancellation') + ' - ' : ''
@ -162,7 +162,6 @@ class PDF::Invoice < Prawn::Document
END: I18n.l(subscription_end_at.to_date))
end
elsif item.object_type == Reservation.name
case invoice.main_item.object.try(:reservable_type)
### Machine reservation
@ -243,7 +242,8 @@ class PDF::Invoice < Prawn::Document
else
data += [[I18n.t('invoices.total_including_all_taxes'), number_to_currency(total)]]
vat_rate_group.each do |_type, rate|
data += [[I18n.t('invoices.including_VAT_RATE', RATE: rate[:vat_rate], AMOUNT: number_to_currency(rate[:amount] / 100.00)), number_to_currency(rate[:total_vat] / 100.00)]]
data += [[I18n.t('invoices.including_VAT_RATE', RATE: rate[:vat_rate], AMOUNT: number_to_currency(rate[:amount] / 100.00)),
number_to_currency(rate[:total_vat] / 100.00)]]
end
data += [[I18n.t('invoices.including_total_excluding_taxes'), number_to_currency(total_ht / 100.00)]]
data += [[I18n.t('invoices.including_amount_payed_on_ordering'), number_to_currency(total)]]
@ -290,7 +290,7 @@ class PDF::Invoice < Prawn::Document
# payment details
move_down 20
if invoice.is_a?(Avoir)
payment_verbose = I18n.t('invoices.refund_on_DATE', DATE:I18n.l(invoice.avoir_date.to_date)) + ' '
payment_verbose = I18n.t('invoices.refund_on_DATE', DATE: I18n.l(invoice.avoir_date.to_date)) + ' '
case invoice.payment_method
when 'stripe'
payment_verbose += I18n.t('invoices.by_card_online_payment')
@ -351,7 +351,6 @@ class PDF::Invoice < Prawn::Document
text line, style: :bold, inline_format: true
end
# address and legals information
move_down 40
txt = parse_html(Setting.get('invoice_legals'))

View File

@ -72,7 +72,7 @@ class AccountingExportService
rows << "#{wallet_row(invoice)}\n"
when 'StatisticProfilePrepaidPack'
rows << "#{pack_row(invoice)}\n"
when 'Order'
when 'OrderItem'
rows << "#{product_row(invoice)}\n"
when 'Error'
items = invoice.invoice_items.reject { |ii| ii.object_type == 'Subscription' }
@ -248,8 +248,8 @@ class AccountingExportService
Rails.logger.debug { "WARN: Invoice #{invoice.id} has no prepaid-pack" }
end
when :product
if invoice.main_item.object_type == 'Order'
Setting.get("accounting_VAT_#{type}")
if invoice.main_item.object_type == 'OrderItem'
Setting.get("accounting_Product_#{type}")
else
Rails.logger.debug { "WARN: Invoice #{invoice.id} has no prepaid-pack" }
end

View File

@ -15,7 +15,6 @@ class InvoicesService
.page(page)
.per(size)
if filters[:number].size.positive?
invoices = invoices.where(
'invoices.reference LIKE :search',
@ -96,7 +95,7 @@ class InvoicesService
# Generate an array of {InvoiceItem} with the provided elements, price included.
# @param invoice {Invoice} the parent invoice
# @param payment_details {Hash} as generated by ShoppingCart.total
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack>}
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack|OrderItem>}
##
def self.generate_invoice_items(invoice, payment_details, objects)
objects.each_with_index do |object, index|
@ -108,6 +107,8 @@ class InvoicesService
InvoicesService.generate_reservation_item(invoice, object, payment_details, index.zero?)
elsif object.is_a?(StatisticProfilePrepaidPack)
InvoicesService.generate_prepaid_pack_item(invoice, object, payment_details, index.zero?)
elsif object.is_a?(OrderItem)
InvoicesService.generate_order_item(invoice, object, payment_details, index.zero?)
else
InvoicesService.generate_generic_item(invoice, object, payment_details, index.zero?)
end
@ -123,16 +124,16 @@ class InvoicesService
reservation.slots_reservations.map(&:slot).each do |slot|
description = "#{reservation.reservable.name}\n"
description += if slot.start_at.to_date != slot.end_at.to_date
description += if slot.start_at.to_date == slot.end_at.to_date
"#{I18n.l slot.start_at.to_date, format: :long} #{I18n.l slot.start_at, format: :hour_minute}" \
" - #{I18n.l slot.end_at, format: :hour_minute}"
else
I18n.t('events.from_STARTDATE_to_ENDDATE',
STARTDATE: I18n.l(slot.start_at.to_date, format: :long),
ENDDATE: I18n.l(slot.end_at.to_date, format: :long)) + ' ' +
I18n.t('events.from_STARTTIME_to_ENDTIME',
STARTTIME: I18n.l(slot.start_at, format: :hour_minute),
ENDTIME: I18n.l(slot.end_at, format: :hour_minute))
else
"#{I18n.l slot.start_at.to_date, format: :long} #{I18n.l slot.start_at, format: :hour_minute}" \
" - #{I18n.l slot.end_at, format: :hour_minute}"
end
price_slot = payment_details[:elements][:slots].detect { |p_slot| p_slot[:start_at].to_time.in_time_zone == slot[:start_at] }
@ -196,6 +197,21 @@ class InvoicesService
)
end
##
# Generate an InvoiceItem for given OrderItem and sva it in invoice.invoice_items
# This method must be called whith an order
##
def self.generate_order_item(invoice, item, _payment_details, main = false)
raise TypeError unless item
invoice.invoice_items.push InvoiceItem.new(
amount: item.is_offered ? 0 : item.amount * item.quantity,
description: "#{item.orderable.name} x #{item.quantity}",
object: item,
main: main
)
end
def self.generate_generic_item(invoice, item, payment_details, main = false)
invoice.invoice_items.push InvoiceItem.new(
amount: payment_details[:elements][item.class.name.to_sym],
@ -205,7 +221,6 @@ class InvoicesService
)
end
##
# Set the total price to the reservation's invoice, summing its whole items.
# Additionally a coupon may be applied to this invoice to make a discount on the total price

View File

@ -35,7 +35,21 @@ module Payments::PaymentConcern
order.order_items.each do |item|
ProductService.update_stock(item.orderable, 'external', 'sold', -item.quantity, item.id)
end
order.save
if order.save
invoice = InvoicesService.create(
{ total: order.total, coupon: coupon },
order.operator_profile_id,
order.order_items,
order.statistic_profile.user,
payment_id: payment_id,
payment_type: payment_type,
payment_method: order.payment_method
)
invoice.wallet_amount = order.wallet_amount
invoice.wallet_transaction_id = order.wallet_transaction_id
invoice.save
order.update_attribute(:invoice_id, invoice.id)
end
order.reload
end
end

View File

@ -121,6 +121,7 @@ en:
error_invoice: "Erroneous invoice. The items below ware not booked. Please contact the FabLab for a refund."
prepaid_pack: "Prepaid pack of hours"
pack_item: "Pack of %{COUNT} hours for the %{ITEM}"
order: "Order of products"
#PDF payment schedule generation
payment_schedules:
schedule_reference: "Payment schedule reference: %{REF}"

View File

@ -121,6 +121,7 @@ fr:
error_invoice: "Facture en erreur. Les éléments ci-dessous n'ont pas été réservés. Veuillez contacter le Fablab pour un remboursement."
prepaid_pack: "Paquet d'heures prépayé"
pack_item: "Pack de %{COUNT} heures pour la %{ITEM}"
order: "La commande des produits"
#PDF payment schedule generation
payment_schedules:
schedule_reference: "Référence de l'échéancier : %{REF}"

View File

@ -0,0 +1,5 @@
class AddInvoiceIdToOrder < ActiveRecord::Migration[5.2]
def change
add_reference :orders, :invoice, index: true, foreign_key: true
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_08_26_175129) do
ActiveRecord::Schema.define(version: 2022_09_09_131300) do
# These are extensions that must be enabled in order to support this database
enable_extension "fuzzystrmatch"
@ -475,7 +475,9 @@ ActiveRecord::Schema.define(version: 2022_08_26_175129) do
t.string "environment"
t.bigint "coupon_id"
t.integer "paid_total"
t.bigint "invoice_id"
t.index ["coupon_id"], name: "index_orders_on_coupon_id"
t.index ["invoice_id"], name: "index_orders_on_invoice_id"
t.index ["operator_profile_id"], name: "index_orders_on_operator_profile_id"
t.index ["statistic_profile_id"], name: "index_orders_on_statistic_profile_id"
end
@ -1171,6 +1173,7 @@ ActiveRecord::Schema.define(version: 2022_08_26_175129) do
add_foreign_key "invoicing_profiles", "users"
add_foreign_key "order_items", "orders"
add_foreign_key "orders", "coupons"
add_foreign_key "orders", "invoices"
add_foreign_key "orders", "invoicing_profiles", column: "operator_profile_id"
add_foreign_key "orders", "statistic_profiles"
add_foreign_key "organizations", "invoicing_profiles"