merge automatic group order invoice generation
see https://github.com/foodcoops/foodsoft/pull/907 for reference and original work by viehlieb Co-authored-by: viehlieb <pf@pragma-shift.net> fix PDF Pdf
This commit is contained in:
parent
a96f21134e
commit
85bdf28f91
28 changed files with 751 additions and 18 deletions
|
@ -274,6 +274,8 @@ Lint/Void:
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 143
|
Max: 143
|
||||||
|
Exclude:
|
||||||
|
- 'app/documents/group_order_invoice_pdf.rb'
|
||||||
|
|
||||||
# Offense count: 13
|
# Offense count: 13
|
||||||
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
|
||||||
|
@ -407,6 +409,7 @@ RSpec/Capybara/FeatureMethods:
|
||||||
- 'spec/integration/receive_spec.rb'
|
- 'spec/integration/receive_spec.rb'
|
||||||
- 'spec/integration/session_spec.rb'
|
- 'spec/integration/session_spec.rb'
|
||||||
- 'spec/integration/supplier_spec.rb'
|
- 'spec/integration/supplier_spec.rb'
|
||||||
|
- 'spec/integration/group_order_invoices_spec.rb'
|
||||||
|
|
||||||
# Offense count: 4
|
# Offense count: 4
|
||||||
RSpec/Capybara/SpecificMatcher:
|
RSpec/Capybara/SpecificMatcher:
|
||||||
|
|
|
@ -241,6 +241,9 @@ table {
|
||||||
tr.order-article:hover .article-info {
|
tr.order-article:hover .article-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
tr.order-article:focus .article-info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#order-footer {
|
#order-footer {
|
||||||
|
@ -275,11 +278,13 @@ tr.order-article .article-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.order-article:hover .article-info {
|
tr.order-article:focus{
|
||||||
|
background-color: #E4EED6;
|
||||||
|
}
|
||||||
|
tr.order-article:focus .article-info {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ********* Articles
|
// ********* Articles
|
||||||
|
|
||||||
tr.just-updated {
|
tr.just-updated {
|
||||||
|
|
13
app/controllers/concerns/send_group_order_invoice_pdf.rb
Normal file
13
app/controllers/concerns/send_group_order_invoice_pdf.rb
Normal 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
|
|
@ -5,7 +5,7 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@order = Order.find(params[:order_id])
|
@order = Order.find(params[:order_id])
|
||||||
flash.now.alert = t('.alert') if @order.closed?
|
flash.now.alert = t('finance.balancing.new.alert') if @order.closed? && flash[:alert].blank?
|
||||||
@comments = @order.comments
|
@comments = @order.comments
|
||||||
|
|
||||||
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
|
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
|
||||||
|
@ -81,9 +81,24 @@ class Finance::BalancingController < Finance::BaseController
|
||||||
@order = Order.find(params[:id])
|
@order = Order.find(params[:id])
|
||||||
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
|
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
|
||||||
@order.close!(@current_user, @type)
|
@order.close!(@current_user, @type)
|
||||||
redirect_to finance_order_index_url, notice: t('.notice')
|
note = t('finance.balancing.close.notice')
|
||||||
rescue StandardError => e
|
if @order.closed?
|
||||||
redirect_to new_finance_order_url(order_id: @order.id), alert: t('.alert', message: e.message)
|
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), notice: note, alert: alert, msg: error.message
|
||||||
end
|
end
|
||||||
|
|
||||||
# Close the order directly, without automaticly updating ordergroups account balances
|
# Close the order directly, without automaticly updating ordergroups account balances
|
||||||
|
|
59
app/controllers/group_order_invoices_controller.rb
Normal file
59
app/controllers/group_order_invoices_controller.rb
Normal 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
|
202
app/documents/group_order_invoice_pdf.rb
Normal file
202
app/documents/group_order_invoice_pdf.rb
Normal 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
|
10
app/jobs/notify_group_order_invoice_job.rb
Normal file
10
app/jobs/notify_group_order_invoice_job.rb
Normal 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
|
|
@ -70,7 +70,7 @@ class RenderPdf < Prawn::Document
|
||||||
options[:skip_page_creation] = true
|
options[:skip_page_creation] = true
|
||||||
@options = options
|
@options = options
|
||||||
@first_page = true
|
@first_page = true
|
||||||
|
no_footer = @options&.[](:no_footer) ? true : false
|
||||||
super(options)
|
super(options)
|
||||||
|
|
||||||
# Use ttf for better utf-8 compability
|
# Use ttf for better utf-8 compability
|
||||||
|
@ -84,11 +84,11 @@ class RenderPdf < Prawn::Document
|
||||||
)
|
)
|
||||||
|
|
||||||
header = options[:title] || title
|
header = options[:title] || title
|
||||||
footer = I18n.l(Time.now, format: :long)
|
footer = I18n.l(Time.now, format: :long) unless no_footer
|
||||||
|
|
||||||
header_size = 0
|
header_size = 0
|
||||||
header_size = height_of(header, size: HEADER_FONT_SIZE, font: DEFAULT_FONT) + HEADER_SPACE if header
|
header_size = height_of(header, size: HEADER_FONT_SIZE, font: DEFAULT_FONT) + HEADER_SPACE if header
|
||||||
footer_size = height_of(footer, size: FOOTER_FONT_SIZE, font: DEFAULT_FONT) + FOOTER_SPACE
|
footer_size = no_footer ? 0 : height_of(footer, size: FOOTER_FONT_SIZE, font: DEFAULT_FONT) + FOOTER_SPACE
|
||||||
|
|
||||||
start_new_page(top_margin: TOP_MARGIN + header_size, bottom_margin: BOTTOM_MARGIN + footer_size)
|
start_new_page(top_margin: TOP_MARGIN + header_size, bottom_margin: BOTTOM_MARGIN + footer_size)
|
||||||
|
|
||||||
|
@ -98,12 +98,15 @@ class RenderPdf < Prawn::Document
|
||||||
bounding_box [bounds.left, bounds.top + header_size], width: bounds.width, height: header_size do
|
bounding_box [bounds.left, bounds.top + header_size], width: bounds.width, height: header_size do
|
||||||
text header, size: HEADER_FONT_SIZE, align: :center, overflow: :shrink_to_fit if header
|
text header, size: HEADER_FONT_SIZE, align: :center, overflow: :shrink_to_fit if header
|
||||||
end
|
end
|
||||||
font_size FOOTER_FONT_SIZE do
|
|
||||||
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
|
unless no_footer
|
||||||
text footer, align: :left, valign: :bottom
|
font_size FOOTER_FONT_SIZE do
|
||||||
end
|
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
|
||||||
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
|
text footer, align: :left, valign: :bottom
|
||||||
text I18n.t('lib.render_pdf.page', number: page_number, count: page_count), align: :right, valign: :bottom
|
end
|
||||||
|
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
|
||||||
|
text I18n.t('lib.render_pdf.page', number: page_number, count: page_count), align: :right, valign: :bottom
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,6 +51,18 @@ class Mailer < ActionMailer::Base
|
||||||
subject: I18n.t('mailer.welcome.subject')
|
subject: I18n.t('mailer.welcome.subject')
|
||||||
end
|
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
|
# Sends order result for specific Ordergroup
|
||||||
def order_result(user, group_order)
|
def order_result(user, group_order)
|
||||||
@order = group_order.order
|
@order = group_order.order
|
||||||
|
@ -169,6 +181,11 @@ class Mailer < ActionMailer::Base
|
||||||
attachments['order.csv'] = OrderCsv.new(order, options).to_csv
|
attachments['order.csv'] = OrderCsv.new(order, options).to_csv
|
||||||
end
|
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
|
# separate method to allow plugins to mess with the text
|
||||||
def additonal_welcome_text(user); end
|
def additonal_welcome_text(user); end
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ class GroupOrder < ApplicationRecord
|
||||||
has_many :group_order_articles, dependent: :destroy
|
has_many :group_order_articles, dependent: :destroy
|
||||||
has_many :order_articles, through: :group_order_articles
|
has_many :order_articles, through: :group_order_articles
|
||||||
has_one :financial_transaction
|
has_one :financial_transaction
|
||||||
|
has_one :group_order_invoice
|
||||||
belongs_to :updated_by, optional: true, class_name: 'User', foreign_key: 'updated_by_user_id'
|
belongs_to :updated_by, optional: true, class_name: 'User', foreign_key: 'updated_by_user_id'
|
||||||
|
|
||||||
validates :order_id, presence: true
|
validates :order_id, presence: true
|
||||||
|
|
58
app/models/group_order_invoice.rb
Normal file
58
app/models/group_order_invoice.rb
Normal 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
|
|
@ -7,4 +7,5 @@
|
||||||
= config_input c, :country, as: :string, input_html: {class: 'input-xlarge'}
|
= config_input c, :country, as: :string, input_html: {class: 'input-xlarge'}
|
||||||
= config_input c, :email, required: true, input_html: {class: 'input-xlarge'}
|
= config_input c, :email, required: true, input_html: {class: 'input-xlarge'}
|
||||||
= config_input c, :phone, input_html: {class: 'input-medium'}
|
= config_input c, :phone, input_html: {class: 'input-medium'}
|
||||||
|
= config_input c, :tax_number, input_html: {class: 'input-medium'}
|
||||||
= config_input form, :homepage, required: true, as: :url, input_html: {class: 'input-xlarge'}
|
= config_input form, :homepage, required: true, as: :url, input_html: {class: 'input-xlarge'}
|
||||||
|
|
|
@ -13,6 +13,11 @@
|
||||||
= config_input form, :charge_members_manually, as: :boolean
|
= config_input form, :charge_members_manually, as: :boolean
|
||||||
= config_input form, :use_iban, as: :boolean
|
= config_input form, :use_iban, as: :boolean
|
||||||
= config_input form, :use_self_service, as: :boolean
|
= config_input form, :use_self_service, as: :boolean
|
||||||
|
%h4= t '.group_order_invoices'
|
||||||
|
= form.fields_for :group_order_invoices do |field|
|
||||||
|
= config_input field, :use_automatic_invoices, as: :boolean
|
||||||
|
= config_input field, :vat_exempt, as: :boolean
|
||||||
|
= config_input field, :payment_method, as: :string, input_html: {class: 'input-medium'}
|
||||||
|
|
||||||
%h4= t '.schedule_title'
|
%h4= t '.schedule_title'
|
||||||
= form.simple_fields_for :order_schedule do |fields|
|
= form.simple_fields_for :order_schedule do |fields|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
%th= t('.end')
|
%th= t('.end')
|
||||||
%th= t('.state')
|
%th= t('.state')
|
||||||
%th= heading_helper Order, :updated_by
|
%th= heading_helper Order, :updated_by
|
||||||
|
%th= heading_helper GroupOrderInvoice, :name
|
||||||
|
%th
|
||||||
%th
|
%th
|
||||||
%tbody
|
%tbody
|
||||||
- @orders.each do |order|
|
- @orders.each do |order|
|
||||||
|
@ -17,6 +19,14 @@
|
||||||
%td=h format_time(order.ends) unless order.ends.nil?
|
%td=h format_time(order.ends) unless order.ends.nil?
|
||||||
%td= order.closed? ? t('.cleared', amount: number_to_currency(order.foodcoop_result)) : t('.ended')
|
%td= order.closed? ? t('.cleared', amount: number_to_currency(order.foodcoop_result)) : t('.ended')
|
||||||
%td= show_user(order.updated_by)
|
%td= show_user(order.updated_by)
|
||||||
|
%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')
|
||||||
%td
|
%td
|
||||||
- unless order.closed?
|
- unless order.closed?
|
||||||
- if current_user.role_orders?
|
- if current_user.role_orders?
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- title t('.title')
|
- title t('.title')
|
||||||
|
- puts params
|
||||||
- content_for :actionbar do
|
- content_for :actionbar do
|
||||||
- if FoodsoftConfig[:charge_members_manually]
|
- if FoodsoftConfig[:charge_members_manually]
|
||||||
= link_to t('.close_all_direct_with_invoice'), close_all_direct_with_invoice_finance_order_index_path, method: :post, class: 'btn'
|
= link_to t('.close_all_direct_with_invoice'), close_all_direct_with_invoice_finance_order_index_path, method: :post, class: 'btn'
|
||||||
|
|
25
app/views/group_order_invoices/_links.html.haml
Normal file
25
app/views/group_order_invoices/_links.html.haml
Normal 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
|
||||||
|
|
1
app/views/group_order_invoices/create.js.erb
Normal file
1
app/views/group_order_invoices/create.js.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
$("#generate-invoice<%= params[:id] %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
1
app/views/group_order_invoices/create_multiple.js.erb
Normal file
1
app/views/group_order_invoices/create_multiple.js.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
1
app/views/group_order_invoices/destroy.js.erb
Normal file
1
app/views/group_order_invoices/destroy.js.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
|
@ -69,7 +69,7 @@
|
||||||
= f.hidden_field :order_id
|
= f.hidden_field :order_id
|
||||||
= f.hidden_field :updated_by_user_id
|
= f.hidden_field :updated_by_user_id
|
||||||
= f.hidden_field :ordergroup_id
|
= f.hidden_field :ordergroup_id
|
||||||
%table.table.table-hover
|
%table.table
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th= heading_helper Article, :name
|
%th= heading_helper Article, :name
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
%i.icon-tag
|
%i.icon-tag
|
||||||
%td{colspan: "9"}
|
%td{colspan: "9"}
|
||||||
- order_articles.each do |order_article|
|
- order_articles.each do |order_article|
|
||||||
%tr{class: "#{cycle('even', 'odd', name: 'articles')} order-article #{get_missing_units_css_class(@ordering_data[:order_articles][order_article.id][:missing_units])}", valign: "top"}
|
%tr{class: "#{cycle('even', 'odd', name: 'articles')} order-article #{get_missing_units_css_class(@ordering_data[:order_articles][order_article.id][:missing_units])}", valign: "top", tabindex: "0"}
|
||||||
%td.name= order_article.article.name
|
%td.name= order_article.article.name
|
||||||
- if @order.stockit?
|
- if @order.stockit?
|
||||||
%td= truncate order_article.article.supplier.name, length: 15
|
%td= truncate order_article.article.supplier.name, length: 15
|
||||||
|
|
1
app/views/mailer/group_order_invoice.text.haml
Normal file
1
app/views/mailer/group_order_invoice.text.haml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
= raw t '.text', group: @group.name, supplier: @supplier , foodcoop: FoodsoftConfig[:name]
|
|
@ -90,6 +90,17 @@ de:
|
||||||
tolerance: Toleranz
|
tolerance: Toleranz
|
||||||
total_price: Summe
|
total_price: Summe
|
||||||
unit_price: Preis/Einheit
|
unit_price: Preis/Einheit
|
||||||
|
group_order_invoice:
|
||||||
|
name: Bestellgruppenrechnung
|
||||||
|
links:
|
||||||
|
delete: Rechnung löschen
|
||||||
|
download: Rechnung herunterladen
|
||||||
|
generate: Rechnung erzeugen
|
||||||
|
invoice_date: Datum der Bestellgruppenrechnung
|
||||||
|
generate_with_date: setzen & erzeugen
|
||||||
|
|
||||||
|
payment_method: Guthaben
|
||||||
|
tax_number_not_set: Steuernummer in den Einstellungen nicht gesetzt
|
||||||
invoice:
|
invoice:
|
||||||
amount: Betrag
|
amount: Betrag
|
||||||
attachment: Anhang
|
attachment: Anhang
|
||||||
|
@ -318,6 +329,7 @@ de:
|
||||||
emails_title: E-Mails versenden
|
emails_title: E-Mails versenden
|
||||||
tab_payment:
|
tab_payment:
|
||||||
schedule_title: Bestellschema
|
schedule_title: Bestellschema
|
||||||
|
group_order_invoices: Bestellgruppenrechnungen
|
||||||
tab_security:
|
tab_security:
|
||||||
default_roles_title: Zugriff auf
|
default_roles_title: Zugriff auf
|
||||||
default_roles_paragraph: Jedes Mitglied der Foodcoop hat automatisch Zugriff auf folgende Bereiche.
|
default_roles_paragraph: Jedes Mitglied der Foodcoop hat automatisch Zugriff auf folgende Bereiche.
|
||||||
|
@ -602,6 +614,10 @@ de:
|
||||||
email_from: E-Mails werden so aussehen, als ob sie von dieser Adresse gesendet wurden. Kann leer gelassen werden, um die Kontaktadresse der Foodcoop zu benutzen.
|
email_from: E-Mails werden so aussehen, als ob sie von dieser Adresse gesendet wurden. Kann leer gelassen werden, um die Kontaktadresse der Foodcoop zu benutzen.
|
||||||
email_replyto: Setze diese Adresse, wenn Du Antworten auf Foodsoft E-Mails auf eine andere, als die oben angegebene Absenderadresse bekommen möchtest.
|
email_replyto: Setze diese Adresse, wenn Du Antworten auf Foodsoft E-Mails auf eine andere, als die oben angegebene Absenderadresse bekommen möchtest.
|
||||||
email_sender: E-Mails werden so aussehen, als ob sie von dieser Adresse versendet wurden. Um zu vermeiden, dass E-Mails dadurch als Spam eingeordnet werden, muss der Webserver möglicherweise im SPF Eintrag der Domain der E-Mail Adresse eingetragen werden.
|
email_sender: E-Mails werden so aussehen, als ob sie von dieser Adresse versendet wurden. Um zu vermeiden, dass E-Mails dadurch als Spam eingeordnet werden, muss der Webserver möglicherweise im SPF Eintrag der Domain der E-Mail Adresse eingetragen werden.
|
||||||
|
group_order_invoices:
|
||||||
|
use_automativ_go_invoices: Es werden auf die Bestellgruppen zugeschnittene Rechnungen für die jeweilige Bestellung beim Klicken auf "abrechnen" an alle Bestellgruppenmitglieder per Mail versendet.
|
||||||
|
payment_method: Zahlungsart wird auf der Bestellgruppenrechnung deklariert
|
||||||
|
vat_exempt: Eine Auflistung der Rechnungsartikel erfolgt ohne explizite Ausweisung der MwSt. und die Rechnung erhält den notwendigen Zusatz bzgl. der Kleinunternehmerregelung §19 (FoodCoop Marge ebenfalls nicht in Rechnung enthalten)
|
||||||
help_url: Link zur Dokumentationsseite
|
help_url: Link zur Dokumentationsseite
|
||||||
homepage: Webseite der Foodcoop
|
homepage: Webseite der Foodcoop
|
||||||
ignore_browser_locale: Ignoriere die Sprache des Computers des Anwenders, wenn der Anwender noch keine Sprache gewählt hat.
|
ignore_browser_locale: Ignoriere die Sprache des Computers des Anwenders, wenn der Anwender noch keine Sprache gewählt hat.
|
||||||
|
@ -644,6 +660,7 @@ de:
|
||||||
phone: Telefon
|
phone: Telefon
|
||||||
street: Straße
|
street: Straße
|
||||||
zip_code: Postleitzahl
|
zip_code: Postleitzahl
|
||||||
|
tax_number: Steuernummer
|
||||||
currency_space: Leerzeichen hinzufügen
|
currency_space: Leerzeichen hinzufügen
|
||||||
currency_unit: Währung
|
currency_unit: Währung
|
||||||
custom_css: Angepasstes CSS
|
custom_css: Angepasstes CSS
|
||||||
|
@ -659,6 +676,10 @@ de:
|
||||||
email_from: Absenderadresse
|
email_from: Absenderadresse
|
||||||
email_replyto: Antwortadresse
|
email_replyto: Antwortadresse
|
||||||
email_sender: Senderadresse
|
email_sender: Senderadresse
|
||||||
|
group_order_invoices:
|
||||||
|
use_automatic_invoices: Automatisch bei Abrechnung per Mail versenden
|
||||||
|
payment_method: Zahlungsart
|
||||||
|
vat_exempt: Diese Foodcoop ist MwSt. befreit
|
||||||
help_url: URL Dokumentation
|
help_url: URL Dokumentation
|
||||||
homepage: Webseite
|
homepage: Webseite
|
||||||
ignore_browser_locale: Browsersprache ignorieren
|
ignore_browser_locale: Browsersprache ignorieren
|
||||||
|
@ -744,6 +765,46 @@ de:
|
||||||
update:
|
update:
|
||||||
notice: Lieferung wurde aktualisiert.
|
notice: Lieferung wurde aktualisiert.
|
||||||
documents:
|
documents:
|
||||||
|
group_order_invoice_pdf:
|
||||||
|
filename: Rechnung%{number}
|
||||||
|
invoicer: Rechnungsteller*in
|
||||||
|
invoicee: Rechnungsempfänger*in
|
||||||
|
invoice_date: 'Rechnungsdatum: %{invoice_date}'
|
||||||
|
invoice_number: 'Rechnungsnummer: %{invoice_number}'
|
||||||
|
markup_included: zzgl. Foodcoop Marge auf brutto Preis %{marge}%
|
||||||
|
ordergroup:
|
||||||
|
contact_phone: 'Telefonnummer: %{contact_phone}'
|
||||||
|
contact_address: 'Adresse : %{contact_address}'
|
||||||
|
name: Bestellgruppe %{ordergroup}
|
||||||
|
payment_method: 'Zahlungsart: %{payment_method}'
|
||||||
|
sum_to_pay: Zu zahlen gesamt
|
||||||
|
sum_to_pay_net: Zu zahlen gesamt (netto)
|
||||||
|
sum_to_pay_gross: Zu zahlen gesamt (brutto)
|
||||||
|
small_business_regulation: Als Kleinunternehmer*in im Sinne von §19 Abs. 1 Umsatzsteuergesetz (UStG) wird keine Umsatzsteuer berechnet.
|
||||||
|
table_headline: 'Für die Bestellung fallen folgende Posten an:'
|
||||||
|
tax_excluded: exkl. MwSt.
|
||||||
|
tax_included: zzgl. Gesamtsumme MwSt. %{tax}%
|
||||||
|
tax_number: 'Steuernummer: %{number}'
|
||||||
|
title: Rechnung für die Bestellung bei %{supplier}
|
||||||
|
vat_exempt_rows:
|
||||||
|
- Name
|
||||||
|
- Anzahl
|
||||||
|
- Einzelpreis
|
||||||
|
- Artikel Gesamtpreis
|
||||||
|
no_price_markup_rows:
|
||||||
|
- Name
|
||||||
|
- Anzahl
|
||||||
|
- Einzelpreis (netto)
|
||||||
|
- Artikel Gesamtpreis (netto)
|
||||||
|
- MwSt.
|
||||||
|
- Artikel Gesamtpreis (brutto)
|
||||||
|
price_markup_rows:
|
||||||
|
- Name
|
||||||
|
- Anzahl
|
||||||
|
- Einzelpreis (netto)
|
||||||
|
- Artikel Gesamtpreis (netto)
|
||||||
|
- MwSt.
|
||||||
|
- Artikel Gesamtpreis (brutto) inkl. Foodcoopmarge %{marge}%
|
||||||
order_by_articles:
|
order_by_articles:
|
||||||
filename: Bestellung %{name}-%{date} - Artikelsortierung
|
filename: Bestellung %{name}-%{date} - Artikelsortierung
|
||||||
title: 'Artikelsortierung der Bestellung: %{name}, beendet am %{date}'
|
title: 'Artikelsortierung der Bestellung: %{name}, beendet am %{date}'
|
||||||
|
@ -767,6 +828,7 @@ de:
|
||||||
heading: Artikelübersicht (%{count})
|
heading: Artikelübersicht (%{count})
|
||||||
title: 'Sortiermatrix der Bestellung: %{name}, beendet am %{date}'
|
title: 'Sortiermatrix der Bestellung: %{name}, beendet am %{date}'
|
||||||
errors:
|
errors:
|
||||||
|
check_tax_number: Überprüft, ob die Steuernummer der Foodcoop richtig gesetzt ist
|
||||||
general: Ein Problem ist aufgetreten.
|
general: Ein Problem ist aufgetreten.
|
||||||
general_again: Ein Fehler ist aufgetreten. Bitte erneut versuchen.
|
general_again: Ein Fehler ist aufgetreten. Bitte erneut versuchen.
|
||||||
general_msg: 'Ein Fehler ist aufgetreten: %{msg}'
|
general_msg: 'Ein Fehler ist aufgetreten: %{msg}'
|
||||||
|
@ -790,6 +852,8 @@ de:
|
||||||
close:
|
close:
|
||||||
alert: 'Ein Fehler ist beim Abrechnen aufgetreten: %{message}'
|
alert: 'Ein Fehler ist beim Abrechnen aufgetreten: %{message}'
|
||||||
notice: Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert.
|
notice: Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert.
|
||||||
|
notice_mail: Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert. Außerdem wurden automatisch Rechnungen an die Bestellgruppenmitglieder geschickt.
|
||||||
|
settings_not_set: Keine Emails mit Bestellgruppenrechnungen versendet. Bitte überprüfe die Einstellungen. Steuernummer gesetzt?
|
||||||
close_all_direct_with_invoice:
|
close_all_direct_with_invoice:
|
||||||
notice: 'Es wurden %{count} Bestellung abgerechnet.'
|
notice: 'Es wurden %{count} Bestellung abgerechnet.'
|
||||||
close_direct:
|
close_direct:
|
||||||
|
@ -854,6 +918,7 @@ de:
|
||||||
ended: beendet
|
ended: beendet
|
||||||
name: Lieferantin
|
name: Lieferantin
|
||||||
no_closed_orders: Derzeit gibt es keine beendeten Bestellungen.
|
no_closed_orders: Derzeit gibt es keine beendeten Bestellungen.
|
||||||
|
|
||||||
state: Status
|
state: Status
|
||||||
summary:
|
summary:
|
||||||
changed: Daten wurden verändert!
|
changed: Daten wurden verändert!
|
||||||
|
@ -1268,6 +1333,15 @@ de:
|
||||||
feedback:
|
feedback:
|
||||||
header: "%{user} schrieb am %{date}:"
|
header: "%{user} schrieb am %{date}:"
|
||||||
subject: Feedback zur Foodsoft
|
subject: Feedback zur Foodsoft
|
||||||
|
group_order_invoice:
|
||||||
|
subject: Bestellgruppenrechnung für %{group} bei %{supplier}
|
||||||
|
text: |
|
||||||
|
Liebe Bestellgruppe %{group},
|
||||||
|
|
||||||
|
Die Sammelbestellung bei %{supplier} wurde soeben abgerechnet und für die jeweiligen Bestellgruppen Rechnungen angelegt.
|
||||||
|
Im Anhang befindet sich daher eure Rechnung.
|
||||||
|
|
||||||
|
Viele Grüße von %{foodcoop}
|
||||||
invite:
|
invite:
|
||||||
subject: Einladung in die Foodcoop
|
subject: Einladung in die Foodcoop
|
||||||
text: |
|
text: |
|
||||||
|
@ -1500,6 +1574,7 @@ de:
|
||||||
orders_finished: Beendet
|
orders_finished: Beendet
|
||||||
orders_open: Laufend
|
orders_open: Laufend
|
||||||
orders_settled: Abgerechnet
|
orders_settled: Abgerechnet
|
||||||
|
not_closed: Bestellung noch nicht abgerechnet
|
||||||
title: Bestellungen verwalten
|
title: Bestellungen verwalten
|
||||||
model:
|
model:
|
||||||
close_direct_message: Die Bestellung wurde abgechlossen, ohne die Mitgliederkonten zu belasten.
|
close_direct_message: Die Bestellung wurde abgechlossen, ohne die Mitgliederkonten zu belasten.
|
||||||
|
|
|
@ -90,6 +90,16 @@ en:
|
||||||
tolerance: Tolerance
|
tolerance: Tolerance
|
||||||
total_price: Sum
|
total_price: Sum
|
||||||
unit_price: Price/Unit
|
unit_price: Price/Unit
|
||||||
|
group_order_invoice:
|
||||||
|
name: Group order invoice
|
||||||
|
links:
|
||||||
|
delete: delete invoice
|
||||||
|
download: download invoice
|
||||||
|
invoice_date: date of group order invoice
|
||||||
|
generate: generate invoice
|
||||||
|
generate_with_date: set & generate
|
||||||
|
payment_method: Credit
|
||||||
|
tax_number_not_set: Tax number not set in configs
|
||||||
invoice:
|
invoice:
|
||||||
amount: Amount
|
amount: Amount
|
||||||
attachment: Attachment
|
attachment: Attachment
|
||||||
|
@ -318,6 +328,7 @@ en:
|
||||||
emails_title: Sending email
|
emails_title: Sending email
|
||||||
tab_payment:
|
tab_payment:
|
||||||
schedule_title: Ordering schedule
|
schedule_title: Ordering schedule
|
||||||
|
group_order_invoices: Group order invoices
|
||||||
tab_security:
|
tab_security:
|
||||||
default_roles_title: Access to
|
default_roles_title: Access to
|
||||||
default_roles_paragraph: By default every member of the foodcoop has access to the following areas.
|
default_roles_paragraph: By default every member of the foodcoop has access to the following areas.
|
||||||
|
@ -603,6 +614,9 @@ en:
|
||||||
email_from: Emails will appear to be from this email address. Leave empty to use the foodcoop's contact address.
|
email_from: Emails will appear to be from this email address. Leave empty to use the foodcoop's contact address.
|
||||||
email_replyto: Set this when you want to receive replies from emails sent by Foodsoft on a different address than the above.
|
email_replyto: Set this when you want to receive replies from emails sent by Foodsoft on a different address than the above.
|
||||||
email_sender: Emails will appear to be sent from this email address. To avoid emails sent being classified as spam, the webserver may need to be registered in the SPF record of the email address's domain.
|
email_sender: Emails will appear to be sent from this email address. To avoid emails sent being classified as spam, the webserver may need to be registered in the SPF record of the email address's domain.
|
||||||
|
use_automatic_invoices: A listing of the invoice items is made without explicit display of VAT and the invoice receives the necessary addition regarding the small business regulation §19 (applies to Germany)
|
||||||
|
payment_method: Payment type is declared on the order group invoice
|
||||||
|
vat_exempt: A listing of the invoice items is made without explicit display of VAT and the invoice contains the necessary addition regarding the German Kleinunternehmerregelung §19 UStG (attention! FoodCoop marge not included in nvoice).
|
||||||
help_url: Documentation website.
|
help_url: Documentation website.
|
||||||
homepage: Website of your foodcoop.
|
homepage: Website of your foodcoop.
|
||||||
ignore_browser_locale: Ignore the language of user's computer when the user has not chosen a language yet.
|
ignore_browser_locale: Ignore the language of user's computer when the user has not chosen a language yet.
|
||||||
|
@ -630,6 +644,8 @@ en:
|
||||||
tolerance_is_costly: Order as much of the member tolerance as possible (compared to only as much needed to fill the last box). Enabling this also includes the tolerance in the total price of the open member order.
|
tolerance_is_costly: Order as much of the member tolerance as possible (compared to only as much needed to fill the last box). Enabling this also includes the tolerance in the total price of the open member order.
|
||||||
distribution_strategy: How articles should be distributed after an order has been received.
|
distribution_strategy: How articles should be distributed after an order has been received.
|
||||||
use_apple_points: When the apple point system is enabled, members are required to do some tasks to be able to keep ordering.
|
use_apple_points: When the apple point system is enabled, members are required to do some tasks to be able to keep ordering.
|
||||||
|
use_automatic_invoices: When an order is settled, invoices for the individual order groups are automatically sent by mail
|
||||||
|
payment_method: Payment Method for group order invoices
|
||||||
use_boxfill: When enabled, near end of an order, members are only able to change their order when increases the total amount ordered. This helps to fill any remaining boxes. You still need to set a box-fill date for the orders.
|
use_boxfill: When enabled, near end of an order, members are only able to change their order when increases the total amount ordered. This helps to fill any remaining boxes. You still need to set a box-fill date for the orders.
|
||||||
use_iban: When enabled, supplier and user provide an additonal field for storing the international bank account number.
|
use_iban: When enabled, supplier and user provide an additonal field for storing the international bank account number.
|
||||||
use_nick: Show and use nicknames instead of real names. When enabling this, please check that each user has a nickname.
|
use_nick: Show and use nicknames instead of real names. When enabling this, please check that each user has a nickname.
|
||||||
|
@ -645,6 +661,7 @@ en:
|
||||||
phone: Phone
|
phone: Phone
|
||||||
street: Street
|
street: Street
|
||||||
zip_code: Postcode
|
zip_code: Postcode
|
||||||
|
tax_number: Tax number
|
||||||
currency_space: add space
|
currency_space: add space
|
||||||
currency_unit: Currency
|
currency_unit: Currency
|
||||||
custom_css: Custom CSS
|
custom_css: Custom CSS
|
||||||
|
@ -689,6 +706,10 @@ en:
|
||||||
first_order_first_serve: First distribute to those who ordered first
|
first_order_first_serve: First distribute to those who ordered first
|
||||||
no_automatic_distribution: No automatic distribution
|
no_automatic_distribution: No automatic distribution
|
||||||
use_apple_points: Apple points
|
use_apple_points: Apple points
|
||||||
|
group_order_invoices:
|
||||||
|
use_automatic_invoices: Send automatically via mail after oder settlement
|
||||||
|
payment_method: Payment method
|
||||||
|
vat_exempt: This foodcoopis VAT exempt
|
||||||
use_boxfill: Box-fill phase
|
use_boxfill: Box-fill phase
|
||||||
use_iban: Use IBAN
|
use_iban: Use IBAN
|
||||||
use_nick: Use nicknames
|
use_nick: Use nicknames
|
||||||
|
@ -746,6 +767,46 @@ en:
|
||||||
update:
|
update:
|
||||||
notice: Delivery was updated.
|
notice: Delivery was updated.
|
||||||
documents:
|
documents:
|
||||||
|
group_order_invoice_pdf:
|
||||||
|
ordergroup:
|
||||||
|
contact_phone: 'Phone: %{contact_phone}'
|
||||||
|
contact_address: 'Adress : %{contact_address}'
|
||||||
|
name: 'Ordergroup: %{ordergroup}'
|
||||||
|
filename: Invoice%{number}
|
||||||
|
invoicee: Invoicee
|
||||||
|
invoicer: Invoicer
|
||||||
|
invoice_date: 'Invoice date: %{invoice_date}'
|
||||||
|
invoice_number: 'Invoice number: %{invoice_number}'
|
||||||
|
markup_included: incl Foodcoop Marge on gross price %{marge}%
|
||||||
|
payment_method: 'Payment_method: %{payment_method}'
|
||||||
|
small_business_regulation: As a small entrepreneur in the sense of §19 para. 1 of the Umsatzsteuergesetz (UStG), no value added tax is charged.
|
||||||
|
sum_to_pay: Total sum
|
||||||
|
sum_to_pay_net: Total sum (net)
|
||||||
|
sum_to_pay_gross: Total sum (gross)
|
||||||
|
table_headline: 'The following items will be charged for the order:'
|
||||||
|
tax_excluded: excl. MwSt.
|
||||||
|
tax_included: incl. VAT %{tax}%
|
||||||
|
tax_number: 'Tax number: %{number}'
|
||||||
|
title: Invoice for order at %{supplier}
|
||||||
|
vat_exempt_rows:
|
||||||
|
- Name
|
||||||
|
- Quantity
|
||||||
|
- Unit price
|
||||||
|
- Total price
|
||||||
|
no_price_markup_rows:
|
||||||
|
- Name
|
||||||
|
- Quantity
|
||||||
|
- Unit price (net)
|
||||||
|
- Total price (net)
|
||||||
|
- VAT
|
||||||
|
- Total price (gross)
|
||||||
|
price_markup_rows:
|
||||||
|
- Name
|
||||||
|
- Quantity
|
||||||
|
- Unit price (net)
|
||||||
|
- Total price (net)
|
||||||
|
- VAT
|
||||||
|
- Total price (gross) incl. foodcoop margin
|
||||||
order_by_articles:
|
order_by_articles:
|
||||||
filename: Order %{name}-%{date} - by articles
|
filename: Order %{name}-%{date} - by articles
|
||||||
title: 'Order sorted by articles: %{name}, closed at %{date}'
|
title: 'Order sorted by articles: %{name}, closed at %{date}'
|
||||||
|
@ -769,6 +830,7 @@ en:
|
||||||
heading: Article overview (%{count})
|
heading: Article overview (%{count})
|
||||||
title: 'Order sorting matrix: %{name}, closed at %{date}'
|
title: 'Order sorting matrix: %{name}, closed at %{date}'
|
||||||
errors:
|
errors:
|
||||||
|
check_tax_number: Please check whether the foodcoop's tax number is set correctly.
|
||||||
general: A problem has occured.
|
general: A problem has occured.
|
||||||
general_again: A problem has occured. Please try again.
|
general_again: A problem has occured. Please try again.
|
||||||
general_msg: 'A problem has occured: %{msg}'
|
general_msg: 'A problem has occured: %{msg}'
|
||||||
|
@ -792,6 +854,7 @@ en:
|
||||||
close:
|
close:
|
||||||
alert: 'An error occured while accounting: %{message}'
|
alert: 'An error occured while accounting: %{message}'
|
||||||
notice: Order was settled succesfully, the balance of the account was updated.
|
notice: Order was settled succesfully, the balance of the account was updated.
|
||||||
|
settings_not_set: No emails with order group invoices sent. Please check the settings. Tax number set?
|
||||||
close_all_direct_with_invoice:
|
close_all_direct_with_invoice:
|
||||||
notice: '%{count} orders have been settled.'
|
notice: '%{count} orders have been settled.'
|
||||||
close_direct:
|
close_direct:
|
||||||
|
@ -1272,6 +1335,15 @@ en:
|
||||||
feedback:
|
feedback:
|
||||||
header: "%{user} wrote at %{date}:"
|
header: "%{user} wrote at %{date}:"
|
||||||
subject: Feedback for Foodsoft
|
subject: Feedback for Foodsoft
|
||||||
|
group_order_invoice:
|
||||||
|
subject: Order group invoice for %{group} at %{supplier}
|
||||||
|
text: |
|
||||||
|
Dear order group %{group},
|
||||||
|
|
||||||
|
The collective order at %{supplier} has just been settled and invoices have been created for the respective order groups.
|
||||||
|
Attached you will find your invoice.
|
||||||
|
|
||||||
|
Best regards from %{foodcoop}
|
||||||
from_via_foodsoft: "%{name} via Foodsoft"
|
from_via_foodsoft: "%{name} via Foodsoft"
|
||||||
invite:
|
invite:
|
||||||
subject: Invitation to the Foodcoop
|
subject: Invitation to the Foodcoop
|
||||||
|
@ -1511,6 +1583,7 @@ en:
|
||||||
orders_finished: Closed
|
orders_finished: Closed
|
||||||
orders_open: Open
|
orders_open: Open
|
||||||
orders_settled: Settled
|
orders_settled: Settled
|
||||||
|
not_closed: Order not yet settled
|
||||||
title: Manage orders
|
title: Manage orders
|
||||||
model:
|
model:
|
||||||
close_direct_message: Order settled without charging member accounts.
|
close_direct_message: Order settled without charging member accounts.
|
||||||
|
|
|
@ -143,6 +143,10 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
post 'finance/group_order_invoice', to: 'group_order_invoices#create_multiple'
|
||||||
|
resources :group_order_invoices
|
||||||
|
|
||||||
resources :article_categories
|
resources :article_categories
|
||||||
|
|
||||||
########### Finance
|
########### Finance
|
||||||
|
|
13
db/migrate/20211208142719_create_group_order_invoices.rb
Normal file
13
db/migrate/20211208142719_create_group_order_invoices.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
class CreateGroupOrderInvoices < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
create_table :group_order_invoices do |t|
|
||||||
|
t.integer :group_order_id
|
||||||
|
t.bigint :invoice_number, unique: true, limit: 8
|
||||||
|
t.date :invoice_date
|
||||||
|
t.string :payment_method
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
add_index :group_order_invoices, :group_order_id, unique: true
|
||||||
|
end
|
||||||
|
end
|
7
spec/factories/group_order_invoice.rb
Normal file
7
spec/factories/group_order_invoice.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
require 'factory_bot'
|
||||||
|
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :group_order_invoice do
|
||||||
|
group_order { create :group_order }
|
||||||
|
end
|
||||||
|
end
|
71
spec/integration/group_order_invoices_spec.rb
Normal file
71
spec/integration/group_order_invoices_spec.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
|
||||||
|
feature GroupOrderInvoice, js: true do
|
||||||
|
let(:admin) { create :user, groups: [create(:workgroup, role_finance: true)] }
|
||||||
|
let(:user) { create :user, groups: [create(:ordergroup)] }
|
||||||
|
let(:article) { create :article, unit_quantity: 1 }
|
||||||
|
let(:order) { create :order, supplier: article.supplier, article_ids: [article.id], ends: Time.now } # need to ref article
|
||||||
|
let(:go) { create :group_order, order: order, ordergroup: user.ordergroup}
|
||||||
|
let(:oa) { order.order_articles.find_by_article_id(article.id) }
|
||||||
|
let(:ftt) { create :financial_transaction_type }
|
||||||
|
let(:goa) { create :group_order_article, group_order: go, order_article: oa }
|
||||||
|
|
||||||
|
include ActiveJob::TestHelper
|
||||||
|
|
||||||
|
before { login admin }
|
||||||
|
|
||||||
|
after { clear_enqueued_jobs }
|
||||||
|
|
||||||
|
it 'does not enqueue MailerJob when order is settled if tax_number or options not set' do
|
||||||
|
goa.update_quantities 2, 0
|
||||||
|
oa.update_results!
|
||||||
|
visit confirm_finance_order_path(id: order.id)
|
||||||
|
click_link_or_button I18n.t('finance.balancing.confirm.clear')
|
||||||
|
expect(NotifyGroupOrderInvoiceJob).not_to have_been_enqueued
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'enqueues MailerJob when order is settled if tax_number or options are set' do
|
||||||
|
goa.update_quantities 2, 0
|
||||||
|
oa.update_results!
|
||||||
|
order.reload
|
||||||
|
FoodsoftConfig[:group_order_invoices] = { use_automatic_invoices: true }
|
||||||
|
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
||||||
|
visit confirm_finance_order_path(id: order.id, type: ftt)
|
||||||
|
expect(page).to have_selector(:link_or_button, I18n.t('finance.balancing.confirm.clear'))
|
||||||
|
click_link_or_button I18n.t('finance.balancing.confirm.clear')
|
||||||
|
expect(NotifyGroupOrderInvoiceJob).to have_been_enqueued
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'generates Group Order Invoice when order is closed if tax_number is set' do
|
||||||
|
goa.update_quantities 2, 0
|
||||||
|
oa.update_results!
|
||||||
|
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
||||||
|
order.update!(state: 'closed')
|
||||||
|
order.reload
|
||||||
|
visit finance_order_index_path
|
||||||
|
expect(page).to have_selector(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate'))
|
||||||
|
click_link_or_button I18n.t('activerecord.attributes.group_order_invoice.links.generate')
|
||||||
|
expect(GroupOrderInvoice.all.count).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'generates multiple Group Order Invoice for order when order is closed if tax_number is set' do
|
||||||
|
goa.update_quantities 2, 0
|
||||||
|
oa.update_results!
|
||||||
|
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
||||||
|
order.update!(state: 'closed')
|
||||||
|
order.reload
|
||||||
|
visit finance_order_index_path
|
||||||
|
expect(page).to have_selector(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate_with_date'))
|
||||||
|
click_link_or_button I18n.t('activerecord.attributes.group_order_invoice.links.generate_with_date')
|
||||||
|
expect(GroupOrderInvoice.all.count).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not generate Group Order Invoice when order is closed if tax_number not set' do
|
||||||
|
goa.update_quantities 2, 0
|
||||||
|
oa.update_results!
|
||||||
|
order.update!(state: 'closed')
|
||||||
|
order.reload
|
||||||
|
visit finance_order_index_path
|
||||||
|
expect(page).to have_content(I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set'))
|
||||||
|
end
|
||||||
|
end
|
59
spec/models/group_order_invoice_spec.rb
Normal file
59
spec/models/group_order_invoice_spec.rb
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
|
||||||
|
describe GroupOrderInvoice do
|
||||||
|
let(:user) { create :user, groups: [create(:ordergroup)] }
|
||||||
|
let(:supplier) { create :supplier }
|
||||||
|
let(:article) { create :article, supplier: supplier }
|
||||||
|
let(:order) { create :order }
|
||||||
|
let(:group_order) { create :group_order, order: order, ordergroup: user.ordergroup }
|
||||||
|
|
||||||
|
describe 'erroneous group order invoice' do
|
||||||
|
let(:goi) { create :group_order_invoice, group_order_id: group_order.id }
|
||||||
|
it 'does not create group order invoice if tax_number not set' do
|
||||||
|
expect { goi }.to raise_error(ActiveRecord::RecordInvalid, /.*/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'valid group order invoice' do
|
||||||
|
before do
|
||||||
|
FoodsoftConfig[:contact][:tax_number] = 123_457_8
|
||||||
|
end
|
||||||
|
|
||||||
|
invoice_number1 = Time.now.strftime("%Y%m%d") + '0001'
|
||||||
|
invoice_number2 = Time.now.strftime("%Y%m%d") + '0002'
|
||||||
|
|
||||||
|
let(:user2) { create :user, groups: [create(:ordergroup)] }
|
||||||
|
|
||||||
|
let(:goi1) { create :group_order_invoice, group_order_id: group_order.id }
|
||||||
|
let(:goi2) { create :group_order_invoice, group_order_id: group_order.id }
|
||||||
|
|
||||||
|
let(:group_order2) { create :group_order, order: order, ordergroup: user2.ordergroup }
|
||||||
|
|
||||||
|
let(:goi3) { create :group_order_invoice, group_order_id: group_order2.id }
|
||||||
|
let(:goi4) { create :group_order_invoice, group_order_id: group_order2.id, invoice_number: invoice_number1 }
|
||||||
|
|
||||||
|
it 'creates group order invoice if tax_number is set' do
|
||||||
|
expect(goi1).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets invoice_number according to date' do
|
||||||
|
number = Time.now.strftime("%Y%m%d") + '0001'
|
||||||
|
expect(goi1.invoice_number).to eq(number.to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails to create if group_order_id is used multiple times for creation' do
|
||||||
|
expect(goi1.group_order.id).to eq(group_order.id)
|
||||||
|
expect { goi2 }.to raise_error(ActiveRecord::RecordNotUnique)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates two different group order invoice with different invoice_numbers' do
|
||||||
|
expect(goi1.invoice_number).to eq(invoice_number1.to_i)
|
||||||
|
expect(goi3.invoice_number).to eq(invoice_number2.to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails to create two different group order invoice with same invoice_numbers' do
|
||||||
|
goi1
|
||||||
|
expect { goi4 }.to raise_error(ActiveRecord::RecordInvalid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue