add option vor vat_exempt inoices ~ refs automatic go invoices

This commit is contained in:
viehlieb 2022-01-27 16:42:22 +01:00
parent 1de377c13a
commit bddaebe1a9
6 changed files with 106 additions and 26 deletions

View file

@ -84,7 +84,7 @@ class Finance::BalancingController < Finance::BaseController
note = t('finance.balancing.close.notice') note = t('finance.balancing.close.notice')
if @order.closed? if @order.closed?
alert = t('finance.balancing.close.alert') alert = t('finance.balancing.close.alert')
if FoodsoftConfig[:group_order_invoices]&.[](:use) if FoodsoftConfig[:group_order_invoices]&.[](:use_automatic_invoices)
@order.group_orders.each do |go| @order.group_orders.each do |go|
alert = t('finance.balancing.close.settings_not_set') alert = t('finance.balancing.close.settings_not_set')
goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id) goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)

View file

@ -64,6 +64,64 @@ class GroupOrderInvoicePdf < RenderPDF
move_down 5 move_down 5
#------------- Table Data ----------------------- #------------- 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_gross = 0
total_net = 0 total_net = 0
# Articles # Articles
@ -71,7 +129,6 @@ class GroupOrderInvoicePdf < RenderPDF
tax_hash_net = Hash.new(0) # for summing up article net prices grouped into vat percentage 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 tax_hash_gross = Hash.new(0) # same here with gross prices
group_order = GroupOrder.find(@options[:group_order].id)
marge = FoodsoftConfig[:price_markup] marge = FoodsoftConfig[:price_markup]
# data table looks different when price_markup > 0 # data table looks different when price_markup > 0
@ -80,7 +137,7 @@ class GroupOrderInvoicePdf < RenderPDF
else else
[I18n.t('documents.group_order_invoice_pdf.price_markup_rows', marge: marge)] [I18n.t('documents.group_order_invoice_pdf.price_markup_rows', marge: marge)]
end 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 = 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| goa_tax_hash.each do |tax, group_order_articles|
group_order_articles.each do |goa| group_order_articles.each do |goa|
# if no unit is received, nothing is to be charged # if no unit is received, nothing is to be charged
@ -136,6 +193,10 @@ class GroupOrderInvoicePdf < RenderPDF
table.row(sum.length - 1).columns(5).borders = [:bottom] table.row(sum.length - 1).columns(5).borders = [:bottom]
end end
if(FoodsoftConfig[:group_order_invoices][:vat_exempt])
move_down 15 move_down 15
text I18n.t('documents.group_order_invoice_pdf.small_business_regulation')
end
move_down 10
end end
end end

View file

@ -15,7 +15,7 @@
= config_input form, :use_self_service, as: :boolean = config_input form, :use_self_service, as: :boolean
%h4= t '.group_order_invoices' %h4= t '.group_order_invoices'
= form.fields_for :group_order_invoices do |field| = form.fields_for :group_order_invoices do |field|
= config_input field, :use, as: :boolean = config_input field, :use_automatic_invoices, as: :boolean
= config_input field, :vat_exempt, as: :boolean = config_input field, :vat_exempt, as: :boolean
= config_input field, :payment_method, as: :string, input_html: {class: 'input-medium'} = config_input field, :payment_method, as: :string, input_html: {class: 'input-medium'}

View file

