diff --git a/app/assets/javascripts/order.js b/app/assets/javascripts/order.js index 929feb09..baf0c5f4 100644 --- a/app/assets/javascripts/order.js +++ b/app/assets/javascripts/order.js @@ -1,10 +1,11 @@ function doTheDownload(selectedGroupOrderIds, orderId, url, supplier, mode = "all") { + console.log(selectedGroupOrderIds); if (mode == "all") { var data = { order_id: orderId } } else { - var data = { group_order_ids: selectedGroupOrderIds } + var data = { multi_group_order_ids: selectedGroupOrderIds } } if (mode == "all" || selectedGroupOrderIds.length > 0) { //suppress generic error warning @@ -73,7 +74,6 @@ function doTheDownload(selectedGroupOrderIds, orderId, url, supplier, mode = "al } } - $(document).off('change', '[class^="ajax-update-all-link-"] select').on('change', '[class^="ajax-update-all-link-"] select', function () { var selectedValue = $(this).val(); var url = $(this).closest('a').attr('href'); @@ -108,24 +108,66 @@ $(document).off('change', '[class^="ajax-update-link-"] select').on('change', '[ $(document).on('ready turbolinks:load', function () { $('.expand-trigger').click(function () { - var orderId = $(this).closest('tr').data('order_id'); - var expandedRow = $('#expanded-row-' + orderId); + var tableRow = $(this).closest('tr') + var orderId = tableRow.data('order_id'); + var multiOrderId = tableRow.data('multi_order_id'); + + if(multiOrderId != undefined){ + var expandedRow = $('#expanded-multi-row-' + multiOrderId); + console.log(multiOrderId); + } + else + { + var expandedRow = $('#expanded-row-' + orderId); + } // Toggle visibility of the expanded row - expandedRow.slideToggle(); + + expandedRow.toggleClass('hidden'); + + tableRow.toggleClass('border'); + expandedRow.toggleClass('bordered'); return false; // Prevent the default behavior of the link + }); +}); + +$(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(); + }).get(); + + if (selectedOrderIds.length === 0) { + alert("Bitte wählen Sie mindestens eine Bestellung aus."); + return; + } + + $.ajax({ + url: url, + method: 'POST', + data: { order_ids_for_multi_order: selectedOrderIds }, + success: function (response) { + window.location.reload(); + }, + error: function (xhr) { + window.location.reload(); + } }); }); $(document).off('click', '[id^="collective-direct-debit-link-selected-"]').on('click', '[id^="collective-direct-debit-link-selected-"]', function (e) { e.preventDefault(); + var input = "group_order_ids_for_order_" var orderId = $(this).data("order-id"); var supplier = $(this).data("supplier"); + if (orderId == undefined) { + orderId = $(this).data("multi-order-id"); + input = "group_order_ids_for_multi_order_" + } // Extract selected group_order_ids - var selectedGroupOrderIds = $('input[name^="group_order_ids_for_order_' + orderId + '"]:checked').map(function () { + var selectedGroupOrderIds = $('input[name^="'+ input + orderId + '"]:checked').map(function () { return $(this).val(); }).get(); - console.log(selectedGroupOrderIds); var url = $(this).closest('a').attr('href'); doTheDownload(selectedGroupOrderIds, orderId, url, supplier, "selected"); diff --git a/app/assets/stylesheets/group_order_invoices.css b/app/assets/stylesheets/group_order_invoices.css index 036b9966..f328e955 100644 --- a/app/assets/stylesheets/group_order_invoices.css +++ b/app/assets/stylesheets/group_order_invoices.css @@ -26,6 +26,29 @@ } +.expanded-row{ + td { + padding-top: 0 !important; + } +} +.hidden{ + display: none; +} +.border td{ + background-color: rgb(231, 231, 194) !important; +} + +.bordered { + .order-modal{ + background-color: lightgoldenrodyellow !important; + padding-bottom: 2em ; + } + .multi-order-modal{ + background-color: lightgoldenrodyellow !important; + padding-bottom: 2em ; + } +} + .table.group-order-invoices-table tr{ background-color: rgb(255, 255, 233); } diff --git a/app/controllers/concerns/send_group_order_invoice_pdf.rb b/app/controllers/concerns/send_group_order_invoice_pdf.rb index 76e71c99..0734ea2d 100644 --- a/app/controllers/concerns/send_group_order_invoice_pdf.rb +++ b/app/controllers/concerns/send_group_order_invoice_pdf.rb @@ -7,6 +7,14 @@ 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/balancing_controller.rb b/app/controllers/finance/balancing_controller.rb index 6c63b055..72eb62c0 100644 --- a/app/controllers/finance/balancing_controller.rb +++ b/app/controllers/finance/balancing_controller.rb @@ -1,7 +1,14 @@ class Finance::BalancingController < Finance::BaseController def index - @multi_orders = MultiOrder.includes(:orders, :group_orders).page(params[:page]).per(@per_page).order('ends DESC') - @orders = Order.finished.non_multi_order.page(params[:page]).per(@per_page).order('ends DESC') + page = params[:page].to_i + page = 1 if page < 1 + per_page = @per_page.to_i + offset = (page - 1) * per_page + + multi_orders = MultiOrder.includes(:orders, :group_orders).page(params[:page]).per(@per_page).order('ends DESC') + orders = Order.finished.non_multi_order.page(params[:page]).per(@per_page).order('ends DESC') + combined = (multi_orders + orders).sort_by { |r| r.ends }.reverse + @results = Kaminari.paginate_array(combined).page(params[:page]).per(@per_page) end def new diff --git a/app/controllers/group_order_invoices_controller.rb b/app/controllers/group_order_invoices_controller.rb index eaa3b62e..c72854b5 100644 --- a/app/controllers/group_order_invoices_controller.rb +++ b/app/controllers/group_order_invoices_controller.rb @@ -49,7 +49,7 @@ class GroupOrderInvoicesController < ApplicationController gos.each do |go| goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id) goi.invoice_date = invoice_date - goi.invoice_number = goi.generate_invoice_number(GroupOrderInvoice, 1) + goi.invoice_number = goi.generate_invoice_number(goi, 1) goi.save! end respond_to do |format| diff --git a/app/controllers/multi_orders_controller.rb b/app/controllers/multi_orders_controller.rb index 6039e001..f762a72c 100644 --- a/app/controllers/multi_orders_controller.rb +++ b/app/controllers/multi_orders_controller.rb @@ -1,21 +1,42 @@ class MultiOrdersController < ApplicationController + include SepaHelper + before_action :set_multi_order, only: [:generate_ordergroup_invoices] + def create orders = Order.where(id: multi_order_params[:order_ids_for_multi_order]) unclosed_orders = orders.select { |order| order.closed? == false } multi_orders = orders.select { |order| order.multi_order_id.present? } + invoiced_orders = orders.select {|order| order.group_orders.map(&:group_order_invoice).compact.present? } if multi_order_params[:multi_order_ids_for_multi_multi_order].present? #TODO: do the i18n and add a test - flash[:alert] = "Du kannst keine Multi-Bestellungen in eine Multi-Multi-Bestellung umwandeln." - redirect_to finance_order_index_path + msg = "Du kannst keine Multi-Bestellungen in eine Multi-Multi-Bestellung umwandeln." + flash[:alert] = msg + respond_to do |format| + format.js + format.html { redirect_to finance_order_index_path } + end return end if multi_orders.any? || unclosed_orders.any? #TODO: do the i18n and add a test - flash[:alert] = "Die Bestellung ist noch nicht abgeschlossen oder ist bereits Teil einer Multi-Bestellung." - redirect_to finance_order_index_path + msg = "Die Bestellung ist bereits Teil einer Multi-Bestellung oder ist noch nicht abgeschlossen." + flash[:alert] = msg + respond_to do |format| + format.js + format.html { redirect_to finance_order_index_path } + end + return + end + if invoiced_orders.any? + msg = "Zusammenführen nicht möglich. Es gibt bereits Rechnungen für einige der Bestellgruppen." + flash[:alert] = msg + respond_to do |format| + format.js + format.html { redirect_to finance_order_index_path } + end return end @@ -24,15 +45,6 @@ class MultiOrdersController < ApplicationController @multi_order.orders = orders @multi_order.ends = orders.map(&:ends).max @multi_order.save! - puts " - " + "______________" + " - " + "______________" + " - " + "______________" + " - " + "#{@multi_order.errors.inspect}" + " - " + "______________"+ " - " + "______________"+ " - " + "______________" - #create multi group orders all_group_orders = orders.flat_map(&:group_orders) @@ -42,7 +54,6 @@ class MultiOrdersController < ApplicationController 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) @@ -51,9 +62,19 @@ class MultiOrdersController < ApplicationController redirect_to finance_order_index_path rescue ActiveRecord::RecordInvalid => e flash[:alert] = t('errors.general_msg', msg: e.message) - redirect_to finance_order_index_path + respond_to do |format| + format.js + format.html { redirect_to finance_order_index_path } + end end + end + def destroy + @multi_order = MultiOrder.find(params[:id]) + @multi_order.destroy + respond_to do |format| + format.html { redirect_to finance_order_index_path } + end end def generate_ordergroup_invoices @@ -65,6 +86,77 @@ class MultiOrdersController < ApplicationController redirect_to finance_order_index_path, alert: t('errors.general_msg', msg: e.message) end + def collective_direct_debit + if foodsoft_sepa_ready? + case params[:mode] + when 'all' + multi_group_orders = MultiGroupOrder.where(multi_order_id: params[:id]) + when 'selected' + #TODO: !!! params and javascript + multi_group_orders = MultiGroupOrder.where(id: params[:multi_group_order_ids]) + else + redirect_to finance_order_index_path, alert: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: '') + end + + @multi_order = MultiOrder.find(params[:id]) + ordergroups = multi_group_orders.flat_map(&:group_orders).map(&:ordergroup) + + export_allowed = !ordergroups.map(&:sepa_possible?).include?(false) && !multi_group_orders.map { |go| go.ordergroup_invoice.present? }.include?(false) + group_order_ids = multi_group_orders.map { |mgo| mgo.id if mgo.ordergroup_invoice.present? } + + sepa_possible_ordergroup_names = ordergroups.map { |ordergroup| ordergroup.name if ordergroup.sepa_possible? }.compact_blank + sepa_not_possible_ordergroup_names = ordergroups.map(&:name) - sepa_possible_ordergroup_names + + if export_allowed && multi_group_orders.present? + respond_to do |format| + format.html do + collective_debit = OrderCollectiveDirectDebitXml.new(multi_group_orders) + send_data collective_debit.xml_string, filename: @order.name + '_Sammellastschrift' + '.xml', type: 'text/xml' + multi_group_orders.map(&:ordergroup_invoice).each(&:mark_sepa_downloaded) + rescue SEPA::Error => e + multi_group_orders.map(&:ordergroup_invoice).each(&:unmark_sepa_downloaded) + redirect_to finance_order_index_path, alert: e.message + rescue StandardError => e + multi_group_orders.map(&:ordergroup_invoice).each(&:unmark_sepa_downloaded) + redirect_to finance_order_index_path, alert: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_not_possible_ordergroup_names.join(', '), error: e.message) + end + format.xml do + multi_group_orders.map(&:ordergroup_invoice).each(&:mark_sepa_downloaded) + collective_debit = OrderCollectiveDirectDebitXml.new(multi_group_orders) + send_data collective_debit.xml_string, filename: @multi_order.orders.first.name + '_Sammellastschrift' + '.xml', type: 'text/xml' + rescue SEPA::Error => e + multi_group_orders.map(&:ordergroup_invoice).each(&:unmark_sepa_downloaded) + render json: { error: e.message } + rescue StandardError => e + multi_group_orders.map(&:ordergroup_invoice).each(&:unmark_sepa_downloaded) + render json: { error: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_not_possible_ordergroup_names.join(', '), error: e.message) } + end + format.js + end + else + respond_to do |format| + format.html do + redirect_to finance_order_index_path, alert: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_not_possible_ordergroup_names.join(', '), error: '') + end + format.xml do + render json: { error: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_not_possible_ordergroup_names.join(', '), error: '') } + end + format.js + end + end + else + respond_to do |format| + format.html do + redirect_to finance_order_index_path, alert: "Wichtige SEPA Konfiguration in Administration >> Einstellungen >> Finanzen nicht gesetzt!" + end + format.xml do + redirect_to finance_order_index_path, alert: "Wichtige SEPA Konfiguration in Administration >> Einstellungen >> Finanzen nicht gesetzt!" + end + format.js + end + end + end + private def set_multi_order @@ -72,6 +164,6 @@ class MultiOrdersController < ApplicationController end def multi_order_params - params.permit(:id, order_ids_for_multi_order: [], multi_order_ids_for_multi_multi_order: []) + params.permit(:id, :foodcoop, order_ids_for_multi_order: [], multi_order_ids_for_multi_multi_order: []) end end diff --git a/app/controllers/ordergroup_invoices_controller.rb b/app/controllers/ordergroup_invoices_controller.rb index f20b9ce0..edffe596 100644 --- a/app/controllers/ordergroup_invoices_controller.rb +++ b/app/controllers/ordergroup_invoices_controller.rb @@ -1,3 +1,4 @@ + class OrdergroupInvoicesController < ApplicationController include Concerns::SendGroupOrderInvoicePdf before_action :authenticate_finance @@ -27,9 +28,11 @@ class OrdergroupInvoicesController < ApplicationController end def create - gos = GroupOrder.where(id: params[:group_order_ids]) + mgo = MultiGroupOrder.find(params[:multi_group_order_id]) + @multi_order = mgo.multi_order begin - OrdergroupInvoice.create!(group_order_ids: gos) + + OrdergroupInvoice.create(multi_group_order_id: mgo.id) respond_to do |format| format.js end @@ -38,19 +41,139 @@ class OrdergroupInvoicesController < ApplicationController end end + def destroy + oi = OrdergroupInvoice.find(params[:id]) + @multi_order = oi.multi_group_order.multi_order + oi.destroy + respond_to do |format| + format.js + format.json { head :no_content } + end + end + def create_multiple - invoice_date = params[:ordergroup_invoce][:invoice_date] - multi_order_id = params[:ordergroup_invoce][:multi_order_id] + invoice_date = params[:ordergroup_invoice][:invoice_date] + multi_order_id = params[:ordergroup_invoice][:multi_order_id] @multi_order = MultiOrder.find(multi_order_id) multi_group_orders = MultiGroupOrder.where("multi_order_id = ?", multi_order_id) - mutli_group_orders.each do |multi_group_order| + multi_group_orders.each do |multi_group_order| ordergroup_invoice = OrdergroupInvoice.find_or_create_by!(multi_group_order: multi_group_order) ordergroup_invoice.invoice_date = invoice_date - ordergroup_invoice.invoice_number = ordergroup_invoice.generate_invoice_number(OrdergroupInvoice, 1) + ordergroup_invoice.invoice_number = ordergroup_invoice.generate_invoice_number(ordergroup_invoice, 1) ordergroup_invoice.save! end respond_to do |format| format.js 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] + + 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 } + end + end + end + + def select_all_sepa_sequence_type + @multi_order= MultiOrder.find(params[:multi_order_id]) + @ordergroup_invoices = @multi_order.multi_group_orders.map(&:ordergroup_invoice).compact + return unless params[:sepa_sequence_type] + @sepa_sequence_type = params[:sepa_sequence_type] + @ordergroup_invoices.each do |oi| + oi.sepa_sequence_type = params[:sepa_sequence_type] + oi.save! + end + respond_to do |format| + format.js + 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 + @ordergroup_invoices.each do |oi| + oi.paid = !ActiveRecord::Type::Boolean.new.deserialize(params[:paid]) + oi.save! + end + respond_to do |format| + format.js + end + end + + def toggle_all_sepa_downloaded + @multi_order= MultiOrder.find(params[:multi_order_id]) + @ordergroup_invoices = @multi_order.multi_group_orders.map(&:ordergroup_invoice).compact + @ordergroup_invoices.each do |goi| + goi.sepa_downloaded = !ActiveRecord::Type::Boolean.new.deserialize(params[:sepa_downloaded]) + goi.save! + end + respond_to do |format| + format.js + end + end + + def download_all + multi_order = MultiOrder.find(params[:multi_order_id]) + + invoices = multi_order.multi_group_orders.map(&:ordergroup_invoice) + pdf = {} + file_paths = [] + temp_file = Tempfile.new("all_invoices_for_multi_order_#{multi_order.id}.zip") + Zip::File.open(temp_file.path, Zip::File::CREATE) do |zipfile| + invoices.each do |invoice| + pdf = create_invoice_pdf(invoice) + file_path = File.join("tmp", pdf.filename) + File.open(file_path, 'w:ASCII-8BIT') do |file| + file.write(pdf.to_pdf) + end + file_paths << file_path + zipfile.add(pdf.filename, file_path) unless zipfile.find_entry(pdf.filename) + end + end + + zip_data = File.read(temp_file.path) + file_paths.each do |file_path| + File.delete(file_path) + end + respond_to do |format| + format.html do + send_data(zip_data, type: 'application/zip', filename: "#{l multi_order.ends, format: :file}-#{multi_order.orders.first.supplier.name}-#{multi_order.id}.zip", disposition: 'attachment') + end + end + end + end diff --git a/app/documents/group_order_invoice_pdf.rb b/app/documents/group_order_invoice_pdf.rb index b2b149a5..52ea0b38 100644 --- a/app/documents/group_order_invoice_pdf.rb +++ b/app/documents/group_order_invoice_pdf.rb @@ -11,7 +11,7 @@ class GroupOrderInvoicePdf < RenderPdf def body contact = FoodsoftConfig[:contact].symbolize_keys ordergroup = @options[:ordergroup] - + # TODO: group_by supplier, sort alphabetically # From paragraph bounding_box [margin_box.right - 200, margin_box.top - 20], width: 200 do text I18n.t('documents.group_order_invoice_pdf.invoicer') @@ -69,8 +69,6 @@ class GroupOrderInvoicePdf < RenderPdf #------------- Table Data ----------------------- - @group_order = GroupOrder.find(@options[:group_order].id) - if FoodsoftConfig[:group_order_invoices][:vat_exempt] body_for_vat_exempt else @@ -82,7 +80,8 @@ class GroupOrderInvoicePdf < RenderPdf 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) + # no sinle group_order_id, capice? get all the articles. + group_order_articles = GroupOrderArticle.where(group_order_ids: @options.group_order_ids) separate_deposits = FoodsoftConfig[:group_order_invoices]&.[](:separate_deposits) group_order_articles.each do |goa| # if no unit is received, nothing is to be charged @@ -173,7 +172,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: @options[:group_order_ids]).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 diff --git a/app/models/group_order.rb b/app/models/group_order.rb index 6dbc8d09..2045ac12 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 + belongs_to :multi_group_order, optional: true, dependent: :destroy 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/multi_group_order.rb b/app/models/multi_group_order.rb index 9cba7466..9cb89aad 100644 --- a/app/models/multi_group_order.rb +++ b/app/models/multi_group_order.rb @@ -1,21 +1,21 @@ class MultiGroupOrder < ApplicationRecord belongs_to :multi_order - has_many :group_orders, dependent: :nullify - - after_destroy :delete_ordergroup_invoices - - def ordergroup_invoice - #TODO: delete if deleted - group_orders.joins(:ordergroup_invoice).first&.ordergroup_invoice - end + has_many :group_orders + has_one :ordergroup_invoice, dependent: :destroy def ordergroup - ordergroup_invoice&.ordergroup + group_orders.first&.ordergroup end - private + def price + group_orders.map(&:price).sum + end - def delete_ordergroup_invoices - ordergroup_invoice&.destroy + def group_order_invoice + ordergroup_invoice + end + + def order + multi_order end end diff --git a/app/models/multi_order.rb b/app/models/multi_order.rb index f17eaa67..c6ab683f 100644 --- a/app/models/multi_order.rb +++ b/app/models/multi_order.rb @@ -2,9 +2,10 @@ class MultiOrder < ApplicationRecord has_many :orders, dependent: :nullify has_many :order_articles, through: :orders has_many :multi_group_orders, dependent: :destroy + + #TODO: diese association lösen has_many :group_orders, through: :multi_group_orders - has_many :ordergroups, through: :group_orders - #TODO: wenn groupOrderInvoice existiert, dann fehler schmeißen.. + # has_many :ordergroups, through: :group_orders has_many :ordergroup_invoices, through: :group_orders #make sure order has no multi_order_id @@ -33,6 +34,11 @@ class MultiOrder < ApplicationRecord orders.map(&:foodcoop_result).compact_blank.sum end + def supplier + #todo: who is this? + orders.map(&:supplier).compact.first + end + private def check_orders orders.each do |order| diff --git a/app/models/ordergroup_invoice.rb b/app/models/ordergroup_invoice.rb index 15b99728..02eda363 100644 --- a/app/models/ordergroup_invoice.rb +++ b/app/models/ordergroup_invoice.rb @@ -3,16 +3,27 @@ class OrdergroupInvoice < ApplicationRecord belongs_to :multi_group_order, optional: true - has_many :group_orders, dependent: :nullify + # 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 - self.payment_method = group_orders.first&.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 + self.payment_method = multi_group_order.group_orders.first&.financial_transaction&.financial_transaction_type&.name || FoodsoftConfig[:ordergroup_invoices]&.[](:payment_method) || I18n.t('activerecord.attributes.ordergroup_invoice.payment_method') unless self.payment_method end def ordergroup @@ -27,13 +38,26 @@ class OrdergroupInvoice < ApplicationRecord 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.group_order_invoice.name') + "_#{invoice_number}" + I18n.t('activerecord.attributes.ordergroup_invoice.name') + "_#{invoice_number}" end def load_data_for_invoice invoice_data = {} + group_orders = multi_group_order.group_orders order = group_orders.map(&:order).first + #how to define one order? + invoice_data[:pickup] = order.pickup invoice_data[:supplier] = order.supplier&.name invoice_data[:ordergroup] = group_orders.first.ordergroup diff --git a/app/views/finance/balancing/_multi_order_row.html.haml b/app/views/finance/balancing/_multi_order_row.html.haml index 5228eb1a..5fa1474b 100644 --- a/app/views/finance/balancing/_multi_order_row.html.haml +++ b/app/views/finance/balancing/_multi_order_row.html.haml @@ -1,6 +1,5 @@ %tr{:class => cycle("even","odd", :name => "multi_order"), 'data-multi_order_id' => multi_order.id} %td - = check_box_tag "multi_order_ids_for_multi_multi_order[]", multi_order.id, false, class: "multi_order-checkbox", id: "multi_order_#{multi_order.id}_combine", data: { multi_order_id: multi_order.id } %td = "*Multi*" -multi_order.orders.each do |order| @@ -9,14 +8,19 @@ %td=h format_time(multi_order.ends) unless multi_order.ends.nil? %td= multi_order.closed? ? t('finance.balancing.orders.cleared', amount: number_to_currency(multi_order.foodcoop_result)) : t('finance.balancing.orders.ended') %td= show_user(multi_order.updated_by) - %td{id: "group-multi_order-invoices-for-multi-order-#{multi_order.id}", class: 'expand-trigger'} + %td{id: "group-multi_order-invoices-for-multi-order-#{multi_order.id}"} - if multi_order.closed? - -if FoodsoftConfig[:contact][:tax_number] && multi_order.group_orders.present? - = link_to I18n.t('activerecord.attributes.group_order_invoice.open_details_modal'), "#", remote: true, class: 'btn btn-small' + -if FoodsoftConfig[:contact][:tax_number] && multi_order.multi_group_orders.present? + = link_to I18n.t('activerecord.attributes.group_order_invoice.open_details_modal'), "#", remote: true, class: 'btn btn-small expand-trigger' -else = I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set') - else = t('orders.index.not_closed') -%tr{:class => 'expanded-row', :id => "expanded-row-#{multi_order.id}", :style => 'display: none;'} - %td{:colspan => '6'} + %td + = link_to multi_order, method: :delete, data: { confirm: I18n.t('ui.confirm_delete', name: "Multi Bestellung #{multi_order.name}" ) } do + Multi Bestellung auflösen + %i.icon-remove + +%tr{:class => 'expanded-row hidden', :id => "expanded-multi-row-#{multi_order.id}"} + %td{:colspan => '7'} = render partial: 'ordergroup_invoices/modal', locals: { multi_order: multi_order } \ No newline at end of file diff --git a/app/views/finance/balancing/_order_row.html.haml b/app/views/finance/balancing/_order_row.html.haml index 39900bcf..be95c1fd 100644 --- a/app/views/finance/balancing/_order_row.html.haml +++ b/app/views/finance/balancing/_order_row.html.haml @@ -5,10 +5,10 @@ %td=h format_time(order.ends) unless order.ends.nil? %td= order.closed? ? t('finance.balancing.orders.cleared', amount: number_to_currency(order.foodcoop_result)) : t('finance.balancing.orders.ended') %td= show_user(order.updated_by) - %td{id: "group-order-invoices-for-order-#{order.id}", class: 'expand-trigger'} + %td{id: "group-order-invoices-for-order-#{order.id}"} - if order.closed? -if FoodsoftConfig[:contact][:tax_number] && order.ordergroups.present? - = link_to I18n.t('activerecord.attributes.group_order_invoice.open_details_modal'), "#", remote: true, class: 'btn btn-small' + = link_to I18n.t('activerecord.attributes.group_order_invoice.open_details_modal'), "#", remote: true, class: 'btn btn-small expand-trigger' -else = I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set') - else @@ -21,6 +21,6 @@ - else = link_to t('orders.index.action_receive'), '#', class: 'btn btn-small disabled' = link_to t('finance.balancing.orders.clear'), new_finance_order_path(order_id: order.id), class: 'btn btn-small btn-primary' -%tr{:class => 'expanded-row', :id => "expanded-row-#{order.id}", :style => 'display: none;'} +%tr{:class => 'expanded-row hidden', :id => "expanded-row-#{order.id}"} %td{:colspan => '7'} = render partial: 'group_order_invoices/modal', locals: { order: order } \ No newline at end of file diff --git a/app/views/finance/balancing/_orders.html.haml b/app/views/finance/balancing/_orders.html.haml index 267a9847..52b08f62 100644 --- a/app/views/finance/balancing/_orders.html.haml +++ b/app/views/finance/balancing/_orders.html.haml @@ -1,27 +1,24 @@ -- unless @orders.empty? && @multi_orders.empty? +- unless @results.empty? - if Order.finished.count > 20 = items_per_page - = pagination_links_remote @orders - -# following posed a real problem, multiple submit buttons are within the same form, so only one of them will be submitted - -# convert to javascript - = form_with url: multi_orders_path, method: :post, local: true do |form| - %table.table.table-striped - %thead - %tr - %th=I18n.t('activerecord.attributes.group_order_invoice.links.combine') - %th= t('.name') - %th= t('.end') - %th= t('.state') - %th= heading_helper Order, :updated_by - %th= heading_helper GroupOrderInvoice, :name - %th - %th - %tbody - - @multi_orders.each do |multi_order| - = render partial: 'multi_order_row', locals: { multi_order: multi_order } - - @orders.each do |order| - = render partial: 'order_row', locals: { order: order } - = form.submit "Zusammenführen", class: 'btn btn-primary' + %table.table.table-striped + %thead + %tr + %th=I18n.t('activerecord.attributes.group_order_invoice.links.combine') + %th= t('.name') + %th= t('.end') + %th= t('.state') + %th= heading_helper Order, :updated_by + %th= heading_helper GroupOrderInvoice, :name + %th + %th + %tbody + - @results.each do |result| + - if result.class == Order + = render partial: 'order_row', locals: { order: result } + - else + = render partial: 'multi_order_row', locals: { multi_order: result } + = button_tag 'Zusammenführen', class: 'btn btn-primary merge-orders-btn', type: 'button', data: { url: multi_orders_path } - else %i= t('.no_closed_orders') diff --git a/app/views/finance/balancing/index.js.haml b/app/views/finance/balancing/index.js.haml index d344fb68..7204a2b7 100644 --- a/app/views/finance/balancing/index.js.haml +++ b/app/views/finance/balancing/index.js.haml @@ -5,7 +5,11 @@ $('#ordersTable').html('#{j(render('orders'))}'); var orderId = $(this).closest('tr').data('order_id'); var expandedRow = $('#expanded-row-' + orderId); // Toggle visibility of the expanded row - expandedRow.slideToggle(); + expandedRow.toggleClass('hidden'); + + tableRow.toggleClass('border'); + expandedRow.toggleClass('bordered'); + return false; // Prevent the default behavior of the link }); diff --git a/app/views/group_order_invoices/_links.html.haml b/app/views/group_order_invoices/_links.html.haml index 70ca78e4..bb091bb0 100644 --- a/app/views/group_order_invoices/_links.html.haml +++ b/app/views/group_order_invoices/_links.html.haml @@ -5,7 +5,7 @@ - if go.group_order_invoice.present? - show_generate_with_date = false - if show_generate_with_date - = form_for :group_order_invoice, url: url_for('group_order_invoice#create_multiple'), remote: true do |f| + = form_for :group_order_invoice, url: finance_group_order_invoice_path(foodcoop: FoodsoftConfig[:default_scope], protocol: :https), remote: true do |f| = f.label :invoice_date, I18n.t('activerecord.attributes.group_order_invoice.links.invoice_date') = f.date_field :invoice_date, {value: Date.today, max: Date.today, required: true} = f.hidden_field :order_id, value: order.id @@ -37,6 +37,8 @@ =render :partial => 'group_order_invoices/select_sepa_sequence_type', locals:{ group_order_invoice: go.group_order_invoice, group_order: go } %td = check_box_tag "group_order_ids_for_order_#{order.id}", go.id, false, class: "group-order-checkbox", id: "group_order_#{go.id}_included_in_sepa", data: { order_id: go.id } + %td + %b= go.group_order_invoice.invoice_number %td = link_to I18n.t('activerecord.attributes.group_order_invoice.links.download'), group_order_invoice_path(go.group_order_invoice, :format => 'pdf'), class: 'btn btn-block' = link_to I18n.t('activerecord.attributes.group_order_invoice.links.delete'), go.group_order_invoice, method: :delete, class: 'btn btn-block btn-danger', remote: true, data: { confirm: I18n.t('ui.confirm_delete', name: "Bestellgruppenrechnung für #{go.ordergroup.name}" ) } diff --git a/app/views/group_order_invoices/create_multiple.js.erb b/app/views/group_order_invoices/create_multiple.js.erb index 9b29c466..5e29c1cb 100644 --- a/app/views/group_order_invoices/create_multiple.js.erb +++ b/app/views/group_order_invoices/create_multiple.js.erb @@ -1,2 +1 @@ - $("#order_<%= @order.id %>_modal").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>"); diff --git a/app/views/ordergroup_invoices/_collective_direct_debit.html.haml b/app/views/ordergroup_invoices/_collective_direct_debit.html.haml new file mode 100644 index 00000000..4241d7b1 --- /dev/null +++ b/app/views/ordergroup_invoices/_collective_direct_debit.html.haml @@ -0,0 +1,8 @@ +- if foodsoft_sepa_ready? + = link_to 'Sammellastschrift für alle (.xml)', collective_direct_debit_multi_order_path(id: multi_order.id, mode: 'all'), class: 'btn btn-block', data: { turbolinks: false, multi_order_id: multi_order.id, supplier: FoodsoftConfig[:name] }, id: "collective-direct-debit-link-all-#{multi_order.id}" + = link_to 'Sammellastschrift für ausgewählt (.xml)', collective_direct_debit_multi_order_path(id: multi_order.id, mode: 'selected'), class: 'btn btn-block', data: { turbolinks: false, multi_order_id: multi_order.id, supplier: FoodsoftConfig[:name] }, id: "collective-direct-debit-link-selected-#{multi_order.id}" +- else + %i + = t('activerecord.attributes.group_order_invoice.links.sepa_not_ready') + +-# solve hotfix: multi_order.orders.first.supplier&.name \ No newline at end of file diff --git a/app/views/ordergroup_invoices/_links.html.haml b/app/views/ordergroup_invoices/_links.html.haml index de58374e..5153b362 100644 --- a/app/views/ordergroup_invoices/_links.html.haml +++ b/app/views/ordergroup_invoices/_links.html.haml @@ -5,7 +5,7 @@ - if mgo.ordergroup_invoice.present? - show_generate_with_date = false - if show_generate_with_date - = form_for :ordergroup_invoice, url: url_for('ordergroup_invoice#create_multiple'), remote: true do |f| + = form_for :ordergroup_invoice, url: finance_ordergroup_invoice_path(foodcoop: FoodsoftConfig[:default_scope], protocol: :https), remote: true do |f| = f.label :invoice_date, I18n.t('activerecord.attributes.group_order_invoice.links.invoice_date') = f.date_field :invoice_date, {value: Date.today, max: Date.today, required: true} = f.hidden_field :multi_order_id, value: multi_order.id @@ -20,52 +20,55 @@ %th=I18n.t('activerecord.attributes.group_order_invoice.links.sepa_downloaded') %th=I18n.t('activerecord.attributes.group_order_invoice.links.sepa_sequence_type') %th=I18n.t('activerecord.attributes.group_order_invoice.links.sepa_select') + %th=I18n.t('activerecord.attributes.group_order_invoice.links.invoice_number') %th %tbody - multi_order.multi_group_orders.each do |mgo| -if mgo.ordergroup_invoice.present? - if mgo.ordergroup_invoice - = mgo.ordergroup_invoice.invoice_number %tr.order-row{id: "multi_group_order_#{mgo.id}"} - %td= link_to mgo.ordergroup&.name, edit_admin_ordergroup_path(mgo.ordergroup) %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' - -# .div{id: "paid_#{mgo.ordergroup_invoice.id}"} - -# = render :partial => "group_order_invoices/toggle_paid", locals: { group_order_invoice: go.group_order_invoice } - -# %td - -# .div{id: "sepa_downloaded_#{go.group_order_invoice.id}"} - -# = render :partial => "group_order_invoices/toggle_sepa_downloaded", locals: { group_order_invoice: go.group_order_invoice } - -# %td - -# .div{id: "select_sepa_sequence_type_#{go.group_order_invoice.id}"} - -# =render :partial => 'group_order_invoices/select_sepa_sequence_type', locals:{ group_order_invoice: go.group_order_invoice, group_order: go } - -# %td - -# = check_box_tag "group_order_ids_for_order_#{multi_order.id}", go.id, false, class: "group-order-checkbox", id: "group_order_#{go.id}_included_in_sepa", data: { order_id: go.id } - -# = link_to I18n.t('activerecord.attributes.group_order_invoice.links.delete'), go.group_order_invoice, method: :delete, class: 'btn btn-block btn-danger', remote: true, data: { confirm: I18n.t('ui.confirm_delete', name: "Bestellgruppenrechnung für #{go.ordergroup.name}" ) } - -# - else - -# %tr - -# %td - -# = go.ordergroup&.name - -# = 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: multi_order.id}, remote: true - -# %td - -# %td - -# %td - -# %td - -# %td - - -# - if multi_order.group_orders.map(&:group_order_invoice).compact.present? - -# %tr.order-row - -# %td= I18n.t('activerecord.attributes.group_order_invoice.links.actions_for_all') - -# %td - -# .div{id: "toggle_all_paid_#{multi_order.id}"} - -# = render :partial => 'group_order_invoices/toggle_all_paid', locals: { order: order } - -# %td - -# .div{id: "toggle_all_sepa_downloaded_#{multi_order.id}"} - -# = render :partial => 'group_order_invoices/toggle_all_sepa_downloaded', locals: { order: order } - -# %td - -# .div{id: "select_all_sepa_sequence_type_#{multi_order.id}"} - -# = render :partial => 'group_order_invoices/select_all_sepa_sequence_type', locals: { order: multi_order } - -# %td - -# .div{id: "select_all_sepa_#{multi_order.id}"} - -# = render :partial => 'group_order_invoices/collective_direct_debit', locals: { order: multi_order } - -# %td - -# = link_to I18n.t('activerecord.attributes.group_order_invoice.links.download_all_zip'), download_all_group_order_invoices_path(multi_order), class: 'btn btn-block' + - else + %tr + %td + = mgo.group_orders.first.ordergroup&.name + = button_to I18n.t('activerecord.attributes.group_order_invoice.links.generate'), ordergroup_invoices_path(:method => :post, multi_group_order_id: mgo) ,class: 'btn btn-small', remote: true + %td + %td + %td + %td + %td + - if multi_order.multi_group_orders.map(&:ordergroup_invoice).compact.present? + %tr.order-row + %td= I18n.t('activerecord.attributes.group_order_invoice.links.actions_for_all') + %td + .div{id: "toggle_all_paid_multi_#{multi_order.id}"} + = render :partial => 'ordergroup_invoices/toggle_all_paid', locals: { multi_order: multi_order } + %td + .div{id: "toggle_all_sepa_downloaded_multi_#{multi_order.id}"} + = render :partial => 'ordergroup_invoices/toggle_all_sepa_downloaded', locals: { multi_order: multi_order } + %td + .div{id: "select_all_sepa_sequence_type_multi_#{multi_order.id}"} + = render :partial => 'ordergroup_invoices/select_all_sepa_sequence_type', locals: { multi_order: multi_order } + %td + .div{id: "select_all_sepa_#{multi_order.id}"} + = render :partial => 'ordergroup_invoices/collective_direct_debit', locals: { multi_order: multi_order } + %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' 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 new file mode 100644 index 00000000..a4dc9580 --- /dev/null +++ b/app/views/ordergroup_invoices/_select_all_sepa_sequence_type.html.haml @@ -0,0 +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 diff --git a/app/views/ordergroup_invoices/_select_sepa_sequence_type.html.haml b/app/views/ordergroup_invoices/_select_sepa_sequence_type.html.haml new file mode 100644 index 00000000..61799883 --- /dev/null +++ b/app/views/ordergroup_invoices/_select_sepa_sequence_type.html.haml @@ -0,0 +1,2 @@ += 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 diff --git a/app/views/ordergroup_invoices/_toggle_all_paid.html.haml b/app/views/ordergroup_invoices/_toggle_all_paid.html.haml new file mode 100644 index 00000000..9cb1817f --- /dev/null +++ b/app/views/ordergroup_invoices/_toggle_all_paid.html.haml @@ -0,0 +1,2 @@ += link_to toggle_all_paid_ordergroup_invoices_path(multi_order_id: multi_order.id, paid: multi_order.multi_group_orders.map(&:ordergroup_invoice).compact.map(&:paid)&.all? ), remote: true, method: :patch, data: { confirm: I18n.t('ui.confirm_mark_all', name: multi_order.multi_group_orders.map(&:ordergroup_invoice).compact.map(&:paid)&.all? ? I18n.t('activerecord.attributes.group_order_invoice.links.not_paid'): I18n.t('activerecord.attributes.group_order_invoice.links.paid') ) } do + = check_box_tag :paid, '1', multi_order.multi_group_orders.map(&:ordergroup_invoice).compact.map(&:paid)&.all? , class: 'form-check-input', id: "paid_all_multi_#{multi_order.id}" \ No newline at end of file diff --git a/app/views/ordergroup_invoices/_toggle_all_sepa_downloaded.html.haml b/app/views/ordergroup_invoices/_toggle_all_sepa_downloaded.html.haml new file mode 100644 index 00000000..ff212377 --- /dev/null +++ b/app/views/ordergroup_invoices/_toggle_all_sepa_downloaded.html.haml @@ -0,0 +1,2 @@ += link_to toggle_all_sepa_downloaded_ordergroup_invoices_path(multi_order_id: multi_order.id, sepa_downloaded: multi_order.multi_group_orders.map(&:ordergroup_invoice).compact.map(&:sepa_downloaded)&.all? ), remote: true, method: :patch, data: { confirm: I18n.t('ui.confirm_mark_all', name: multi_order.multi_group_orders.map(&:ordergroup_invoice).compact.map(&:sepa_downloaded)&.all? ? I18n.t('activerecord.attributes.group_order_invoice.links.sepa_not_downloaded') : I18n.t('activerecord.attributes.group_order_invoice.links.sepa_downloaded')) } do + = check_box_tag :sepa_downloaded, '1', multi_order.multi_group_orders.map(&:ordergroup_invoice).compact.map(&:sepa_downloaded)&.all? , class: 'form-check-input', id: "sepa_downloaded_all_multi_#{multi_order.id}" \ No newline at end of file diff --git a/app/views/ordergroup_invoices/_toggle_paid.html.haml b/app/views/ordergroup_invoices/_toggle_paid.html.haml new file mode 100644 index 00000000..4d2715d6 --- /dev/null +++ b/app/views/ordergroup_invoices/_toggle_paid.html.haml @@ -0,0 +1,2 @@ += link_to toggle_paid_ordergroup_invoice_path(ordergroup_invoice), remote: true, method: :patch, data: { turbolinks: false } do + = check_box_tag 'paid', '1', ordergroup_invoice.paid , class: 'form-check-input', id: "paid_multi_#{ordergroup_invoice.id}" \ 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 new file mode 100644 index 00000000..f4bfcbd1 --- /dev/null +++ b/app/views/ordergroup_invoices/_toggle_sepa_downloaded.html.haml @@ -0,0 +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 diff --git a/app/views/ordergroup_invoices/create.js.erb b/app/views/ordergroup_invoices/create.js.erb new file mode 100644 index 00000000..bb012a28 --- /dev/null +++ b/app/views/ordergroup_invoices/create.js.erb @@ -0,0 +1 @@ +$("#multi_order_<%= @multi_order.id %>_modal").html("<%= escape_javascript(render partial: 'links', locals: {multi_order: @multi_order}) %>"); diff --git a/app/views/ordergroup_invoices/create_multiple.js.erb b/app/views/ordergroup_invoices/create_multiple.js.erb new file mode 100644 index 00000000..bb012a28 --- /dev/null +++ b/app/views/ordergroup_invoices/create_multiple.js.erb @@ -0,0 +1 @@ +$("#multi_order_<%= @multi_order.id %>_modal").html("<%= escape_javascript(render partial: 'links', locals: {multi_order: @multi_order}) %>"); diff --git a/app/views/ordergroup_invoices/destroy.js.erb b/app/views/ordergroup_invoices/destroy.js.erb new file mode 100644 index 00000000..ac4ebddd --- /dev/null +++ b/app/views/ordergroup_invoices/destroy.js.erb @@ -0,0 +1 @@ +$("#multi_order_<%= @multi_order.id %>_modal").html("<%= escape_javascript(render partial: 'links', locals: {multi_order: @multi_order}) %>"); \ No newline at end of file diff --git a/app/views/ordergroup_invoices/select_all_sepa_sequence_type.js.erb b/app/views/ordergroup_invoices/select_all_sepa_sequence_type.js.erb new file mode 100644 index 00000000..60519b8b --- /dev/null +++ b/app/views/ordergroup_invoices/select_all_sepa_sequence_type.js.erb @@ -0,0 +1 @@ +$("#multi_order_<%= @multi_order.id %>_modal").html("<%= escape_javascript(render partial: 'modal', locals: { multi_order: @multi_order, sequence_type: @sequence_type }) %>"); diff --git a/app/views/ordergroup_invoices/select_sepa_sequence_type.js.erb b/app/views/ordergroup_invoices/select_sepa_sequence_type.js.erb new file mode 100644 index 00000000..fa249c87 --- /dev/null +++ b/app/views/ordergroup_invoices/select_sepa_sequence_type.js.erb @@ -0,0 +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 diff --git a/app/views/ordergroup_invoices/toggle_all_paid.js.erb b/app/views/ordergroup_invoices/toggle_all_paid.js.erb new file mode 100644 index 00000000..40a788cc --- /dev/null +++ b/app/views/ordergroup_invoices/toggle_all_paid.js.erb @@ -0,0 +1,4 @@ +<% @ordergroup_invoices.each do |ordergroup_invoice| %> + $("#paid_multi_<%= ordergroup_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_paid', locals: { ordergroup_invoice: ordergroup_invoice }) %>"); +<% end %> + $("#toggle_all_paid_multi_<%= @multi_order.id %>").html("<%= escape_javascript(render partial: 'toggle_all_paid', locals: { multi_order: @multi_order }) %>"); diff --git a/app/views/ordergroup_invoices/toggle_all_sepa_downloaded.js.erb b/app/views/ordergroup_invoices/toggle_all_sepa_downloaded.js.erb new file mode 100644 index 00000000..0c68246b --- /dev/null +++ b/app/views/ordergroup_invoices/toggle_all_sepa_downloaded.js.erb @@ -0,0 +1,4 @@ +<% @ordergroup_invoices.each do |ordergroup_invoice| %> + $("#sepa_downloaded_multi_<%= ordergroup_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_sepa_downloaded', locals: { ordergroup_invoice: ordergroup_invoice }) %>"); +<% end %> + $("#toggle_all_sepa_downloaded_multi_<%= @multi_order.id %>").html("<%= escape_javascript(render partial: 'toggle_all_sepa_downloaded', locals: { multi_order: @multi_order }) %>"); diff --git a/app/views/ordergroup_invoices/toggle_paid.js.erb b/app/views/ordergroup_invoices/toggle_paid.js.erb new file mode 100644 index 00000000..4097f4d6 --- /dev/null +++ b/app/views/ordergroup_invoices/toggle_paid.js.erb @@ -0,0 +1 @@ +$("#paid_multi_<%= @ordergroup_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_paid', locals: {ordergroup_invoice: @ordergroup_invoice}) %>"); diff --git a/app/views/ordergroup_invoices/toggle_sepa_downloaded.js.erb b/app/views/ordergroup_invoices/toggle_sepa_downloaded.js.erb new file mode 100644 index 00000000..9a57c052 --- /dev/null +++ b/app/views/ordergroup_invoices/toggle_sepa_downloaded.js.erb @@ -0,0 +1 @@ +$("#sepa_downloaded_multi_<%= @ordergroup_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_sepa_downloaded', locals: {ordergroup_invoice: @ordergroup_invoice}) %>"); diff --git a/config/app_config.yml.SAMPLE b/config/app_config.yml.SAMPLE deleted file mode 100644 index b43b7935..00000000 --- a/config/app_config.yml.SAMPLE +++ /dev/null @@ -1,186 +0,0 @@ -# Foodsoft configuration - -default: &defaults - # If you wanna serve more than one foodcoop with one installation - # Don't forget to setup databases for each foodcoop. See also MULTI_COOP_INSTALL - multi_coop_install: false - - # If multi_coop_install you have to use a coop name, which you you wanna be selected by default - # Please use basic characters for scope names, matching /[-a-zA-Z0-9_]+/ - default_scope: 'f' - - # name of this foodcoop - name: FC Test - # foodcoop contact information (used for FAX messages) - contact: - street: Grüne Straße 103 - zip_code: "10997" - city: Berlin - country: Deutschland - email: foodsoft@foodcoop.test - phone: "030 323 23249" - - # Homepage - homepage: http://www.foodcoop.test - - # foodsoft documentation URL - help_url: https://github.com/foodcoops/foodsoft/wiki/Doku - - # documentation URL for the apples&pears work system - applepear_url: https://github.com/foodcoops/foodsoft/wiki/%C3%84pfel-u.-Birnen - - # custom foodsoft software URL (used in footer) - #foodsoft_url: https://github.com/foodcoops/foodsoft - - # URL to redirect to after logging out - # logout_redirect_url: https://foodcoop.test - - # Default language - #default_locale: en - # By default, foodsoft takes the language from the webbrowser/operating system. - # In case you really want foodsoft in a certain language by default, set this to true. - # When members are logged in, the language from their profile settings is still used. - #ignore_browser_locale: false - # Default timezone, e.g. UTC, Amsterdam, Berlin, etc. - #time_zone: Berlin - # Currency symbol, and whether to add a whitespace after the unit. - #currency_unit: € - #currency_space: true - - # price markup in percent - price_markup: 2.0 - - # default vat percentage for new articles - tax_default: 7.0 - - # tolerance order option: If set to false, article tolerance values do not count - # for total article price as long as the order is not finished. - tolerance_is_costly: false - - # Ordergroups, which have less than 75 apples should not be allowed to make new orders - # Comment out this option to activate this restriction - #stop_ordering_under: 75 - - # Comment out to completely hide apple points (be sure to comment stop_ordering_under) - #use_apple_points: false - - # ordergroups can only order when their balance is higher than or equal to this - # not fully enforced right now, since the check is only client-side - #minimum_balance: 0 - - # how many days there are between two periodic tasks - #tasks_period_days: 7 - # how many days upfront periodic tasks are created - #tasks_upfront_days: 49 - - # default order schedule, used to provide initial dates for new orders - # (recurring dates in ical format; no spaces!) - #order_schedule: - # ends: - # recurr: FREQ=WEEKLY;INTERVAL=2;BYDAY=MO - # time: '9:00' - # # reference point, this is generally the first pickup day; empty is often ok - # #initial: - - # When use_nick is enabled, there will be a nickname field in the user form, - # and the option to show a nickname instead of full name to foodcoop members. - # Members of a user's groups and administrators can still see full names. - use_nick: false - - # Most plugins can be enabled/disabled here as well. Messages and wiki are enabled - # by default and need to be set to false to disable. Most other plugins needs to - # be enabled before they do anything. - #use_wiki: true - #use_messages: true - - # When enabled only administrators can access the member list. - #disable_members_overview: true - - # Base font size for generated PDF documents - #pdf_font_size: 12 - # Page size for generated PDF documents - #pdf_page_size: A4 - # Some documents (like group and article PDFs) can include page breaks - # after each sublist. - #pdf_add_page_breaks: true - # Alternatively, this can be set for each document. - #pdf_add_page_breaks: - # order_by_groups: true - # order_by_articles: true - - # Page footer (html allowed). Default is a Foodsoft footer. Set to `blank` for no footer. - #page_footer: FC Test is supported by Hoster. - - # Custom CSS to add - #custom_css: 'body { background-color: #fcffba; }' - - # Custom fields for invoice, odergroup, supplier and user. - # Check out https://github.com/plataformatec/simple_form for details about the supported options. - #custom_fields: - # user: - # - name: address - # label: Address - # - name: birthday - # label: Birthday - # as: date_picker - - # Uncomment to add tracking code for web statistics, e.g. for Matomo. (Added to bottom of page) - #webstats_tracking_code: | - # - # ...... - - # email address to be used as sender - email_sender: foodsoft@foodcoop.test - - # domain to be used for reply emails - #reply_email_domain: reply.foodcoop.test - - # If your foodcoop uses a mailing list instead of internal messaging system - #mailing_list: list@example.org - #mailing_list_subscribe: list-subscribe@example.org - - # Config for the exception_notification plugin - notification: - error_recipients: - - admin@foodcoop.test - feedback_recipients: - - support@foodcoop.test - sender_address: "\"Foodsoft Error\" " - email_prefix: "[Foodsoft]" - - # http config for this host to generate links in emails (uses environment config when not set) - #protocol: http - #host: localhost - #port: 3000 - #script_name: "/" - - # Access to sharedlists, the external article-database. - # This allows a foodcoop to subscribe to a selection of a supplier's full assortment, - # and makes it possible to share data with several foodcoops. Using this requires installing - # an additional application with a separate database. - #shared_lists: - # adapter: mysql2 - # host: localhost - # database: sharedlists_development - # username: root - # password: - # encoding: utf8 - # socket: /opt/lampp/var/mysql/mysql.sock - # - # Instead of defining all details here, you can also point to an entry in database.yml. - #shared_lists: shared_lists - - # default to allow automatically adding new articles on sync only when less than 200 articles in total - #shared_supplier_article_sync_limit: 200 - - # number of days after which attachment files get deleted - #attachment_retention_days: 365 - -development: - <<: *defaults - -test: - <<: *defaults - -production: - <<: *defaults diff --git a/config/environments/development.rb.SAMPLE b/config/environments/development.rb.SAMPLE index 50787eca..ed5bb855 100644 --- a/config/environments/development.rb.SAMPLE +++ b/config/environments/development.rb.SAMPLE @@ -69,8 +69,8 @@ Rails.application.configure do # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. - config.file_watcher = ActiveSupport::EventedFileUpdateChecker - + config.file_watcher = ActiveSupport::FileUpdateChecker + # Run resque tasks as part of the main app (not recommended for production) Resque.inline = true unless ENV['REDIS_URL'] end diff --git a/config/locales/de.yml b/config/locales/de.yml index c5087c68..7b50fca7 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -107,12 +107,14 @@ de: generate: Rechnung erzeugen generate_with_date: setzen & erzeugen invoice_date: Datum der Bestellgruppenrechnung + invoice_number: Rechnungsnummer ordergroup: Bestellgruppe paid: Bezahlt not_paid: Nicht Bezahlt sepa_downloaded: SEPA exportiert sepa_not_downloaded: SEPA nicht exportiert - sepa_not_ready: Wichtige Einstellungen für SEPA Export in Administration -> Einstellungen-> Finanzen fehlen + sepa_not_ready: + SEPA Export nicht verfügbar. Wichtige Einstellungen für SEPA Export in Administration -> Einstellungen-> Finanzen fehlen sepa_select: Für SEPA Export markieren sepa_sequence_type: SEPA Typ open_details_modal: Details ein/ausklappen diff --git a/config/locales/en.yml b/config/locales/en.yml index 52e9b249..ae1f3cc5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -107,6 +107,7 @@ en: generate: generate invoice generate_with_date: set & generate invoice_date: date of group order invoice + invoice_number: invoice number ordergroup: Ordergroup paid: paid not_paid: unpaid diff --git a/config/routes.rb b/config/routes.rb index cc965d67..9d839c04 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -148,13 +148,24 @@ Rails.application.routes.draw do end + post 'finance/ordergroup_invoice', to: 'ordergroup_invoices#create_multiple' 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' resources :ordergroup_invoices do member do get :download_collective + patch :select_sepa_sequence_type + patch :toggle_paid + patch :toggle_sepa_downloaded + end + collection do + get :download_within_date + patch :select_all_sepa_sequence_type + patch :toggle_all_sepa_downloaded + patch :toggle_all_paid end end @@ -172,9 +183,10 @@ Rails.application.routes.draw do end end - resources :multi_orders, only: [:create, :show] do + resources :multi_orders, only: [:create, :show, :destroy] do member do get :generate_ordergroup_invoices + get :collective_direct_debit end end diff --git a/db/migrate/20250515111627_add_multi_group_order_id_to_ordergroup_invoices.rb b/db/migrate/20250515111627_add_multi_group_order_id_to_ordergroup_invoices.rb new file mode 100644 index 00000000..4c7bb708 --- /dev/null +++ b/db/migrate/20250515111627_add_multi_group_order_id_to_ordergroup_invoices.rb @@ -0,0 +1,5 @@ +class AddMultiGroupOrderIdToOrdergroupInvoices < ActiveRecord::Migration[7.0] + def change + add_reference :ordergroup_invoices, :multi_group_order, foreign_key: true, type: :integer + end +end diff --git a/db/migrate/20250516104953_make_group_order_key_nullable.rb b/db/migrate/20250516104953_make_group_order_key_nullable.rb new file mode 100644 index 00000000..eebcceb1 --- /dev/null +++ b/db/migrate/20250516104953_make_group_order_key_nullable.rb @@ -0,0 +1,12 @@ +class MakeGroupOrderKeyNullable < ActiveRecord::Migration[7.0] + def change + # Make column nullable (safe even if already is) + change_column_null :group_orders, :multi_group_order_id, true + + # Remove old FK if it exists (avoid name conflict) + remove_foreign_key :group_orders, column: :multi_group_order_id + + # Re-add with ON DELETE SET NULL + add_foreign_key :group_orders, :multi_group_orders, column: :multi_group_order_id, on_delete: :nullify + end +end diff --git a/db/routes.rb b/db/routes.rb index cc965d67..e0c41b48 100644 --- a/db/routes.rb +++ b/db/routes.rb @@ -148,6 +148,7 @@ Rails.application.routes.draw do end + post 'finance/ordergroup_invoice', to: 'ordergroup_invoices#create_multiple', 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' @@ -155,6 +156,15 @@ Rails.application.routes.draw do resources :ordergroup_invoices do member do get :download_collective + patch :select_sepa_sequence_type + patch :toggle_paid + patch :toggle_sepa_downloaded + end + collection do + get :download_within_date + patch :select_all_sepa_sequence_type + patch :toggle_all_sepa_downloaded + patch :toggle_all_paid end end diff --git a/db/schema.rb b/db/schema.rb index 7e82048e..51a2ac73 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_04_30_091541) do +ActiveRecord::Schema[7.0].define(version: 2025_05_16_104953) 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 @@ -226,7 +226,6 @@ ActiveRecord::Schema[7.0].define(version: 2025_04_30_091541) do t.index ["order_id"], name: "index_group_orders_on_order_id" t.index ["ordergroup_id", "order_id"], name: "index_group_orders_on_ordergroup_id_and_order_id", unique: true t.index ["ordergroup_id"], name: "index_group_orders_on_ordergroup_id" - t.index ["ordergroup_invoice_id"], name: "index_group_orders_on_ordergroup_invoice_id" end create_table "groups", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| @@ -407,6 +406,8 @@ ActiveRecord::Schema[7.0].define(version: 2025_04_30_091541) do t.string "sepa_sequence_type", default: "RCUR" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "multi_group_order_id" + t.index ["multi_group_order_id"], name: "index_ordergroup_invoices_on_multi_group_order_id" end create_table "orders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| @@ -626,7 +627,8 @@ ActiveRecord::Schema[7.0].define(version: 2025_04_30_091541) do add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "group_order_invoice_group_orders", "group_orders" - add_foreign_key "group_orders", "multi_group_orders" + add_foreign_key "group_orders", "multi_group_orders", on_delete: :nullify add_foreign_key "multi_group_orders", "multi_orders" + add_foreign_key "ordergroup_invoices", "multi_group_orders" add_foreign_key "orders", "multi_orders" end