-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'hotfix' for release 4.0.4
- Loading branch information
Showing
12 changed files
with
279 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
# frozen_string_literal: false | ||
|
||
# Provides the routine to export the accounting data to an external accounting software | ||
class AccountingExportService | ||
attr_reader :encoding, :format, :separator, :journal_code, :date_format, :columns, :vat_service | ||
|
||
def initialize(columns, encoding = 'UTF-8', format = 'CSV', separator = ';', date_format = '%d/%m/%Y') | ||
@encoding = encoding | ||
@format = format | ||
@separator = separator | ||
@journal_code = Setting.find_by(name: 'accounting_journal_code')&.value || '' | ||
@date_format = date_format | ||
@columns = columns | ||
@vat_service = VatHistoryService.new | ||
end | ||
|
||
def export(start_date, end_date, file) | ||
# build CVS content | ||
content = header_row | ||
invoices = Invoice.where('created_at >= ? AND created_at <= ?', start_date, end_date).order('created_at ASC') | ||
invoices.each do |i| | ||
content << generate_rows(i) | ||
end | ||
|
||
# write content to file | ||
File.open(file, "w:#{encoding}") { |f| f.puts content.encode(encoding, invalid: :replace, undef: :replace) } | ||
end | ||
|
||
private | ||
|
||
def header_row | ||
row = '' | ||
columns.each do |column| | ||
row << I18n.t("accounting_export.#{column}") << separator | ||
end | ||
"#{row}\n" | ||
end | ||
|
||
def generate_rows(invoice) | ||
"#{client_row(invoice)}\n" \ | ||
"#{items_rows(invoice)}" \ | ||
"#{vat_row(invoice)}\n" | ||
end | ||
|
||
# Generate the "subscription" and "reservation" rows associated with the provided invoice | ||
def items_rows(invoice) | ||
rows = invoice.subscription_invoice? ? "#{subscription_row(invoice)}\n" : '' | ||
if invoice.invoiced_type == 'Reservation' | ||
invoice.invoice_items.each do |item| | ||
rows << "#{reservation_row(invoice, item)}\n" | ||
end | ||
end | ||
rows | ||
end | ||
|
||
# Generate the "client" row, which contains the debit to the client account, all taxes included | ||
def client_row(invoice) | ||
total = invoice.total / 100.00 | ||
row = '' | ||
columns.each do |column| | ||
case column | ||
when 'journal_code' | ||
row << journal_code | ||
when 'date' | ||
row << invoice.created_at&.strftime(date_format) | ||
when 'account_code' | ||
row << account(invoice, :client) | ||
when 'account_label' | ||
row << account(invoice, :client, :label) | ||
when 'piece' | ||
row << invoice.reference | ||
when 'line_label' | ||
row << label(invoice.invoicing_profile.full_name) | ||
when 'debit_origin' | ||
row << debit_client(invoice, total) | ||
when 'credit_origin' | ||
row << credit_client(invoice, total) | ||
when 'debit_euro' | ||
row << debit_client(invoice, total) | ||
when 'credit_euro' | ||
row << credit_client(invoice, total) | ||
when 'lettering' | ||
row << '' | ||
else | ||
puts "Unsupported column: #{column}" | ||
end | ||
row << separator | ||
end | ||
row | ||
end | ||
|
||
# Generate the "reservation" row, which contains the credit to the reservation account, all taxes excluded | ||
def reservation_row(invoice, item) | ||
wo_taxes = (item.amount / (vat_service.invoice_vat(invoice) / 100.00 + 1)) / 100.00 | ||
row = '' | ||
columns.each do |column| | ||
case column | ||
when 'journal_code' | ||
row << journal_code | ||
when 'date' | ||
row << invoice.created_at&.strftime(date_format) | ||
when 'account_code' | ||
row << account(invoice, :reservation) | ||
when 'account_label' | ||
row << account(invoice, :reservation, :label) | ||
when 'piece' | ||
row << invoice.reference | ||
when 'line_label' | ||
row << label(item.description) | ||
when 'debit_origin' | ||
row << debit(invoice, wo_taxes) | ||
when 'credit_origin' | ||
row << credit(invoice, wo_taxes) | ||
when 'debit_euro' | ||
row << debit(invoice, wo_taxes) | ||
when 'credit_euro' | ||
row << credit(invoice, wo_taxes) | ||
when 'lettering' | ||
row << '' | ||
else | ||
puts "Unsupported column: #{column}" | ||
end | ||
row << separator | ||
end | ||
row | ||
end | ||
|
||
# Generate the "subscription" row, which contains the credit to the subscription account, all taxes excluded | ||
def subscription_row(invoice) | ||
subscription_item = invoice.invoice_items.select(&:subscription).first | ||
wo_taxes = (subscription_item.amount / (vat_service.invoice_vat(invoice) / 100.00 + 1)) / 100.00 | ||
row = '' | ||
columns.each do |column| | ||
case column | ||
when 'journal_code' | ||
row << journal_code | ||
when 'date' | ||
row << invoice.created_at&.strftime(date_format) | ||
when 'account_code' | ||
row << account(invoice, :subscription) | ||
when 'account_label' | ||
row << account(invoice, :subscription, :label) | ||
when 'piece' | ||
row << invoice.reference | ||
when 'line_label' | ||
row << label(subscription_item.description) | ||
when 'debit_origin' | ||
row << debit(invoice, wo_taxes) | ||
when 'credit_origin' | ||
row << credit(invoice, wo_taxes) | ||
when 'debit_euro' | ||
row << debit(invoice, wo_taxes) | ||
when 'credit_euro' | ||
row << credit(invoice, wo_taxes) | ||
when 'lettering' | ||
row << '' | ||
else | ||
puts "Unsupported column: #{column}" | ||
end | ||
row << separator | ||
end | ||
row | ||
end | ||
|
||
# Generate the "VAT" row, which contains the credit to the VAT account, with VAT amount only | ||
def vat_row(invoice) | ||
vat = (invoice.total - (invoice.total / (vat_service.invoice_vat(invoice) / 100.00 + 1))) / 100.00 | ||
row = '' | ||
columns.each do |column| | ||
case column | ||
when 'journal_code' | ||
row << journal_code | ||
when 'date' | ||
row << invoice.created_at&.strftime(date_format) | ||
when 'account_code' | ||
row << account(invoice, :vat) | ||
when 'account_label' | ||
row << account(invoice, :vat, :label) | ||
when 'piece' | ||
row << invoice.reference | ||
when 'line_label' | ||
row << I18n.t('accounting_export.VAT') | ||
when 'debit_origin' | ||
row << debit(invoice, vat) | ||
when 'credit_origin' | ||
row << credit(invoice, vat) | ||
when 'debit_euro' | ||
row << debit(invoice, vat) | ||
when 'credit_euro' | ||
row << credit(invoice, vat) | ||
when 'lettering' | ||
row << '' | ||
else | ||
puts "Unsupported column: #{column}" | ||
end | ||
row << separator | ||
end | ||
row | ||
end | ||
|
||
# Get the account code (or label) for the given invoice and the specified line type (client, vat, subscription or reservation) | ||
def account(invoice, account, type = :code) | ||
res = case account | ||
when :client | ||
Setting.find_by(name: "accounting_client_#{type}")&.value | ||
when :vat | ||
Setting.find_by(name: "accounting_VAT_#{type}")&.value | ||
when :subscription | ||
if invoice.subscription_invoice? | ||
Setting.find_by(name: "accounting_subscription_#{type}")&.value | ||
else | ||
puts "WARN: Invoice #{invoice.id} has no subscription" | ||
end | ||
when :reservation | ||
if invoice.invoiced_type == 'Reservation' | ||
Setting.find_by(name: "accounting_#{invoice.invoiced.reservable_type}_#{type}")&.value | ||
else | ||
puts "WARN: Invoice #{invoice.id} has no reservation" | ||
end | ||
else | ||
puts "Unsupported account #{account}" | ||
end | ||
res || '' | ||
end | ||
|
||
# Fill the value of the "debit" column: if the invoice is a refund, returns the given amount, returns 0 otherwise | ||
def debit(invoice, amount) | ||
avoir = invoice.is_a? Avoir | ||
avoir ? amount.to_s : '0' | ||
end | ||
|
||
# Fill the value of the "credit" column: if the invoice is a refund, returns 0, otherwise, returns the given amount | ||
def credit(invoice, amount) | ||
avoir = invoice.is_a? Avoir | ||
avoir ? '0' : amount.to_s | ||
end | ||
|
||
# Fill the value of the "debit" column for the client row: if the invoice is a refund, returns 0, otherwise, returns the given amount | ||
def debit_client(invoice, amount) | ||
credit(invoice, amount) | ||
end | ||
|
||
# Fill the value of the "credit" column, for the client row: if the invoice is a refund, returns the given amount, returns 0 otherwise | ||
def credit_client(invoice, amount) | ||
debit(invoice, amount) | ||
end | ||
|
||
# Format the given text to match the accounting software rules for the labels | ||
def label(text) | ||
res = text.tr separator, '' | ||
res.truncate(50) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.