changing view for group_order_invoices
testing ui of goi restructuring
This commit is contained in:
parent
de6643722a
commit
90c5450525
52 changed files with 783 additions and 57 deletions
1
Gemfile
1
Gemfile
|
@ -60,6 +60,7 @@ gem 'rswag-api'
|
|||
gem 'rswag-ui'
|
||||
gem 'ruby-filemagic'
|
||||
gem 'spreadsheet'
|
||||
gem 'sepa_king'
|
||||
gem "terser", "~> 1.1"
|
||||
|
||||
# we use the git version of acts_as_versioned, and need to include it in this Gemfile
|
||||
|
|
|
@ -253,6 +253,7 @@ GEM
|
|||
i18n (>= 0.6.6, < 2)
|
||||
i18n-spec (0.6.0)
|
||||
iso
|
||||
iban-tools (1.1.0)
|
||||
ice_cube (0.16.4)
|
||||
image_processing (1.12.2)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
|
@ -515,6 +516,10 @@ GEM
|
|||
tilt
|
||||
sd_notify (0.1.1)
|
||||
select2-rails (4.0.13)
|
||||
sepa_king (0.14.0)
|
||||
activemodel (>= 4.2)
|
||||
iban-tools
|
||||
nokogiri
|
||||
simple-navigation (3.14.0)
|
||||
activesupport (>= 2.3.2)
|
||||
simple-navigation-bootstrap (1.0.2)
|
||||
|
@ -678,6 +683,7 @@ DEPENDENCIES
|
|||
sassc-rails
|
||||
sd_notify
|
||||
select2-rails
|
||||
sepa_king
|
||||
simple-navigation (~> 3.14.0)
|
||||
simple-navigation-bootstrap
|
||||
simple_form
|
||||
|
@ -694,4 +700,4 @@ DEPENDENCIES
|
|||
whenever
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.8
|
||||
2.4.21
|
||||
|
|
0
app/assets/javascripts/autocomplete_sepa.js
Normal file
0
app/assets/javascripts/autocomplete_sepa.js
Normal file
|
@ -1,4 +1,5 @@
|
|||
/*
|
||||
*= require group_order_invoices
|
||||
*= require bootstrap_and_overrides
|
||||
*= require select2
|
||||
*= require select2-bootstrap
|
||||
|
|
46
app/assets/stylesheets/group_order_invoices.css
Normal file
46
app/assets/stylesheets/group_order_invoices.css
Normal file
|
@ -0,0 +1,46 @@
|
|||
.checkbox-icon {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-icon::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #000;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.checkbox-icon.checked::before {
|
||||
content: "\2713"; /* Unicode checkmark symbol */
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
line-height: 20px; /* Align the checkmark vertically */
|
||||
color: #00ff00; /* Change the color to represent a checked state */
|
||||
}
|
||||
|
||||
|
||||
.table.group-order-invoices-table tr{
|
||||
background-color: rgb(255, 255, 233);
|
||||
}
|
||||
.table.group-order-invoices-table thead tr{
|
||||
background-color: lightgoldenrodyellow;
|
||||
}
|
||||
|
||||
.table.group-order-invoices-table tr:nth-child(odd) > td,
|
||||
.table.group-order-invoices-table tr:nth-child(even) > td{
|
||||
background-color: rgb(255, 255, 233);
|
||||
padding-right: 0;
|
||||
.group-order-checkbox {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.form-check-input{
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,22 @@ class Admin::OrdergroupsController < Admin::BaseController
|
|||
@ordergroups = @ordergroups.page(params[:page]).per(@per_page)
|
||||
end
|
||||
|
||||
def update
|
||||
sepa_account_holder_params = params[:ordergroup][:sepa_account_holder_attributes]
|
||||
if sepa_account_holder_params&.[](:user_id).blank? || sepa_account_holder_params&.[](:group_id).blank?
|
||||
if sepa_account_holder_params&.[](:id).present?
|
||||
SepaAccountHolder.find(sepa_account_holder_params[:id]).destroy
|
||||
end
|
||||
params[:ordergroup].delete(:sepa_account_holder_attributes)
|
||||
end
|
||||
@ordergroup = Ordergroup.find(params[:id])
|
||||
if @ordergroup.update(params[:ordergroup])
|
||||
redirect_to admin_ordergroup_path(@ordergroup), notice: t('.notice')
|
||||
else
|
||||
redirect_to edit_admin_ordergroup_path(@ordergroup), alert: @ordergroup.errors.full_messages.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@ordergroup = Ordergroup.find(params[:id])
|
||||
@ordergroup.mark_as_deleted
|
||||
|
|
|
@ -7,6 +7,9 @@ class GroupOrderInvoicesController < ApplicationController
|
|||
raise RecordInvalid unless FoodsoftConfig[:contact][:tax_number]
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
send_group_order_invoice_pdf @group_order_invoice if FoodsoftConfig[:contact][:tax_number]
|
||||
end
|
||||
format.pdf do
|
||||
send_group_order_invoice_pdf @group_order_invoice if FoodsoftConfig[:contact][:tax_number]
|
||||
end
|
||||
|
@ -18,13 +21,14 @@ class GroupOrderInvoicesController < ApplicationController
|
|||
def create
|
||||
go = GroupOrder.find(params[:group_order])
|
||||
@order = go.order
|
||||
GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
|
||||
respond_to do |format|
|
||||
format.js
|
||||
begin
|
||||
GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
|
||||
respond_to do |format|
|
||||
format.js
|
||||
end
|
||||
rescue StandardError => e
|
||||
redirect_back fallback_location: root_path, notice: 'Something went wrong', :alert => I18n.t('errors.general_msg', :msg => e)
|
||||
end
|
||||
redirect_back fallback_location: root_path
|
||||
rescue StandardError => e
|
||||
redirect_back fallback_location: root_path, notice: 'Something went wrong', :alert => I18n.t('errors.general_msg', :msg => e)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -53,6 +57,83 @@ class GroupOrderInvoicesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def select_sepa_sequence_type
|
||||
@group_order_invoice = GroupOrderInvoice.find(params[:id])
|
||||
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
|
||||
return unless params[:sepa_sequence_type]
|
||||
@sepa_sequence_type = params[:sepa_sequence_type]
|
||||
@group_order_invoices.each do |goi|
|
||||
goi.sepa_sequence_type = params[:sepa_sequence_type]
|
||||
goi.save!
|
||||
end
|
||||
respond_to do |format|
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
def toggle_payed
|
||||
@group_order_invoice = GroupOrderInvoice.find(params[:id])
|
||||
respond_to do |format|
|
||||
@group_order_invoice.payed = !@group_order_invoice.payed
|
||||
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_payed
|
||||
@order = Order.find(params[:order_id])
|
||||
@group_order_invoices = @order.group_orders.map(&:group_order_invoice).compact
|
||||
@group_order_invoices.each do |goi|
|
||||
goi.payed = !ActiveRecord::Type::Boolean.new.deserialize(params[:payed])
|
||||
goi.save!
|
||||
end
|
||||
respond_to do |format|
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
def toggle_all_sepa_downloaded
|
||||
@order = Order.find(params[:order_id])
|
||||
@group_order_invoices = @order.group_orders.map(&:group_order_invoice).compact
|
||||
@group_order_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
|
||||
order = Order.find(params[:order_id])
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Normal ordering actions of members of order groups is handled by the OrderingController.
|
||||
class OrdersController < ApplicationController
|
||||
include Concerns::SendOrderPdf
|
||||
include SepaHelper
|
||||
|
||||
before_action :authenticate_pickups_or_orders
|
||||
before_action :authenticate_orders,
|
||||
|
@ -159,6 +160,69 @@ class OrdersController < ApplicationController
|
|||
render layout: false
|
||||
end
|
||||
|
||||
def collective_direct_debit
|
||||
if foodsoft_sepa_ready?
|
||||
case params[:mode]
|
||||
when 'all'
|
||||
group_orders = GroupOrder.where(order_id: params[:id])
|
||||
when 'selected'
|
||||
group_orders = GroupOrder.where(id: params[:group_order_ids])
|
||||
else
|
||||
redirect_to finance_order_index_path, alert: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: '')
|
||||
end
|
||||
|
||||
@order = Order.find(params[:id])
|
||||
ordergroups = group_orders.map(&:ordergroup)
|
||||
|
||||
export_allowed = !ordergroups.map(&:sepa_possible?).include?(false) && !group_orders.map { |go| go.group_order_invoice.present? }.include?(false)
|
||||
group_order_ids = group_orders.map { |go| go.id if go.group_order_invoice.present? }
|
||||
|
||||
sepa_possible_ordergroup_names = ordergroups.map { |ordergroup| ordergroup.name unless ordergroup.sepa_possible? }.compact_blank
|
||||
|
||||
if export_allowed && group_orders.present?
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
group_orders.map(&:group_order_invoice).each(&:mark_sepa_downloaded)
|
||||
collective_debit = OrderCollectiveDirectDebitXml.new(group_orders)
|
||||
send_data collective_debit.xml_string, filename: @order.name + '_Sammellastschrift' + '.xml', type: 'text/xml'
|
||||
rescue StandardError => e
|
||||
group_orders.map(&:group_order_invoice).each(&:unmark_sepa_downloaded)
|
||||
redirect_to finance_order_index_path, alert: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_possible_ordergroup_names.join(', '))
|
||||
end
|
||||
format.xml do
|
||||
group_orders.map(&:group_order_invoice).each(&:mark_sepa_downloaded)
|
||||
collective_debit = OrderCollectiveDirectDebitXml.new(group_orders)
|
||||
send_data collective_debit.xml_string, filename: @order.name + '_Sammellastschrift' + '.xml', type: 'text/xml'
|
||||
rescue SEPA::Error => e
|
||||
group_orders.map(&:group_order_invoice).each(&:unmark_sepa_downloaded)
|
||||
render json: { error: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_possible_ordergroup_names.join(', ')) }
|
||||
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_possible_ordergroup_names.join(', '))
|
||||
end
|
||||
format.xml do
|
||||
render json: { error: I18n.t('orders.collective_direct_debit.alert', ordergroup_names: sepa_possible_ordergroup_names.join(', ')) }
|
||||
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
|
||||
|
||||
protected
|
||||
|
||||
def update_order_amounts
|
||||
|
|
|
@ -63,8 +63,8 @@ class GroupOrderInvoicePdf < RenderPdf
|
|||
end
|
||||
|
||||
move_down 20
|
||||
text I18n.t('documents.group_order_invoice_pdf.payment_method', payment_method: @options[:payment_method])
|
||||
|
||||
text I18n.t('documents.group_order_invoice_pdf.payment_method', payment_method: FinancialTransactionType.find(@options[:payment_method]).name)
|
||||
|
||||
text I18n.t('documents.group_order_invoice_pdf.table_headline')
|
||||
move_down 5
|
||||
|
||||
|
|
5
app/helpers/sepa_helper.rb
Normal file
5
app/helpers/sepa_helper.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
module SepaHelper
|
||||
def foodsoft_sepa_ready?
|
||||
FoodsoftConfig[:group_order_invoices]&.[](:iban) && FoodsoftConfig[:group_order_invoices]&.[](:bic) && FoodsoftConfig[:name].present? && FoodsoftConfig[:group_order_invoices]&.[](:creditor_identifier).present?
|
||||
end
|
||||
end
|
116
app/lib/order_collective_direct_debit_xml.rb
Normal file
116
app/lib/order_collective_direct_debit_xml.rb
Normal file
|
@ -0,0 +1,116 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class OrderCollectiveDirectDebitXml
|
||||
attr_reader :xml_string
|
||||
|
||||
def initialize(group_orders)
|
||||
batch_booking = group_orders.count > 1 ? true : false
|
||||
begin
|
||||
sdd = SEPA::DirectDebit.new(
|
||||
# Name of the initiating party and creditor, in German: "Auftraggeber"
|
||||
# String, max. 70 char
|
||||
#TODO: needed from config
|
||||
name: FoodsoftConfig[:name],
|
||||
|
||||
# OPTIONAL: Business Identifier Code (SWIFT-Code) of the creditor
|
||||
# String, 8 or 11 char
|
||||
#TODO: needed - config
|
||||
bic: FoodsoftConfig[:group_order_invoices][:bic],
|
||||
#'BANKDEFFXXX',
|
||||
|
||||
# International Bank Account Number of the creditor
|
||||
# String, max. 34 chars
|
||||
#TODO: needed
|
||||
iban: FoodsoftConfig[:group_order_invoices][:iban],
|
||||
#'DE87200500001234567890',
|
||||
|
||||
# Creditor Identifier, in German: Gläubiger-Identifikationsnummer
|
||||
# String, max. 35 chars
|
||||
#TODO: needed - config
|
||||
creditor_identifier: FoodsoftConfig[:group_order_invoices][:creditor_identifier],
|
||||
#'DE98ZZZ09999999999'
|
||||
)
|
||||
rescue StandardError => e
|
||||
raise "SEPA Direct Debit XML could not be created: #{e.message}"
|
||||
end
|
||||
group_orders.each do |group_order|
|
||||
|
||||
# Second: Add transactions
|
||||
sdd.add_transaction(
|
||||
# Name of the debtor, in German: "Zahlungspflichtiger"
|
||||
# String, max. 70 char
|
||||
#TODO: From ordergroup
|
||||
name: group_order.ordergroup.name,
|
||||
|
||||
#Ende zu Ende Referenz
|
||||
reference: 'NOTPROVIDED',
|
||||
|
||||
# OPTIONAL: Business Identifier Code (SWIFT-Code) of the debtor's account
|
||||
# String, 8 or 11 char
|
||||
#TODO: needed where does it come from?
|
||||
bic: group_order.ordergroup.sepa_account_holder.bic,
|
||||
|
||||
# International Bank Account Number of the debtor's account
|
||||
# String, max. 34 chars
|
||||
#TODO: needed
|
||||
iban: group_order.ordergroup.sepa_account_holder.iban,
|
||||
|
||||
# Amount
|
||||
# Number with two decimal digit
|
||||
#TODO: needed comesfrom group_order.price
|
||||
amount: group_order.price,
|
||||
|
||||
# OPTIONAL: Currency, EUR by default (ISO 4217 standard)
|
||||
# String, 3 char
|
||||
currency: 'EUR',
|
||||
|
||||
# OPTIONAL: Instruction Identification, will not be submitted to the debtor
|
||||
# String, max. 35 char
|
||||
#TODO: is this neccessary?
|
||||
#instruction: '12345',
|
||||
|
||||
# OPTIONAL: Unstructured remittance information, in German "Verwendungszweck"
|
||||
# String, max. 140 char
|
||||
#TODO: invoice_number + supplier
|
||||
remittance_information: "#{group_order.group_order_invoice.invoice_number} #{group_order.order.supplier.name}",
|
||||
|
||||
# Mandate identifikation, in German "Mandatsreferenz"
|
||||
# String, max. 35 char
|
||||
#TODO: get it from fsconfig? rather from SEPA
|
||||
mandate_id: group_order.ordergroup.sepa_account_holder.mandate_id,
|
||||
|
||||
# Mandate Date of signature, in German "Datum, zu dem das Mandat unterschrieben wurde"
|
||||
# Date
|
||||
#TODO: neccessary?? if yes, get it from ordergroup
|
||||
mandate_date_of_signature: group_order.ordergroup.sepa_account_holder.mandate_date_of_signature,
|
||||
|
||||
# Local instrument, in German "Lastschriftart"
|
||||
# One of these strings:
|
||||
# 'CORE' ("Basis-Lastschrift")
|
||||
# 'COR1' ("Basis-Lastschrift mit verkürzter Vorlagefrist")
|
||||
# 'B2B' ("Firmen-Lastschrift")
|
||||
local_instrument: 'CORE',
|
||||
|
||||
# Sequence type
|
||||
# One of these strings:
|
||||
# 'FRST' ("Erst-Lastschrift")
|
||||
# 'RCUR' ("Folge-Lastschrift")
|
||||
# 'OOFF' ("Einmalige Lastschrift")
|
||||
# 'FNAL' ("Letztmalige Lastschrift")
|
||||
#TODO: selectable and default RCUR
|
||||
sequence_type: group_order.group_order_invoice.sepa_sequence_type || 'RCUR',
|
||||
|
||||
# OPTIONAL: Requested collection date, in German "Fälligkeitsdatum der Lastschrift"
|
||||
# Date
|
||||
#TODO: two weeks from now?
|
||||
requested_date: Time.zone.today + 2.days,
|
||||
|
||||
# OPTIONAL: Enables or disables batch booking, in German "Sammelbuchung / Einzelbuchung"
|
||||
# True or False
|
||||
batch_booking: batch_booking
|
||||
)
|
||||
# Last: create XML string
|
||||
end
|
||||
@xml_string = sdd.to_xml # Use schema pain.008.001.02
|
||||
end
|
||||
end
|
|
@ -6,6 +6,8 @@ class Group < ApplicationRecord
|
|||
|
||||
has_many :memberships, dependent: :destroy
|
||||
has_many :users, -> { where(deleted_at: nil) }, through: :memberships
|
||||
has_one :sepa_account_holder, dependent: :destroy
|
||||
accepts_nested_attributes_for :sepa_account_holder, allow_destroy: true
|
||||
|
||||
validates :name, presence: true, length: { in: 1..25 }
|
||||
validates :name, uniqueness: true
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
class GroupOrderInvoice < ApplicationRecord
|
||||
|
||||
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 generate_invoice_number(count)
|
||||
trailing_number = count.to_s.rjust(4, '0')
|
||||
if GroupOrderInvoice.find_by(invoice_number: self.invoice_date.strftime("%Y%m%d") + trailing_number)
|
||||
|
@ -20,6 +28,16 @@ class GroupOrderInvoice < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def mark_sepa_downloaded
|
||||
self.sepa_downloaded = true
|
||||
self.save
|
||||
end
|
||||
|
||||
def unmark_sepa_downloaded
|
||||
self.sepa_downloaded = false
|
||||
self.save
|
||||
end
|
||||
|
||||
def init
|
||||
self.invoice_date = Time.now unless invoice_date
|
||||
self.invoice_number = generate_invoice_number(1) unless self.invoice_number
|
||||
|
|
|
@ -8,6 +8,8 @@ class Ordergroup < Group
|
|||
|
||||
APPLE_MONTH_AGO = 6 # How many month back we will count tasks and orders sum
|
||||
|
||||
attr_accessor :sepa_account_holder_user_id
|
||||
|
||||
serialize :stats
|
||||
|
||||
has_many :financial_transactions
|
||||
|
@ -182,6 +184,10 @@ class Ordergroup < Group
|
|||
result.order(Arel.sql(sort_param_map[param]))
|
||||
end
|
||||
|
||||
def sepa_possible?
|
||||
sepa_account_holder&.all_fields_present? || false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Make sure, that a user can only be in one ordergroup
|
||||
|
|
8
app/models/sepa_account_holder.rb
Normal file
8
app/models/sepa_account_holder.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
class SepaAccountHolder < ApplicationRecord
|
||||
belongs_to :group
|
||||
belongs_to :user
|
||||
|
||||
def all_fields_present?
|
||||
iban.present? && bic.present? && mandate_id.present? && user_id.present? && mandate_date_of_signature.present? && group_id.present?
|
||||
end
|
||||
end
|
|
@ -17,6 +17,7 @@ class User < ApplicationRecord
|
|||
has_many :send_messages, class_name: 'Message', foreign_key: 'sender_id'
|
||||
has_many :created_orders, class_name: 'Order', foreign_key: 'created_by_user_id', dependent: :nullify
|
||||
has_many :mail_delivery_status, class_name: 'MailDeliveryStatus', foreign_key: 'email', primary_key: 'email'
|
||||
has_many :accountable_payment_groups, class_name: 'PaymentAccountHolder'
|
||||
|
||||
attr_accessor :create_ordergroup, :password, :send_welcome_mail, :settings_attributes
|
||||
|
||||
|
@ -120,6 +121,10 @@ class User < ApplicationRecord
|
|||
settings.messages['send_as_email'] && email.present?
|
||||
end
|
||||
|
||||
def sepa_possible?
|
||||
iban.present? && bic.present? && creditor_identifier.present? && mandate_id.present?
|
||||
end
|
||||
|
||||
# Sets the user's password. It will be stored encrypted along with a random salt.
|
||||
def set_password
|
||||
return if password.blank?
|
||||
|
|
|
@ -15,10 +15,16 @@
|
|||
= config_input form, :use_self_service, as: :boolean
|
||||
%h4= t '.group_order_invoices'
|
||||
= form.fields_for :group_order_invoices do |field|
|
||||
= config_input field, :ignore_minimum_balance, as: :boolean
|
||||
= config_input field, :use_automatic_invoices, as: :boolean
|
||||
= config_input field, :separate_deposits, as: :boolean
|
||||
= config_input field, :vat_exempt, as: :boolean
|
||||
= config_input field, :payment_method, as: :string, input_html: {class: 'input-medium'}
|
||||
= config_input field, :payment_method, collection: FinancialTransactionType.all.map { |t| [t.name, t.id] }, as: :select, include_blank: true
|
||||
%p
|
||||
%i Für SEPA-Lastschrift-Export:
|
||||
= config_input field, :iban
|
||||
= config_input field, :bic
|
||||
= config_input field, :creditor_identifier
|
||||
|
||||
%h4= t '.schedule_title'
|
||||
= form.simple_fields_for :order_schedule do |fields|
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
= f.input :contact_person
|
||||
= f.input :contact_phone
|
||||
= f.input :contact_address
|
||||
= render 'shared/sepa_account_holder', f: f
|
||||
= render 'shared/custom_form_fields', f: f, type: :ordergroup
|
||||
.fold-line
|
||||
= f.input :break_start, as: :date_picker, label: Ordergroup.human_attribute_name('break')
|
||||
= f.input :break_end, as: :date_picker, label: Ordergroup.human_attribute_name('break_until')
|
||||
- if FoodsoftConfig[:use_apple_points]
|
||||
= f.input :ignore_apple_restriction, :label => false, :inline_label => true
|
||||
|
||||
= render 'shared/group_form_fields', f: f, captured: captured
|
||||
.form-actions
|
||||
= f.button :submit
|
||||
|
|
|
@ -14,15 +14,15 @@
|
|||
%th
|
||||
%tbody
|
||||
- @orders.each do |order|
|
||||
%tr{:class => cycle("even","odd", :name => "order")}
|
||||
%tr{:class => cycle("even","odd", :name => "order"), 'data-order_id' => order.id}
|
||||
%td= link_to truncate(order.name), new_finance_order_path(order_id: order.id)
|
||||
%td=h format_time(order.ends) unless order.ends.nil?
|
||||
%td= order.closed? ? t('.cleared', amount: number_to_currency(order.foodcoop_result)) : t('.ended')
|
||||
%td= show_user(order.updated_by)
|
||||
%td{id: "generate-invoice#{order.id}"}
|
||||
%td{id: "group-order-invoices-for-order-#{order.id}", class: 'expand-trigger'}
|
||||
- if order.closed?
|
||||
-if FoodsoftConfig[:contact][:tax_number] && order.ordergroups.present?
|
||||
= render :partial => 'group_order_invoices/links', locals:{order: order}
|
||||
= link_to I18n.t('activerecord.attributes.group_order_invoice.open_details_modal'), "#", remote: true, class: 'btn btn-small'
|
||||
-else
|
||||
= I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set')
|
||||
- else
|
||||
|
@ -35,5 +35,22 @@
|
|||
- else
|
||||
= link_to t('orders.index.action_receive'), '#', class: 'btn btn-small disabled'
|
||||
= link_to t('.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;'}
|
||||
%td{:colspan => '6'}
|
||||
= render partial: 'group_order_invoices/modal', locals: { order: order }
|
||||
- else
|
||||
%i= t('.no_closed_orders')
|
||||
|
||||
- content_for :javascript do
|
||||
:javascript
|
||||
$(document).ready(function() {
|
||||
$('.expand-trigger').click(function() {
|
||||
var orderId = $(this).closest('tr').data('order_id');
|
||||
var expandedRow = $('#expanded-row-' + orderId);
|
||||
|
||||
// Toggle visibility of the expanded row
|
||||
expandedRow.slideToggle();
|
||||
|
||||
return false; // Prevent the default behavior of the link
|
||||
});
|
||||
});
|
|
@ -3,7 +3,7 @@
|
|||
- if FinancialTransactionType.has_multiple_types
|
||||
%p
|
||||
%b= heading_helper FinancialTransaction, :financial_transaction_type
|
||||
= select_tag :type, options_for_select(FinancialTransactionType.order(:name).map { |t| [ t.name, t.id ] })
|
||||
= select_tag :type, options_for_select(FinancialTransactionType.order(:name).map { |t| [ t.name, t.id ] }, selected: FoodsoftConfig[:group_order_invoices]&.[](:payment_method))
|
||||
%table.table.table-striped{:style => "width:35em"}
|
||||
- for group_order in @order.group_orders
|
||||
%tr{:class => cycle('even', 'odd')}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
- if foodsoft_sepa_ready?
|
||||
= link_to 'Sammellastschrift für alle (.xml)', collective_direct_debit_order_path(id: order.id, mode: 'all'), class: 'btn btn-block'
|
||||
= link_to 'Sammellastschrift für ausgewählt (.xml)', collective_direct_debit_order_path(id: order.id, mode: 'selected'), remote: true, class: 'btn btn-block', data: { turbolinks: true }, id: "collective-direct-debit-link-#{order.id}"
|
||||
- else
|
||||
%i
|
||||
= t('activerecord.attributes.group_order_invoice.links.sepa_not_ready')
|
||||
- content_for :javascript do
|
||||
:javascript
|
||||
$(document).off('click', "#collective-direct-debit-link-#{order.id}").on('click', "#collective-direct-debit-link-#{order.id}", function(e) {
|
||||
e.preventDefault();
|
||||
// Extract selected group_order_ids
|
||||
var selectedGroupOrderIds = $('input[name^="group_order_ids_for_order_#{order.id}"]:checked').map(function() {
|
||||
return $(this).val();
|
||||
}).get();
|
||||
|
||||
// Log the selected IDs (for testing purposes)
|
||||
console.log('Selected Group Order IDs:', selectedGroupOrderIds);
|
||||
var url = $(this).closest('a').attr('href');
|
||||
//suppress generic error warning
|
||||
$.ajaxSetup({
|
||||
global: false,
|
||||
});
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: 'GET', // You may adjust the HTTP method as needed
|
||||
data: { group_order_ids: selectedGroupOrderIds },
|
||||
dataType: 'xml',
|
||||
success: function(response) {
|
||||
// Handle success response
|
||||
console.log('AJAX request successful:', response);
|
||||
// Convert XML response to a Blob
|
||||
var blob = new Blob([new XMLSerializer().serializeToString(response)], { type: 'text/xml' });
|
||||
|
||||
// Create a temporary link element
|
||||
var link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
|
||||
if(selectedGroupOrderIds.length > 1){
|
||||
link.download = "#{order.supplier.name}-Sammellastschrift.xml";
|
||||
} else {
|
||||
link.download = "#{order.supplier.name}-Lastschrift.xml";
|
||||
}
|
||||
|
||||
// Append the link to the document and trigger the click event
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// Clean up
|
||||
document.body.removeChild(link);
|
||||
$("group-order-invoices-for-order-#{order.id}" + " .expand-trigger a").click();
|
||||
|
||||
},
|
||||
error: function(error) {
|
||||
// Handle error
|
||||
if (error.responseJSON) {
|
||||
alert('AJAX request error:' + "\n" + error.responseJSON.message);
|
||||
} else{
|
||||
var errorText = JSON.parse(error.responseText).error;
|
||||
var alertDiv = '<div class="alert fade in alert-error"><button class="close" data-dismiss="alert">×</button>' + errorText + '</div>';
|
||||
$('.page-header').before(alertDiv);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,29 +1,69 @@
|
|||
.row
|
||||
.column.small-12
|
||||
- show_generate_with_date = true
|
||||
- order.group_orders.each do |go|
|
||||
- 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|
|
||||
= 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
|
||||
= f.submit I18n.t('activerecord.attributes.group_order_invoice.links.generate_with_date'), class: 'btn btn small'
|
||||
|
||||
- order.group_orders.includes([:group_order_invoice, :ordergroup]).each do |go|
|
||||
.row
|
||||
.column.small-3
|
||||
= label_tag go.ordergroup.name
|
||||
- if go.group_order_invoice
|
||||
.column.small-3
|
||||
= link_to I18n.t('activerecord.attributes.group_order_invoice.links.download'), group_order_invoice_path(go.group_order_invoice, :format => 'pdf'), class: 'btn btn-small'
|
||||
.column.small-3
|
||||
= link_to I18n.t('activerecord.attributes.group_order_invoice.links.delete'), go.group_order_invoice, method: :delete, class: 'btn btn-danger btn-small', remote: true
|
||||
- else
|
||||
= button_to I18n.t('activerecord.attributes.group_order_invoice.links.generate'), group_order_invoices_path(:method => :post, group_order: go) ,class: 'btn btn-small', params: {id: order.id}, remote: true
|
||||
- if order.group_orders.map(&:group_order_invoice).compact.present?
|
||||
%br/
|
||||
.row
|
||||
.column.small-3
|
||||
= link_to I18n.t('activerecord.attributes.group_order_invoice.links.download_all_zip'), download_all_group_order_invoices_path(order), class: 'btn btn-small'
|
||||
|
||||
- show_generate_with_date = true
|
||||
- order.group_orders.each do |go|
|
||||
- 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|
|
||||
= 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
|
||||
|
||||
= f.submit I18n.t('activerecord.attributes.group_order_invoice.links.generate_with_date'), class: 'btn btn'
|
||||
|
||||
%table.table.group-order-invoices-table
|
||||
%thead
|
||||
%tr
|
||||
%th=I18n.t('activerecord.attributes.group_order_invoice.links.ordergroup')
|
||||
%th=I18n.t('activerecord.attributes.group_order_invoice.links.payed')
|
||||
%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
|
||||
%tbody
|
||||
- order.group_orders.includes([:group_order_invoice, :ordergroup]).each do |go|
|
||||
- if go.group_order_invoice
|
||||
%tr.order-row
|
||||
%td= link_to go.ordergroup.name, edit_admin_ordergroup_path(go.ordergroup)
|
||||
%td
|
||||
.div{id: "payed_#{go.group_order_invoice.id}"}
|
||||
= render :partial => "group_order_invoices/toggle_payed", 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 }
|
||||
%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
|
||||
= 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}" ) }
|
||||
- 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: order.id}, remote: true
|
||||
%td
|
||||
%td
|
||||
%td
|
||||
%td
|
||||
%td
|
||||
|
||||
- if 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_payed_#{order.id}"}
|
||||
= render :partial => 'group_order_invoices/toggle_all_payed', locals: { order: order }
|
||||
%td
|
||||
.div{id: "toggle_all_sepa_downloaded_#{order.id}"}
|
||||
= render :partial => 'group_order_invoices/toggle_all_sepa_downloaded', locals: { order: order }
|
||||
%td
|
||||
.div{id: "select_all_sepa_sequence_type_#{order.id}"}
|
||||
= render :partial => 'group_order_invoices/select_all_sepa_sequence_type', locals: { order: order }
|
||||
%td
|
||||
= render :partial => 'group_order_invoices/collective_direct_debit', locals: { order: order }
|
||||
%td
|
||||
= link_to I18n.t('activerecord.attributes.group_order_invoice.links.download_all_zip'), download_all_group_order_invoices_path(order), class: 'btn btn-block'
|
||||
|
|
2
app/views/group_order_invoices/_modal.html.haml
Normal file
2
app/views/group_order_invoices/_modal.html.haml
Normal file
|
@ -0,0 +1,2 @@
|
|||
.div{id: "order_#{order.id}_modal"}
|
||||
= render :partial => 'group_order_invoices/links', locals: { order: order }
|
|
@ -0,0 +1,19 @@
|
|||
= 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}"
|
||||
|
||||
- content_for :javascript do
|
||||
:javascript
|
||||
$(document).off('change', ".ajax-update-all-link-#{order.id} select").on('change', ".ajax-update-all-link-#{order.id} select", function() {
|
||||
var selectedValue = $(this).val();
|
||||
var url = $(this).closest('a').attr('href');
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: 'PATCH',
|
||||
data: { sepa_sequence_type: selectedValue },
|
||||
success: function(response) {
|
||||
// Handle success response
|
||||
},
|
||||
error: function(error) {
|
||||
console.log(error);
|
||||
}
|
||||
});})
|
|
@ -0,0 +1,19 @@
|
|||
= link_to select_sepa_sequence_type_group_order_invoice_path(group_order_invoice), remote: true, method: :patch, class: "ajax-update-link-#{group_order_invoice.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_invoice.id}"
|
||||
|
||||
- content_for :javascript do
|
||||
:javascript
|
||||
$(document).off('change', ".ajax-update-link-#{group_order_invoice.id} select").on('change', ".ajax-update-link-#{group_order_invoice.id} select", function() {
|
||||
var selectedValue = $(this).val();
|
||||
var url = $(this).closest('a').attr('href');
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: 'PATCH',
|
||||
data: { sepa_sequence_type: selectedValue },
|
||||
success: function(response) {
|
||||
// Handle success response
|
||||
},
|
||||
error: function(error) {
|
||||
// Handle error
|
||||
}
|
||||
});})
|
|
@ -0,0 +1,2 @@
|
|||
= link_to toggle_all_payed_group_order_invoices_path(order_id: order.id,payed: order.group_orders.map(&:group_order_invoice).compact.map(&:payed)&.all? ), remote: true, method: :patch, data: { confirm: I18n.t('ui.confirm_mark_all', name: "payed" ) } do
|
||||
= check_box_tag :payed, '1', order.group_orders.map(&:group_order_invoice).compact.map(&:payed)&.all? , class: 'form-check-input'
|
|
@ -0,0 +1,2 @@
|
|||
= link_to toggle_all_sepa_downloaded_group_order_invoices_path(order_id: order.id, sepa_downloaded: order.group_orders.map(&:group_order_invoice).compact.map(&:sepa_downloaded)&.all? ), remote: true, method: :patch, data: { confirm: I18n.t('ui.confirm_mark_all', name: "sepa_downloaded" ) } do
|
||||
= check_box_tag :sepa_downloaded, '1', order.group_orders.map(&:group_order_invoice).compact.map(&:sepa_downloaded)&.all? , class: 'form-check-input'
|
2
app/views/group_order_invoices/_toggle_payed.html.haml
Normal file
2
app/views/group_order_invoices/_toggle_payed.html.haml
Normal file
|
@ -0,0 +1,2 @@
|
|||
= link_to toggle_payed_group_order_invoice_path(group_order_invoice), remote: true, method: :patch, data: { turbolinks: false } do
|
||||
= check_box_tag 'payed', '1', group_order_invoice.payed , class: 'form-check-input', id: "payed_#{group_order_invoice.id}"
|
|
@ -0,0 +1,2 @@
|
|||
= link_to toggle_sepa_downloaded_group_order_invoice_path(group_order_invoice), remote: true, method: :patch do
|
||||
= check_box_tag 'sepa_downloaded', '1', group_order_invoice.sepa_downloaded , class: 'form-check-input', id: "sepa_downloaded_#{group_order_invoice.id}"
|
|
@ -1 +1 @@
|
|||
$("#generate-invoice<%= params[:id] %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
||||
$("#order_<%= @order.id %>_modal").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
||||
|
|
|
@ -1 +1 @@
|
|||
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
||||
$("#order_<%= @order.id %>_modal").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
||||
|
|
|
@ -1 +1 @@
|
|||
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
||||
$("#order_<%= @order.id %>_modal").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
|
@ -0,0 +1 @@
|
|||
$("#order_<%= @order.id %>_modal").html("<%= escape_javascript(render partial: 'modal', locals: { order: @order, sequence_type: @sequence_type }) %>");
|
|
@ -0,0 +1 @@
|
|||
$("#select_sepa_sequence_type_<%= @group_order_invoice.id %>").html("<%= j(render partial: 'select_sepa_sequence_type', locals: {group_order_invoice: @group_order_invoice}) %>");
|
4
app/views/group_order_invoices/toggle_all_payed.js.erb
Normal file
4
app/views/group_order_invoices/toggle_all_payed.js.erb
Normal file
|
@ -0,0 +1,4 @@
|
|||
<% @group_order_invoices.each do |group_order_invoice| %>
|
||||
$("#payed_<%= group_order_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_payed', locals: { group_order_invoice: group_order_invoice }) %>");
|
||||
<% end %>
|
||||
$("#toggle_all_payed_<%= @order.id %>").html("<%= escape_javascript(render partial: 'toggle_all_payed', locals: { order: @order }) %>");
|
|
@ -0,0 +1,4 @@
|
|||
<% @group_order_invoices.each do |group_order_invoice| %>
|
||||
$("#sepa_downloaded_<%= group_order_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_sepa_downloaded', locals: { group_order_invoice: group_order_invoice }) %>");
|
||||
<% end %>
|
||||
$("#toggle_all_sepa_downloaded_<%= @order.id %>").html("<%= escape_javascript(render partial: 'toggle_all_sepa_downloaded', locals: { order: @order }) %>");
|
1
app/views/group_order_invoices/toggle_payed.js.erb
Normal file
1
app/views/group_order_invoices/toggle_payed.js.erb
Normal file
|
@ -0,0 +1 @@
|
|||
$("#payed_<%= @group_order_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_payed', locals: {group_order_invoice: @group_order_invoice}) %>");
|
|
@ -0,0 +1 @@
|
|||
$("#sepa_downloaded_<%= @group_order_invoice.id %>").html("<%= escape_javascript(render partial: 'toggle_sepa_downloaded', locals: {group_order_invoice: @group_order_invoice}) %>");
|
|
@ -174,7 +174,10 @@
|
|||
%strong
|
||||
%span#new_balance= number_to_currency(old_balance - @group_order.price)
|
||||
#order-button
|
||||
= submit_tag( t('.action_save'), id: 'submit_button', class: 'btn btn-primary' )
|
||||
- if FoodsoftConfig[:group_order_invoices]&.[](:ignore_minimum_balance)
|
||||
= submit_tag( t('.action_save'), id: 'submit_sepa_button', class: 'btn btn-primary' )
|
||||
- else
|
||||
= submit_tag( t('.action_save'), id: 'submit_button', class: 'btn btn-primary' )
|
||||
#{link_to t('ui.or_cancel'), group_orders_path}
|
||||
%input#total_balance{name: "total_balance", type: "hidden", value: @ordergroup.account_balance - @group_order.price}/
|
||||
%input{name: "version", type: "hidden", value: @version}/
|
||||
|
|
1
app/views/orders/collective_direct_debit.js.erb
Normal file
1
app/views/orders/collective_direct_debit.js.erb
Normal file
|
@ -0,0 +1 @@
|
|||
$("#order_<%= @order.id %>_modal").html("<%= escape_javascript(render partial: 'group_order_invoices/links', locals: {order: @order}) %>");
|
2
app/views/orders/render_modal.js.erb
Normal file
2
app/views/orders/render_modal.js.erb
Normal file
|
@ -0,0 +1,2 @@
|
|||
$('#modalContainer').html('<%= j(render("group_order_invoices/modal", order: @order)) %>');
|
||||
$('#modalContainer').modal();
|
20
app/views/shared/_sepa_account_holder.html.haml
Normal file
20
app/views/shared/_sepa_account_holder.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
|||
= f.simple_fields_for :sepa_account_holder, @ordergroup.sepa_account_holder || @ordergroup.build_sepa_account_holder do |sepa_f|
|
||||
= sepa_f.input :user_id, collection: f.object.users.map { |user| [user.name, user.id, { 'data-iban' => user.iban, 'data-bic' => user.bic }] }, selected: sepa_f.object.user_id, as: :select, label: 'SEPA Account Holder', include_blank: true, input_html: { id: 'user_id_select' }
|
||||
= sepa_f.hidden_field :group_id, value: @ordergroup.id
|
||||
= sepa_f.input :iban
|
||||
= sepa_f.input :bic
|
||||
= sepa_f.input :mandate_id
|
||||
= sepa_f.input :mandate_date_of_signature, as: :date_picker
|
||||
|
||||
- content_for :javascript do
|
||||
:javascript
|
||||
$(document).ready(function() {
|
||||
$('#user_id_select').on('change', function() {
|
||||
var selectedOption = $(this).find('option:selected');
|
||||
var iban = selectedOption.data('iban');
|
||||
var bic = selectedOption.data('bic');
|
||||
|
||||
$('#ordergroup_sepa_account_holder_attributes_iban').val(iban || ''); // Update the IBAN input field
|
||||
$('#ordergroup_sepa_account_holder_attributes_bic').val(bic || ''); // Update the BIC input field
|
||||
});
|
||||
});
|
|
@ -12,6 +12,7 @@
|
|||
= f.input :phone
|
||||
- if FoodsoftConfig[:use_iban]
|
||||
= f.input :iban
|
||||
= f.input :bic
|
||||
|
||||
- if local_assigns[:with_address] && (f.object.ordergroup || f.object.new_record?)
|
||||
= f.fields_for [:ordergroup, f.object.ordergroup || Ordergroup.new] do |ogf|
|
||||
|
|
|
@ -92,13 +92,26 @@ de:
|
|||
unit_price: Preis/Einheit
|
||||
group_order_invoice:
|
||||
name: Bestellgruppenrechnung
|
||||
sequence_type:
|
||||
FRST: "Erst-Lastschrift"
|
||||
RCUR: "Folge-Lastschrift"
|
||||
OOFF: "Einmalige Lastschrift"
|
||||
FNAL: "Letztmalige Lastschrift"
|
||||
links:
|
||||
actions_for_all: Aktion für alle ausführen
|
||||
delete: Rechnung löschen
|
||||
download: Rechnung herunterladen
|
||||
generate: Rechnung erzeugen
|
||||
invoice_date: Datum der Bestellgruppenrechnung
|
||||
generate_with_date: setzen & erzeugen
|
||||
download_all_zip: Alle Rechnungen herunterladen (zip)
|
||||
generate: Rechnung erzeugen
|
||||
generate_with_date: setzen & erzeugen
|
||||
invoice_date: Datum der Bestellgruppenrechnung
|
||||
ordergroup: Bestellgruppe
|
||||
payed: Bezahlt
|
||||
sepa_downloaded: SEPA exportiert
|
||||
sepa_not_ready: 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
|
||||
payment_method: Guthaben
|
||||
tax_number_not_set: Steuernummer in den Einstellungen nicht gesetzt
|
||||
invoice:
|
||||
|
@ -392,6 +405,8 @@ de:
|
|||
confirm: Bist Du sicher?
|
||||
edit: Gruppe/Mitglieder bearbeiten
|
||||
title: Bestellgruppe %{name}
|
||||
update:
|
||||
notice: Bestellgruppe wurde aktualisiert.
|
||||
search_placeholder: Name ...
|
||||
users:
|
||||
controller:
|
||||
|
@ -617,9 +632,13 @@ de:
|
|||
email_replyto: Setze diese Adresse, wenn Du Antworten auf Foodsoft E-Mails auf eine andere, als die oben angegebene Absenderadresse bekommen möchtest.
|
||||
email_sender: E-Mails werden so aussehen, als ob sie von dieser Adresse versendet wurden. Um zu vermeiden, dass E-Mails dadurch als Spam eingeordnet werden, muss der Webserver möglicherweise im SPF Eintrag der Domain der E-Mail Adresse eingetragen werden.
|
||||
group_order_invoices:
|
||||
use_automativ_go_invoices: Es werden auf die Bestellgruppen zugeschnittene Rechnungen für die jeweilige Bestellung beim Klicken auf "abrechnen" an alle Bestellgruppenmitglieder per Mail versendet.
|
||||
ignore_minimum_balance: Mitglieder können auch bestellen, wenn ihr Kontostand unter dem Minimum liegt. (Wichtig, wenn SEPA Lastschrift als Zahlart verwendet wird)
|
||||
use_automatic_go_invoices: Es werden auf die Bestellgruppen zugeschnittene Rechnungen für die jeweilige Bestellung beim Klicken auf "abrechnen" an alle Bestellgruppenmitglieder per Mail versendet.
|
||||
payment_method: Zahlungsart wird auf der Bestellgruppenrechnung deklariert
|
||||
vat_exempt: Eine Auflistung der Rechnungsartikel erfolgt ohne explizite Ausweisung der MwSt. und die Rechnung erhält den notwendigen Zusatz bzgl. der Kleinunternehmerregelung §19 (FoodCoop Marge ebenfalls nicht in Rechnung enthalten)
|
||||
iban: IBAN ohne Leerzeichen angeben
|
||||
bic: BIC ohne Leerzeichen angeben
|
||||
creditor_identifier: Gläubiger-ID ohne Leerzeichen angeben
|
||||
help_url: Link zur Dokumentationsseite
|
||||
homepage: Webseite der Foodcoop
|
||||
ignore_browser_locale: Ignoriere die Sprache des Computers des Anwenders, wenn der Anwender noch keine Sprache gewählt hat.
|
||||
|
@ -679,10 +698,14 @@ de:
|
|||
email_replyto: Antwortadresse
|
||||
email_sender: Senderadresse
|
||||
group_order_invoices:
|
||||
ignore_minimum_balance: Mindestkontostand ignorieren
|
||||
use_automatic_invoices: Automatisch bei Abrechnung per Mail versenden
|
||||
separate_deposits: Pfand getrennt abrechnen
|
||||
payment_method: Zahlungsart
|
||||
vat_exempt: Diese Foodcoop ist MwSt. befreit
|
||||
iban: IBAN
|
||||
bic: BIC
|
||||
creditor_identifier: Gläubiger-ID
|
||||
help_url: URL Dokumentation
|
||||
homepage: Webseite
|
||||
ignore_browser_locale: Browsersprache ignorieren
|
||||
|
@ -1557,6 +1580,8 @@ de:
|
|||
units_ordered: Bestellte Einheiten
|
||||
create:
|
||||
notice: Die Bestellung wurde erstellt.
|
||||
collective_direct_debit:
|
||||
alert: Wichtige Daten für eine SEPA Lastschrift bei %{ordergroup_names} fehlen.
|
||||
edit:
|
||||
title: 'Bestellung bearbeiten: %{name}'
|
||||
edit_amount:
|
||||
|
|
|
@ -92,14 +92,26 @@ en:
|
|||
unit_price: Price/Unit
|
||||
group_order_invoice:
|
||||
name: Group order invoice
|
||||
sequence_type:
|
||||
FRST: "First Direct Debit"
|
||||
RCUR: "Recurring Direct Debit"
|
||||
OOFF: "One-time Direct Debit"
|
||||
FNAL: "Final Direct Debit"
|
||||
links:
|
||||
actions_for_all: Actions for all group orders
|
||||
delete: delete invoice
|
||||
download: download invoice
|
||||
invoice_date: date of group order invoice
|
||||
download_all_zip: download all invoices as zip
|
||||
generate: generate invoice
|
||||
generate_with_date: set & generate
|
||||
download_all_zip: download all invoices as zip
|
||||
|
||||
invoice_date: date of group order invoice
|
||||
ordergroup: Ordergroup
|
||||
payed: payed
|
||||
sepa_downloaded: SEPA exported
|
||||
sepa_not_ready: Configurations for SEPA are missing in Admin->Settings->Finances
|
||||
sepa_select: mark for SEPA export
|
||||
sepa_sequence_type: SEPA type
|
||||
open_details_modal: Toggle details
|
||||
payment_method: Credit
|
||||
tax_number_not_set: Tax number not set in configs
|
||||
invoice:
|
||||
|
@ -1954,6 +1966,7 @@ en:
|
|||
cancel: Cancel
|
||||
close: Close
|
||||
confirm_delete: Do you really want to delete %{name}?
|
||||
confirm_mark_all: Do you want to mark all as %{name}?
|
||||
confirm_restore: Do you really want to restore %{name}?
|
||||
copy: Copy
|
||||
delete: Delete
|
||||
|
|
|
@ -48,8 +48,12 @@ Rails.application.routes.draw do
|
|||
get :receive
|
||||
post :receive
|
||||
|
||||
get :render_modal
|
||||
|
||||
get :receive_on_order_article_create
|
||||
get :receive_on_order_article_update
|
||||
|
||||
get :collective_direct_debit
|
||||
end
|
||||
|
||||
resources :order_articles
|
||||
|
@ -148,7 +152,18 @@ Rails.application.routes.draw do
|
|||
|
||||
get 'orders/:order_id/group_order_invoices/download_all', to: 'group_order_invoices#download_all', as: 'download_all_group_order_invoices'
|
||||
|
||||
resources :group_order_invoices
|
||||
resources :group_order_invoices do
|
||||
member do
|
||||
patch :select_sepa_sequence_type
|
||||
patch :toggle_payed
|
||||
patch :toggle_sepa_downloaded
|
||||
end
|
||||
collection do
|
||||
patch :select_all_sepa_sequence_type
|
||||
patch :toggle_all_sepa_downloaded
|
||||
patch :toggle_all_payed
|
||||
end
|
||||
end
|
||||
|
||||
resources :article_categories
|
||||
|
||||
|
@ -171,6 +186,8 @@ Rails.application.routes.draw do
|
|||
|
||||
get :new_on_order_article_create
|
||||
get :new_on_order_article_update
|
||||
|
||||
get :collective_direct_debit
|
||||
end
|
||||
|
||||
post :close_all_direct_with_invoice, on: :collection
|
||||
|
|
14
db/migrate/20231103110755_create_sepa_account_holders.rb
Normal file
14
db/migrate/20231103110755_create_sepa_account_holders.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
class CreateSepaAccountHolders < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :sepa_account_holders do |t|
|
||||
t.references :user, null: false
|
||||
t.references :group, null: false
|
||||
|
||||
t.string :iban
|
||||
t.string :bic
|
||||
t.string :mandate_id
|
||||
t.date :mandate_date_of_signature
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
5
db/migrate/20231109125021_add_bic_to_users.rb
Normal file
5
db/migrate/20231109125021_add_bic_to_users.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
class AddBicToUsers < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :users, :bic, :string
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
class AddPayedToGroupOrderInvoices < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :group_order_invoices, :payed, :boolean, default: false
|
||||
add_column :group_order_invoices, :sepa_downloaded, :boolean, default: false
|
||||
add_column :group_order_invoices, :sepa_sequence_type, :string, default: 'RCUR'
|
||||
end
|
||||
end
|
19
db/schema.rb
19
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: 2023_08_22_120005) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_11_10_131415) do
|
||||
create_table "action_text_rich_texts", charset: "utf8mb4", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.text "body", size: :long
|
||||
|
@ -199,6 +199,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_22_120005) do
|
|||
t.string "payment_method"
|
||||
t.datetime "created_at", precision: nil, null: false
|
||||
t.datetime "updated_at", precision: nil, null: false
|
||||
t.boolean "payed", default: false
|
||||
t.boolean "sepa_downloaded", default: false
|
||||
t.string "sepa_sequence_type", default: "RCUR"
|
||||
t.index ["group_order_id"], name: "index_group_order_invoices_on_group_order_id", unique: true
|
||||
end
|
||||
|
||||
|
@ -475,6 +478,19 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_22_120005) do
|
|||
t.index ["finished_at"], name: "index_printer_jobs_on_finished_at"
|
||||
end
|
||||
|
||||
create_table "sepa_account_holders", charset: "utf8mb4", force: :cascade do |t|
|
||||
t.bigint "user_id", null: false
|
||||
t.bigint "group_id", null: false
|
||||
t.string "iban"
|
||||
t.string "bic"
|
||||
t.string "mandate_id"
|
||||
t.date "mandate_date_of_signature"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["group_id"], name: "index_sepa_account_holders_on_group_id"
|
||||
t.index ["user_id"], name: "index_sepa_account_holders_on_user_id"
|
||||
end
|
||||
|
||||
create_table "settings", id: :integer, charset: "utf8mb4", force: :cascade do |t|
|
||||
t.string "var", null: false
|
||||
t.text "value"
|
||||
|
@ -566,6 +582,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_22_120005) do
|
|||
t.datetime "last_activity", precision: nil
|
||||
t.datetime "deleted_at", precision: nil
|
||||
t.string "iban"
|
||||
t.string "bic"
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
t.index ["nick"], name: "index_users_on_nick", unique: true
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require_relative '../spec_helper'
|
||||
|
||||
feature GroupOrderInvoice, js: true do
|
||||
feature GroupOrderInvoice, type: :feature, js: true do
|
||||
let(:admin) { create :user, groups: [create(:workgroup, role_finance: true)] }
|
||||
let(:user) { create :user, groups: [create(:ordergroup)] }
|
||||
let(:article) { create :article, unit_quantity: 1 }
|
||||
|
@ -35,7 +35,7 @@ feature GroupOrderInvoice, js: true do
|
|||
click_link_or_button I18n.t('finance.balancing.confirm.clear')
|
||||
expect(NotifyGroupOrderInvoiceJob).to have_been_enqueued
|
||||
end
|
||||
|
||||
|
||||
it 'generates Group Order Invoice when order is closed if tax_number is set' do
|
||||
goa.update_quantities 2, 0
|
||||
oa.update_results!
|
||||
|
@ -44,6 +44,7 @@ feature GroupOrderInvoice, js: true do
|
|||
go.reload
|
||||
order.reload
|
||||
visit finance_order_index_path
|
||||
click_link_or_button 'Toggle details'
|
||||
expect(page).to have_selector(:link_or_button, I18n.t('activerecord.attributes.group_order_invoice.links.generate'))
|
||||
click_link_or_button I18n.t('activerecord.attributes.group_order_invoice.links.generate')
|
||||
expect(GroupOrderInvoice.all.count).to eq(1)
|
||||
|
@ -56,7 +57,10 @@ feature GroupOrderInvoice, js: true do
|
|||
order.update!(state: 'closed')
|
||||
order.reload
|
||||
visit finance_order_index_path
|
||||
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')
|
||||
expect(GroupOrderInvoice.all.count).to eq(1)
|
||||
end
|
||||
|
@ -69,4 +73,35 @@ feature GroupOrderInvoice, js: true do
|
|||
visit finance_order_index_path
|
||||
expect(page).to have_content(I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set'))
|
||||
end
|
||||
|
||||
it 'shows info message instead of sepa download link', js: true do
|
||||
goa.update_quantities 2, 0
|
||||
oa.update_results!
|
||||
FoodsoftConfig[:group_order_invoices] = { use_automatic_invoices: true }
|
||||
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
||||
order.update!(state: 'closed')
|
||||
order.reload
|
||||
visit finance_order_index_path
|
||||
sleep 2
|
||||
click_link_or_button 'Toggle details'
|
||||
click_link_or_button I18n.t('activerecord.attributes.group_order_invoice.links.generate')
|
||||
expect(page).to have_content(I18n.t('activerecord.attributes.group_order_invoice.links.sepa_not_ready'))
|
||||
end
|
||||
|
||||
it 'shows sepa download link', js: true do
|
||||
goa.update_quantities 2, 0
|
||||
oa.update_results!
|
||||
FoodsoftConfig[:group_order_invoices] = { use_automatic_invoices: true, bic: "a", iban: "b", creditor_identifier: "c" }
|
||||
FoodsoftConfig[:contact][:tax_number]= 12_345_678
|
||||
FoodsoftConfig[:name] = "Foodsoft"
|
||||
order.update!(state: 'closed')
|
||||
order.reload
|
||||
visit finance_order_index_path
|
||||
sleep 2
|
||||
click_link_or_button 'Toggle details'
|
||||
click_link_or_button I18n.t('activerecord.attributes.group_order_invoice.links.generate')
|
||||
sleep 1
|
||||
expect(page).to have_selector(:link_or_button, "collective-direct-debit-link-#{order.id}")
|
||||
expect(page).to have_selector(".ajax-update-all-link-#{order.id}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -51,6 +51,8 @@ RSpec.configure do |config|
|
|||
# --seed 1234
|
||||
config.order = 'random'
|
||||
|
||||
config.include SepaHelper
|
||||
|
||||
config.include SpecTestHelper, type: :controller
|
||||
config.include SessionHelper, type: :feature
|
||||
# Automatically determine spec from directory structure, see:
|
||||
|
|
Loading…
Reference in a new issue