Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/tw automatic group order invoice generation #907

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0fa696c
add groupo order invoice and relation to group order
Viehlieb Dec 23, 2021
817c680
add mailer relevant files, add pdf relevant files and confgurations f…
Viehlieb Dec 23, 2021
50017fe
add functionality in views and controllers for generation of group or…
Viehlieb Dec 23, 2021
eadfbd5
add locales
Viehlieb Dec 23, 2021
298a476
add tests (integration + model)
Viehlieb Dec 23, 2021
72fa7c1
add finally schema.rb
Viehlieb Dec 23, 2021
06eb56a
add tax_number, payment_method and automatic invoice options to be ed…
Viehlieb Dec 23, 2021
f592b27
fix tiny issue where flash alert not shown
Viehlieb Dec 24, 2021
f428450
left over todo for naming od pdf file
Viehlieb Dec 24, 2021
303f902
locales for prev commits
Viehlieb Dec 24, 2021
0967981
configure rubocop_todo to prevent errors
Viehlieb Dec 24, 2021
bafa163
fix rubocop errors
Viehlieb Dec 24, 2021
1de377c
add vat exempt option in payment_tab ~ refs #automatic_go_invoices
Viehlieb Jan 27, 2022
bddaebe
add option vor vat_exempt inoices ~ refs automatic go invoices
Viehlieb Jan 27, 2022
1fc5823
fix order.ordergroup nil pointer error ~ automatic go invoices
Viehlieb Jan 28, 2022
81daddf
fix nil pointer~ refs automatic go invoice
Viehlieb Feb 3, 2022
bb69c35
rubocop styling ~ refs automatic go invoice
Viehlieb Feb 3, 2022
81a5194
add vat exempt option in payment_tab ~ refs #automatic_go_invoices
Viehlieb Jan 27, 2022
07b058a
Merge branch 'feature/tw_automatic_group_order_invoice_generation' of…
Viehlieb Feb 3, 2022
bb3e049
accept changesfrom upstream and resolve merge conflict ~ automatic go…
Viehlieb Feb 3, 2022
b2882d8
Merge branch 'feature/tw_automatic_group_order_invoice_generation'
Viehlieb Mar 1, 2022
76be8fd
wip on entering individual date for invoices
Viehlieb Mar 29, 2022
3683758
wip on generating invoices with date
Viehlieb Mar 30, 2022
5a817a9
add datefield default values max value cannot be blank
Viehlieb Mar 30, 2022
3149e00
adapt tests for manually generating group order invoices for order
Viehlieb Apr 5, 2022
d37a752
Merge branch 'master' of github.com:foodcoops/foodsoft into feature/t…
Viehlieb Apr 5, 2022
aab1dca
fix behavior - when link is provided in article details not clickable…
Viehlieb Apr 5, 2022
f3914f3
solve hover problem for ordering articles
Viehlieb Apr 7, 2022
59fd8da
remove footer on group order invoices showing time.now
Viehlieb Apr 11, 2022
0539edc
remove unneccessary files in app/view/group_order_invoices
Viehlieb Sep 6, 2022
b6854bc
add authentication to group_order_invoices controller
Viehlieb Sep 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ Lint/Void:
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 143
Exclude:
- 'app/documents/group_order_invoice_pdf.rb'

# Offense count: 73
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
Expand Down Expand Up @@ -412,6 +414,7 @@ RSpec/Capybara/FeatureMethods:
- 'spec/integration/receive_spec.rb'
- 'spec/integration/session_spec.rb'
- 'spec/integration/supplier_spec.rb'
- 'spec/integration/group_order_invoices_spec.rb'

# Offense count: 27
# Configuration parameters: Prefixes.
Expand Down
7 changes: 6 additions & 1 deletion app/assets/stylesheets/bootstrap_and_overrides.css.less
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ table {
tr.order-article:hover .article-info {
display: none;
}
tr.order-article:focus .article-info {
display: none;
}
}

#order-footer {
Expand Down Expand Up @@ -278,7 +281,9 @@ tr.order-article .article-info {
tr.order-article:hover .article-info {
display: block;
}

tr.order-article:focus .article-info {
display: block;
}

// ********* Articles

Expand Down
12 changes: 12 additions & 0 deletions app/controllers/concerns/send_group_order_invoice_pdf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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])
pdf = GroupOrderInvoicePdf.new group_order_invoice.load_data_for_invoice
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
end
end
21 changes: 18 additions & 3 deletions app/controllers/finance/balancing_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def index

