diff --git a/app/assets/javascripts/order.js b/app/assets/javascripts/order.js index baf0c5f4..c7e49b20 100644 --- a/app/assets/javascripts/order.js +++ b/app/assets/javascripts/order.js @@ -90,18 +90,20 @@ $(document).off('change', '[class^="ajax-update-all-link-"] select').on('change' }); }); -$(document).off('change', '[class^="ajax-update-link-"] select').on('change', '[class^="ajax-update-link-"] select', function () { +$(document).off('change', '.ajax-update-sepa-select').on('change', '.ajax-update-sepa-select', function () { var selectedValue = $(this).val(); - var url = $(this).closest('a').attr('href'); + var url = $(this).data('url'); + console.log(url); + console.log(selectedValue); $.ajax({ url: url, method: 'PATCH', data: { sepa_sequence_type: selectedValue }, success: function (response) { - // Handle success response + console.log("succeeded"); }, error: function (error) { - console.log(error); + console.error(error); } }); }); @@ -132,6 +134,7 @@ $(document).on('ready turbolinks:load', function () { }); $(document).on('click', '.merge-orders-btn', function () { + const url = $(this).data('url'); const selectedOrderIds = $('input[name="order_ids_for_multi_order[]"]:checked').map(function () { return $(this).val(); diff --git a/app/controllers/concerns/send_group_order_invoice_pdf.rb b/app/controllers/concerns/send_group_order_invoice_pdf.rb index 0734ea2d..76e71c99 100644 --- a/app/controllers/concerns/send_group_order_invoice_pdf.rb +++ b/app/controllers/concerns/send_group_order_invoice_pdf.rb @@ -7,14 +7,6 @@ module Concerns::SendGroupOrderInvoicePdf 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 - puts " - " + "____________" + " - " + "____________" + " - " + "____________" + " - " + "#{invoice_data.inspect}" + " - " + "____________"+ " - " + "____________"+ " - " + "____________" GroupOrderInvoicePdf.new invoice_data end diff --git a/app/controllers/finance/invoices_controller.rb b/app/controllers/finance/invoices_controller.rb index d70b92ec..c85ae16b 100644 --- a/app/controllers/finance/invoices_controller.rb +++ b/app/controllers/finance/invoices_controller.rb @@ -1,4 +1,4 @@ -class Finance::InvoicesController < ApplicationController +class Finance::InvoicesController < OrderInvoiceControllerBase before_action :authenticate_finance_or_invoices before_action :find_invoice, only: %i[show edit update destroy] diff --git a/app/controllers/group_order_invoices_controller.rb b/app/controllers/group_order_invoices_controller.rb index c72854b5..4994515a 100644 --- a/app/controllers/group_order_invoices_controller.rb +++ b/app/controllers/group_order_invoices_controller.rb @@ -1,6 +1,5 @@ -class GroupOrderInvoicesController < ApplicationController +class GroupOrderInvoicesController < OrderInvoicesControllerBase include Concerns::SendGroupOrderInvoicePdf - before_action :authenticate_finance def show @group_order_invoice = GroupOrderInvoice.find(params[:id]) @@ -57,21 +56,6 @@ class GroupOrderInvoicesController < ApplicationController end end - def select_sepa_sequence_type - @group_order_invoice = GroupOrderInvoice.find(params[:id]) - @group_order = @group_order_invoice.group_order - return unless params[:sepa_sequence_type] - - respond_to do |format| - @group_order_invoice.sepa_sequence_type = params[:sepa_sequence_type] - if @group_order_invoice.save! - format.js - else - format.json { render json: @group_order_invoice.errors, status: :unprocessable_entity } - end - end - end - def select_all_sepa_sequence_type @order = Order.find(params[:order_id]) @group_order_invoices = @order.group_orders.map(&:group_order_invoice).compact @@ -86,31 +70,6 @@ class GroupOrderInvoicesController < ApplicationController end end - def toggle_paid - @group_order_invoice = GroupOrderInvoice.find(params[:id]) - respond_to do |format| - @group_order_invoice.paid = !@group_order_invoice.paid - if @group_order_invoice.save! - format.js - else - format.json { render json: @group_order_invoice.errors, status: :unprocessable_entity } - end - end - end - - def toggle_sepa_downloaded - @group_order_invoice = GroupOrderInvoice.find(params[:id]) - @order = @group_order_invoice.group_order.order - respond_to do |format| - @group_order_invoice.sepa_downloaded = !@group_order_invoice.sepa_downloaded - if @group_order_invoice.save! - format.js - else - format.json { render json: @group_order_invoice.errors, status: :unprocessable_entity } - end - end - end - def toggle_all_paid @order = Order.find(params[:order_id]) @group_order_invoices = @order.group_orders.map(&:group_order_invoice).compact @@ -164,4 +123,14 @@ class GroupOrderInvoicesController < ApplicationController end end end + + protected + + def invoice_class + GroupOrderInvoice + end + + def set_related_group_order(invoice) + invoice.group_order + end end diff --git a/app/controllers/multi_orders_controller.rb b/app/controllers/multi_orders_controller.rb index f762a72c..68e9f174 100644 --- a/app/controllers/multi_orders_controller.rb +++ b/app/controllers/multi_orders_controller.rb @@ -39,27 +39,18 @@ class MultiOrdersController < ApplicationController end return end - begin @multi_order = MultiOrder.new @multi_order.orders = orders @multi_order.ends = orders.map(&:ends).max @multi_order.save! - #create multi group orders - all_group_orders = orders.flat_map(&:group_orders) - - grouped_by_ordergroup = all_group_orders.group_by(&:ordergroup_id) - - grouped_by_ordergroup.each do |ordergroup_id, group_orders| - multi_group_order = MultiGroupOrder.create!( - multi_order: @multi_order, group_orders: group_orders - ) - # Now, associate each group_order with the new multi_group_order - group_orders.each do |group_order| - group_order.update!(multi_group_order: multi_group_order) - end + suppliers = orders.map(&:supplier).map(&:name).join(', ') + msg = "Multi Bestellung für #{suppliers} erstellt" + respond_to do |format| + flash[:notice] = msg + format.js + format.html { redirect_to finance_order_index_path } end - redirect_to finance_order_index_path rescue ActiveRecord::RecordInvalid => e flash[:alert] = t('errors.general_msg', msg: e.message) respond_to do |format| @@ -71,9 +62,13 @@ class MultiOrdersController < ApplicationController def destroy @multi_order = MultiOrder.find(params[:id]) - @multi_order.destroy - respond_to do |format| - format.html { redirect_to finance_order_index_path } + if @multi_order.ordergroup_invoices.any? + flash[:alert]= "Lösche erst die Rechnungen" + redirect_to finance_order_index_path + else + @multi_order.destroy + redirect_to finance_order_index_path + end end diff --git a/app/controllers/order_invoices_controller_base.rb b/app/controllers/order_invoices_controller_base.rb new file mode 100644 index 00000000..77442945 --- /dev/null +++ b/app/controllers/order_invoices_controller_base.rb @@ -0,0 +1,44 @@ +class OrderInvoicesControllerBase < ApplicationController + before_action :authenticate_finance + + def select_sepa_sequence_type + @invoice = invoice_class.find(params[:id]) + return unless params[:sepa_sequence_type] + + @group_order = set_related_group_order(@invoice) + @multi_group_order = set_related_group_order(@invoice) + + @invoice.sepa_sequence_type = params[:sepa_sequence_type] + save_and_respond(@invoice) + end + + def toggle_paid + @invoice = invoice_class.find(params[:id]) + @invoice.paid = !@invoice.paid + save_and_respond(@invoice) + end + + def toggle_sepa_downloaded + @invoice = invoice_class.find(params[:id]) + @invoice.sepa_downloaded = !@invoice.sepa_downloaded + save_and_respond(@invoice) + end + + protected + + def save_and_respond(record) + if record.save! + respond_to { |format| format.js } + else + respond_to { |format| format.json { render json: record.errors, status: :unprocessable_entity } } + end + end + + def invoice_class + raise NotImplementedError + end + + def set_related_group_order(invoice) + raise NotImplementedError + end +end diff --git a/app/controllers/ordergroup_invoices_controller.rb b/app/controllers/ordergroup_invoices_controller.rb index edffe596..2d0525ea 100644 --- a/app/controllers/ordergroup_invoices_controller.rb +++ b/app/controllers/ordergroup_invoices_controller.rb @@ -1,15 +1,12 @@ -class OrdergroupInvoicesController < ApplicationController +class OrdergroupInvoicesController < OrderInvoicesControllerBase include Concerns::SendGroupOrderInvoicePdf - before_action :authenticate_finance - # download create and new ordergroupinvoice - # has multiple group orders and one ordergroup - def new - @ordergroup_invoice = OrdergroupInvoice.new - @ordergroup_invoice.payment_method = FoodsoftConfig[:ordergroup_invoices][:payment_method] || I18n.t('activerecord.attributes.ordergroup_invoice.payment_method') - @ordergroup_invoice.sepa_sequence_type = params[:sepa_sequence_type] - end + # def new + # @ordergroup_invoice = OrdergroupInvoice.new + # @ordergroup_invoice.payment_method = FoodsoftConfig[:ordergroup_invoices][:payment_method] || I18n.t('activerecord.attributes.ordergroup_invoice.payment_method') + # @ordergroup_invoice.sepa_sequence_type = params[:sepa_sequence_type] + # end def show @ordergroup_invoice = OrdergroupInvoice.find(params[:id]) @@ -67,18 +64,15 @@ class OrdergroupInvoicesController < ApplicationController end end - - def select_sepa_sequence_type - @ordergroup_invoice = OrdergroupInvoice.find(params[:id]) - @multi_group_order = @ordergroup_invoice.multi_group_order - return unless params[:sepa_sequence_type] - + def send_all + @multi_order = MultiOrder.find(params[:multi_order_id]) + @ordergroup_invoices = @multi_order.multi_group_orders.map(&:ordergroup_invoice).compact + @ordergroup_invoices.each do |oi| + oi.send_invoice + end respond_to do |format| - @ordergroup_invoice.sepa_sequence_type = params[:sepa_sequence_type] - if @ordergroup_invoice.save! - format.js - else - format.json { render json: @ordergroup_invoice.errors, status: :unprocessable_entity } + format.html do + redirect_to finance_order_index_path, notice: I18n.t('ordergroup_invoices.send_all.success') end end end @@ -97,31 +91,6 @@ class OrdergroupInvoicesController < ApplicationController end end - def toggle_paid - @ordergroup_invoice = OrdergroupInvoice.find(params[:id]) - respond_to do |format| - @ordergroup_invoice.paid = !@ordergroup_invoice.paid - if @ordergroup_invoice.save! - format.js - else - format.json { render json: @ordergroup_invoice.errors, status: :unprocessable_entity } - end - end - end - - def toggle_sepa_downloaded - @ordergroup_invoice = OrdergroupInvoice.find(params[:id]) - @multi_order= @ordergroup_invoice.multi_group_order.multi_order - respond_to do |format| - @ordergroup_invoice.sepa_downloaded = !@ordergroup_invoice.sepa_downloaded - if @ordergroup_invoice.save! - format.js - else - format.json { render json: @ordergroup_invoice.errors, status: :unprocessable_entity } - end - end - end - def toggle_all_paid @multi_order= MultiOrder.find(params[:multi_order_id]) @ordergroup_invoices = @multi_order.multi_group_orders.map(&:ordergroup_invoice).compact @@ -175,5 +144,12 @@ class OrdergroupInvoicesController < ApplicationController end end end + protected + def invoice_class + OrdergroupInvoice + end + def set_related_group_order(invoice) + invoice.multi_group_order + end end diff --git a/app/helpers/invoice_helper.rb b/app/helpers/invoice_helper.rb index 36d511cc..41ff2d36 100644 --- a/app/helpers/invoice_helper.rb +++ b/app/helpers/invoice_helper.rb @@ -1,4 +1,12 @@ module InvoiceHelper + + SEPA_SEQUENCE_TYPES = { + FRST: "Erst-Lastschrift", + RCUR: "Folge-Lastschrift", + OOFF: "Einmalige Lastschrift", + FNAL: "Letztmalige Lastschrift" + }.freeze + def generate_invoice_number(instance, count) trailing_number = count.to_s.rjust(4, '0') if GroupOrderInvoice.find_by(invoice_number: instance.invoice_date.strftime("%Y%m%d") + trailing_number) || OrdergroupInvoice.find_by(invoice_number: instance.invoice_date.strftime("%Y%m%d") + trailing_number) diff --git a/app/jobs/notify_ordergroup_invoice_job.rb b/app/jobs/notify_ordergroup_invoice_job.rb new file mode 100644 index 00000000..b218745b --- /dev/null +++ b/app/jobs/notify_ordergroup_invoice_job.rb @@ -0,0 +1,12 @@ +class NotifyOrdergroupInvoiceJob < ApplicationJob + def perform(ordergroup_invoice) + ordergroup = ordergroup_invoice.multi_group_order.ordergroup + ordergroup.users.each do |user| + ordergroup_invoice.update!(email_sent_at: Time.current) + Mailer.deliver_now_with_user_locale user do + Mailer.ordergroup_invoice(ordergroup_invoice, user) + end + end + end + end + \ No newline at end of file diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb index 2b06ce8e..152c8b28 100644 --- a/app/mailers/mailer.rb +++ b/app/mailers/mailer.rb @@ -63,6 +63,18 @@ class Mailer < ActionMailer::Base subject: I18n.t('mailer.group_order_invoice.subject', group: @group.name, supplier: @supplier) end + def ordergroup_invoice(ordergroup_invoice, user) + @user = user + @ordergroup_invoice = ordergroup_invoice + @multi_group_order = ordergroup_invoice.multi_group_order + @multi_order = @multi_group_order.multi_order + @supplier = @multi_order.orders.map(&:supplier).map(&:name).uniq.join(', ') + @group = @multi_group_order.ordergroup + add_ordergroup_invoice_attachments(ordergroup_invoice) + mail to: user, + subject: I18n.t('mailer.ordergroup_invoice.subject', group: @group.name, supplier: @supplier) + end + # Sends order result for specific Ordergroup def order_result(user, group_order) @order = group_order.order @@ -186,6 +198,10 @@ class Mailer < ActionMailer::Base attachments[attachment_name] = GroupOrderInvoicePdf.new(group_order_invoice.load_data_for_invoice).to_pdf end + def add_ordergroup_invoice_attachments(ordergroup_invoice) + add_group_order_invoice_attachments(ordergroup_invoice) + end + # separate method to allow plugins to mess with the text def additonal_welcome_text(user); end diff --git a/app/models/concerns/invoice_common.rb b/app/models/concerns/invoice_common.rb new file mode 100644 index 00000000..7f9b8d87 --- /dev/null +++ b/app/models/concerns/invoice_common.rb @@ -0,0 +1,34 @@ +# app/models/concerns/invoice_common.rb +module InvoiceCommon + extend ActiveSupport::Concern + + included do + include InvoiceHelper + + validates_presence_of :invoice_number + validates_uniqueness_of :invoice_number + validate :tax_number_set + + after_initialize :init, unless: :persisted? + end + + def mark_sepa_downloaded + self.sepa_downloaded = true + save + end + + def unmark_sepa_downloaded + self.sepa_downloaded = false + save + end + + def name + I18n.t("activerecord.attributes.#{self.class.name.underscore}.name") + "_#{invoice_number}" + end + + def tax_number_set + return if FoodsoftConfig[:contact][:tax_number].present? + errors.add(:base, "Keine Steuernummer in FoodsoftConfig :contact gesetzt") + end + end + \ No newline at end of file diff --git a/app/models/group_order.rb b/app/models/group_order.rb index 2045ac12..6dbc8d09 100644 --- a/app/models/group_order.rb +++ b/app/models/group_order.rb @@ -11,7 +11,7 @@ class GroupOrder < ApplicationRecord has_one :financial_transaction has_one :group_order_invoice belongs_to :ordergroup_invoice, optional: true - belongs_to :multi_group_order, optional: true, dependent: :destroy + belongs_to :multi_group_order, optional: true belongs_to :multi_order, optional: true belongs_to :updated_by, optional: true, class_name: 'User', foreign_key: 'updated_by_user_id' diff --git a/app/models/group_order_invoice.rb b/app/models/group_order_invoice.rb index 8fcb65f9..310e082b 100644 --- a/app/models/group_order_invoice.rb +++ b/app/models/group_order_invoice.rb @@ -1,34 +1,9 @@ class GroupOrderInvoice < ApplicationRecord - include InvoiceHelper + include InvoiceCommon belongs_to :group_order validates_presence_of :group_order - validates_uniqueness_of :invoice_number - validate :tax_number_set - after_initialize :init, unless: :persisted? - - enum sequence_type: { - FRST: "Erst-Lastschrift", - RCUR: "Folge-Lastschrift", - OOFF: "Einmalige Lastschrift", - FNAL: "Letztmalige Lastschrift" - } - - def tax_number_set - return unless FoodsoftConfig[:contact][:tax_number].blank? - - errors.add(:group_order_invoice, "Keine Steuernummer in FoodsoftConfig :contact gesetzt") - end - - def mark_sepa_downloaded - self.sepa_downloaded = true - self.save - end - - def unmark_sepa_downloaded - self.sepa_downloaded = false - self.save - end + validates_uniqueness_of :group_order_id def init self.invoice_date = Time.now unless invoice_date @@ -36,10 +11,6 @@ class GroupOrderInvoice < ApplicationRecord self.payment_method = group_order&.financial_transaction&.financial_transaction_type&.name || 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 diff --git a/app/models/multi_group_order.rb b/app/models/multi_group_order.rb index 9cb89aad..7a66afb0 100644 --- a/app/models/multi_group_order.rb +++ b/app/models/multi_group_order.rb @@ -1,8 +1,10 @@ class MultiGroupOrder < ApplicationRecord - belongs_to :multi_order - has_many :group_orders + belongs_to :multi_order, optional: false + has_many :group_orders, dependent: :nullify has_one :ordergroup_invoice, dependent: :destroy + validates :multi_order, presence: true + def ordergroup group_orders.first&.ordergroup end diff --git a/app/models/multi_order.rb b/app/models/multi_order.rb index c6ab683f..78253bde 100644 --- a/app/models/multi_order.rb +++ b/app/models/multi_order.rb @@ -6,11 +6,10 @@ class MultiOrder < ApplicationRecord #TODO: diese association lösen has_many :group_orders, through: :multi_group_orders # has_many :ordergroups, through: :group_orders - has_many :ordergroup_invoices, through: :group_orders + has_many :ordergroup_invoices, through: :multi_group_orders - #make sure order has no multi_order_id - #make sure orders are not in a multi_order - before_create :check_orders + validate :check_orders + after_create :create_multi_group_orders def name orders.map(&:name).join(', ') @@ -40,15 +39,38 @@ class MultiOrder < ApplicationRecord end private + def check_orders - orders.each do |order| - errors.add(:base, "Order #{order.name} is already in a multi order") unless order.multi_order_id.nil? - #closed==abgerechnet - errors.add(:base, "Order #{order.name} not closed") unless order.closed? + unless orders.present? + errors.add(:base, "No orders selected") + return end - if errors.any? - errors.add(:base, "Cannot create multi order with unfinished orders") - raise ActiveRecord::Rollback + orders.each do |order| + if order.group_orders.blank? + errors.add(:base, "Order #{order.name} has no group orders") + end + unless order.closed? + errors.add(:base, "Order #{order.name} is not closed") + end + if order.group_orders.any? { |go| go.group_order_invoice.present? } + errors.add(:base, "Order #{order.name} has group order invoices") + end + end + end + + def create_multi_group_orders + return if orders.empty? + all_group_orders = orders.flat_map(&:group_orders) + grouped_by_ordergroup = all_group_orders.group_by(&:ordergroup_id) + + grouped_by_ordergroup.each do |ordergroup_id, group_orders| + multi_group_order = MultiGroupOrder.create!( + multi_order: self, group_orders: group_orders + ) + # Now, associate each group_order with the new multi_group_order + group_orders.each do |group_order| + group_order.update!(multi_group_order: multi_group_order) + end end end end diff --git a/app/models/ordergroup_invoice.rb b/app/models/ordergroup_invoice.rb index 02eda363..1b1e4414 100644 --- a/app/models/ordergroup_invoice.rb +++ b/app/models/ordergroup_invoice.rb @@ -1,25 +1,10 @@ class OrdergroupInvoice < ApplicationRecord - include InvoiceHelper + include InvoiceCommon - belongs_to :multi_group_order, optional: true + belongs_to :multi_group_order - # has_many :group_orders, through: :multi_group_order, dependent: :nullify - - validates_presence_of :invoice_number - validates_uniqueness_of :invoice_number - validate :tax_number_set after_initialize :init, unless: :persisted? - # accepts_nested_attributes_for :group_orders, :multi_group_order - - - enum sequence_type: { - FRST: "Erst-Lastschrift", - RCUR: "Folge-Lastschrift", - OOFF: "Einmalige Lastschrift", - FNAL: "Letztmalige Lastschrift" - } - def init self.invoice_date = Time.now unless invoice_date self.invoice_number = generate_invoice_number(self, 1) unless self.invoice_number @@ -32,24 +17,8 @@ class OrdergroupInvoice < ApplicationRecord group_orders.first.ordergroup end - def tax_number_set - return if FoodsoftConfig[:contact][:tax_number].present? - - errors.add(:cumulative_invoice, "Keine Steuernummer in FoodsoftConfig :contact gesetzt") - end - - def mark_sepa_downloaded - self.sepa_downloaded = true - self.save - end - - def unmark_sepa_downloaded - self.sepa_downloaded = false - self.save - end - - def name - I18n.t('activerecord.attributes.ordergroup_invoice.name') + "_#{invoice_number}" + def send_invoice + NotifyOrdergroupInvoiceJob.perform_now(self) end def load_data_for_invoice diff --git a/app/views/group_order_invoices/_select_all_sepa_sequence_type.html.haml b/app/views/group_order_invoices/_select_all_sepa_sequence_type.html.haml index 9caffeb1..a7b3deca 100644 --- a/app/views/group_order_invoices/_select_all_sepa_sequence_type.html.haml +++ b/app/views/group_order_invoices/_select_all_sepa_sequence_type.html.haml @@ -1,2 +1,2 @@ = link_to select_all_sepa_sequence_type_group_order_invoices_path(order_id: order.id), remote: true, method: :patch, class: "ajax-update-all-link-#{order.id}" , data: { turbolinks: false } do - = select_tag 'sepa_sequence_type', options_for_select(GroupOrderInvoice.sequence_types.keys.map { |st| [I18n.t("activerecord.attributes.group_order_invoice.sequence_type.#{st}"), st] }, selected: @sequence_type || order.group_orders.map(&:group_order_invoice)&.compact&.first.sepa_sequence_type), class: 'form-control', id: "all_sepa_sequence_type_#{order.id}" \ No newline at end of file + = select_tag 'sepa_sequence_type', options_for_select(InvoiceHelper::SEPA_SEQUENCE_TYPES.keys.map { |st| [I18n.t("activerecord.attributes.group_order_invoice.sequence_type.#{st}"), st] }, selected: @sequence_type || order.group_orders.map(&:group_order_invoice)&.compact&.first.sepa_sequence_type), class: 'form-control', id: "all_sepa_sequence_type_#{order.id}" \ No newline at end of file diff --git a/app/views/group_order_invoices/_select_sepa_sequence_type.html.haml b/app/views/group_order_invoices/_select_sepa_sequence_type.html.haml index 1c930df1..c4121b5e 100644 --- a/app/views/group_order_invoices/_select_sepa_sequence_type.html.haml +++ b/app/views/group_order_invoices/_select_sepa_sequence_type.html.haml @@ -1,2 +1 @@ -= link_to select_sepa_sequence_type_group_order_invoice_path(group_order_invoice), remote: true, method: :patch, class: "ajax-update-link-#{group_order.id}", data: { turbolinks: false } do - = select_tag 'sepa_sequence_type', options_for_select(GroupOrderInvoice.sequence_types.keys.map { |st| [I18n.t("activerecord.attributes.group_order_invoice.sequence_type.#{st}"), st] }, selected: group_order_invoice.sepa_sequence_type ), class: 'form-control', id: "sepa_sequence_type_#{group_order.id}" \ No newline at end of file += select_tag :sepa_sequence_type, options_for_select(InvoiceHelper::SEPA_SEQUENCE_TYPES.map { |k, v| [v, k] }, group_order_invoice.sepa_sequence_type), class: 'form-control ajax-update-sepa-select', id: "sepa_sequence_type_multi_#{group_order.id}", data: { url: select_sepa_sequence_type_group_order_invoice_path(group_order_invoice) } \ No newline at end of file diff --git a/app/views/group_order_invoices/select_sepa_sequence_type.js.erb b/app/views/group_order_invoices/select_sepa_sequence_type.js.erb index 96bf519f..d92ef8ed 100644 --- a/app/views/group_order_invoices/select_sepa_sequence_type.js.erb +++ b/app/views/group_order_invoices/select_sepa_sequence_type.js.erb @@ -1 +1 @@ -$("#select_sepa_sequence_type_<%= @group_order_invoice.id %>").html("<%= j(render partial: 'select_sepa_sequence_type', locals: {group_order_invoice: @group_order_invoice, group_order: @group_order}) %>"); \ No newline at end of file +$("#select_sepa_sequence_type_<%= @invoice.id %>").html("<%= j(render partial: 'select_sepa_sequence_type', locals: {group_order_invoice: @invoice, group_order: @group_order}) %>"); diff --git a/app/views/mailer/ordergroup_invoice.text.haml b/app/views/mailer/ordergroup_invoice.text.haml new file mode 100644 index 00000000..75948fbe --- /dev/null +++ b/app/views/mailer/ordergroup_invoice.text.haml @@ -0,0 +1 @@ += raw t '.text', group: @group.name, supplier: @supplier , foodcoop: FoodsoftConfig[:name] diff --git a/app/views/ordergroup_invoices/_links.html.haml b/app/views/ordergroup_invoices/_links.html.haml index 5153b362..0abd0570 100644 --- a/app/views/ordergroup_invoices/_links.html.haml +++ b/app/views/ordergroup_invoices/_links.html.haml @@ -25,26 +25,29 @@ %tbody - multi_order.multi_group_orders.each do |mgo| -if mgo.ordergroup_invoice.present? - - if mgo.ordergroup_invoice - %tr.order-row{id: "multi_group_order_#{mgo.id}"} - %td - = link_to mgo.ordergroup&.name, edit_admin_ordergroup_path(mgo.ordergroup) - %td - .div{id: "paid_multi_#{mgo.ordergroup_invoice.id}"} - = render :partial => "ordergroup_invoices/toggle_paid", locals: { ordergroup_invoice: mgo.ordergroup_invoice } - %td - .div{id: "sepa_downloaded_multi_#{mgo.ordergroup_invoice.id}"} - = render :partial => "ordergroup_invoices/toggle_sepa_downloaded", locals: { ordergroup_invoice: mgo.ordergroup_invoice } - %td - .div{id: "select_sepa_sequence_type_multi_#{mgo.ordergroup_invoice.id}"} - =render :partial => 'ordergroup_invoices/select_sepa_sequence_type', locals:{ ordergroup_invoice: mgo.ordergroup_invoice, multi_group_order: mgo } - %td - = check_box_tag "group_order_ids_for_multi_order_#{multi_order.id}", mgo.id, false, class: "group-order-checkbox", id: "group_order_#{mgo.id}_included_in_sepa", data: { multi_group_order_id: mgo.id } - %td - %b= mgo.ordergroup_invoice.invoice_number - %td - = link_to I18n.t('activerecord.attributes.group_order_invoice.links.delete'), mgo.ordergroup_invoice, method: :delete, class: 'btn btn-block btn-danger', remote: true, data: { confirm: I18n.t('ui.confirm_delete', name: "Bestellgruppenrechnung für #{mgo.ordergroup.name}" ) } - = link_to I18n.t('activerecord.attributes.group_order_invoice.links.download'), ordergroup_invoice_path(mgo.ordergroup_invoice, :format => 'pdf'), class: 'btn btn-block' + %tr.order-row{id: "multi_group_order_#{mgo.id}"} + %td + = link_to mgo.ordergroup&.name, edit_admin_ordergroup_path(mgo.ordergroup) + %td + .div{id: "paid_multi_#{mgo.ordergroup_invoice.id}"} + = render :partial => "ordergroup_invoices/toggle_paid", locals: { ordergroup_invoice: mgo.ordergroup_invoice } + %td + .div{id: "sepa_downloaded_multi_#{mgo.ordergroup_invoice.id}"} + = render :partial => "ordergroup_invoices/toggle_sepa_downloaded", locals: { ordergroup_invoice: mgo.ordergroup_invoice } + %td + .div{id: "select_sepa_sequence_type_multi_#{mgo.ordergroup_invoice.id}"} + =render :partial => 'ordergroup_invoices/select_sepa_sequence_type', locals:{ ordergroup_invoice: mgo.ordergroup_invoice, multi_group_order: mgo } + %td + = check_box_tag "group_order_ids_for_multi_order_#{multi_order.id}", mgo.id, false, class: "group-order-checkbox", id: "group_order_#{mgo.id}_included_in_sepa", data: { multi_group_order_id: mgo.id } + %td + %b= mgo.ordergroup_invoice.invoice_number + - if mgo.ordergroup_invoice.email_sent_at.present? + %br/ + = I18n.t('activerecord.attributes.ordergroup_invoice.email_sent') + = mgo.ordergroup_invoice.email_sent_at.strftime("%d.%m.%Y %H:%M") + %td + = link_to I18n.t('activerecord.attributes.group_order_invoice.links.delete'), mgo.ordergroup_invoice, method: :delete, class: 'btn btn-block btn-danger', remote: true, data: { confirm: I18n.t('ui.confirm_delete', name: "Bestellgruppenrechnung für #{mgo.ordergroup.name}" ) } + = link_to I18n.t('activerecord.attributes.group_order_invoice.links.download'), ordergroup_invoice_path(mgo.ordergroup_invoice, :format => 'pdf'), class: 'btn btn-block' - else %tr %td @@ -70,5 +73,8 @@ %td .div{id: "select_all_sepa_#{multi_order.id}"} = render :partial => 'ordergroup_invoices/collective_direct_debit', locals: { multi_order: multi_order } + %td %td = link_to I18n.t('activerecord.attributes.group_order_invoice.links.download_all_zip'), download_all_ordergroup_invoices_path(multi_order), class: 'btn btn-block' + -# sends all ordergroup invoices to the ordergoups mail address via notifyjob + = link_to I18n.t('activerecord.attributes.group_order_invoice.links.send_all_by_email'), send_all_ordergroup_invoices_path(multi_order), class: 'btn btn-block', method: :post, data: { confirm: I18n.t('activerecord.attributes.group_order_invoice.links.confirm_send_all', ordergroups: "#{multi_order.multi_group_orders.map(&:ordergroup).map(&:name).join(', ')}" ) } diff --git a/app/views/ordergroup_invoices/_select_all_sepa_sequence_type.html.haml b/app/views/ordergroup_invoices/_select_all_sepa_sequence_type.html.haml index a4dc9580..01c26923 100644 --- a/app/views/ordergroup_invoices/_select_all_sepa_sequence_type.html.haml +++ b/app/views/ordergroup_invoices/_select_all_sepa_sequence_type.html.haml @@ -1,2 +1,2 @@ = link_to select_all_sepa_sequence_type_ordergroup_invoices_path(multi_order_id: multi_order.id), remote: true, method: :patch, class: "ajax-update-all-link-#{multi_order.id}" , data: { turbolinks: false } do - = select_tag 'sepa_sequence_type', options_for_select(OrdergroupInvoice.sequence_types.keys.map { |st| [I18n.t("activerecord.attributes.group_order_invoice.sequence_type.#{st}"), st] }, selected: @sequence_type || multi_order.multi_group_orders.map(&:ordergroup_invoice)&.compact&.first&.sepa_sequence_type), class: 'form-control', id: "all_sepa_sequence_type_multi_#{multi_order.id}" \ No newline at end of file + = select_tag 'sepa_sequence_type', options_for_select(InvoiceHelper::SEPA_SEQUENCE_TYPES.keys.map { |st| [I18n.t("activerecord.attributes.group_order_invoice.sequence_type.#{st}"), st] }, selected: @sequence_type || multi_order.multi_group_orders.map(&:ordergroup_invoice)&.compact&.first&.sepa_sequence_type), class: 'form-control', id: "all_sepa_sequence_type_multi_#{multi_order.id}" \ No newline at end of file diff --git a/app/views/ordergroup_invoices/_select_sepa_sequence_type.html.haml b/app/views/ordergroup_invoices/_select_sepa_sequence_type.html.haml index 61799883..8b92abfd 100644 --- a/app/views/ordergroup_invoices/_select_sepa_sequence_type.html.haml +++ b/app/views/ordergroup_invoices/_select_sepa_sequence_type.html.haml @@ -1,2 +1 @@ -= link_to select_sepa_sequence_type_ordergroup_invoice_path(ordergroup_invoice), remote: true, method: :patch, class: "ajax-update-link-#{multi_group_order.id}", data: { turbolinks: false } do - = select_tag 'sepa_sequence_type', options_for_select(OrdergroupInvoice.sequence_types.keys.map { |st| [I18n.t("activerecord.attributes.group_order_invoice.sequence_type.#{st}"), st] }, selected: ordergroup_invoice.sepa_sequence_type ), class: 'form-control', id: "sepa_sequence_type_multi_#{multi_group_order.id}" \ No newline at end of file += select_tag :sepa_sequence_type, options_for_select(InvoiceHelper::SEPA_SEQUENCE_TYPES.map { |k, v| [v, k] }, ordergroup_invoice.sepa_sequence_type), class: 'form-control ajax-update-sepa-select', id: "sepa_sequence_type_multi_#{multi_group_order.id}", data: { url: select_sepa_sequence_type_ordergroup_invoice_path(ordergroup_invoice) } \ No newline at end of file diff --git a/app/views/ordergroup_invoices/_toggle_sepa_downloaded.html.haml b/app/views/ordergroup_invoices/_toggle_sepa_downloaded.html.haml index f4bfcbd1..064d1838 100644 --- a/app/views/ordergroup_invoices/_toggle_sepa_downloaded.html.haml +++ b/app/views/ordergroup_invoices/_toggle_sepa_downloaded.html.haml @@ -1,2 +1,2 @@ = link_to toggle_sepa_downloaded_ordergroup_invoice_path(ordergroup_invoice), remote: true, method: :patch do - = check_box_tag 'sepa_downloaded', '1', ordergroup_invoice.sepa_downloaded , class: 'form-check-input', id: "sepa_downloaded_multii_#{ordergroup_invoice.id}" \ No newline at end of file + = check_box_tag 'sepa_downloaded', '1', ordergroup_invoice.sepa_downloaded , class: 'form-check-input', id: "sepa_downloaded_multi_#{ordergroup_invoice.id}" \ No newline at end of file diff --git a/app/views/ordergroup_invoices/select_sepa_sequence_type.js.erb b/app/views/ordergroup_invoices/select_sepa_sequence_type.js.erb index fa249c87..b8fe4813 100644 --- a/app/views/ordergroup_invoices/select_sepa_sequence_type.js.erb +++ b/app/views/ordergroup_invoices/select_sepa_sequence_type.js.erb @@ -1 +1 @@ -$("#select_sepa_sequence_type_multi_<%= @ordergroup_invoice.id %>").html("<%= j(render partial: 'select_sepa_sequence_type', locals: {ordergroup_invoice: @ordergroup_invoice, multi_group_order: @multi_group_order}) %>"); \ No newline at end of file +$("#select_sepa_sequence_type_multi_<%= @invoice.id %>").html("<%= j(render partial: 'select_sepa_sequence_type', locals: {ordergroup_invoice: @invoice, multi_group_order: @group_order}) %>"); diff --git a/app/views/ordergroup_invoices/toggle_paid.js.erb b/app/views/ordergroup_invoices/toggle_paid.js.erb index 4097f4d6..0832d8bf 100644 --- a/app/views/ordergroup_invoices/toggle_paid.js.erb +++ b/app/views/ordergroup_invoices/toggle_paid.js.erb @@ -1 +1 @@ -$("#paid_multi_<%= @ordergroup_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_paid', locals: {ordergroup_invoice: @ordergroup_invoice}) %>"); +$("#paid_multi_<%= @invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_paid', locals: {ordergroup_invoice: @invoice}) %>"); diff --git a/app/views/ordergroup_invoices/toggle_sepa_downloaded.js.erb b/app/views/ordergroup_invoices/toggle_sepa_downloaded.js.erb index 9a57c052..ffe34b60 100644 --- a/app/views/ordergroup_invoices/toggle_sepa_downloaded.js.erb +++ b/app/views/ordergroup_invoices/toggle_sepa_downloaded.js.erb @@ -1 +1 @@ -$("#sepa_downloaded_multi_<%= @ordergroup_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_sepa_downloaded', locals: {ordergroup_invoice: @ordergroup_invoice}) %>"); +$("#sepa_downloaded_multi_<%= @invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_sepa_downloaded', locals: {ordergroup_invoice: @invoice}) %>"); diff --git a/config/environments/development.rb.SAMPLE b/config/environments/development.rb.SAMPLE index ed5bb855..40443e01 100644 --- a/config/environments/development.rb.SAMPLE +++ b/config/environments/development.rb.SAMPLE @@ -36,6 +36,9 @@ Rails.application.configure do # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + config.action_mailer.perform_deliveries = true + config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } + config.action_mailer.delivery_method = :letter_opener_web config.action_mailer.perform_caching = false @@ -57,12 +60,12 @@ Rails.application.configure do config.assets.initialize_on_precompile = true # Configure hostname for action mailer (can be overridden in foodcoop config) - config.action_mailer.default_url_options = { host: 'localhost', port: 3000, protocol: 'http' } + config.action_mailer.default_url_options = { host: 'localhost', port: 3000} # Mailcatcher config, start mailcatcher from console with 'mailcatcher' # Mailcatcher can be installed by gem install mailcatcher - config.action_mailer.delivery_method = :smtp - config.action_mailer.smtp_settings = { address: ENV.fetch("MAILCATCHER_ADDRESS", "localhost"), port: ENV.fetch("MAILCATCHER_PORT", 1025) } + #config.action_mailer.delivery_method = :smtp + #config.action_mailer.smtp_settings = { address: ENV.fetch("MAILCATCHER_ADDRESS", "localhost"), port: ENV.fetch("MAILCATCHER_PORT", 1025) } # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/config/locales/de.yml b/config/locales/de.yml index 7b50fca7..113df2f7 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -101,6 +101,7 @@ de: actions_for_all: Aktion für alle ausführen delete: Rechnung löschen create_cumulative_invoice: Kumulative Rechnung erstellen + confirm_send_all: Möchtest Du wirklich diese Bestellgruppenrechnungen an %{ordergroups} versenden? combine: Rechnungen zusammenfassen download: Rechnung herunterladen download_all_zip: Alle Rechnungen herunterladen (zip) @@ -111,6 +112,8 @@ de: ordergroup: Bestellgruppe paid: Bezahlt not_paid: Nicht Bezahlt + send_all_by_email: Alle Rechnungen versenden + send_all_success: Rechnungen erfolgreich versendet sepa_downloaded: SEPA exportiert sepa_not_downloaded: SEPA nicht exportiert sepa_not_ready: @@ -195,6 +198,9 @@ de: last_user_activity: Zuletzt aktiv name: Name user_tokens: Mitglieder + ordergroup_invoice: + name: Bestellgruppenrechnung + email_sent: versendet am sepa_account_holder: bic: BIC holder: SEPA Kontoinhaber*in @@ -1393,6 +1399,15 @@ de: Im Anhang befindet sich daher eure Rechnung. Viele Grüße von %{foodcoop} + ordergroup_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: subject: Einladung in die Foodcoop text: | diff --git a/config/locales/en.yml b/config/locales/en.yml index ae1f3cc5..742dc116 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -91,7 +91,7 @@ en: total_price: Sum unit_price: Price/Unit group_order_invoice: - name: Group order invoice + name: group-order-invoice sequence_type: FRST: "First Direct Debit" RCUR: "Recurring Direct Debit" @@ -101,6 +101,7 @@ en: actions_for_all: Actions for all group orders create_cumulative_invoice: create cumulative invoice combine: combine invoices + confirm_send_all: Are you sure you want to send these ordergroup invoices to %{ordergroups}? delete: delete invoice download: download invoice download_all_zip: download all invoices as zip @@ -111,6 +112,8 @@ en: ordergroup: Ordergroup paid: paid not_paid: unpaid + send_all_by_email: send all invoices + send_all_success: Invoices were sent to all ordergroups sepa_downloaded: SEPA exported sepa_not_downloaded: SEPA not exported sepa_not_ready: Configurations for SEPA are missing in Admin->Settings->Finances @@ -194,6 +197,9 @@ en: last_user_activity: Last activity name: Name user_tokens: Members + ordergroup_invoice: + name: ordergroup-invoice + email_sent: Email sent at stock_article: available: Available price: Price @@ -1358,7 +1364,16 @@ en: header: "%{user} wrote at %{date}:" subject: Feedback for Foodsoft group_order_invoice: - subject: Order group invoice for %{group} at %{supplier} + subject: Group order 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} + ordergroup_invoice: + subject: Group order invoice for %{group} at %{supplier} text: | Dear order group %{group}, diff --git a/config/routes.rb b/config/routes.rb index 9d839c04..d1c9a160 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,11 @@ Rails.application.routes.draw do mount Rswag::Ui::Engine => '/api-docs' mount Rswag::Api::Engine => '/api-docs' + + if Rails.env.development? + mount LetterOpenerWeb::Engine, at: "/letter_opener" + end + get 'order_comments/new' get 'comments/new' @@ -152,7 +157,8 @@ Rails.application.routes.draw do post 'finance/group_order_invoice', to: 'group_order_invoices#create_multiple' get 'orders/:order_id/group_order_invoices/download_all', to: 'group_order_invoices#download_all', as: 'download_all_group_order_invoices' - get 'multi_orders/:multi_order_id/group_order_invoices/download_all', to: 'ordergroup_invoices#download_all', as: 'download_all_ordergroup_invoices' + get 'multi_orders/:multi_order_id/ordergroup_invoices/download_all', to: 'ordergroup_invoices#download_all', as: 'download_all_ordergroup_invoices' + post 'multi_orders/:multi_order_id/ordergroup_invoices/send_all', to: 'ordergroup_invoices#send_all', as: 'send_all_ordergroup_invoices' resources :ordergroup_invoices do member do diff --git a/db/migrate/20250521134157_add_email_sent_to_ordergroup_invoice.rb b/db/migrate/20250521134157_add_email_sent_to_ordergroup_invoice.rb new file mode 100644 index 00000000..5b369e85 --- /dev/null +++ b/db/migrate/20250521134157_add_email_sent_to_ordergroup_invoice.rb @@ -0,0 +1,5 @@ +class AddEmailSentToOrdergroupInvoice < ActiveRecord::Migration[7.0] + def change + add_column :ordergroup_invoices, :email_sent_at, :datetime + end +end diff --git a/db/routes.rb b/db/routes.rb index e0c41b48..efde401b 100644 --- a/db/routes.rb +++ b/db/routes.rb @@ -162,6 +162,7 @@ Rails.application.routes.draw do end collection do get :download_within_date + get :send_all patch :select_all_sepa_sequence_type patch :toggle_all_sepa_downloaded patch :toggle_all_paid diff --git a/db/schema.rb b/db/schema.rb index 51a2ac73..e6d9fd05 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2025_05_16_104953) do +ActiveRecord::Schema[7.0].define(version: 2025_05_21_134157) do create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.string "name", null: false t.text "body", size: :long @@ -407,6 +407,7 @@ ActiveRecord::Schema[7.0].define(version: 2025_05_16_104953) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "multi_group_order_id" + t.datetime "email_sent_at" t.index ["multi_group_order_id"], name: "index_ordergroup_invoices_on_multi_group_order_id" end diff --git a/db/seeds.rb b/db/seeds.rb index 37a996ff..d34f28db 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,4 +1,4 @@ # default seed is minimal -require Rails.root.join('db/seeds/minimal.seeds.rb') +require Rails.root.join('db/seeds/demo.seeds.rb') # to generate new seeds, use the seed_dumper gem diff --git a/spec/factories/group_order.rb b/spec/factories/group_order.rb index d62172ea..561edc11 100644 --- a/spec/factories/group_order.rb +++ b/spec/factories/group_order.rb @@ -5,5 +5,6 @@ FactoryBot.define do factory :group_order do ordergroup { create(:user, groups: [FactoryBot.create(:ordergroup)]).ordergroup } updated_by { create(:user) } + order end end diff --git a/spec/factories/multi_group_order.rb b/spec/factories/multi_group_order.rb new file mode 100644 index 00000000..0ca0f023 --- /dev/null +++ b/spec/factories/multi_group_order.rb @@ -0,0 +1,8 @@ +require 'factory_bot' + +FactoryBot.define do + factory :multi_group_order do + group_orders { [create(:group_order)] } + association :multi_order + end +end diff --git a/spec/factories/multi_order.rb b/spec/factories/multi_order.rb new file mode 100644 index 00000000..f7814c68 --- /dev/null +++ b/spec/factories/multi_order.rb @@ -0,0 +1,21 @@ +require 'factory_bot' + +FactoryBot.define do + factory :multi_order do + transient do + orders { [create(:order, state: 'closed')] } + end + + after(:build) do |multi_order, evaluator| + # Assign orders before validation so custom validations can see them + multi_order.orders = evaluator.orders + end + + after(:create) do |multi_order, _evaluator| + # Persist the relationship by updating the orders (if needed) + multi_order.orders.each do |order| + order.update!(multi_order: multi_order) + end + end + end +end diff --git a/spec/factories/ordergroup_invoice.rb b/spec/factories/ordergroup_invoice.rb new file mode 100644 index 00000000..c8b85ae7 --- /dev/null +++ b/spec/factories/ordergroup_invoice.rb @@ -0,0 +1,8 @@ +require 'factory_bot' + +FactoryBot.define do + factory :ordergroup_invoice do + multi_group_order { create :multi_group_order } + after(:build) { |ogi| ogi.init } + end +end diff --git a/spec/integration/multi_order_spec.rb b/spec/integration/multi_order_spec.rb new file mode 100644 index 00000000..cc0fa783 --- /dev/null +++ b/spec/integration/multi_order_spec.rb @@ -0,0 +1,82 @@ +require_relative '../spec_helper' + +feature MultiOrder, type: :feature, js: true do + let(:admin) { create :user, groups: [create(:workgroup, role_finance: true), create(:ordergroup, name: "AdminOrders")] } + let(:user) { create :user, groups: [create(:ordergroup)] } + + let(:article) { create :article, unit_quantity: 4 } + let(:article1) { create :article, unit_quantity: 2 } + let(:order) { create :order, supplier: article.supplier, article_ids: [article.id], ends: Time.now } + let(:order1) { create :order, supplier: article1.supplier, article_ids: [article1.id], ends: Time.now } + + let(:go) { create :group_order, order: order, ordergroup: user.ordergroup} + let(:go1) { create :group_order, order: order1, ordergroup: admin.ordergroup} + + let(:oa) { order.order_articles.find_by_article_id(article.id) } + let(:oa1) { order1.order_articles.find_by_article_id(article1.id) } + let(:ftt) { create :financial_transaction_type } + + let(:goa) { create :group_order_article, group_order: go, order_article: oa } + let(:goa1) { create :group_order_article, group_order: go1, order_article: oa1 } + + include ActiveJob::TestHelper + + before do + login admin + goa.update_quantities 2, 0 + goa1.update_quantities 2, 0 + oa.update_results! + oa1.update_results! + FoodsoftConfig[:contact][:tax_number] = 12_345_678 + order.update!(state: 'closed') + order1.update!(state: 'closed') + end + + after { clear_enqueued_jobs } + + # first ensure theres multiple orders and the check box to combine them to a multiorder + + it 'shows the elements to combine orders' do + visit finance_order_index_path + expect(page).to have_css("#order_#{order.id}_combine") + expect(page).to have_css("#order_#{order1.id}_combine") + expect(page).to have_css(".merge-orders-btn") + end + + it 'cannot combine orders when orders are not closed' do + order.update!(state: 'finished') + visit finance_order_index_path + expect(page).to have_css("#order_#{order.id}_combine") + expect(page).to have_css("#order_#{order1.id}_combine") + expect(page).to have_css(".merge-orders-btn") + # check the checkboxes and click the button + check("order_#{order.id}_combine") + check("order_#{order1.id}_combine") + click_link_or_button 'Zusammenführen' + expect(page).to have_content('Die Bestellung ist bereits Teil einer Multi-Bestellung oder ist noch nicht abgeschlossen.') + end + + it 'cannot combine orders when group_orders already have an invoice' do + group_order_invoice = create(:group_order_invoice, group_order: go) + visit finance_order_index_path + expect(page).to have_css("#order_#{order.id}_combine") + expect(page).to have_css("#order_#{order1.id}_combine") + expect(page).to have_css(".merge-orders-btn") + # check the checkboxes and click the button + check("order_#{order.id}_combine") + check("order_#{order1.id}_combine") + click_link_or_button 'Zusammenführen' + expect(page).to have_content('Zusammenführen nicht möglich. Es gibt bereits Rechnungen für einige der Bestellgruppen.') + end + + it 'combines orders into multi_order' do + visit finance_order_index_path + expect(MultiOrder.count).to eq(0) + check("order_#{order.id}_combine") + check("order_#{order1.id}_combine") + click_link_or_button 'Zusammenführen' + sleep 2 + expect(page).to have_content("Multi Bestellung für #{MultiOrder.last.name} erstellt") + expect(MultiOrder.count).to eq(1) + end +end diff --git a/spec/integration/ordergroup_invoice_spec.rb b/spec/integration/ordergroup_invoice_spec.rb new file mode 100644 index 00000000..3f07521c --- /dev/null +++ b/spec/integration/ordergroup_invoice_spec.rb @@ -0,0 +1,131 @@ +require_relative '../spec_helper' + +feature OrdergroupInvoice, type: :feature, js: true do + let(:admin) { create :user, groups: [create(:workgroup, role_finance: true), create(:ordergroup, name: "AdminOrders")] } + let(:user) { create :user, groups: [create(:ordergroup)] } + + let(:article) { create :article, unit_quantity: 4 } + let(:article1) { create :article, unit_quantity: 2 } + let(:order) { create :order, supplier: article.supplier, article_ids: [article.id], ends: Time.now } + let(:order1) { create :order, supplier: article1.supplier, article_ids: [article1.id], ends: Time.now } + + let(:go) { create :group_order, order: order, ordergroup: user.ordergroup} + let(:go1) { create :group_order, order: order1, ordergroup: admin.ordergroup} + + let(:oa) { order.order_articles.find_by_article_id(article.id) } + let(:oa1) { order1.order_articles.find_by_article_id(article1.id) } + let(:ftt) { create :financial_transaction_type } + + let(:goa) { create :group_order_article, group_order: go, order_article: oa } + let(:goa1) { create :group_order_article, group_order: go1, order_article: oa1 } + + include ActiveJob::TestHelper + + before do + login admin + goa.update_quantities 2, 0 + goa1.update_quantities 2, 0 + oa.update_results! + oa1.update_results! + FoodsoftConfig[:contact][:tax_number] = 12_345_678 + order.update!(state: 'closed') + order1.update!(state: 'closed') + end + + after { clear_enqueued_jobs } + + # first ensure theres multiple orders and the check box to combine them to a multiorder + + it 'shows the elements to combine orders' do + visit finance_order_index_path + expect(page).to have_css("#order_#{order.id}_combine") + expect(page).to have_css("#order_#{order1.id}_combine") + expect(page).to have_css(".merge-orders-btn") + end + + it 'cannot combine orders when orders are not closed' do + order.update!(state: 'finished') + visit finance_order_index_path + expect(page).to have_css("#order_#{order.id}_combine") + expect(page).to have_css("#order_#{order1.id}_combine") + expect(page).to have_css(".merge-orders-btn") + # check the checkboxes and click the button + check("order_#{order.id}_combine") + check("order_#{order1.id}_combine") + click_link_or_button 'Zusammenführen' + expect(page).to have_content('Die Bestellung ist bereits Teil einer Multi-Bestellung oder ist noch nicht abgeschlossen.') + end + + it 'cannot combine orders when group_orders already have an invoice' do + group_order_invoice = create(:group_order_invoice, group_order: go) + visit finance_order_index_path + expect(page).to have_css("#order_#{order.id}_combine") + expect(page).to have_css("#order_#{order1.id}_combine") + expect(page).to have_css(".merge-orders-btn") + # check the checkboxes and click the button + check("order_#{order.id}_combine") + check("order_#{order1.id}_combine") + click_link_or_button 'Zusammenführen' + expect(page).to have_content('Zusammenführen nicht möglich. Es gibt bereits Rechnungen für einige der Bestellgruppen.') + end + + it 'can generate multiple ordergroup invoice' do + multi_order = create(:multi_order, orders: [order, order1]) + visit finance_order_index_path + expect(page).to have_selector(:link_or_button, "Multi Bestellung auflösen") + click_link_or_button 'Toggle details' + 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') + sleep 1 + expect(OrdergroupInvoice.all.count).to eq(2) + end + + it 'can generate single ordergroup invoice' do + multi_order = create(:multi_order, orders: [order, order1]) + visit finance_order_index_path + expect(page).to have_selector(:link_or_button, "Multi Bestellung auflösen") + click_link_or_button 'Toggle details' + expect(page).to have_selector(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate')) + first(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate')).click + sleep 1 + expect(OrdergroupInvoice.all.count).to eq(1) + end + it 'cannot toggel details when config wrong' do + FoodsoftConfig[:contact][:tax_number] = nil + multi_order = create(:multi_order, orders: [order, order1]) + visit finance_order_index_path + expect(page).not_to have_selector(:link_or_button, 'Toggle details') + end + + it 'cannot destroy multi_order if invoice attached' do + multi_order = create(:multi_order, orders: [order, order1]) + visit finance_order_index_path + expect(page).to have_selector(:link_or_button, "Multi Bestellung auflösen") + click_link_or_button 'Toggle details' + expect(page).to have_selector(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate')) + sleep 1 + first(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate')).click + sleep 1 + expect(OrdergroupInvoice.all.count).to eq(1) + click_link_or_button 'Multi Bestellung auflösen' + expect(page).to have_content("Lösche erst die Rechnungen") + end + + it 'can toggle elements for ordergroup invoice' do + multi_order = create(:multi_order, orders: [order, order1]) + visit finance_order_index_path + expect(page).to have_selector(:link_or_button, "Multi Bestellung auflösen") + click_link_or_button 'Toggle details' + 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') + sleep 1 + expect(OrdergroupInvoice.all.count).to eq(2) + check("sepa_downloaded_all_multi_#{multi_order.id}") + sleep 1 + expect(page).to have_checked_field("sepa_downloaded_multi_#{OrdergroupInvoice.last.id}") + expect(page).to have_checked_field("sepa_downloaded_multi_#{OrdergroupInvoice.first.id}") + check("paid_all_multi_#{multi_order.id}") + expect(page).to have_checked_field("paid_multi_#{OrdergroupInvoice.first.id}") + expect(page).to have_checked_field("paid_multi_#{OrdergroupInvoice.last.id}") + end +end diff --git a/spec/models/group_order_invoice_spec.rb b/spec/models/group_order_invoice_spec.rb index 24bfcf7e..e2349838 100644 --- a/spec/models/group_order_invoice_spec.rb +++ b/spec/models/group_order_invoice_spec.rb @@ -43,7 +43,7 @@ describe GroupOrderInvoice do 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) + expect { goi2 }.to raise_error(ActiveRecord::RecordInvalid) end it 'creates two different group order invoice with different invoice_numbers' do diff --git a/spec/models/multi_group_order_spec.rb b/spec/models/multi_group_order_spec.rb new file mode 100644 index 00000000..58250750 --- /dev/null +++ b/spec/models/multi_group_order_spec.rb @@ -0,0 +1,31 @@ +require_relative '../spec_helper' +describe MultiGroupOrder do + let(:admin) { create :user, groups: [create(:workgroup, role_finance: true), create(:ordergroup, name: "AdminOrders")] } + let(:user) { create :user, groups: [create(:ordergroup)] } + + let(:article1) { create :article, unit_quantity: 1 } + let(:article2) { create :article, unit_quantity: 3 } + + context 'does not generate Multi Group Order without valid multi_order' do + it 'when orders are not closed' do + order1 = create :order, multi_order: nil + order2 = create :order, multi_order: nil + group_order1 = create :group_order, ordergroup: user.ordergroup, order: order1 + group_order2 = create :group_order, ordergroup: user.ordergroup, order: order2 + expect { create(:multi_order, orders:[order1, order2]) }.to raise_error(ActiveRecord::RecordInvalid) + expect(MultiGroupOrder.count).to eq(0) + end + end + context 'Multi Group Order is created by MultiOrder' do + it 'when orders are closed' do + order1 = create :order, multi_order: nil + order2 = create :order, multi_order: nil + group_order1 = create :group_order, ordergroup: user.ordergroup, order: order1 + group_order2 = create :group_order, ordergroup: user.ordergroup, order: order2 + order1.update!(state: 'closed') + order2.update!(state: 'closed') + multi_order = create(:multi_order, orders: [order1, order2]) + expect(MultiGroupOrder.count).to eq(1) + end + end +end \ No newline at end of file diff --git a/spec/models/multi_order_spec.rb b/spec/models/multi_order_spec.rb new file mode 100644 index 00000000..c96c8128 --- /dev/null +++ b/spec/models/multi_order_spec.rb @@ -0,0 +1,68 @@ +require_relative '../spec_helper' +describe MultiOrder do + let(:admin) { create :user, groups: [create(:workgroup, role_finance: true), create(:ordergroup, name: "AdminOrders")] } + let(:user) { create :user, groups: [create(:ordergroup)] } + + let(:article1) { create :article, unit_quantity: 1 } + let(:article2) { create :article, unit_quantity: 3 } + + context 'does not generate Multi Order' do + let(:order1) { create :order } + let(:order2) { create :order} + let!(:group_order1) { create :group_order, ordergroup: user.ordergroup, order: order1 } + let!(:group_order2) { create :group_order, ordergroup: user.ordergroup, order: order2 } + let!(:group_order3) { create :group_order, ordergroup: admin.ordergroup, order: order1 } + + before do + order1.update!(state: 'open') + order2.update!(state: 'open') + FoodsoftConfig[:contact][:tax_number] = 123_457_8 + end + + it 'when orders are not closed' do + expect { create(:multi_order, orders: [order1]) }.to raise_error(ActiveRecord::RecordInvalid) + end + + it 'when orders are not finished' do + expect { create(:multi_order, orders: [order2]) }.to raise_error(ActiveRecord::RecordInvalid) + end + + + it 'when group order invoices are present' do + order1.update!(state: 'closed') + order2.update!(state: 'closed') + group_order1.update!(group_order_invoice: create(:group_order_invoice)) + group_order2.update!(group_order_invoice: create(:group_order_invoice)) + expect { create(:multi_order, orders: [order1, order2]) }.to raise_error(ActiveRecord::RecordInvalid) + end + end + + context 'generates Multi Order' do + + let(:order3) { create :order, multi_order: nil } + let(:order4) { create :order, multi_order: nil } + let(:order5) { create :order, multi_order: nil } + + let!(:group_order1) { create :group_order, ordergroup: user.ordergroup, order: order3 } + let!(:group_order2) { create :group_order, ordergroup: user.ordergroup, order: order4 } + let!(:group_order3) { create :group_order, ordergroup: admin.ordergroup, order: order5 } + let!(:group_order4) { create :group_order, ordergroup: admin.ordergroup, order: order4 } + + before do + order3.update!(state: 'closed') + order4.update!(state: 'closed') + order5.update!(state: 'closed') + end + + it 'when orders are closed' do + #as it is right now orders can be in multiple multi orders + # expect(create(:multi_order, orders: [order3])).to be_valid + expect(create(:multi_order, orders: [order3])).to be_valid + end + + it 'when group order invoices are not present' do + multi_order2 = create(:multi_order, orders: [order3, order4]) + expect(multi_order2).to be_valid + end + end +end \ No newline at end of file diff --git a/spec/models/ordergroup_invoice_spec.rb b/spec/models/ordergroup_invoice_spec.rb new file mode 100644 index 00000000..2eba7946 --- /dev/null +++ b/spec/models/ordergroup_invoice_spec.rb @@ -0,0 +1,60 @@ +require_relative '../spec_helper' + +describe OrdergroupInvoice do + # TODO: DO REAL TESTING! + let(:user) { create :user, groups: [create(:ordergroup)] } + let(:supplier) { create :supplier } + let(:article) { create :article, supplier: supplier } + let(:order) { create :order, state: 'closed' } + let(:group_order) { create :group_order, order: order, ordergroup: user.ordergroup } + let(:multi_group_order) { create :multi_group_order, multi_order: create(:multi_order, orders: [order]) } + + describe 'erroneous group order invoice' do + before do + order.update!(state: 'closed') + end + let(:ogi) do + build(:ordergroup_invoice, multi_group_order: multi_group_order).tap(&:init) + end + it 'does not create group order invoice if tax_number not set' do + expect { ogi }.to raise_error(ActiveRecord::RecordInvalid, /.*/) + end + end + + describe 'valid group order invoice' do + before do + FoodsoftConfig[:contact][:tax_number] = 123_457_8 + order.update!(state: 'closed') + end + let(:multi_group_order1) { create :multi_group_order, multi_order: create(:multi_order, orders: [order]) } + let(:ogi) do + build(:ordergroup_invoice, multi_group_order: multi_group_order1).tap(&:init) + end + invoice_number1 = Time.now.strftime("%Y%m%d") + '0001' + invoice_number2 = Time.now.strftime("%Y%m%d") + '0002' + + it 'creates group order invoice if tax_number is set' do + expect(ogi).to be_valid + end + + it 'sets invoice_number according to date' do + number = Time.now.strftime("%Y%m%d") + '0001' + expect(odi1.invoice_number).to eq(number.to_i) + end + + it 'fails to create if group_order_id is used multiple times for creation' do + expect(odi1.group_order.id).to eq(group_order.id) + expect { odi2 }.to raise_error(ActiveRecord::RecordInvalid) + end + + it 'creates two different group order invoice with different invoice_numbers' do + expect(odi1.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 + odi1 + expect { goi4 }.to raise_error(ActiveRecord::RecordInvalid) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ec5e7a35..59a47b9a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,6 +19,8 @@ end # in spec/support/ and its subdirectories. Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } +ActiveRecord::Migration.maintain_test_schema! + RSpec.configure do |config| # We use capybara with webkit, and need database_cleaner config.before do