initial commit

Co-authored-by: viehlieb <pf@pragma-shift.net>
This commit is contained in:
Philipp Rothmann 2023-07-24 12:04:33 +02:00
commit 4139bfd40b
73 changed files with 1747 additions and 0 deletions

View file

@ -0,0 +1,13 @@
module Concerns::SendGroupOrderInvoicePdf
extend ActiveSupport::Concern
protected
def send_group_order_invoice_pdf(group_order_invoice)
invoice_data = group_order_invoice.load_data_for_invoice
invoice_data[:title] = t('documents.group_order_invoice_pdf.title', supplier: invoice_data[:supplier])
invoice_data[:no_footer] = true
pdf = GroupOrderInvoicePdf.new invoice_data
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
end
end

View file

@ -0,0 +1,59 @@
class GroupOrderInvoicesController < ApplicationController
include Concerns::SendGroupOrderInvoicePdf
before_action :authenticate_finance
def show
begin
@group_order_invoice = GroupOrderInvoice.find(params[:id])
if FoodsoftConfig[:contact][:tax_number]
respond_to do |format|
format.pdf do
send_group_order_invoice_pdf @group_order_invoice if FoodsoftConfig[:contact][:tax_number]
end
end
else
raise RecordInvalid
end
rescue ActiveRecord::RecordInvalid => error
redirect_back fallback_location: root_path, notice: 'Something went wrong', alert: I18n.t('errors.general_msg', msg: "#{error} " + I18n.t('errors.check_tax_number'))
end
end
def destroy
goi = GroupOrderInvoice.find(params[:id])
@order = goi.group_order.order
goi.destroy
respond_to do |format|
format.js
format.json { head :no_content }
end
end
def create_multiple
invoice_date = params[:group_order_invoice][:invoice_date]
order_id = params[:group_order_invoice][:order_id]
@order = Order.find(order_id)
gos = GroupOrder.where("order_id = ?", order_id)
gos.each do |go|
goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
goi.invoice_date = invoice_date
goi.invoice_number = goi.generate_invoice_number(1)
goi.save!
end
respond_to do |format|
format.js
end
end
def create
go = GroupOrder.find(params[:group_order])
@order = go.order
GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
respond_to do |format|
format.js
end
redirect_back fallback_location: root_path
rescue => error
redirect_back fallback_location: root_path, notice: 'Something went wrong', :alert => I18n.t('errors.general_msg', :msg => error)
end
end

View file