def new
@order = Order.find(params[:order_id])
flash.now.alert = t('finance.balancing.new.alert') if @order.closed?
flash.now.alert = t('finance.balancing.new.alert') if @order.closed? && flash[:alert].blank?
@comments = @order.comments

@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
Expand Down Expand Up @@ -81,9 +81,24 @@ def close
@order = Order.find(params[:id])
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
@order.close!(@current_user, @type)
redirect_to finance_order_index_url, notice: t('finance.balancing.close.notice')
note = t('finance.balancing.close.notice')
if @order.closed?
alert = t('finance.balancing.close.alert')
if FoodsoftConfig[:group_order_invoices]&.[](:use_automatic_invoices)
@order.group_orders.each do |go|
alert = t('finance.balancing.close.settings_not_set')
goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
if goi.save!
NotifyGroupOrderInvoiceJob.perform_later(goi)
note = t('finance.balancing.close.notice_mail')
end
end
end
end
alert ||= t('finance.balancing.close.alert')
redirect_to finance_order_index_url, notice: note
rescue => error
redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: error.message)
redirect_to new_finance_order_url(order_id: @order.id), notice: note, alert: alert, msg: error.message
end

# Close the order directly, without automaticly updating ordergroups account balances
Expand Down
56 changes: 56 additions & 0 deletions app/controllers/group_order_invoices_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
class GroupOrderInvoicesController < ApplicationController
include Concerns::SendGroupOrderInvoicePdf

def show
@group_order_invoice = GroupOrderInvoice.find(params[:id])
if FoodsoftConfig[:contact][:tax_number]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why can't we show an invoice without an tax number?

respond_to do |format|
format.pdf do
send_group_order_invoice_pdf @group_order_invoice if FoodsoftConfig[:contact][:tax_number]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you check already for the tax_number in line 6. what's the purpose of the second check here?

end
end
else
raise RecordInvalid
end
rescue => 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

def destroy
goi = GroupOrderInvoice.find(params[:id])
@order = goi.group_order.order
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the purpose of @order here?

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the purpose of @order here?

GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
respond_to do |format|
format.js
end
redirect_back fallback_location: root_path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you redirect here to root_path?

rescue => error
redirect_back fallback_location: root_path, notice: 'Something went wrong', :alert => I18n.t('errors.general_msg', :msg => error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the comments form line 13 apply here too

end
end
202 changes: 202 additions & 0 deletions app/documents/group_order_invoice_pdf.rb
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions app/jobs/notify_group_order_invoice_job.rb
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions app/mailers/mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ def welcome(user)
subject: I18n.t('mailer.welcome.subject')
end

# Sends automatically generated invoicesfor group orders to ordergroup members
def group_order_invoice(group_order_invoice, user)
@user = user
@group_order_invoice = group_order_invoice
@group_order = group_order_invoice.group_order
@supplier = @group_order.order.supplier.name
@group = @group_order.ordergroup
add_group_order_invoice_attachments(group_order_invoice)
mail to: user,
subject: I18n.t('mailer.group_order_invoice.subject', group: @group.name, supplier: @supplier)
end

# Sends order result for specific Ordergroup
def order_result(user, group_order)
@order = group_order.order
Expand Down Expand Up @@ -168,6 +180,11 @@ def add_order_result_attachments(order, options = {})
attachments['order.csv'] = OrderCsv.new(order, options).to_csv
end

def add_group_order_invoice_attachments(group_order_invoice)
attachment_name = group_order_invoice.name + '.pdf'
attachments[attachment_name] = GroupOrderInvoicePdf.new(group_order_invoice.load_data_for_invoice).to_pdf
end

# separate method to allow plugins to mess with the text
def additonal_welcome_text(user)
end
Expand Down
1 change: 1 addition & 0 deletions app/models/group_order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class GroupOrder < ApplicationRecord
has_many :group_order_articles, :dependent => :destroy
has_many :order_articles, :through => :group_order_articles
has_one :financial_transaction
has_one :group_order_invoice
belongs_to :updated_by, optional: true, class_name: 'User', foreign_key: 'updated_by_user_id'

validates_presence_of :order_id
Expand Down
Loading