@ -582,6 +582,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.
@ -609,8 +613,6 @@ de:
tolerance_is_costly: Eine möglichst große Menge im Rahmen der Tolerenz bestellen. Wenn dies nicht aktiviert ist, wird im Rahmen der Toleranz nur so viel bestellt, dass damit komplette Einheiten (Boxen) bestellt werden können. Die Option wirkt sich auch auf die Toleranz des Gesamtpreises einer offenen Mitgliederbestellung aus. tolerance_is_costly: Eine möglichst große Menge im Rahmen der Tolerenz bestellen. Wenn dies nicht aktiviert ist, wird im Rahmen der Toleranz nur so viel bestellt, dass damit komplette Einheiten (Boxen) bestellt werden können. Die Option wirkt sich auch auf die Toleranz des Gesamtpreises einer offenen Mitgliederbestellung aus.
distribution_strategy: Wie bei der Verteilung von Artikeln nach dem Empfangen einer Bestellung vorgegangen werden soll. distribution_strategy: Wie bei der Verteilung von Artikeln nach dem Empfangen einer Bestellung vorgegangen werden soll.
use_apple_points: Wenn das Apfel Punktesystem aktiviert ist, ist es erforderlich, dass Mitglieder Aufgaben erledigen um bestellen zu können. use_apple_points: Wenn das Apfel Punktesystem aktiviert ist, ist es erforderlich, dass Mitglieder Aufgaben erledigen um bestellen zu können.
use_automatic_invoices: Bei der Abrechnung einer Bestellung werden Rechnungen für die einzelnen Bestellgruppenautomatisch per Mail versandt
payment_method: Zahlungsart für Bestellgruppenrechnungen
use_boxfill: Wenn aktiviert, können Benutzer nahe am Ende der Bestellung diese nur mehr so verändern, dass sich die Gesamtsumme erhöht. Dies hilft beim auffüllen der verbleibenden Kisten. Es muss trotzdem noch das Kistenauffülldatum bei der Bestellung gesetzt werden. use_boxfill: Wenn aktiviert, können Benutzer nahe am Ende der Bestellung diese nur mehr so verändern, dass sich die Gesamtsumme erhöht. Dies hilft beim auffüllen der verbleibenden Kisten. Es muss trotzdem noch das Kistenauffülldatum bei der Bestellung gesetzt werden.
use_iban: Zusätzlich Feld für die internationale Kontonummer bei Benutzern und Lieferanten anzeigen use_iban: Zusätzlich Feld für die internationale Kontonummer bei Benutzern und Lieferanten anzeigen
use_nick: Benutzernamen anstatt reale Namen zeigen und verwenden, jeder Benutzer muss dazu einen Benutzernamen (Spitznamen) haben. use_nick: Benutzernamen anstatt reale Namen zeigen und verwenden, jeder Benutzer muss dazu einen Benutzernamen (Spitznamen) haben.
@ -641,6 +643,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
@ -670,10 +676,6 @@ de:
first_order_first_serve: Zuerst an die verteilen, die zuerst bestellt haben first_order_first_serve: Zuerst an die verteilen, die zuerst bestellt haben
no_automatic_distribution: Keine automatische Verteilung no_automatic_distribution: Keine automatische Verteilung
use_apple_points: Apfelpunkte verwenden use_apple_points: Apfelpunkte verwenden
group_order_invoices:
use: Automatisch bei Abrechnung per Mail versenden
payment_method: Zahlungsart
vat_exempt: Diese Foodcoop ist MwSt. befreit
use_boxfill: Kistenauffüllphase use_boxfill: Kistenauffüllphase
use_iban: IBAN verwenden use_iban: IBAN verwenden
use_nick: Benutzernamen verwenden use_nick: Benutzernamen verwenden
@ -742,13 +744,20 @@ de:
contact_address: 'Adresse : %{contact_address}' contact_address: 'Adresse : %{contact_address}'
name: Bestellgruppe %{ordergroup} name: Bestellgruppe %{ordergroup}
payment_method: 'Zahlungsart: %{payment_method}' payment_method: 'Zahlungsart: %{payment_method}'
sum_to_pay: Zu zahlen gesamt
sum_to_pay_net: Zu zahlen gesamt (netto) sum_to_pay_net: Zu zahlen gesamt (netto)
sum_to_pay_gross: Zu zahlen gesamt (brutto) 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:' table_headline: 'Für die Bestellung fallen folgende Posten an:'
tax_excluded: exkl. MwSt. tax_excluded: exkl. MwSt.
tax_included: zzgl. Gesamtsumme MwSt. %{tax}% tax_included: zzgl. Gesamtsumme MwSt. %{tax}%
tax_number: 'Steuernummer: %{number}' tax_number: 'Steuernummer: %{number}'
title: Rechnung für die Bestellung bei %{supplier} title: Rechnung für die Bestellung bei %{supplier}
vat_exempt_rows:
- Name
- Anzahl
- Einzelpreis
- Artikel Gesamtpreis
no_price_markup_rows: no_price_markup_rows:
- Name - Name
- Anzahl - Anzahl