@ -0,0 +1,202 @@
class GroupOrderInvoicePdf < RenderPdf
def filename
I18n.t('documents.group_order_invoice_pdf.filename', :number => @options[:invoice_number]) + '.pdf'
end
def title
I18n.t('documents.group_order_invoice_pdf.title', :supplier => @options[:supplier])
end
def body
contact = FoodsoftConfig[:contact].symbolize_keys
ordergroup = @options[:ordergroup]
# From paragraph
bounding_box [margin_box.right - 200, margin_box.top - 20], width: 200 do
text I18n.t('documents.group_order_invoice_pdf.invoicer')
move_down 7
text FoodsoftConfig[:name], size: fontsize(9), align: :left
move_down 5
text contact[:street], size: fontsize(9), align: :left
move_down 5
text "#{contact[:zip_code]} #{contact[:city]}", size: fontsize(9), align: :left
move_down 5
unless contact[:phone].blank?
text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :left
move_down 5
end
unless contact[:email].blank?
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :left
end
move_down 5
text I18n.t('documents.group_order_invoice_pdf.tax_number', :number => @options[:tax_number]), size: fontsize(9), align: :left
end
# Receiving Ordergroup
bounding_box [margin_box.left, margin_box.top - 20], width: 200 do
text I18n.t('documents.group_order_invoice_pdf.invoicee')
move_down 7
text I18n.t('documents.group_order_invoice_pdf.ordergroup.name', ordergroup: ordergroup.name.to_s), size: fontsize(9)
move_down 5
if ordergroup.contact_address
text I18n.t('documents.group_order_invoice_pdf.ordergroup.contact_address', contact_address: ordergroup.contact_address.to_s), size: fontsize(9)
move_down 5
end
if ordergroup.contact_phone
text I18n.t('documents.group_order_invoice_pdf.ordergroup.contact_phone', contact_phone: ordergroup.contact_phone.to_s), size: fontsize(9)
move_down 5
end
end
# invoice Date and nnvoice number
bounding_box [margin_box.right - 200, margin_box.top - 150], width: 200 do
text I18n.t('documents.group_order_invoice_pdf.invoice_date', invoice_date: @options[:invoice_date].strftime(I18n.t('date.formats.default'))), align: :left
move_down 5
text I18n.t('documents.group_order_invoice_pdf.invoice_number', invoice_number: @options[:invoice_number]), align: :left
end
move_down 15
# kind of the "body" of the invoice
text I18n.t('documents.group_order_invoice_pdf.payment_method', payment_method: @options[:payment_method])
move_down 15
text I18n.t('documents.group_order_invoice_pdf.table_headline')
move_down 5
#------------- Table Data -----------------------
@group_order = GroupOrder.find(@options[:group_order].id)
if FoodsoftConfig[:group_order_invoices][:vat_exempt]
body_for_vat_exempt
else
body_with_vat
end
end
def body_for_vat_exempt
total_gross = 0
data = [I18n.t('documents.group_order_invoice_pdf.vat_exempt_rows')]
move_down 10
group_order_articles = GroupOrderArticle.where(group_order_id: @group_order.id)
group_order_articles.each do |goa|
# if no unit is received, nothing is to be charged
next if goa.result.to_i == 0
goa_total_gross = goa.result * goa.order_article.price.gross_price
data << [goa.order_article.article.name,
goa.result.to_i,
number_to_currency(goa.order_article.price.gross_price),
number_to_currency(goa.total_price)]
total_gross += goa_total_gross
end
table data, position: :left, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
table.header = true
table.position = :center
table.cells.border_width = 1
table.cells.border_color = '666666'
table.row(0).column(1).width = 40
table.row(0).border_bottom_width = 2
table.columns(1).align = :right
table.columns(1..6).align = :right
end
move_down 5
sum = []
sum << [nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay'), number_to_currency(total_gross)]
# table for sum
indent(200) do
table sum, position: :center, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
sum.length.times do |count|
table.row(count).columns(0..3).borders = []
end
table.row(sum.length - 1).columns(0..2).borders = []
table.row(sum.length - 1).border_bottom_width = 2
table.row(sum.length - 1).columns(3).borders = [:bottom]
end
end
move_down 25
text I18n.t('documents.group_order_invoice_pdf.small_business_regulation')
move_down 10
end
def body_with_vat
total_gross = 0
total_net = 0
# Articles
tax_hash_net = Hash.new(0) # for summing up article net prices grouped into vat percentage
tax_hash_gross = Hash.new(0) # same here with gross prices
marge = FoodsoftConfig[:price_markup]
# data table looks different when price_markup > 0
data = if marge == 0
[I18n.t('documents.group_order_invoice_pdf.no_price_markup_rows')]
else
[I18n.t('documents.group_order_invoice_pdf.price_markup_rows', marge: marge)]
end
goa_tax_hash = GroupOrderArticle.where(group_order_id: @group_order.id).find_each.group_by { |oat| oat.order_article.price.tax }
goa_tax_hash.each do |tax, group_order_articles|
group_order_articles.each do |goa|
# if no unit is received, nothing is to be charged
next if goa.result.to_i == 0
order_article = goa.order_article
goa_total_net = goa.result * order_article.price.price
goa_total_gross = goa.result * order_article.price.gross_price
data << [order_article.article.name,
goa.result.to_i,
number_to_currency(order_article.price.price),
number_to_currency(goa_total_net),
tax.to_s + '%',
number_to_currency(goa.total_price)]
tax_hash_net[tax.to_i] += goa_total_net
tax_hash_gross[tax.to_i] += goa_total_gross
total_net += goa_total_net
total_gross += goa_total_gross
end
end
# Two separate tables for sum and individual data
# article information + data
table data, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
table.header = true
table.position = :center
table.cells.border_width = 1
table.cells.border_color = '666666'
table.row(0).column(1).width = 40
table.row(0).border_bottom_width = 2
table.columns(1).align = :right
table.columns(1..6).align = :right
end
sum = []
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay_net'), number_to_currency(total_net)]
tax_hash_net.each_key.each do |tax|
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.tax_included', tax: tax), number_to_currency(tax_hash_gross[tax] - tax_hash_net[tax])]
end
unless marge == 0
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.markup_included', marge: marge), number_to_currency(total_gross * marge / 100.0)]
end
end_sum = total_gross * (1 + marge / 100.0)
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay_gross'), number_to_currency(end_sum)]
# table for sum
table sum, position: :right, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
sum.length.times do |count|
table.row(count).columns(0..5).borders = []
end
table.row(sum.length - 1).columns(0..4).borders = []
table.row(sum.length - 1).border_bottom_width = 2
table.row(sum.length - 1).columns(5).borders = [:bottom]
end
if(FoodsoftConfig[:group_order_invoices][:vat_exempt])
move_down 15
text I18n.t('documents.group_order_invoice_pdf.small_business_regulation')
end
move_down 10
end
end

View file

@ -0,0 +1,10 @@
class NotifyGroupOrderInvoiceJob < ApplicationJob
def perform(group_order_invoice)
ordergroup = group_order_invoice.group_order.ordergroup
ordergroup.users.each do |user|
Mailer.deliver_now_with_user_locale user do
Mailer.group_order_invoice(group_order_invoice, user)
end
end
end
end

View file

@ -0,0 +1,58 @@
class GroupOrderInvoice < ApplicationRecord
belongs_to :group_order
validates_presence_of :group_order
validates_uniqueness_of :invoice_number
validate :tax_number_set
after_initialize :init, unless: :persisted?
def generate_invoice_number(count)
trailing_number = count.to_s.rjust(4, '0')
if GroupOrderInvoice.find_by(invoice_number: self.invoice_date.strftime("%Y%m%d") + trailing_number)
generate_invoice_number(count.to_i + 1)
else
self.invoice_date.strftime("%Y%m%d") + trailing_number
end
end
def tax_number_set
if FoodsoftConfig[:contact][:tax_number].blank?
errors.add(:group_order_invoice, "Keine Steuernummer in FoodsoftConfig :contact gesetzt")
end
end
def init
self.invoice_date = Time.now unless invoice_date
self.invoice_number = generate_invoice_number(1) unless self.invoice_number
self.payment_method = FoodsoftConfig[:group_order_invoices]&.[](:payment_method) || I18n.t('activerecord.attributes.group_order_invoice.payment_method') unless self.payment_method
end
def name
I18n.t('activerecord.attributes.group_order_invoice.name') + "_#{invoice_number}"
end
def load_data_for_invoice
invoice_data = {}
order = group_order.order
invoice_data[:supplier] = order.supplier.name
invoice_data[:ordergroup] = group_order.ordergroup
invoice_data[:group_order] = group_order
invoice_data[:invoice_number] = invoice_number
invoice_data[:invoice_date] = invoice_date
invoice_data[:tax_number] = FoodsoftConfig[:contact][:tax_number]
invoice_data[:payment_method] = payment_method
invoice_data[:order_articles] = {}
group_order.order_articles.each do |order_article|
# Get the result of last time ordering, if possible
goa = group_order.group_order_articles.detect { |tmp_goa| tmp_goa.order_article_id == order_article.id }
# Build hash with relevant data
invoice_data[:order_articles][order_article.id] = {
:price => order_article.article.fc_price,
:quantity => (goa ? goa.quantity : 0),
:total_price => (goa ? goa.total_price : 0),
:tax => order_article.article.tax
}
end
invoice_data
end
end

View file

@ -0,0 +1,13 @@
/ insert_bottom thead
%th= heading_helper GroupOrderInvoice, :name
%th
/ insert_bottom tbody
%td{id: "generate-invoice#{order.id}"}
- if order.closed?
-if FoodsoftConfig[:contact][:tax_number] && order.ordergroups.present?
= render :partial => 'group_order_invoices/links', locals:{order: order}
-else
= I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set')
- else
= t('orders.index.not_closed')

View file

@ -0,0 +1,25 @@
.row
.column.small-12
- show_generate_with_date = true
- order.group_orders.each do |go|
- if go.group_order_invoice.present?
- show_generate_with_date = false
- if show_generate_with_date
= form_for :group_order_invoice, url: url_for('group_order_invoice#create_multiple'), remote: true do |f|
= f.label :invoice_date, I18n.t('activerecord.attributes.group_order_invoice.links.invoice_date')
= f.date_field :invoice_date, {value: Date.today, max: Date.today, required: true}
= f.hidden_field :order_id, value: order.id
= f.submit I18n.t('activerecord.attributes.group_order_invoice.links.generate_with_date'), class: 'btn btn small'
- order.group_orders.includes([:group_order_invoice, :ordergroup]).each do |go|
.row
.column.small-3
= label_tag go.ordergroup.name
- if go.group_order_invoice
.column.small-3
= link_to I18n.t('activerecord.attributes.group_order_invoice.links.download'), group_order_invoice_path(go.group_order_invoice, :format => 'pdf'), class: 'btn btn-small'
.column.small-3
= link_to I18n.t('activerecord.attributes.group_order_invoice.links.delete'), go.group_order_invoice, method: :delete, class: 'btn btn-danger btn-small', remote: true
- else
= button_to I18n.t('activerecord.attributes.group_order_invoice.links.generate'), group_order_invoices_path(:method => :post, group_order: go) ,class: 'btn btn-small', params: {id: order.id}, remote: true

View file

@ -0,0 +1 @@
$("#generate-invoice<%= params[:id] %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");

View file

@ -0,0 +1 @@
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");

View file

@ -0,0 +1 @@
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");