merge automatic group order invoice generation

see https://github.com/foodcoops/foodsoft/pull/907 for reference
and original work by viehlieb

Co-authored-by: viehlieb <pf@pragma-shift.net>

fix PDF Pdf

make explicit deposit in invoices work

add ordergroupname to invoice file name

mark bold sum for vat exempt foodcoops

download multiple group order invoice as zip
This commit is contained in:
Philipp Rothmann 2023-07-24 10:50:35 +02:00 committed by viehlieb
parent 6abf998b56
commit 93143c28f2
37 changed files with 988 additions and 69 deletions

View file

@ -241,6 +241,9 @@ table {
tr.order-article:hover .article-info {
display: none;
}
tr.order-article:focus .article-info {
display: none;
}
}
#order-footer {
@ -275,11 +278,13 @@ tr.order-article .article-info {
display: none;
}
tr.order-article:hover .article-info {
tr.order-article:focus{
background-color: #E4EED6;
}
tr.order-article:focus .article-info {
display: block;
}
// ********* Articles
tr.just-updated {

View file

@ -0,0 +1,17 @@
module Concerns::SendGroupOrderInvoicePdf
extend ActiveSupport::Concern
protected
def create_invoice_pdf(group_order_invoice)
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
GroupOrderInvoicePdf.new invoice_data
end
def send_group_order_invoice_pdf(group_order_invoice)
pdf = create_invoice_pdf(group_order_invoice)
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
end
end

View file

@ -5,7 +5,7 @@ class Finance::BalancingController < Finance::BaseController
def new
@order = Order.find(params[:order_id])
flash.now.alert = t('.alert') if @order.closed?
flash.now.alert = t('finance.balancing.new.alert') if @order.closed? && flash[:alert].blank?
@comments = @order.comments
@articles = @order.order_articles.ordered_or_member.includes(:article, :article_price,
@ -81,9 +81,24 @@ class Finance::BalancingController < Finance::BaseController
@order = Order.find(params[:id])
@type = FinancialTransactionType.find_by_id(params.permit(:type)[:type])
@order.close!(@current_user, @type)
redirect_to finance_order_index_url, notice: t('.notice')
rescue StandardError => e
redirect_to new_finance_order_url(order_id: @order.id), alert: t('.alert', message: e.message)
note = t('finance.balancing.close.notice')
if @order.closed?
alert = t('finance.balancing.close.alert')
if FoodsoftConfig[:group_order_invoices]&.[](:use_automatic_invoices)
@order.group_orders.each do |go|
alert = t('finance.balancing.close.settings_not_set')
goi = GroupOrderInvoice.find_or_create_by!(group_order_id: go.id)
if goi.save!
NotifyGroupOrderInvoiceJob.perform_later(goi)
note = t('finance.balancing.close.notice_mail')
end
end
end
end
alert ||= t('finance.balancing.close.alert')
redirect_to finance_order_index_url, notice: note
rescue => error
redirect_to new_finance_order_url(order_id: @order.id), notice: note, alert: alert, msg: error.message
end
# Close the order directly, without automaticly updating ordergroups account balances

View file

@ -0,0 +1,87 @@
class GroupOrderInvoicesController < ApplicationController
include Concerns::SendGroupOrderInvoicePdf
before_action :authenticate_finance
def show
begin
@group_order_invoice = GroupOrderInvoice.find(params[:id])
if FoodsoftConfig[:contact][:tax_number]
respond_to do |format|
format.pdf do
send_group_order_invoice_pdf @group_order_invoice if FoodsoftConfig[:contact][:tax_number]
end
end
else
raise RecordInvalid
end
rescue ActiveRecord::RecordInvalid => error
redirect_back fallback_location: root_path, notice: 'Something went wrong', alert: I18n.t('errors.general_msg', msg: "#{error} " + I18n.t('errors.check_tax_number'))
end
end
def destroy
goi = GroupOrderInvoice.find(params[:id])
@order = goi.group_order.order
goi.destroy
respond_to do |format|
format.js
format.json { head :no_content }
end
end
def create_multiple
invoice_date = params[:group_order_invoice][:invoice_date]
order_id = params[:group_order_invoice][:order_id]
@order = Order.find(order_id)
gos = GroupOrder.where("order_id = ?", order_id)
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(1)
goi.save!
end
respond_to do |format|
format.js
end
end
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
end
redirect_back fallback_location: root_path
rescue => error
redirect_back fallback_location: root_path, notice: 'Something went wrong', :alert => I18n.t('errors.general_msg', :msg => error)
end
def download_all
order = Order.find(params[:order_id])
invoices = order.group_orders.map(&:group_order_invoice)
pdf = {}
temp_file = Tempfile.new("all_invoices_for_order_#{order.id}.zip")
Zip::File.open(temp_file.path, Zip::File::CREATE) do |zipfile|
invoices.each do |invoice|
pdf = create_invoice_pdf(invoice)
invoice_file = Tempfile.new("#{pdf.filename}")
File.open(invoice_file.path, 'w:ASCII-8BIT') do |file|
file.write(pdf.to_pdf)
end
zipfile.add("#{pdf.filename}", invoice_file.path) unless zipfile.find_entry("#{pdf.filename}")
end
end
zip_data = File.read(temp_file.path)
respond_to do |format|
format.html {
send_data(zip_data, type: 'application/zip', filename: "#{l order.ends, format: :file}-#{order.supplier.name}-#{order.id}.zip", disposition: 'attachment')
}
end
end
end

View file

@ -0,0 +1,264 @@
class GroupOrderInvoicePdf < RenderPdf
def filename
ordergroup_name = @options[:ordergroup].name || "OrderGroup"
"#{ordergroup_name}_" + I18n.t('documents.group_order_invoice_pdf.filename', :number => @options[:invoice_number]) + '.pdf'
end
def title
I18n.t('documents.group_order_invoice_pdf.title', :supplier => @options[:supplier])
end
def body
contact = FoodsoftConfig[:contact].symbolize_keys
ordergroup = @options[:ordergroup]
# From paragraph
bounding_box [margin_box.right - 200, margin_box.top - 20], width: 200 do
text I18n.t('documents.group_order_invoice_pdf.invoicer')
move_down 7
text FoodsoftConfig[:name], size: fontsize(9), align: :left
move_down 5
text contact[:street], size: fontsize(9), align: :left
move_down 5
text "#{contact[:zip_code]} #{contact[:city]}", size: fontsize(9), align: :left
move_down 5
if contact[:phone].present?
text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :left
move_down 5
end
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :left if contact[:email].present?
move_down 5
text I18n.t('documents.group_order_invoice_pdf.tax_number', :number => @options[:tax_number]), size: fontsize(9), align: :left
end
# Receiving Ordergroup
bounding_box [margin_box.left, margin_box.top - 20], width: 200 do
text I18n.t('documents.group_order_invoice_pdf.invoicee')
move_down 7
text I18n.t('documents.group_order_invoice_pdf.ordergroup.name', ordergroup: ordergroup.name.to_s), size: fontsize(9)
move_down 5
if ordergroup.contact_address.present?
text I18n.t('documents.group_order_invoice_pdf.ordergroup.contact_address', contact_address: ordergroup.contact_address.to_s), size: fontsize(9)
move_down 5
end
if ordergroup.contact_phone.present?
text I18n.t('documents.group_order_invoice_pdf.ordergroup.contact_phone', contact_phone: ordergroup.contact_phone.to_s), size: fontsize(9)
move_down 5
end
if ordergroup.customer_number.present?
text I18n.t('documents.group_order_invoice_pdf.ordergroup.customer_number', customer_number: ordergroup.customer_number.to_s), size: fontsize(9)
move_down 5
end
end
# invoice Date and nnvoice number
bounding_box [margin_box.right - 200, margin_box.top - 150], width: 200 do
text I18n.t('documents.group_order_invoice_pdf.invoice_date', invoice_date: @options[:invoice_date].strftime(I18n.t('date.formats.default'))), align: :left
move_down 5
text I18n.t('documents.group_order_invoice_pdf.invoice_number', invoice_number: @options[:invoice_number]), align: :left
end
move_down 15
# kind of the "body" of the invoice
text I18n.t('documents.group_order_invoice_pdf.payment_method', payment_method: @options[:payment_method])
move_down 15
text I18n.t('documents.group_order_invoice_pdf.table_headline')
move_down 5
#------------- Table Data -----------------------
@group_order = GroupOrder.find(@options[:group_order].id)
if FoodsoftConfig[:group_order_invoices][:vat_exempt]
body_for_vat_exempt
else
body_with_vat
end
end
def body_for_vat_exempt
total_gross = 0
data = [I18n.t('documents.group_order_invoice_pdf.vat_exempt_rows')]
move_down 10
group_order_articles = GroupOrderArticle.where(group_order_id: @group_order.id)
separate_deposits = FoodsoftConfig[:group_order_invoices]&.[](:separate_deposits)
group_order_articles.each do |goa|
# if no unit is received, nothing is to be charged
next if goa.result.to_i == 0
goa_total_price = separate_deposits ? goa.total_price_without_deposit : goa.total_price
data << [goa.order_article.article.name,
goa.result.to_i,
number_to_currency(goa.order_article.price.fc_price_without_deposit),
number_to_currency(goa_total_price)]
total_gross += goa_total_price
next unless separate_deposits && goa.order_article.price.deposit > 0.0
goa_total_deposit = goa.result * goa.order_article.price.fc_deposit_price
data << ["zzgl. Pfand",
goa.result.to_i,
number_to_currency(goa.order_article.article.fc_deposit_price),
number_to_currency(goa_total_deposit)]
total_gross += goa_total_deposit
end
table data, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
table.header = true
table.position = :center
table.cells.border_width = 1
table.cells.border_color = '666666'
table.row(0).column(0..4).width = 80
table.row(0).border_bottom_width = 2
table.columns(1).align = :right
table.columns(1..6).align = :right
end
move_down 5
sum = []
sum << [nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay_gross'), number_to_currency(total_gross)]
# table for sum
table sum, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
table.header = true
table.position = :center
table.cells.border_width = 1
table.cells.border_color = '666666'
table.row(0).columns(2..4).style(align: :bottom)
table.row(0).border_bottom_width = 2
table.row(0..-1).columns(0..1).border_width = 0
table.rows(0..-1).columns(0..4).width = 80
table.row(0).column(-1).style(font_style: :bold)
table.row(0).column(-2).style(font_style: :bold)
table.row(0).column(-1).size = fontsize(10)
table.row(0).column(-2).size = fontsize(10)
table.columns(1).align = :right
table.columns(1..6).align = :right
end
move_down 25
text I18n.t('documents.group_order_invoice_pdf.small_business_regulation')
move_down 10
end
def body_with_vat
separate_deposits = FoodsoftConfig[:group_order_invoices]&.[](:separate_deposits)
total_gross = 0
total_net = 0
# Articles
tax_hash_net = Hash.new(0) # for summing up article net prices grouped into vat percentage
tax_hash_gross = Hash.new(0) # same here with gross prices
if separate_deposits
total_deposit = 0
total_deposit_gross = 0
tax_hash_deposit_gross = Hash.new(0) # for summing up deposit gross prices grouped into vat percentage
tax_hash_deposit_net = Hash.new(0) # same here with gross prices
end
marge = FoodsoftConfig[:price_markup]
# data table looks different when price_markup > 0
data = if marge == 0
[I18n.t('documents.group_order_invoice_pdf.no_price_markup_rows')]
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.each do |tax, group_order_articles|
group_order_articles.each do |goa|
# if no unit is received, nothing is to be charged
next if goa.result.to_i == 0
order_article = goa.order_article
goa_total_net = goa.result * order_article.price.price
goa_total_gross = separate_deposits ? goa.total_price_without_deposit : goa.total_price
data << [order_article.article.name,
goa.result.to_i,
number_to_currency(order_article.price.price),
number_to_currency(goa_total_net),
tax.to_s + '%',
number_to_currency(goa_total_gross)]
if separate_deposits && order_article.price.deposit > 0.0
goa_deposit = goa.result * order_article.price.deposit
goa_total_deposit = goa.result * order_article.price.fc_deposit_price
data << ["zzgl. Pfand",
goa.result.to_i,
number_to_currency(order_article.price.deposit),
number_to_currency(goa_deposit),
tax.to_s + '%',
number_to_currency(goa_total_deposit)]
total_deposit += goa_deposit
total_deposit_gross += goa_total_deposit
tax_hash_deposit_net[tax.to_i] += goa_deposit
tax_hash_deposit_gross[tax.to_i] += goa_total_deposit
end
tax_hash_net[tax.to_i] += goa_total_net
tax_hash_gross[tax.to_i] += goa_total_gross
total_net += goa_total_net
total_gross += goa_total_gross
end
end
# Two separate tables for sum and individual data
# article information + data
table data, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
table.header = true
table.position = :center
table.cells.border_width = 1
table.cells.border_color = '666666'
table.row(0).columns(0..6).style(background_color: 'cccccc', font_style: :bold)
table.rows(0..-1).columns(0..6).width = 80
table.row(0).border_bottom_width = 2
table.columns(1).align = :right
table.columns(1..6).align = :right
end
sum = [[nil, nil, nil, "Netto", "MwSt", "Brutto"]]
[7, 19].each do |key|
sum << [nil, nil, "Produkte mit #{key}%", number_to_currency(tax_hash_net[key]), number_to_currency(tax_hash_gross[key] - tax_hash_net[key]), number_to_currency(tax_hash_gross[key])]
sum << [nil, nil, "Pfand mit #{key}%", number_to_currency(tax_hash_deposit_net[key]), number_to_currency(tax_hash_deposit_gross[key] - tax_hash_deposit_net[key]), number_to_currency(tax_hash_deposit_gross[key])] if separate_deposits
end
total_deposit_gross ||= 0
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay_gross'), number_to_currency(total_gross + total_deposit_gross)]
move_down 10
table sum, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
table.header = true
table.position = :center
table.cells.border_width = 1
table.cells.border_color = '666666'
table.row(0).columns(2..6).style(align: :bottom)
table.row(0).border_bottom_width = 2
table.row(0..-1).columns(0..1).border_width = 0
table.rows(0..-1).columns(0..6).width = 80
table.row(-1).column(-1).style(font_style: :bold)
table.row(-1).column(-2).style(font_style: :bold)
table.row(-1).column(-1).size = fontsize(10)
table.row(-1).column(-2).size = fontsize(10)
table.columns(1).align = :right
table.columns(1..6).align = :right
end
if FoodsoftConfig[:group_order_invoices][:vat_exempt]
move_down 15
text I18n.t('documents.group_order_invoice_pdf.small_business_regulation')
end
move_down 10
end
end

View file

@ -0,0 +1,10 @@
class NotifyGroupOrderInvoiceJob < ApplicationJob
def perform(group_order_invoice)
ordergroup = group_order_invoice.group_order.ordergroup
ordergroup.users.each do |user|
Mailer.deliver_now_with_user_locale user do
Mailer.group_order_invoice(group_order_invoice, user)
end
end
end
end

View file

@ -70,7 +70,7 @@ class RenderPdf < Prawn::Document
options[:skip_page_creation] = true
@options = options
@first_page = true
no_footer = @options&.[](:no_footer) ? true : false
super(options)
# Use ttf for better utf-8 compability
@ -84,11 +84,11 @@ class RenderPdf < Prawn::Document
)
header = options[:title] || title
footer = I18n.l(Time.now, format: :long)
footer = I18n.l(Time.now, format: :long) unless no_footer
header_size = 0
header_size = height_of(header, size: HEADER_FONT_SIZE, font: DEFAULT_FONT) + HEADER_SPACE if header
footer_size = height_of(footer, size: FOOTER_FONT_SIZE, font: DEFAULT_FONT) + FOOTER_SPACE
footer_size = no_footer ? 0 : height_of(footer, size: FOOTER_FONT_SIZE, font: DEFAULT_FONT) + FOOTER_SPACE
start_new_page(top_margin: TOP_MARGIN + header_size, bottom_margin: BOTTOM_MARGIN + footer_size)
@ -98,12 +98,15 @@ class RenderPdf < Prawn::Document
bounding_box [bounds.left, bounds.top + header_size], width: bounds.width, height: header_size do
text header, size: HEADER_FONT_SIZE, align: :center, overflow: :shrink_to_fit if header
end
font_size FOOTER_FONT_SIZE do
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
text footer, align: :left, valign: :bottom
end
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
text I18n.t('lib.render_pdf.page', number: page_number, count: page_count), align: :right, valign: :bottom
unless no_footer
font_size FOOTER_FONT_SIZE do
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
text footer, align: :left, valign: :bottom
end
bounding_box [bounds.left, bounds.bottom - FOOTER_SPACE], width: bounds.width, height: footer_size do
text I18n.t('lib.render_pdf.page', number: page_number, count: page_count), align: :right, valign: :bottom
end
end
end
end

View file

@ -51,6 +51,18 @@ class Mailer < ActionMailer::Base
subject: I18n.t('mailer.welcome.subject')
end
# Sends automatically generated invoicesfor group orders to ordergroup members
def group_order_invoice(group_order_invoice, user)
@user = user
@group_order_invoice = group_order_invoice
@group_order = group_order_invoice.group_order
@supplier = @group_order.order.supplier.name
@group = @group_order.ordergroup
add_group_order_invoice_attachments(group_order_invoice)
mail to: user,
subject: I18n.t('mailer.group_order_invoice.subject', group: @group.name, supplier: @supplier)
end
# Sends order result for specific Ordergroup
def order_result(user, group_order)
@order = group_order.order
@ -169,6 +181,11 @@ class Mailer < ActionMailer::Base
attachments['order.csv'] = OrderCsv.new(order, options).to_csv
end
def add_group_order_invoice_attachments(group_order_invoice)
attachment_name = group_order_invoice.name + '.pdf'
attachments[attachment_name] = GroupOrderInvoicePdf.new(group_order_invoice.load_data_for_invoice).to_pdf
end
# separate method to allow plugins to mess with the text
def additonal_welcome_text(user); end

View file

@ -7,11 +7,27 @@ module PriceCalculation
add_percent(price + deposit, tax)
end
def gross_price_without_deposit
add_percent(price, tax)
end
def gross_deposit_price
add_percent(deposit, tax)
end
# @return [Number] Price for the foodcoop-member.
def fc_price
add_percent(gross_price, FoodsoftConfig[:price_markup].to_i)
end
def fc_price_without_deposit
add_percent(gross_price_without_deposit, FoodsoftConfig[:price_markup].to_i)
end
def fc_deposit_price
add_percent(gross_deposit_price, FoodsoftConfig[:price_markup].to_i)
end
private
def add_percent(value, percent)

View file

@ -9,6 +9,7 @@ class GroupOrder < ApplicationRecord
has_many :group_order_articles, dependent: :destroy
has_many :order_articles, through: :group_order_articles
has_one :financial_transaction
has_one :group_order_invoice
belongs_to :updated_by, optional: true, class_name: 'User', foreign_key: 'updated_by_user_id'
validates :order_id, presence: true

View file

@ -208,6 +208,18 @@ class GroupOrderArticle < ApplicationRecord
end
end
def total_price_without_deposit(order_article = self.order_article)
if order_article.order.open?
if FoodsoftConfig[:tolerance_is_costly]
order_article.price.fc_price_without_deposit * (quantity + tolerance)
else
order_article.price.fc_price_without_deposit * quantity
end
else
order_article.price.fc_price_without_deposit * result
end
end
# Check if the result deviates from the result_computed
def result_manually_changed?
result != result_computed unless result.nil?

View file

@ -0,0 +1,58 @@
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?
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)
generate_invoice_number(count.to_i + 1)
else
self.invoice_date.strftime("%Y%m%d") + trailing_number
end
end
def tax_number_set
if FoodsoftConfig[:contact][:tax_number].blank?
errors.add(:group_order_invoice, "Keine Steuernummer in FoodsoftConfig :contact gesetzt")
end
end
def init
self.invoice_date = Time.now unless invoice_date
self.invoice_number = generate_invoice_number(1) unless self.invoice_number
self.payment_method = FoodsoftConfig[:group_order_invoices]&.[](:payment_method) || I18n.t('activerecord.attributes.group_order_invoice.payment_method') unless self.payment_method
end
def name
I18n.t('activerecord.attributes.group_order_invoice.name') + "_#{invoice_number}"
end
def load_data_for_invoice
invoice_data = {}
order = group_order.order
invoice_data[:supplier] = order.supplier.name
invoice_data[:ordergroup] = group_order.ordergroup
invoice_data[:group_order] = group_order
invoice_data[:invoice_number] = invoice_number
invoice_data[:invoice_date] = invoice_date
invoice_data[:tax_number] = FoodsoftConfig[:contact][:tax_number]
invoice_data[:payment_method] = payment_method
invoice_data[:order_articles] = {}
group_order.order_articles.each do |order_article|
# Get the result of last time ordering, if possible
goa = group_order.group_order_articles.detect { |tmp_goa| tmp_goa.order_article_id == order_article.id }
# Build hash with relevant data
invoice_data[:order_articles][order_article.id] = {
:price => order_article.article.fc_price,
:quantity => (goa ? goa.quantity : 0),
:total_price => (goa ? goa.total_price : 0),
:tax => order_article.article.tax
}
end
invoice_data
end
end

View file

@ -207,7 +207,7 @@ class Order < ApplicationRecord
# :fc, guess what...
def sum(type = :gross)
total = 0
if %i[net gross fc].include?(type)
if %i[net gross gross_deposit fc_deposit deposit fc].include?(type)
for oa in order_articles.ordered.includes(:article, :article_price)
quantity = oa.units * oa.price.unit_quantity
case type
@ -217,6 +217,12 @@ class Order < ApplicationRecord
total += quantity * oa.price.gross_price
when :fc
total += quantity * oa.price.fc_price
when :gross_deposit
total += quantity * oa.price.gross_deposit_price
when :fc_deposit
total += quantity * oa.price.fc_deposit_price
when :deposit
total += quantity * oa.price.deposit
end
end
elsif %i[groups groups_without_markup].include?(type)
@ -224,7 +230,11 @@ class Order < ApplicationRecord
for goa in go.group_order_articles
case type
when :groups
total += goa.result * goa.order_article.price.fc_price
total += if FoodsoftConfig[:group_order_invoices]&.[](:separate_deposits)
goa.result * (goa.order_article.price.fc_price + goa.order_article.price.fc_deposit_price)
else
goa.result * goa.order_article.price.fc_price
end
when :groups_without_markup
total += goa.result * goa.order_article.price.gross_price
end

View file

@ -7,4 +7,5 @@
= config_input c, :country, as: :string, input_html: {class: 'input-xlarge'}
= config_input c, :email, required: true, input_html: {class: 'input-xlarge'}
= config_input c, :phone, input_html: {class: 'input-medium'}
= config_input c, :tax_number, input_html: {class: 'input-medium'}
= config_input form, :homepage, required: true, as: :url, input_html: {class: 'input-xlarge'}

View file

@ -13,6 +13,12 @@
= config_input form, :charge_members_manually, as: :boolean
= config_input form, :use_iban, as: :boolean
= config_input form, :use_self_service, as: :boolean
%h4= t '.group_order_invoices'
= form.fields_for :group_order_invoices do |field|
= config_input field, :use_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'}
%h4= t '.schedule_title'
= form.simple_fields_for :order_schedule do |fields|

View file

@ -2,6 +2,7 @@
%p= t('.first_paragraph', url: link_to(t('.here'), new_invite_path(id: @ordergroup.id), remote: true)).html_safe
= simple_form_for [:admin, @ordergroup] do |f|
- captured = capture do
= f.input :customer_number
= f.input :contact_person
= f.input :contact_phone
= f.input :contact_address

View file

@ -9,6 +9,8 @@
%th= t('.end')
%th= t('.state')
%th= heading_helper Order, :updated_by
%th= heading_helper GroupOrderInvoice, :name
%th
%th
%tbody
- @orders.each do |order|
@ -17,6 +19,14 @@
%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}"}
- if order.closed?
-if FoodsoftConfig[:contact][:tax_number] && order.ordergroups.present?
= render :partial => 'group_order_invoices/links', locals:{order: order}
-else
= I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set')
- else
= t('orders.index.not_closed')
%td
- unless order.closed?
- if current_user.role_orders?

View file

@ -12,6 +12,16 @@
%tr
%td= t('.fc_amount')
%td.numeric= number_to_currency(order.sum(:fc))
- if FoodsoftConfig[:group_order_invoices]&.[](:separate_deposits)
%tr
%td= t('.deposit')
%td.numeric= number_to_currency(order.sum(:deposit))
%tr
%td= t('.gross_deposit')
%td.numeric= number_to_currency(order.sum(:gross_deposit))
%tr
%td= t('.fc_deposit')
%td.numeric= number_to_currency(order.sum(:fc_deposit))
%tr
%td= t('.groups_amount')
%td.numeric= number_to_currency(order.sum(:groups))

View file

@ -1,5 +1,4 @@
- title t('.title')
- content_for :actionbar do
- if FoodsoftConfig[:charge_members_manually]
= link_to t('.close_all_direct_with_invoice'), close_all_direct_with_invoice_finance_order_index_path, method: :post, class: 'btn'

View file

@ -0,0 +1,29 @@
.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'

View file

@ -0,0 +1 @@
$("#generate-invoice<%= params[:id] %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");

View file

@ -0,0 +1 @@
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");

View file

@ -0,0 +1 @@
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");

View file

@ -69,7 +69,7 @@
= f.hidden_field :order_id
= f.hidden_field :updated_by_user_id
= f.hidden_field :ordergroup_id
%table.table.table-hover
%table.table
%thead
%tr
%th= heading_helper Article, :name
@ -94,7 +94,7 @@
%i.icon-tag
%td{colspan: "9"}
- order_articles.each do |order_article|
%tr{class: "#{cycle('even', 'odd', name: 'articles')} order-article #{get_missing_units_css_class(@ordering_data[:order_articles][order_article.id][:missing_units])}", valign: "top"}
%tr{class: "#{cycle('even', 'odd', name: 'articles')} order-article #{get_missing_units_css_class(@ordering_data[:order_articles][order_article.id][:missing_units])}", valign: "top", tabindex: "0"}
%td.name= order_article.article.name
- if @order.stockit?
%td= truncate order_article.article.supplier.name, length: 15

View file

@ -0,0 +1 @@
= raw t '.text', group: @group.name, supplier: @supplier , foodcoop: FoodsoftConfig[:name]

View file

@ -57,6 +57,10 @@
= f.label :contact_person
%br/
= f.text_field :contact_person
%p
= f.label :customer_number
%br/
= f.text_field :customer_number
%p
= f.label :contact_phone
%br/

View file

@ -6,6 +6,8 @@
%dd=h group.contact
%dt= heading_helper(Ordergroup, :contact_address) + ':'
%dd= link_to_gmaps group.contact_address
%dt= heading_helper(Ordergroup, :customer_number) + ':'
%dd=h group.customer_number
- if group.break_start? or group.break_end?
%dt= heading_helper(Ordergroup, :break) + ':'
%dd= raw t '.break', start: format_date(group.break_start), end: format_date(group.break_end)