View file

@ -583,6 +583,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.
@ -672,7 +675,7 @@ en:
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: group_order_invoices:
use: Send automatically via mail after oder settlement use_automatic_invoices: Send automatically via mail after oder settlement
payment_method: Payment method payment_method: Payment method
vat_exempt: This foodcoopis VAT exempt vat_exempt: This foodcoopis VAT exempt
use_boxfill: Box-fill phase use_boxfill: Box-fill phase
@ -744,6 +747,8 @@ en:
invoice_number: 'Invoice number: %{invoice_number}' invoice_number: 'Invoice number: %{invoice_number}'
markup_included: incl Foodcoop Marge on gross price %{marge}% markup_included: incl Foodcoop Marge on gross price %{marge}%
payment_method: 'Payment_method: %{payment_method}' 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_net: Total sum (net)
sum_to_pay_gross: Total sum (gross) sum_to_pay_gross: Total sum (gross)
table_headline: 'The following items will be charged for the order:' table_headline: 'The following items will be charged for the order:'
@ -751,16 +756,21 @@ en:
tax_included: incl. VAT %{tax}% tax_included: incl. VAT %{tax}%
tax_number: 'Tax number: %{number}' tax_number: 'Tax number: %{number}'
title: Invoice for order at %{supplier} title: Invoice for order at %{supplier}
vat_exempt_rows:
- Name
- Quantity
- Unit price
- Total price
no_price_markup_rows: no_price_markup_rows:
- Name - Name
- Amount - Quantity
- Unit price (net) - Unit price (net)
- Total price (net) - Total price (net)
- VAT - VAT
- Total price (gross) - Total price (gross)
price_markup_rows: price_markup_rows:
- Name - Name
- Amount - Quantity
- Unit price (net) - Unit price (net)
- Total price (net) - Total price (net)
- VAT - VAT

View file

@ -4,7 +4,7 @@ feature GroupOrderInvoice, js: true do
let(:admin) { create :user, groups: [create(:workgroup, role_finance: true)] } let(:admin) { create :user, groups: [create(:workgroup, role_finance: true)] }
let(:article) { create :article, unit_quantity: 1 } 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(:order) { create :order, supplier: article.supplier, article_ids: [article.id], ends: Time.now } # need to ref article
let(:go) { create :group_order, order: order } let(:go) { create :group_order, order: order}
let(:oa) { order.order_articles.find_by_article_id(article.id) } let(:oa) { order.order_articles.find_by_article_id(article.id) }
let(:ftt) { create :financial_transaction_type } let(:ftt) { create :financial_transaction_type }
let(:goa) { create :group_order_article, group_order: go, order_article: oa } let(:goa) { create :group_order_article, group_order: go, order_article: oa }
@ -27,7 +27,7 @@ feature GroupOrderInvoice, js: true do
goa.update_quantities 2, 0 goa.update_quantities 2, 0
oa.update_results! oa.update_results!
order.reload order.reload
FoodsoftConfig[:group_order_invoices] = { use: true } FoodsoftConfig[:group_order_invoices] = { use_automatic_invoices: true }
FoodsoftConfig[:contact][:tax_number] = 12_345_678 FoodsoftConfig[:contact][:tax_number] = 12_345_678
visit confirm_finance_order_path(id: order.id, type: ftt) visit confirm_finance_order_path(id: order.id, type: ftt)
expect(page).to have_selector(:link_or_button, I18n.t('finance.balancing.confirm.clear')) expect(page).to have_selector(:link_or_button, I18n.t('finance.balancing.confirm.clear'))
@ -35,15 +35,6 @@ feature GroupOrderInvoice, js: true do
expect(NotifyGroupOrderInvoiceJob).to have_been_enqueued expect(NotifyGroupOrderInvoiceJob).to have_been_enqueued
end 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
it 'generates Group Order Invoice when order is closed if tax_number is set' do it 'generates Group Order Invoice when order is closed if tax_number is set' do
goa.update_quantities 2, 0 goa.update_quantities 2, 0
oa.update_results! oa.update_results!
@ -54,4 +45,13 @@ feature GroupOrderInvoice, js: true do
click_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) expect(GroupOrderInvoice.all.count).to eq(1)
end 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 end