From 1de377c13a8818fc78cad79a12c9d108bb7b1265 Mon Sep 17 00:00:00 2001 From: viehlieb Date: Thu, 27 Jan 2022 12:42:29 +0100 Subject: [PATCH 1/5] add vat exempt option in payment_tab ~ refs #automatic_go_invoices --- app/views/admin/configs/_tab_payment.html.haml | 4 +++- config/locales/de.yml | 6 ++++-- config/locales/en.yml | 6 ++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/views/admin/configs/_tab_payment.html.haml b/app/views/admin/configs/_tab_payment.html.haml index de7ea627..47f77b04 100644 --- a/app/views/admin/configs/_tab_payment.html.haml +++ b/app/views/admin/configs/_tab_payment.html.haml @@ -13,9 +13,11 @@ = config_input form, :charge_members_manually, as: :boolean = config_input form, :use_iban, 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, as: :boolean - = config_input field, :payment_method, as: :string, input_html: {class: 'input-mini'} + = config_input field, :vat_exempt, as: :boolean + = config_input field, :payment_method, as: :string, input_html: {class: 'input-medium'} %h4= t '.schedule_title' = form.simple_fields_for :order_schedule do |fields| diff --git a/config/locales/de.yml b/config/locales/de.yml index 1512514a..e7641244 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -304,6 +304,7 @@ de: emails_title: E-Mails versenden tab_payment: schedule_title: Bestellschema + group_order_invoices: Bestellgruppenrechnungen tab_security: default_roles_title: Zugriff auf default_roles_paragraph: Jedes Mitglied der Foodcoop hat automatisch Zugriff auf folgende Bereiche. @@ -670,8 +671,9 @@ de: no_automatic_distribution: Keine automatische Verteilung use_apple_points: Apfelpunkte verwenden group_order_invoices: - use: Bestellgruppen Rechnungen automatisch bei Abrechnung einer Bestellung versenden - payment_method: Zahlungsart für Bestellgruppenrechnungen + 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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 30e864f0..2afc3afe 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -304,6 +304,7 @@ en: emails_title: Sending email tab_payment: schedule_title: Ordering schedule + group_order_invoices: Group order invoices tab_security: default_roles_title: Access to default_roles_paragraph: By default every member of the foodcoop has access to the following areas. @@ -671,8 +672,9 @@ en: no_automatic_distribution: No automatic distribution use_apple_points: Apple points group_order_invoices: - use: Automatically send group order invoices when an order is settled - payment_method: Payment method for group order invoices + use: Send automatically via mail after oder settlement + payment_method: Payment method + vat_exempt: This foodcoopis VAT exempt use_boxfill: Box-fill phase use_iban: Use IBAN use_nick: Use nicknames From bddaebe1a9a51ce0ac01bc79095bcb87629e4d11 Mon Sep 17 00:00:00 2001 From: viehlieb Date: Thu, 27 Jan 2022 16:42:22 +0100 Subject: [PATCH 2/5] 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 From 1fc582319f64652b9080d81d40cc3b6bdd648943 Mon Sep 17 00:00:00 2001 From: viehlieb Date: Fri, 28 Jan 2022 13:39:17 +0100 Subject: [PATCH 3/5] fix order.ordergroup nil pointer error ~ automatic go invoices --- app/views/finance/balancing/_orders.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/finance/balancing/_orders.html.haml b/app/views/finance/balancing/_orders.html.haml index addbf930..dddb00c2 100644 --- a/app/views/finance/balancing/_orders.html.haml +++ b/app/views/finance/balancing/_orders.html.haml @@ -21,7 +21,7 @@ %td= show_user(order.updated_by) %td{id: "generate-invoice#{order.id}"} - if order.closed? - -if FoodsoftConfig[:contact][:tax_number] + -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') From 81daddfb10b73875aed906615574456fed5365f8 Mon Sep 17 00:00:00 2001 From: viehlieb Date: Thu, 3 Feb 2022 10:56:43 +0100 Subject: [PATCH 4/5] fix nil pointer~ refs automatic go invoice --- app/views/group_order_invoices/_links.html.haml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/views/group_order_invoices/_links.html.haml b/app/views/group_order_invoices/_links.html.haml index 0d270756..78922d76 100644 --- a/app/views/group_order_invoices/_links.html.haml +++ b/app/views/group_order_invoices/_links.html.haml @@ -1,11 +1,12 @@ -.row - - order.group_orders.includes([:group_order_invoice, :ordergroup]).each do |go| - .row +- 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 + - 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 - %br + - 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 From bb69c3581a25fea64cb35c9a53333c3e623c58b8 Mon Sep 17 00:00:00 2001 From: viehlieb Date: Thu, 3 Feb 2022 11:33:16 +0100 Subject: [PATCH 5/5] rubocop styling ~ refs automatic go invoice --- app/documents/group_order_invoice_pdf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/documents/group_order_invoice_pdf.rb b/app/documents/group_order_invoice_pdf.rb index f3d93d33..aba39506 100644 --- a/app/documents/group_order_invoice_pdf.rb +++ b/app/documents/group_order_invoice_pdf.rb @@ -66,7 +66,7 @@ class GroupOrderInvoicePdf < RenderPDF #------------- Table Data ----------------------- @group_order = GroupOrder.find(@options[:group_order].id) - if (FoodsoftConfig[:group_order_invoices][:vat_exempt]) + if FoodsoftConfig[:group_order_invoices][:vat_exempt] body_for_vat_exempt else body_with_vat