From bddaebe1a9a51ce0ac01bc79095bcb87629e4d11 Mon Sep 17 00:00:00 2001 From: viehlieb Date: Thu, 27 Jan 2022 16:42:22 +0100 Subject: [PATCH] add option vor vat_exempt inoices ~ refs automatic go invoices --- .../finance/balancing_controller.rb | 2 +- app/documents/group_order_invoice_pdf.rb | 67 ++++++++++++++++++- .../admin/configs/_tab_payment.html.haml | 2 +- config/locales/de.yml | 21 ++++-- config/locales/en.yml | 16 ++++- spec/integration/group_order_invoices_spec.rb | 24 +++---- 6 files changed, 106 insertions(+), 26 deletions(-) diff --git a/app/controllers/finance/balancing_controller.rb b/app/controllers/finance/balancing_controller.rb index 485fe7b7..1bd575e7 100644 --- a/app/controllers/finance/balancing_controller.rb +++ b/app/controllers/finance/balancing_controller.rb @@ -84,7 +84,7 @@ class Finance::BalancingController < Finance::BaseController note = t('finance.balancing.close.notice') if @order.closed? 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| alert = t('finance.balancing.close.settings_not_set') goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id) diff --git a/app/documents/group_order_invoice_pdf.rb b/app/documents/group_order_invoice_pdf.rb index 76bf107f..f3d93d33 100644 --- a/app/documents/group_order_invoice_pdf.rb +++ b/app/documents/group_order_invoice_pdf.rb @@ -64,6 +64,64 @@ class GroupOrderInvoicePdf < RenderPDF 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 @@ -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_gross = Hash.new(0) # same here with gross prices - group_order = GroupOrder.find(@options[:group_order].id) marge = FoodsoftConfig[:price_markup] # data table looks different when price_markup > 0 @@ -80,7 +137,7 @@ class GroupOrderInvoicePdf < RenderPDF 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 = 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 @@ -136,6 +193,10 @@ class GroupOrderInvoicePdf < RenderPDF table.row(sum.length - 1).columns(5).borders = [:bottom] end - move_down 15 + 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 diff --git a/app/views/admin/configs/_tab_payment.html.haml b/app/views/admin/configs/_tab_payment.html.haml index 47f77b04..70502c90 100644 --- a/app/views/admin/configs/_tab_payment.html.haml +++ b/app/views/admin/configs/_tab_payment.html.haml @@ -15,7 +15,7 @@ = 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, as: :boolean + = 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'} diff --git a/config/locales/de.yml b/config/locales/de.yml index e7641244..d7dc910f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -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_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. + 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 homepage: Webseite der Foodcoop 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. 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_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_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. @@ -641,6 +643,10 @@ de: email_from: Absenderadresse email_replyto: Antwortadresse 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 homepage: Webseite ignore_browser_locale: Browsersprache ignorieren @@ -670,10 +676,6 @@ de: first_order_first_serve: Zuerst an die verteilen, die zuerst bestellt haben no_automatic_distribution: Keine automatische Verteilung 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_iban: IBAN verwenden use_nick: Benutzernamen verwenden @@ -742,13 +744,20 @@ de: 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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 2afc3afe..ef091a49 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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_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. + 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. homepage: Website of your foodcoop. 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 use_apple_points: Apple points 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 vat_exempt: This foodcoopis VAT exempt use_boxfill: Box-fill phase @@ -744,6 +747,8 @@ en: 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:' @@ -751,16 +756,21 @@ en: 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 - - Amount + - Quantity - Unit price (net) - Total price (net) - VAT - Total price (gross) price_markup_rows: - Name - - Amount + - Quantity - Unit price (net) - Total price (net) - VAT diff --git a/spec/integration/group_order_invoices_spec.rb b/spec/integration/group_order_invoices_spec.rb index f2a76847..615bc218 100644 --- a/spec/integration/group_order_invoices_spec.rb +++ b/spec/integration/group_order_invoices_spec.rb @@ -4,7 +4,7 @@ feature GroupOrderInvoice, js: true do let(:admin) { create :user, groups: [create(:workgroup, role_finance: true)] } 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 } + let(:go) { create :group_order, order: order} 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 } @@ -27,23 +27,14 @@ feature GroupOrderInvoice, js: true do goa.update_quantities 2, 0 oa.update_results! order.reload - FoodsoftConfig[:group_order_invoices] = { use: true } + 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 '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 goa.update_quantities 2, 0 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') 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