initial commit
Co-authored-by: viehlieb <pf@pragma-shift.net>
This commit is contained in:
commit
4139bfd40b
73 changed files with 1747 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
/.bundle/
|
||||
/doc/
|
||||
/log/*.log
|
||||
/pkg/
|
||||
/tmp/
|
||||
/test/dummy/db/*.sqlite3
|
||||
/test/dummy/db/*.sqlite3-*
|
||||
/test/dummy/log/*.log
|
||||
/test/dummy/storage/
|
||||
/test/dummy/tmp/
|
4
Gemfile
Normal file
4
Gemfile
Normal file
|
@ -0,0 +1,4 @@
|
|||
git_source(:github) { |repo| "https://git.local-it.org/#{repo}.git" }
|
||||
|
||||
# Specify your gem's dependencies in group_order_invoice.gemspec.
|
||||
gemspec
|
155
Gemfile.lock
Normal file
155
Gemfile.lock
Normal file
|
@ -0,0 +1,155 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
foodsoft_group_order_invoice (0.1.0)
|
||||
rails (>= 7.0.4)
|
||||
|
||||
GEM
|
||||
specs:
|
||||
actioncable (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activejob (= 7.0.5)
|
||||
activerecord (= 7.0.5)
|
||||
activestorage (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
actionview (= 7.0.5)
|
||||
activejob (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (7.0.5)
|
||||
actionview (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
rack (~> 2.0, >= 2.2.4)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activerecord (= 7.0.5)
|
||||
activestorage (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activejob (7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
activerecord (7.0.5)
|
||||
activemodel (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
activestorage (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activejob (= 7.0.5)
|
||||
activerecord (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (7.0.5)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
builder (3.2.4)
|
||||
concurrent-ruby (1.2.2)
|
||||
crass (1.0.6)
|
||||
date (3.3.3)
|
||||
erubi (1.12.0)
|
||||
globalid (1.1.0)
|
||||
activesupport (>= 5.0)
|
||||
i18n (1.13.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
loofah (2.21.3)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.8.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
marcel (1.0.2)
|
||||
method_source (1.0.0)
|
||||
mini_mime (1.1.2)
|
||||
minitest (5.18.0)
|
||||
net-imap (0.3.4)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
net-protocol
|
||||
net-protocol (0.2.1)
|
||||
timeout
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.15.2-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
racc (1.6.2)
|
||||
rack (2.2.7)
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rails (7.0.5)
|
||||
actioncable (= 7.0.5)
|
||||
actionmailbox (= 7.0.5)
|
||||
actionmailer (= 7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
actiontext (= 7.0.5)
|
||||
actionview (= 7.0.5)
|
||||
activejob (= 7.0.5)
|
||||
activemodel (= 7.0.5)
|
||||
activerecord (= 7.0.5)
|
||||
activestorage (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.0.5)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.6.0)
|
||||
loofah (~> 2.21)
|
||||
nokogiri (~> 1.14)
|
||||
railties (7.0.5)
|
||||
actionpack (= 7.0.5)
|
||||
activesupport (= 7.0.5)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
zeitwerk (~> 2.5)
|
||||
rake (13.0.6)
|
||||
thor (1.2.2)
|
||||
timeout (0.3.2)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
websocket-driver (0.7.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
zeitwerk (2.6.8)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
foodsoft_group_order_invoice!
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.12
|
28
README.md
Normal file
28
README.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
# GroupOrderInvoice
|
||||
Short description and motivation.
|
||||
|
||||
## Usage
|
||||
How to use my plugin.
|
||||
|
||||
## Installation
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
```ruby
|
||||
gem "group_order_invoice"
|
||||
```
|
||||
|
||||
And then execute:
|
||||
```bash
|
||||
$ bundle
|
||||
```
|
||||
|
||||
Or install it yourself as:
|
||||
```bash
|
||||
$ gem install group_order_invoice
|
||||
```
|
||||
|
||||
## Contributing
|
||||
Contribution directions go here.
|
||||
|
||||
## License
|
||||
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
3
Rakefile
Normal file
3
Rakefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
require "bundler/setup"
|
||||
|
||||
require "bundler/gem_tasks"
|
13
app/controllers/concerns/send_group_order_invoice_pdf.rb
Normal file
13
app/controllers/concerns/send_group_order_invoice_pdf.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
module Concerns::SendGroupOrderInvoicePdf
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
protected
|
||||
|
||||
def send_group_order_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
|
||||
pdf = GroupOrderInvoicePdf.new invoice_data
|
||||
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
|
||||
end
|
||||
end
|
59
app/controllers/group_order_invoices_controller.rb
Normal file
59
app/controllers/group_order_invoices_controller.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
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
|
||||
end
|
202
app/documents/group_order_invoice_pdf.rb
Normal file
202
app/documents/group_order_invoice_pdf.rb
Normal file
|
@ -0,0 +1,202 @@
|
|||
class GroupOrderInvoicePdf < RenderPdf
|
||||
def filename
|
||||
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
|
||||
unless contact[:phone].blank?
|
||||
text "#{Supplier.human_attribute_name :phone}: #{contact[:phone]}", size: fontsize(9), align: :left
|
||||
move_down 5
|
||||
end
|
||||
unless contact[:email].blank?
|
||||
text "#{Supplier.human_attribute_name :email}: #{contact[:email]}", size: fontsize(9), align: :left
|
||||
end
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
group_order_articles.each do |goa|
|
||||
# if no unit is received, nothing is to be charged
|
||||
next if goa.result.to_i == 0
|
||||
goa_total_gross = goa.result * goa.order_article.price.gross_price
|
||||
data << [goa.order_article.article.name,
|
||||
goa.result.to_i,
|
||||
number_to_currency(goa.order_article.price.gross_price),
|
||||
number_to_currency(goa.total_price)]
|
||||
total_gross += goa_total_gross
|
||||
end
|
||||
|
||||
table data, position: :left, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
|
||||
table.header = true
|
||||
table.position = :center
|
||||
table.cells.border_width = 1
|
||||
table.cells.border_color = '666666'
|
||||
|
||||
table.row(0).column(1).width = 40
|
||||
table.row(0).border_bottom_width = 2
|
||||
table.columns(1).align = :right
|
||||
table.columns(1..6).align = :right
|
||||
end
|
||||
|
||||
move_down 5
|
||||
sum = []
|
||||
sum << [nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay'), number_to_currency(total_gross)]
|
||||
# table for sum
|
||||
indent(200) do
|
||||
table sum, position: :center, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
|
||||
sum.length.times do |count|
|
||||
table.row(count).columns(0..3).borders = []
|
||||
end
|
||||
table.row(sum.length - 1).columns(0..2).borders = []
|
||||
table.row(sum.length - 1).border_bottom_width = 2
|
||||
table.row(sum.length - 1).columns(3).borders = [:bottom]
|
||||
end
|
||||
end
|
||||
|
||||
move_down 25
|
||||
text I18n.t('documents.group_order_invoice_pdf.small_business_regulation')
|
||||
move_down 10
|
||||
end
|
||||
|
||||
def body_with_vat
|
||||
total_gross = 0
|
||||
total_net = 0
|
||||
# Articles
|
||||
|
||||
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
|
||||
|
||||
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 = goa.result * order_article.price.gross_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_price)]
|
||||
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).column(1).width = 40
|
||||
table.row(0).border_bottom_width = 2
|
||||
table.columns(1).align = :right
|
||||
table.columns(1..6).align = :right
|
||||
end
|
||||
|
||||
sum = []
|
||||
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay_net'), number_to_currency(total_net)]
|
||||
tax_hash_net.each_key.each do |tax|
|
||||
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.tax_included', tax: tax), number_to_currency(tax_hash_gross[tax] - tax_hash_net[tax])]
|
||||
end
|
||||
unless marge == 0
|
||||
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.markup_included', marge: marge), number_to_currency(total_gross * marge / 100.0)]
|
||||
end
|
||||
end_sum = total_gross * (1 + marge / 100.0)
|
||||
sum << [nil, nil, nil, nil, I18n.t('documents.group_order_invoice_pdf.sum_to_pay_gross'), number_to_currency(end_sum)]
|
||||
# table for sum
|
||||
table sum, position: :right, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
|
||||
sum.length.times do |count|
|
||||
table.row(count).columns(0..5).borders = []
|
||||
end
|
||||
table.row(sum.length - 1).columns(0..4).borders = []
|
||||
table.row(sum.length - 1).border_bottom_width = 2
|
||||
table.row(sum.length - 1).columns(5).borders = [:bottom]
|
||||
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
|
10
app/jobs/notify_group_order_invoice_job.rb
Normal file
10
app/jobs/notify_group_order_invoice_job.rb
Normal 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
|
58
app/models/group_order_invoice.rb
Normal file
58
app/models/group_order_invoice.rb
Normal 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
|
|
@ -0,0 +1,13 @@
|
|||
/ insert_bottom thead
|
||||
%th= heading_helper GroupOrderInvoice, :name
|
||||
%th
|
||||
|
||||
/ insert_bottom tbody
|
||||
%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')
|
25
app/views/group_order_invoices/_links.html.haml
Normal file
25
app/views/group_order_invoices/_links.html.haml
Normal file
|
@ -0,0 +1,25 @@
|
|||
.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
|
||||
|
1
app/views/group_order_invoices/create.js.erb
Normal file
1
app/views/group_order_invoices/create.js.erb
Normal file
|
@ -0,0 +1 @@
|
|||
$("#generate-invoice<%= params[:id] %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
1
app/views/group_order_invoices/create_multiple.js.erb
Normal file
1
app/views/group_order_invoices/create_multiple.js.erb
Normal file
|
@ -0,0 +1 @@
|
|||
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
1
app/views/group_order_invoices/destroy.js.erb
Normal file
1
app/views/group_order_invoices/destroy.js.erb
Normal file
|
@ -0,0 +1 @@
|
|||
$("#generate-invoice<%= @order.id %>").html("<%= escape_javascript(render partial: 'links', locals: {order: @order}) %>");
|
5
bin/test
Executable file
5
bin/test
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
$: << File.expand_path("../test", __dir__)
|
||||
|
||||
require "bundler/setup"
|
||||
require "rails/plugin/test"
|
87
config/locales/de.yml
Normal file
87
config/locales/de.yml
Normal file
|
@ -0,0 +1,87 @@
|
|||
de:
|
||||
activerecord:
|
||||
attributes:
|
||||
group_order_invoice:
|
||||
name: Bestellgruppenrechnung
|
||||
links:
|
||||
delete: Rechnung löschen
|
||||
download: Rechnung herunterladen
|
||||
generate: Rechnung erzeugen
|
||||
invoice_date: Datum der Bestellgruppenrechnung
|
||||
generate_with_date: setzen & erzeugen
|
||||
|
||||
payment_method: Guthaben
|
||||
tax_number_not_set: Steuernummer in den Einstellungen nicht gesetzt
|
||||
admin:
|
||||
configs:
|
||||
tab_payment:
|
||||
group_order_invoices: Bestellgruppenrechnungen
|
||||
config:
|
||||
hints:
|
||||
group_order_invoices:
|
||||
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)
|
||||
keys:
|
||||
group_order_invoices:
|
||||
use_automatic_invoices: Automatisch bei Abrechnung per Mail versenden
|
||||
payment_method: Zahlungsart
|
||||
documents:
|
||||
group_order_invoice_pdf:
|
||||
filename: Rechnung%{number}
|
||||
invoicer: Rechnungsteller*in
|
||||
invoicee: Rechnungsempfänger*in
|
||||
invoice_date: 'Rechnungsdatum: %{invoice_date}'
|
||||
invoice_number: 'Rechnungsnummer: %{invoice_number}'
|
||||
markup_included: zzgl. Foodcoop Marge auf brutto Preis %{marge}%
|
||||
ordergroup:
|
||||
contact_phone: 'Telefonnummer: %{contact_phone}'
|
||||
contact_address: 'Adresse : %{contact_address}'
|
||||
name: Bestellgruppe %{ordergroup}
|
||||
payment_method: 'Zahlungsart: %{payment_method}'
|
||||
sum_to_pay: Zu zahlen gesamt
|
||||
sum_to_pay_net: Zu zahlen gesamt (netto)
|
||||
sum_to_pay_gross: Zu zahlen gesamt (brutto)
|
||||
small_business_regulation: Als Kleinunternehmer*in im Sinne von §19 Abs. 1 Umsatzsteuergesetz (UStG) wird keine Umsatzsteuer berechnet.
|
||||
table_headline: 'Für die Bestellung fallen folgende Posten an:'
|
||||
tax_excluded: exkl. MwSt.
|
||||
tax_included: zzgl. Gesamtsumme MwSt. %{tax}%
|
||||
tax_number: 'Steuernummer: %{number}'
|
||||
title: Rechnung für die Bestellung bei %{supplier}
|
||||
vat_exempt_rows:
|
||||
- Name
|
||||
- Anzahl
|
||||
- Einzelpreis
|
||||
- Artikel Gesamtpreis
|
||||
no_price_markup_rows:
|
||||
- Name
|
||||
- Anzahl
|
||||
- Einzelpreis (netto)
|
||||
- Artikel Gesamtpreis (netto)
|
||||
- MwSt.
|
||||
- Artikel Gesamtpreis (brutto)
|
||||
price_markup_rows:
|
||||
- Name
|
||||
- Anzahl
|
||||
- Einzelpreis (netto)
|
||||
- Artikel Gesamtpreis (netto)
|
||||
- MwSt.
|
||||
- Artikel Gesamtpreis (brutto) inkl. Foodcoopmarge %{marge}%
|
||||
finance:
|
||||
balancing:
|
||||
close:
|
||||
notice_mail: Bestellung wurde erfolgreich abgerechnet, die Kontostände aktualisiert. Außerdem wurden automatisch Rechnungen an die Bestellgruppenmitglieder geschickt.
|
||||
settings_not_set: Keine Emails mit Bestellgruppenrechnungen versendet. Bitte überprüfe die Einstellungen. Steuernummer gesetzt?
|
||||
mailer:
|
||||
group_order_invoice:
|
||||
subject: Bestellgruppenrechnung für %{group} bei %{supplier}
|
||||
text: |
|
||||
Liebe Bestellgruppe %{group},
|
||||
|
||||
Die Sammelbestellung bei %{supplier} wurde soeben abgerechnet und für die jeweiligen Bestellgruppen Rechnungen angelegt.
|
||||
Im Anhang befindet sich daher eure Rechnung.
|
||||
|
||||
Viele Grüße von %{foodcoop}
|
||||
orders:
|
||||
index:
|
||||
not_closed: Bestellung noch nicht abgerechnet
|
86
config/locales/en.yml
Normal file
86
config/locales/en.yml
Normal file
|
@ -0,0 +1,86 @@
|
|||
en:
|
||||
activerecord:
|
||||
attributes:
|
||||
group_order_invoice:
|
||||
name: Group order invoice
|
||||
links:
|
||||
delete: delete invoice
|
||||
download: download invoice
|
||||
invoice_date: date of group order invoice
|
||||
generate: generate invoice
|
||||
generate_with_date: set & generate
|
||||
payment_method: Credit
|
||||
tax_number_not_set: Tax number not set in configs
|
||||
admin:
|
||||
configs:
|
||||
tab_payment:
|
||||
group_order_invoices: Group order invoices
|
||||
config:
|
||||
hints:
|
||||
group_order_invoices:
|
||||
payment_method: Payment type is declared on the order group invoice
|
||||
vat_exempt: A listing of the invoice items is made without explicit display of VAT and the invoice contains the necessary addition regarding the German Kleinunternehmerregelung §19 UStG (attention! FoodCoop marge not included in nvoice).
|
||||
use_automatic_invoices: A listing of the invoice items is made without explicit display of VAT and the invoice receives the necessary addition regarding the small business regulation §19 (applies to Germany)
|
||||
keys:
|
||||
group_order_invoices:
|
||||
use_automatic_invoices: Send automatically via mail after oder settlement
|
||||
payment_method: Payment method
|
||||
vat_exempt: This foodcoopis VAT exempt
|
||||
documents:
|
||||
group_order_invoice_pdf:
|
||||
ordergroup:
|
||||
contact_phone: 'Phone: %{contact_phone}'
|
||||
contact_address: 'Adress : %{contact_address}'
|
||||
name: 'Ordergroup: %{ordergroup}'
|
||||
filename: Invoice%{number}
|
||||
invoicee: Invoicee
|
||||
invoicer: Invoicer
|
||||
invoice_date: 'Invoice date: %{invoice_date}'
|
||||
invoice_number: 'Invoice number: %{invoice_number}'
|
||||
markup_included: incl Foodcoop Marge on gross price %{marge}%
|
||||
payment_method: 'Payment_method: %{payment_method}'
|
||||
small_business_regulation: As a small entrepreneur in the sense of §19 para. 1 of the Umsatzsteuergesetz (UStG), no value added tax is charged.
|
||||
sum_to_pay: Total sum
|
||||
sum_to_pay_net: Total sum (net)
|
||||
sum_to_pay_gross: Total sum (gross)
|
||||
table_headline: 'The following items will be charged for the order:'
|
||||
tax_excluded: excl. MwSt.
|
||||
tax_included: incl. VAT %{tax}%
|
||||
tax_number: 'Tax number: %{number}'
|
||||
title: Invoice for order at %{supplier}
|
||||
vat_exempt_rows:
|
||||
- Name
|
||||
- Quantity
|
||||
- Unit price
|
||||
- Total price
|
||||
no_price_markup_rows:
|
||||
- Name
|
||||
- Quantity
|
||||
- Unit price (net)
|
||||
- Total price (net)
|
||||
- VAT
|
||||
- Total price (gross)
|
||||
price_markup_rows:
|
||||
- Name
|
||||
- Quantity
|
||||
- Unit price (net)
|
||||
- Total price (net)
|
||||
- VAT
|
||||
- Total price (gross) incl. foodcoop margin
|
||||
finance:
|
||||
balancing:
|
||||
close:
|
||||
settings_not_set: No emails with order group invoices sent. Please check the settings. Tax number set?
|
||||
mailer:
|
||||
group_order_invoice:
|
||||
subject: Order group invoice for %{group} at %{supplier}
|
||||
text: |
|
||||
Dear order group %{group},
|
||||
|
||||
The collective order at %{supplier} has just been settled and invoices have been created for the respective order groups.
|
||||
Attached you will find your invoice.
|
||||
|
||||
Best regards from %{foodcoop}
|
||||
orders:
|
||||
index:
|
||||
not_closed: Order not yet settled
|
6
config/routes.rb
Normal file
6
config/routes.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
Rails.application.routes.draw do
|
||||
scope '/:foodcoop' do
|
||||
post 'finance/group_order_invoice', to: 'group_order_invoices#create_multiple'
|
||||
resources :group_order_invoices
|
||||
end
|
||||
end
|
13
db/migrate/20211208142719_create_group_order_invoices.rb
Normal file
13
db/migrate/20211208142719_create_group_order_invoices.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
class CreateGroupOrderInvoices < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :group_order_invoices do |t|
|
||||
t.integer :group_order_id
|
||||
t.bigint :invoice_number, unique: true, limit: 8
|
||||
t.date :invoice_date
|
||||
t.string :payment_method
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
add_index :group_order_invoices, :group_order_id, unique: true
|
||||
end
|
||||
end
|
25
group_order_invoice.gemspec
Normal file
25
group_order_invoice.gemspec
Normal file
|
@ -0,0 +1,25 @@
|
|||
require_relative "lib/group_order_invoice/version"
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "foodsoft_group_order_invoice"
|
||||
spec.version = GroupOrderInvoice::VERSION
|
||||
spec.authors = [""]
|
||||
spec.email = [""]
|
||||
spec.homepage = "https://git.local-it.org/Foodsoft/foodsoft_group_order_invoice"
|
||||
spec.summary = ""
|
||||
spec.description = ""
|
||||
|
||||
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the "allowed_push_host"
|
||||
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
||||
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
||||
|
||||
spec.metadata["homepage_uri"] = spec.homepage
|
||||
spec.metadata["source_code_uri"] = "https://git.local-it.org/Foodsoft/foodsoft_group_order_invoice"
|
||||
spec.metadata["changelog_uri"] = "https://git.local-it.org/Foodsoft/foodsoft_group_order_invoice"
|
||||
|
||||
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
||||
Dir["{app,config,db,lib,spec}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
|
||||
end
|
||||
|
||||
spec.add_dependency "rails", ">= 7.0.4"
|
||||
end
|
6
lib/group_order_invoice.rb
Normal file
6
lib/group_order_invoice.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
require "group_order_invoice/version"
|
||||
require "group_order_invoice/railtie"
|
||||
|
||||
module GroupOrderInvoice
|
||||
# Your code goes here...
|
||||
end
|
4
lib/group_order_invoice/railtie.rb
Normal file
4
lib/group_order_invoice/railtie.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
module GroupOrderInvoice
|
||||
class Railtie < ::Rails::Railtie
|
||||
end
|
||||
end
|
3
lib/group_order_invoice/version.rb
Normal file
3
lib/group_order_invoice/version.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
module GroupOrderInvoice
|
||||
VERSION = "0.1.0"
|
||||
end
|
4
lib/tasks/group_order_invoice_tasks.rake
Normal file
4
lib/tasks/group_order_invoice_tasks.rake
Normal file
|
@ -0,0 +1,4 @@
|
|||
# desc "Explaining what the task does"
|
||||
# task :group_order_invoice do
|
||||
# # Task goes here
|
||||
# end
|
7
spec/factories/group_order_invoice.rb
Normal file
7
spec/factories/group_order_invoice.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require 'factory_bot'
|
||||
|
||||
FactoryBot.define do
|
||||
factory :group_order_invoice do
|
||||
group_order { create :group_order }
|
||||
end
|
||||
end
|
71
spec/integration/group_order_invoices_spec.rb
Normal file
71
spec/integration/group_order_invoices_spec.rb
Normal file
|
@ -0,0 +1,71 @@
|
|||
require_relative '../spec_helper'
|
||||
|
||||
feature GroupOrderInvoice, 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 }
|
||||
let(:order) { create :order, supplier: article.supplier, article_ids: [article.id], ends: Time.now } # need to ref article
|
||||
let(:go) { create :group_order, order: order, ordergroup: user.ordergroup}
|
||||
let(:oa) { order.order_articles.find_by_article_id(article.id) }
|
||||
let(:ftt) { create :financial_transaction_type }
|
||||
let(:goa) { create :group_order_article, group_order: go, order_article: oa }
|
||||
|
||||
include ActiveJob::TestHelper
|
||||
|
||||
before { login admin }
|
||||
|
||||
after { clear_enqueued_jobs }
|
||||
|
||||
it 'does not enqueue MailerJob when order is settled if tax_number or options not set' do
|
||||
goa.update_quantities 2, 0
|
||||
oa.update_results!
|
||||
visit confirm_finance_order_path(id: order.id)
|
||||
click_link_or_button I18n.t('finance.balancing.confirm.clear')
|
||||
expect(NotifyGroupOrderInvoiceJob).not_to have_been_enqueued
|
||||
end
|
||||
|
||||
it 'enqueues MailerJob when order is settled if tax_number or options are set' do
|
||||
goa.update_quantities 2, 0
|
||||
oa.update_results!
|
||||
order.reload
|
||||
FoodsoftConfig[:group_order_invoices] = { use_automatic_invoices: true }
|
||||
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
||||
visit confirm_finance_order_path(id: order.id, type: ftt)
|
||||
expect(page).to have_selector(:link_or_button, I18n.t('finance.balancing.confirm.clear'))
|
||||
click_link_or_button I18n.t('finance.balancing.confirm.clear')
|
||||
expect(NotifyGroupOrderInvoiceJob).to have_been_enqueued
|
||||
end
|
||||
|
||||
it 'generates Group Order Invoice when order is closed if tax_number is set' do
|
||||
goa.update_quantities 2, 0
|
||||
oa.update_results!
|
||||
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
||||
order.update!(state: 'closed')
|
||||
order.reload
|
||||
visit finance_order_index_path
|
||||
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)
|
||||
end
|
||||
|
||||
it 'generates multiple Group Order Invoice for order when order is closed if tax_number is set' do
|
||||
goa.update_quantities 2, 0
|
||||
oa.update_results!
|
||||
FoodsoftConfig[:contact][:tax_number] = 12_345_678
|
||||
order.update!(state: 'closed')
|
||||
order.reload
|
||||
visit finance_order_index_path
|
||||
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
|
||||
|
||||
it 'does not generate Group Order Invoice when order is closed if tax_number not set' do
|
||||
goa.update_quantities 2, 0
|
||||
oa.update_results!
|
||||
order.update!(state: 'closed')
|
||||
order.reload
|
||||
visit finance_order_index_path
|
||||
expect(page).to have_content(I18n.t('activerecord.attributes.group_order_invoice.tax_number_not_set'))
|
||||
end
|
||||
end
|
59
spec/models/group_order_invoice_spec.rb
Normal file
59
spec/models/group_order_invoice_spec.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
require_relative '../spec_helper'
|
||||
|
||||
describe GroupOrderInvoice do
|
||||
let(:user) { create :user, groups: [create(:ordergroup)] }
|
||||
let(:supplier) { create :supplier }
|
||||
let(:article) { create :article, supplier: supplier }
|
||||
let(:order) { create :order }
|
||||
let(:group_order) { create :group_order, order: order, ordergroup: user.ordergroup }
|
||||
|
||||
describe 'erroneous group order invoice' do
|
||||
let(:goi) { create :group_order_invoice, group_order_id: group_order.id }
|
||||
it 'does not create group order invoice if tax_number not set' do
|
||||
expect { goi }.to raise_error(ActiveRecord::RecordInvalid, /.*/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'valid group order invoice' do
|
||||
before do
|
||||
FoodsoftConfig[:contact][:tax_number] = 123_457_8
|
||||
end
|
||||
|
||||
invoice_number1 = Time.now.strftime("%Y%m%d") + '0001'
|
||||
invoice_number2 = Time.now.strftime("%Y%m%d") + '0002'
|
||||
|
||||
let(:user2) { create :user, groups: [create(:ordergroup)] }
|
||||
|
||||
let(:goi1) { create :group_order_invoice, group_order_id: group_order.id }
|
||||
let(:goi2) { create :group_order_invoice, group_order_id: group_order.id }
|
||||
|
||||
let(:group_order2) { create :group_order, order: order, ordergroup: user2.ordergroup }
|
||||
|
||||
let(:goi3) { create :group_order_invoice, group_order_id: group_order2.id }
|
||||
let(:goi4) { create :group_order_invoice, group_order_id: group_order2.id, invoice_number: invoice_number1 }
|
||||
|
||||
it 'creates group order invoice if tax_number is set' do
|
||||
expect(goi1).to be_valid
|
||||
end
|
||||
|
||||
it 'sets invoice_number according to date' do
|
||||
number = Time.now.strftime("%Y%m%d") + '0001'
|
||||
expect(goi1.invoice_number).to eq(number.to_i)
|
||||
end
|
||||
|
||||
it 'fails to create if group_order_id is used multiple times for creation' do
|
||||
expect(goi1.group_order.id).to eq(group_order.id)
|
||||
expect { goi2 }.to raise_error(ActiveRecord::RecordNotUnique)
|
||||
end
|
||||
|
||||
it 'creates two different group order invoice with different invoice_numbers' do
|
||||
expect(goi1.invoice_number).to eq(invoice_number1.to_i)
|
||||
expect(goi3.invoice_number).to eq(invoice_number2.to_i)
|
||||
end
|
||||
|
||||
it 'fails to create two different group order invoice with same invoice_numbers' do
|
||||
goi1
|
||||
expect { goi4 }.to raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
end
|
||||
end
|
6
test/dummy/Rakefile
Normal file
6
test/dummy/Rakefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require_relative "config/application"
|
||||
|
||||
Rails.application.load_tasks
|
0
test/dummy/app/assets/images/.keep
Normal file
0
test/dummy/app/assets/images/.keep
Normal file
1
test/dummy/app/assets/stylesheets/application.css
Normal file
1
test/dummy/app/assets/stylesheets/application.css
Normal file
|
@ -0,0 +1 @@
|
|||
/* Application styles */
|
4
test/dummy/app/channels/application_cable/channel.rb
Normal file
4
test/dummy/app/channels/application_cable/channel.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
module ApplicationCable
|
||||
class Channel < ActionCable::Channel::Base
|
||||
end
|
||||
end
|
4
test/dummy/app/channels/application_cable/connection.rb
Normal file
4
test/dummy/app/channels/application_cable/connection.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
module ApplicationCable
|
||||
class Connection < ActionCable::Connection::Base
|
||||
end
|
||||
end
|
2
test/dummy/app/controllers/application_controller.rb
Normal file
2
test/dummy/app/controllers/application_controller.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
class ApplicationController < ActionController::Base
|
||||
end
|
0
test/dummy/app/controllers/concerns/.keep
Normal file
0
test/dummy/app/controllers/concerns/.keep
Normal file
2
test/dummy/app/helpers/application_helper.rb
Normal file
2
test/dummy/app/helpers/application_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
module ApplicationHelper
|
||||
end
|
7
test/dummy/app/jobs/application_job.rb
Normal file
7
test/dummy/app/jobs/application_job.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class ApplicationJob < ActiveJob::Base
|
||||
# Automatically retry jobs that encountered a deadlock
|
||||
# retry_on ActiveRecord::Deadlocked
|
||||
|
||||
# Most jobs are safe to ignore if the underlying records are no longer available
|
||||
# discard_on ActiveJob::DeserializationError
|
||||
end
|
4
test/dummy/app/mailers/application_mailer.rb
Normal file
4
test/dummy/app/mailers/application_mailer.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: "from@example.com"
|
||||
layout "mailer"
|
||||
end
|
3
test/dummy/app/models/application_record.rb
Normal file
3
test/dummy/app/models/application_record.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
class ApplicationRecord < ActiveRecord::Base
|
||||
primary_abstract_class
|
||||
end
|
0
test/dummy/app/models/concerns/.keep
Normal file
0
test/dummy/app/models/concerns/.keep
Normal file
15
test/dummy/app/views/layouts/application.html.erb
Normal file
15
test/dummy/app/views/layouts/application.html.erb
Normal file
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Dummy</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
<%= stylesheet_link_tag "application" %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
13
test/dummy/app/views/layouts/mailer.html.erb
Normal file
13
test/dummy/app/views/layouts/mailer.html.erb
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
/* Email styles need to be inline */
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
1
test/dummy/app/views/layouts/mailer.text.erb
Normal file
1
test/dummy/app/views/layouts/mailer.text.erb
Normal file
|
@ -0,0 +1 @@
|
|||
<%= yield %>
|
4
test/dummy/bin/rails
Executable file
4
test/dummy/bin/rails
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env ruby
|
||||
APP_PATH = File.expand_path("../config/application", __dir__)
|
||||
require_relative "../config/boot"
|
||||
require "rails/commands"
|
4
test/dummy/bin/rake
Executable file
4
test/dummy/bin/rake
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env ruby
|
||||
require_relative "../config/boot"
|
||||
require "rake"
|
||||
Rake.application.run
|
33
test/dummy/bin/setup
Executable file
33
test/dummy/bin/setup
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env ruby
|
||||
require "fileutils"
|
||||
|
||||
# path to your application root.
|
||||
APP_ROOT = File.expand_path("..", __dir__)
|
||||
|
||||
def system!(*args)
|
||||
system(*args) || abort("\n== Command #{args} failed ==")
|
||||
end
|
||||
|
||||
FileUtils.chdir APP_ROOT do
|
||||
# This script is a way to set up or update your development environment automatically.
|
||||
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
||||
# Add necessary setup steps to this file.
|
||||
|
||||
puts "== Installing dependencies =="
|
||||
system! "gem install bundler --conservative"
|
||||
system("bundle check") || system!("bundle install")
|
||||
|
||||
# puts "\n== Copying sample files =="
|
||||
# unless File.exist?("config/database.yml")
|
||||
# FileUtils.cp "config/database.yml.sample", "config/database.yml"
|
||||
# end
|
||||
|
||||
puts "\n== Preparing database =="
|
||||
system! "bin/rails db:prepare"
|
||||
|
||||
puts "\n== Removing old logs and tempfiles =="
|
||||
system! "bin/rails log:clear tmp:clear"
|
||||
|
||||
puts "\n== Restarting application server =="
|
||||
system! "bin/rails restart"
|
||||
end
|
6
test/dummy/config.ru
Normal file
6
test/dummy/config.ru
Normal file
|
@ -0,0 +1,6 @@
|
|||
# This file is used by Rack-based servers to start the application.
|
||||
|
||||
require_relative "config/environment"
|
||||
|
||||
run Rails.application
|
||||
Rails.application.load_server
|
22
test/dummy/config/application.rb
Normal file
22
test/dummy/config/application.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
require_relative "boot"
|
||||
|
||||
require "rails/all"
|
||||
|
||||
# Require the gems listed in Gemfile, including any gems
|
||||
# you've limited to :test, :development, or :production.
|
||||
Bundler.require(*Rails.groups)
|
||||
require "group_order_invoice"
|
||||
|
||||
module Dummy
|
||||
class Application < Rails::Application
|
||||
config.load_defaults Rails::VERSION::STRING.to_f
|
||||
|
||||
# Configuration for the application, engines, and railties goes here.
|
||||
#
|
||||
# These settings can be overridden in specific environments using the files
|
||||
# in config/environments, which are processed later.
|
||||
#
|
||||
# config.time_zone = "Central Time (US & Canada)"
|
||||
# config.eager_load_paths << Rails.root.join("extras")
|
||||
end
|
||||
end
|
5
test/dummy/config/boot.rb
Normal file
5
test/dummy/config/boot.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Set up gems listed in the Gemfile.
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
|
||||
|
||||
require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
|
||||
$LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
|
10
test/dummy/config/cable.yml
Normal file
10
test/dummy/config/cable.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
development:
|
||||
adapter: async
|
||||
|
||||
test:
|
||||
adapter: test
|
||||
|
||||
production:
|
||||
adapter: redis
|
||||
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
|
||||
channel_prefix: dummy_production
|
25
test/dummy/config/database.yml
Normal file
25
test/dummy/config/database.yml
Normal file
|
@ -0,0 +1,25 @@
|
|||
# SQLite. Versions 3.8.0 and up are supported.
|
||||
# gem install sqlite3
|
||||
#
|
||||
# Ensure the SQLite 3 gem is defined in your Gemfile
|
||||
# gem "sqlite3"
|
||||
#
|
||||
default: &default
|
||||
adapter: sqlite3
|
||||
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
||||
timeout: 5000
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
database: db/development.sqlite3
|
||||
|
||||
# Warning: The database defined as "test" will be erased and
|
||||
# re-generated from your development database when you run "rake".
|
||||
# Do not set this db to the same as development or production.
|
||||
test:
|
||||
<<: *default
|
||||
database: db/test.sqlite3
|
||||
|
||||
production:
|
||||
<<: *default
|
||||
database: db/production.sqlite3
|
5
test/dummy/config/environment.rb
Normal file
5
test/dummy/config/environment.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Load the Rails application.
|
||||
require_relative "application"
|
||||
|
||||
# Initialize the Rails application.
|
||||
Rails.application.initialize!
|
68
test/dummy/config/environments/development.rb
Normal file
68
test/dummy/config/environments/development.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require "active_support/core_ext/integer/time"
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
# In the development environment your application's code is reloaded any time
|
||||
# it changes. This slows down response time but is perfect for development
|
||||
# since you don't have to restart the web server when you make code changes.
|
||||
config.cache_classes = false
|
||||
|
||||
# Do not eager load code on boot.
|
||||
config.eager_load = false
|
||||
|
||||
# Show full error reports.
|
||||
config.consider_all_requests_local = true
|
||||
|
||||
# Enable server timing
|
||||
config.server_timing = true
|
||||
|
||||
# Enable/disable caching. By default caching is disabled.
|
||||
# Run rails dev:cache to toggle caching.
|
||||
if Rails.root.join("tmp/caching-dev.txt").exist?
|
||||
config.action_controller.perform_caching = true
|
||||
config.action_controller.enable_fragment_cache_logging = true
|
||||
|
||||
config.cache_store = :memory_store
|
||||
config.public_file_server.headers = {
|
||||
"Cache-Control" => "public, max-age=#{2.days.to_i}"
|
||||
}
|
||||
else
|
||||
config.action_controller.perform_caching = false
|
||||
|
||||
config.cache_store = :null_store
|
||||
end
|
||||
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options).
|
||||
config.active_storage.service = :local
|
||||
|
||||
# Don't care if the mailer can't send.
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
|
||||
# Print deprecation notices to the Rails logger.
|
||||
config.active_support.deprecation = :log
|
||||
|
||||
# Raise exceptions for disallowed deprecations.
|
||||
config.active_support.disallowed_deprecation = :raise
|
||||
|
||||
# Tell Active Support which deprecation messages to disallow.
|
||||
config.active_support.disallowed_deprecation_warnings = []
|
||||
|
||||
# Raise an error on page load if there are pending migrations.
|
||||
config.active_record.migration_error = :page_load
|
||||
|
||||
# Highlight code that triggered database queries in logs.
|
||||
config.active_record.verbose_query_logs = true
|
||||
|
||||
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names.
|
||||
# config.action_view.annotate_rendered_view_with_filenames = true
|
||||
|
||||
# Uncomment if you wish to allow Action Cable access from any origin.
|
||||
# config.action_cable.disable_request_forgery_protection = true
|
||||
end
|
87
test/dummy/config/environments/production.rb
Normal file
87
test/dummy/config/environments/production.rb
Normal file
|
@ -0,0 +1,87 @@
|
|||
require "active_support/core_ext/integer/time"
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
# Code is not reloaded between requests.
|
||||
config.cache_classes = true
|
||||
|
||||
# Eager load code on boot. This eager loads most of Rails and
|
||||
# your application in memory, allowing both threaded web servers
|
||||
# and those relying on copy on write to perform better.
|
||||
# Rake tasks automatically ignore this option for performance.
|
||||
config.eager_load = true
|
||||
|
||||
# Full error reports are disabled and caching is turned on.
|
||||
config.consider_all_requests_local = false
|
||||
config.action_controller.perform_caching = true
|
||||
|
||||
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
|
||||
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
|
||||
# config.require_master_key = true
|
||||
|
||||
# Disable serving static files from the `/public` folder by default since
|
||||
# Apache or NGINX already handles this.
|
||||
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
|
||||
|
||||
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
|
||||
# config.asset_host = "http://assets.example.com"
|
||||
|
||||
# Specifies the header that your server uses for sending files.
|
||||
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
|
||||
# config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
|
||||
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options).
|
||||
config.active_storage.service = :local
|
||||
|
||||
# Mount Action Cable outside main process or domain.
|
||||
# config.action_cable.mount_path = nil
|
||||
# config.action_cable.url = "wss://example.com/cable"
|
||||
# config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
|
||||
|
||||
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
||||
# config.force_ssl = true
|
||||
|
||||
# Include generic and useful information about system operation, but avoid logging too much
|
||||
# information to avoid inadvertent exposure of personally identifiable information (PII).
|
||||
config.log_level = :info
|
||||
|
||||
# Prepend all log lines with the following tags.
|
||||
config.log_tags = [ :request_id ]
|
||||
|
||||
# Use a different cache store in production.
|
||||
# config.cache_store = :mem_cache_store
|
||||
|
||||
# Use a real queuing backend for Active Job (and separate queues per environment).
|
||||
# config.active_job.queue_adapter = :resque
|
||||
# config.active_job.queue_name_prefix = "dummy_production"
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
|
||||
# Ignore bad email addresses and do not raise email delivery errors.
|
||||
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
||||
# config.action_mailer.raise_delivery_errors = false
|
||||
|
||||
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
|
||||
# the I18n.default_locale when a translation cannot be found).
|
||||
config.i18n.fallbacks = true
|
||||
|
||||
# Don't log any deprecations.
|
||||
config.active_support.report_deprecations = false
|
||||
|
||||
# Use default logging formatter so that PID and timestamp are not suppressed.
|
||||
config.log_formatter = ::Logger::Formatter.new
|
||||
|
||||
# Use a different logger for distributed setups.
|
||||
# require "syslog/logger"
|
||||
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name")
|
||||
|
||||
if ENV["RAILS_LOG_TO_STDOUT"].present?
|
||||
logger = ActiveSupport::Logger.new(STDOUT)
|
||||
logger.formatter = config.log_formatter
|
||||
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
||||
end
|
||||
|
||||
# Do not dump schema after migrations.
|
||||
config.active_record.dump_schema_after_migration = false
|
||||
end
|
60
test/dummy/config/environments/test.rb
Normal file
60
test/dummy/config/environments/test.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
require "active_support/core_ext/integer/time"
|
||||
|
||||
# The test environment is used exclusively to run your application's
|
||||
# test suite. You never need to work with it otherwise. Remember that
|
||||
# your test database is "scratch space" for the test suite and is wiped
|
||||
# and recreated between test runs. Don't rely on the data there!
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
# Turn false under Spring and add config.action_view.cache_template_loading = true.
|
||||
config.cache_classes = true
|
||||
|
||||
# Eager loading loads your whole application. When running a single test locally,
|
||||
# this probably isn't necessary. It's a good idea to do in a continuous integration
|
||||
# system, or in some way before deploying your code.
|
||||
config.eager_load = ENV["CI"].present?
|
||||
|
||||
# Configure public file server for tests with Cache-Control for performance.
|
||||
config.public_file_server.enabled = true
|
||||
config.public_file_server.headers = {
|
||||
"Cache-Control" => "public, max-age=#{1.hour.to_i}"
|
||||
}
|
||||
|
||||
# Show full error reports and disable caching.
|
||||
config.consider_all_requests_local = true
|
||||
config.action_controller.perform_caching = false
|
||||
config.cache_store = :null_store
|
||||
|
||||
# Raise exceptions instead of rendering exception templates.
|
||||
config.action_dispatch.show_exceptions = false
|
||||
|
||||
# Disable request forgery protection in test environment.
|
||||
config.action_controller.allow_forgery_protection = false
|
||||
|
||||
# Store uploaded files on the local file system in a temporary directory.
|
||||
config.active_storage.service = :test
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
|
||||
# Tell Action Mailer not to deliver emails to the real world.
|
||||
# The :test delivery method accumulates sent emails in the
|
||||
# ActionMailer::Base.deliveries array.
|
||||
config.action_mailer.delivery_method = :test
|
||||
|
||||
# Print deprecation notices to the stderr.
|
||||
config.active_support.deprecation = :stderr
|
||||
|
||||
# Raise exceptions for disallowed deprecations.
|
||||
config.active_support.disallowed_deprecation = :raise
|
||||
|
||||
# Tell Active Support which deprecation messages to disallow.
|
||||
config.active_support.disallowed_deprecation_warnings = []
|
||||
|
||||
# Raises error for missing translations.
|
||||
# config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names.
|
||||
# config.action_view.annotate_rendered_view_with_filenames = true
|
||||
end
|
25
test/dummy/config/initializers/content_security_policy.rb
Normal file
25
test/dummy/config/initializers/content_security_policy.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Define an application-wide content security policy.
|
||||
# See the Securing Rails Applications Guide for more information:
|
||||
# https://guides.rubyonrails.org/security.html#content-security-policy-header
|
||||
|
||||
# Rails.application.configure do
|
||||
# config.content_security_policy do |policy|
|
||||
# policy.default_src :self, :https
|
||||
# policy.font_src :self, :https, :data
|
||||
# policy.img_src :self, :https, :data
|
||||
# policy.object_src :none
|
||||
# policy.script_src :self, :https
|
||||
# policy.style_src :self, :https
|
||||
# # Specify URI for violation reports
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
#
|
||||
# # Generate session nonces for permitted importmap and inline scripts
|
||||
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
|
||||
# config.content_security_policy_nonce_directives = %w(script-src)
|
||||
#
|
||||
# # Report violations without enforcing the policy.
|
||||
# # config.content_security_policy_report_only = true
|
||||
# end
|
|
@ -0,0 +1,8 @@
|
|||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Configure parameters to be filtered from the log file. Use this to limit dissemination of
|
||||
# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported
|
||||
# notations and behaviors.
|
||||
Rails.application.config.filter_parameters += [
|
||||
:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
|
||||
]
|
16
test/dummy/config/initializers/inflections.rb
Normal file
16
test/dummy/config/initializers/inflections.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Add new inflection rules using the following format. Inflections
|
||||
# are locale specific, and you may define rules for as many different
|
||||
# locales as you wish. All of these examples are active by default:
|
||||
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
# inflect.plural /^(ox)$/i, "\\1en"
|
||||
# inflect.singular /^(ox)en/i, "\\1"
|
||||
# inflect.irregular "person", "people"
|
||||
# inflect.uncountable %w( fish sheep )
|
||||
# end
|
||||
|
||||
# These inflection rules are supported but not enabled by default:
|
||||
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
# inflect.acronym "RESTful"
|
||||
# end
|
11
test/dummy/config/initializers/permissions_policy.rb
Normal file
11
test/dummy/config/initializers/permissions_policy.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Define an application-wide HTTP permissions policy. For further
|
||||
# information see https://developers.google.com/web/updates/2018/06/feature-policy
|
||||
#
|
||||
# Rails.application.config.permissions_policy do |f|
|
||||
# f.camera :none
|
||||
# f.gyroscope :none
|
||||
# f.microphone :none
|
||||
# f.usb :none
|
||||
# f.fullscreen :self
|
||||
# f.payment :self, "https://secure.example.com"
|
||||
# end
|
33
test/dummy/config/locales/en.yml
Normal file
33
test/dummy/config/locales/en.yml
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Files in the config/locales directory are used for internationalization
|
||||
# and are automatically loaded by Rails. If you want to use locales other
|
||||
# than English, add the necessary files in this directory.
|
||||
#
|
||||
# To use the locales, use `I18n.t`:
|
||||
#
|
||||
# I18n.t "hello"
|
||||
#
|
||||
# In views, this is aliased to just `t`:
|
||||
#
|
||||
# <%= t("hello") %>
|
||||
#
|
||||
# To use a different locale, set it with `I18n.locale`:
|
||||
#
|
||||
# I18n.locale = :es
|
||||
#
|
||||
# This would use the information in config/locales/es.yml.
|
||||
#
|
||||
# The following keys must be escaped otherwise they will not be retrieved by
|
||||
# the default I18n backend:
|
||||
#
|
||||
# true, false, on, off, yes, no
|
||||
#
|
||||
# Instead, surround them with single quotes.
|
||||
#
|
||||
# en:
|
||||
# "true": "foo"
|
||||
#
|
||||
# To learn more, please read the Rails Internationalization guide
|
||||
# available at https://guides.rubyonrails.org/i18n.html.
|
||||
|
||||
en:
|
||||
hello: "Hello world"
|
43
test/dummy/config/puma.rb
Normal file
43
test/dummy/config/puma.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Puma can serve each request in a thread from an internal thread pool.
|
||||
# The `threads` method setting takes two numbers: a minimum and maximum.
|
||||
# Any libraries that use thread pools should be configured to match
|
||||
# the maximum value specified for Puma. Default is set to 5 threads for minimum
|
||||
# and maximum; this matches the default thread size of Active Record.
|
||||
#
|
||||
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
|
||||
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
|
||||
threads min_threads_count, max_threads_count
|
||||
|
||||
# Specifies the `worker_timeout` threshold that Puma will use to wait before
|
||||
# terminating a worker in development environments.
|
||||
#
|
||||
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
|
||||
|
||||
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
|
||||
#
|
||||
port ENV.fetch("PORT") { 3000 }
|
||||
|
||||
# Specifies the `environment` that Puma will run in.
|
||||
#
|
||||
environment ENV.fetch("RAILS_ENV") { "development" }
|
||||
|
||||
# Specifies the `pidfile` that Puma will use.
|
||||
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
|
||||
|
||||
# Specifies the number of `workers` to boot in clustered mode.
|
||||
# Workers are forked web server processes. If using threads and workers together
|
||||
# the concurrency of the application would be max `threads` * `workers`.
|
||||
# Workers do not work on JRuby or Windows (both of which do not support
|
||||
# processes).
|
||||
#
|
||||
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
|
||||
|
||||
# Use the `preload_app!` method when specifying a `workers` number.
|
||||
# This directive tells Puma to first boot the application and load code
|
||||
# before forking the application. This takes advantage of Copy On Write
|
||||
# process behavior so workers use less memory.
|
||||
#
|
||||
# preload_app!
|
||||
|
||||
# Allow puma to be restarted by `bin/rails restart` command.
|
||||
plugin :tmp_restart
|
6
test/dummy/config/routes.rb
Normal file
6
test/dummy/config/routes.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
Rails.application.routes.draw do
|
||||
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
|
||||
|
||||
# Defines the root path route ("/")
|
||||
# root "articles#index"
|
||||
end
|
34
test/dummy/config/storage.yml
Normal file
34
test/dummy/config/storage.yml
Normal file
|
@ -0,0 +1,34 @@
|
|||
test:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("tmp/storage") %>
|
||||
|
||||
local:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("storage") %>
|
||||
|
||||
# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
|
||||
# amazon:
|
||||
# service: S3
|
||||
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
|
||||
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
|
||||
# region: us-east-1
|
||||
# bucket: your_own_bucket-<%= Rails.env %>
|
||||
|
||||
# Remember not to checkin your GCS keyfile to a repository
|
||||
# google:
|
||||
# service: GCS
|
||||
# project: your_project
|
||||
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
|
||||
# bucket: your_own_bucket-<%= Rails.env %>
|
||||
|
||||
# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
|
||||
# microsoft:
|
||||
# service: AzureStorage
|
||||
# storage_account_name: your_account_name
|
||||
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
|
||||
# container: your_container_name-<%= Rails.env %>
|
||||
|
||||
# mirror:
|
||||
# service: Mirror
|
||||
# primary: local
|
||||
# mirrors: [ amazon, google, microsoft ]
|
0
test/dummy/lib/assets/.keep
Normal file
0
test/dummy/lib/assets/.keep
Normal file
0
test/dummy/log/.keep
Normal file
0
test/dummy/log/.keep
Normal file
67
test/dummy/public/404.html
Normal file
67
test/dummy/public/404.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>The page you were looking for doesn't exist (404)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<style>
|
||||
.rails-default-error-page {
|
||||
background-color: #EFEFEF;
|
||||
color: #2E2F30;
|
||||
text-align: center;
|
||||
font-family: arial, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog {
|
||||
width: 95%;
|
||||
max-width: 33em;
|
||||
margin: 4em auto 0;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog > div {
|
||||
border: 1px solid #CCC;
|
||||
border-right-color: #999;
|
||||
border-left-color: #999;
|
||||
border-bottom-color: #BBB;
|
||||
border-top: #B00100 solid 4px;
|
||||
border-top-left-radius: 9px;
|
||||
border-top-right-radius: 9px;
|
||||
background-color: white;
|
||||
padding: 7px 12% 0;
|
||||
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
||||
}
|
||||
|
||||
.rails-default-error-page h1 {
|
||||
font-size: 100%;
|
||||
color: #730E15;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog > p {
|
||||
margin: 0 0 1em;
|
||||
padding: 1em;
|
||||
background-color: #F7F7F7;
|
||||
border: 1px solid #CCC;
|
||||
border-right-color: #999;
|
||||
border-left-color: #999;
|
||||
border-bottom-color: #999;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-color: #DADADA;
|
||||
color: #666;
|
||||
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="rails-default-error-page">
|
||||
<!-- This file lives in public/404.html -->
|
||||
<div class="dialog">
|
||||
<div>
|
||||
<h1>The page you were looking for doesn't exist.</h1>
|
||||
<p>You may have mistyped the address or the page may have moved.</p>
|
||||
</div>
|
||||
<p>If you are the application owner check the logs for more information.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
67
test/dummy/public/422.html
Normal file
67
test/dummy/public/422.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>The change you wanted was rejected (422)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<style>
|
||||
.rails-default-error-page {
|
||||
background-color: #EFEFEF;
|
||||
color: #2E2F30;
|
||||
text-align: center;
|
||||
font-family: arial, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog {
|
||||
width: 95%;
|
||||
max-width: 33em;
|
||||
margin: 4em auto 0;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog > div {
|
||||
border: 1px solid #CCC;
|
||||
border-right-color: #999;
|
||||
border-left-color: #999;
|
||||
border-bottom-color: #BBB;
|
||||
border-top: #B00100 solid 4px;
|
||||
border-top-left-radius: 9px;
|
||||
border-top-right-radius: 9px;
|
||||
background-color: white;
|
||||
padding: 7px 12% 0;
|
||||
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
||||
}
|
||||
|
||||
.rails-default-error-page h1 {
|
||||
font-size: 100%;
|
||||
color: #730E15;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog > p {
|
||||
margin: 0 0 1em;
|
||||
padding: 1em;
|
||||
background-color: #F7F7F7;
|
||||
border: 1px solid #CCC;
|
||||
border-right-color: #999;
|
||||
border-left-color: #999;
|
||||
border-bottom-color: #999;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-color: #DADADA;
|
||||
color: #666;
|
||||
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="rails-default-error-page">
|
||||
<!-- This file lives in public/422.html -->
|
||||
<div class="dialog">
|
||||
<div>
|
||||
<h1>The change you wanted was rejected.</h1>
|
||||
<p>Maybe you tried to change something you didn't have access to.</p>
|
||||
</div>
|
||||
<p>If you are the application owner check the logs for more information.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
66
test/dummy/public/500.html
Normal file
66
test/dummy/public/500.html
Normal file
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>We're sorry, but something went wrong (500)</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<style>
|
||||
.rails-default-error-page {
|
||||
background-color: #EFEFEF;
|
||||
color: #2E2F30;
|
||||
text-align: center;
|
||||
font-family: arial, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog {
|
||||
width: 95%;
|
||||
max-width: 33em;
|
||||
margin: 4em auto 0;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog > div {
|
||||
border: 1px solid #CCC;
|
||||
border-right-color: #999;
|
||||
border-left-color: #999;
|
||||
border-bottom-color: #BBB;
|
||||
border-top: #B00100 solid 4px;
|
||||
border-top-left-radius: 9px;
|
||||
border-top-right-radius: 9px;
|
||||
background-color: white;
|
||||
padding: 7px 12% 0;
|
||||
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
||||
}
|
||||
|
||||
.rails-default-error-page h1 {
|
||||
font-size: 100%;
|
||||
color: #730E15;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.rails-default-error-page div.dialog > p {
|
||||
margin: 0 0 1em;
|
||||
padding: 1em;
|
||||
background-color: #F7F7F7;
|
||||
border: 1px solid #CCC;
|
||||
border-right-color: #999;
|
||||
border-left-color: #999;
|
||||
border-bottom-color: #999;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-color: #DADADA;
|
||||
color: #666;
|
||||
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="rails-default-error-page">
|
||||
<!-- This file lives in public/500.html -->
|
||||
<div class="dialog">
|
||||
<div>
|
||||
<h1>We're sorry, but something went wrong.</h1>
|
||||
</div>
|
||||
<p>If you are the application owner check the logs for more information.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
0
test/dummy/public/apple-touch-icon-precomposed.png
Normal file
0
test/dummy/public/apple-touch-icon-precomposed.png
Normal file
0
test/dummy/public/apple-touch-icon.png
Normal file
0
test/dummy/public/apple-touch-icon.png
Normal file
0
test/dummy/public/favicon.ico
Normal file
0
test/dummy/public/favicon.ico
Normal file
7
test/group_order_invoice_test.rb
Normal file
7
test/group_order_invoice_test.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require "test_helper"
|
||||
|
||||
class GroupOrderInvoiceTest < ActiveSupport::TestCase
|
||||
test "it has a version number" do
|
||||
assert GroupOrderInvoice::VERSION
|
||||
end
|
||||
end
|
14
test/test_helper.rb
Normal file
14
test/test_helper.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Configure Rails Environment
|
||||
ENV["RAILS_ENV"] = "test"
|
||||
|
||||
require_relative "../test/dummy/config/environment"
|
||||
ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
|
||||
require "rails/test_help"
|
||||
|
||||
# Load fixtures from the engine
|
||||
if ActiveSupport::TestCase.respond_to?(:fixture_path=)
|
||||
ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__)
|
||||
ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
|
||||
ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
|
||||
ActiveSupport::TestCase.fixtures :all
|
||||
end
|
Loading…
Reference in a new issue