mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-17 06:52:27 +01:00
Merge branch 'async-archive' into dev
This commit is contained in:
commit
1c6d7c266e
@ -1,67 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Various helpers methods
|
||||
module ApplicationHelper
|
||||
|
||||
include Twitter::Autolink
|
||||
require 'message_format'
|
||||
include Twitter::Autolink
|
||||
require 'message_format'
|
||||
|
||||
## machine/spaces availabilities are divided in multiple slots of 60 minutes
|
||||
SLOT_DURATION ||= 60
|
||||
## machine/spaces availabilities are divided in multiple slots of 60 minutes
|
||||
SLOT_DURATION ||= 60
|
||||
|
||||
##
|
||||
# Verify if the provided attribute is in the provided attributes array, whatever it exists or not
|
||||
# @param attributes {Array|nil}
|
||||
# @param attribute {String}
|
||||
##
|
||||
def attribute_requested?(attributes, attribute)
|
||||
attributes.try(:include?, attribute)
|
||||
end
|
||||
##
|
||||
# Verify if the provided attribute is in the provided attributes array, whatever it exists or not
|
||||
# @param attributes {Array|nil}
|
||||
# @param attribute {String}
|
||||
##
|
||||
def attribute_requested?(attributes, attribute)
|
||||
attributes.try(:include?, attribute)
|
||||
end
|
||||
|
||||
def bootstrap_class_for flash_type
|
||||
{ flash: 'alert-success', alert: 'alert-danger', notice: 'alert-info' }[flash_type.to_sym] || flash_type.to_s
|
||||
end
|
||||
def bootstrap_class_for flash_type
|
||||
{ flash: 'alert-success', alert: 'alert-danger', notice: 'alert-info' }[flash_type.to_sym] || flash_type.to_s
|
||||
end
|
||||
|
||||
def flash_messages(opts = {})
|
||||
flash.each do |msg_type, message|
|
||||
concat(content_tag(:div, message, class: "flash-message alert #{bootstrap_class_for(msg_type)} fade in") do
|
||||
concat content_tag(:button, 'x', class: 'close', data: { dismiss: 'alert' })
|
||||
concat message
|
||||
end)
|
||||
end
|
||||
nil
|
||||
end
|
||||
def flash_messages(_opts = {})
|
||||
flash.each do |msg_type, message|
|
||||
concat(content_tag(:div, message, class: "flash-message alert #{bootstrap_class_for(msg_type)} fade in") do
|
||||
concat content_tag(:button, 'x', class: 'close', data: { dismiss: 'alert' })
|
||||
concat message
|
||||
end)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def print_slot(starting, ending)
|
||||
"#{starting.strftime('%H:%M')} - #{ending.strftime('%H:%M')}"
|
||||
end
|
||||
def print_slot(starting, ending)
|
||||
"#{starting.strftime('%H:%M')} - #{ending.strftime('%H:%M')}"
|
||||
end
|
||||
|
||||
def class_exists?(class_name)
|
||||
klass = Module.const_get(class_name)
|
||||
return klass.is_a?(Class)
|
||||
rescue NameError
|
||||
return false
|
||||
end
|
||||
def class_exists?(class_name)
|
||||
klass = Module.const_get(class_name)
|
||||
klass.is_a?(Class)
|
||||
rescue NameError
|
||||
false
|
||||
end
|
||||
|
||||
##
|
||||
# Allow to treat a rails i18n key as a MessageFormat interpolated pattern. Used in ruby views (API/mails)
|
||||
# @param key {String} Ruby-on-Rails I18n key (from config/locales/xx.yml)
|
||||
# @param interpolations {Hash} list of variables to interpolate, following ICU MessageFormat syntax
|
||||
##
|
||||
def _t(key, interpolations)
|
||||
message = MessageFormat.new(I18n.t(scope_key_by_partial(key)), I18n.locale.to_s)
|
||||
text = message.format(interpolations)
|
||||
if html_safe_translation_key?(key)
|
||||
text.html_safe
|
||||
else
|
||||
text
|
||||
end
|
||||
end
|
||||
##
|
||||
# Allow to treat a rails i18n key as a MessageFormat interpolated pattern. Used in ruby views (API/mails)
|
||||
# @param key {String} Ruby-on-Rails I18n key (from config/locales/xx.yml)
|
||||
# @param interpolations {Hash} list of variables to interpolate, following ICU MessageFormat syntax
|
||||
##
|
||||
def _t(key, interpolations)
|
||||
message = MessageFormat.new(I18n.t(scope_key_by_partial(key)), I18n.locale.to_s)
|
||||
text = message.format(interpolations)
|
||||
if html_safe_translation_key?(key)
|
||||
text.html_safe
|
||||
else
|
||||
text
|
||||
end
|
||||
end
|
||||
|
||||
def bool_to_sym(bool)
|
||||
if (bool) then return :true else return :false end
|
||||
end
|
||||
def bool_to_sym(bool)
|
||||
bool ? :true : :false # rubocop:disable Lint/BooleanSymbol
|
||||
end
|
||||
|
||||
def amount_to_f(amount)
|
||||
amount / 100.00
|
||||
end
|
||||
def amount_to_f(amount)
|
||||
amount / 100.00
|
||||
end
|
||||
|
||||
##
|
||||
# Retrieve an item in the given array of items
|
||||
@ -69,46 +72,45 @@ module ApplicationHelper
|
||||
# this can be overridden by passing a third parameter to specify the
|
||||
# property to match
|
||||
##
|
||||
def get_item(array, id, key = nil)
|
||||
array.each do |i|
|
||||
if key.nil?
|
||||
return i if i.id == id
|
||||
else
|
||||
return i if i[key] == id
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
def get_item(array, id, key = nil)
|
||||
array.each do |i|
|
||||
if key.nil?
|
||||
return i if i.id == id
|
||||
elsif i[key] == id
|
||||
return i
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# Apply a correction for a future DateTime due to change in Daylight Saving Time (DST) period
|
||||
# @param reference {ActiveSupport::TimeWithZone}
|
||||
# @param datetime {DateTime}
|
||||
# Inspired by https://stackoverflow.com/a/12065605
|
||||
##
|
||||
def dst_correction(reference, datetime)
|
||||
res = datetime.in_time_zone(reference.time_zone.tzinfo.name)
|
||||
res = res - 1.hour if res.dst? && !reference.dst?
|
||||
res = res + 1.hour if reference.dst? && !res.dst?
|
||||
res
|
||||
end
|
||||
##
|
||||
# Apply a correction for a future DateTime due to change in Daylight Saving Time (DST) period
|
||||
# @param reference {ActiveSupport::TimeWithZone}
|
||||
# @param datetime {DateTime}
|
||||
# Inspired by https://stackoverflow.com/a/12065605
|
||||
##
|
||||
def dst_correction(reference, datetime)
|
||||
res = datetime.in_time_zone(reference.time_zone.tzinfo.name)
|
||||
res -= 1.hour if res.dst? && !reference.dst?
|
||||
res += 1.hour if reference.dst? && !res.dst?
|
||||
res
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
## inspired by gems/actionview-4.2.5/lib/action_view/helpers/translation_helper.rb
|
||||
def scope_key_by_partial(key)
|
||||
if key.to_s.first == "."
|
||||
if @virtual_path
|
||||
@virtual_path.gsub(%r{/_?}, ".") + key.to_s
|
||||
else
|
||||
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
|
||||
end
|
||||
else
|
||||
key
|
||||
end
|
||||
end
|
||||
private
|
||||
|
||||
def html_safe_translation_key?(key)
|
||||
key.to_s =~ /(\b|_|\.)html$/
|
||||
end
|
||||
## inspired by gems/actionview-4.2.5/lib/action_view/helpers/translation_helper.rb
|
||||
def scope_key_by_partial(key)
|
||||
if key.to_s.first == '.'
|
||||
raise "Cannot use t(#{key.inspect}) shortcut because path is not available" unless @virtual_path
|
||||
|
||||
@virtual_path.gsub(%r{/_?}, '.') + key.to_s
|
||||
else
|
||||
key
|
||||
end
|
||||
end
|
||||
|
||||
def html_safe_translation_key?(key)
|
||||
key.to_s =~ /(\b|_|\.)html$/
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Helpers methods about calendar availabilities
|
||||
module AvailabilityHelper
|
||||
MACHINE_COLOR = '#e4cd78'
|
||||
TRAINING_COLOR = '#bd7ae9'
|
||||
@ -9,14 +12,14 @@ module AvailabilityHelper
|
||||
|
||||
def availability_border_color(availability)
|
||||
case availability.available_type
|
||||
when 'machines'
|
||||
MACHINE_COLOR
|
||||
when 'training'
|
||||
TRAINING_COLOR
|
||||
when 'space'
|
||||
SPACE_COLOR
|
||||
else
|
||||
EVENT_COLOR
|
||||
when 'machines'
|
||||
MACHINE_COLOR
|
||||
when 'training'
|
||||
TRAINING_COLOR
|
||||
when 'space'
|
||||
SPACE_COLOR
|
||||
else
|
||||
EVENT_COLOR
|
||||
end
|
||||
end
|
||||
|
||||
@ -45,14 +48,14 @@ module AvailabilityHelper
|
||||
IS_COMPLETED
|
||||
else
|
||||
case availability.available_type
|
||||
when 'training'
|
||||
TRAINING_COLOR
|
||||
when 'event'
|
||||
EVENT_COLOR
|
||||
when 'space'
|
||||
SPACE_COLOR
|
||||
else
|
||||
'#000'
|
||||
when 'training'
|
||||
TRAINING_COLOR
|
||||
when 'event'
|
||||
EVENT_COLOR
|
||||
when 'space'
|
||||
SPACE_COLOR
|
||||
else
|
||||
'#000'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Helpers methods about uploading files
|
||||
module UploadHelper
|
||||
|
||||
def delete_empty_dirs
|
||||
|
@ -10,7 +10,7 @@ class AccountingPeriod < ActiveRecord::Base
|
||||
before_destroy { false }
|
||||
before_update { false }
|
||||
before_create :compute_totals
|
||||
after_create :archive_closed_data
|
||||
after_commit :archive_closed_data, on: [:create]
|
||||
|
||||
validates :start_at, :end_at, :closed_at, :closed_by, presence: true
|
||||
validates_with DateRangeValidator
|
||||
@ -68,6 +68,10 @@ class AccountingPeriod < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def previous_period
|
||||
AccountingPeriod.where('closed_at < ?', closed_at).order(closed_at: :desc).limit(1).last
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def vat_history
|
||||
@ -81,47 +85,8 @@ class AccountingPeriod < ActiveRecord::Base
|
||||
key_dates.sort_by { |k| k[:date] }
|
||||
end
|
||||
|
||||
def to_json_archive(invoices, previous_file, last_checksum)
|
||||
code_checksum = Checksum.code
|
||||
ApplicationController.new.view_context.render(
|
||||
partial: 'archive/accounting',
|
||||
locals: {
|
||||
invoices: invoices_with_vat(invoices),
|
||||
period_total: period_total,
|
||||
perpetual_total: perpetual_total,
|
||||
period_footprint: footprint,
|
||||
code_checksum: code_checksum,
|
||||
last_archive_checksum: last_checksum,
|
||||
previous_file: previous_file,
|
||||
software_version: Version.current,
|
||||
date: Time.now.iso8601
|
||||
},
|
||||
formats: [:json],
|
||||
handlers: [:jbuilder]
|
||||
)
|
||||
end
|
||||
|
||||
def previous_period
|
||||
AccountingPeriod.where('closed_at < ?', closed_at).order(closed_at: :desc).limit(1).last
|
||||
end
|
||||
|
||||
def archive_closed_data
|
||||
data = invoices.includes(:invoice_items).order(id: :asc)
|
||||
previous_file = previous_period&.archive_file
|
||||
last_archive_checksum = previous_file ? Checksum.file(previous_file) : nil
|
||||
json_data = to_json_archive(data, previous_file, last_archive_checksum)
|
||||
current_archive_checksum = Checksum.text(json_data)
|
||||
date = DateTime.iso8601
|
||||
chained = Checksum.text("#{current_archive_checksum}#{last_archive_checksum}#{date}")
|
||||
|
||||
Zip::OutputStream.open(archive_file) do |io|
|
||||
io.put_next_entry(archive_json_file)
|
||||
io.write(json_data)
|
||||
io.put_next_entry('checksum.sha256')
|
||||
io.write("#{current_archive_checksum}\t#{archive_json_file}")
|
||||
io.put_next_entry('chained.sha256')
|
||||
io.write("#{chained}\t#{date}")
|
||||
end
|
||||
ArchiveWorker.perform_async(id)
|
||||
end
|
||||
|
||||
def price_without_taxe(invoice)
|
||||
|
@ -44,6 +44,7 @@ class NotificationType
|
||||
notify_member_reservation_reminder
|
||||
notify_admin_free_disk_space
|
||||
notify_admin_close_period_reminder
|
||||
notify_admin_archive_complete
|
||||
]
|
||||
# deprecated:
|
||||
# - notify_member_subscribed_plan_is_changed
|
||||
|
@ -0,0 +1,7 @@
|
||||
json.title notification.notification_type
|
||||
json.description t('.archive_complete',
|
||||
START: notification.attached_object.start_at,
|
||||
END: notification.attached_object.end_at,
|
||||
ID: notification.attached_object.id
|
||||
)
|
||||
json.url notification_url(notification, format: :json)
|
@ -0,0 +1,12 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<p>
|
||||
<%= t('.body.archive_complete', START: @attached_object.start_at, END: @attached_object.end_at) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= t('.body.click_to_download') %>
|
||||
<%=link_to( t('.body.here'), "#{root_url}api/accounting_periods/#{@attached_object.id}/archive", target: "_blank" )%>
|
||||
</p>
|
||||
<p>
|
||||
<%= t('.body.save_on_secured') %>
|
||||
</p>
|
54
app/workers/archive_worker.rb
Normal file
54
app/workers/archive_worker.rb
Normal file
@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Will generate a ZIP archive file containing all invoicing data for the given period.
|
||||
# This file will be asynchronously generated by sidekiq and a notification will be sent to the requesting user when it's done.
|
||||
class ArchiveWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(accounting_period_id)
|
||||
period = AccountingPeriod.find(accounting_period_id)
|
||||
|
||||
data = period.invoices.includes(:invoice_items).order(id: :asc)
|
||||
previous_file = period.previous_period&.archive_file
|
||||
last_archive_checksum = previous_file ? Checksum.file(previous_file) : nil
|
||||
json_data = to_json_archive(period, data, previous_file, last_archive_checksum)
|
||||
current_archive_checksum = Checksum.text(json_data)
|
||||
date = DateTime.iso8601
|
||||
chained = Checksum.text("#{current_archive_checksum}#{last_archive_checksum}#{date}")
|
||||
|
||||
Zip::OutputStream.open(period.archive_file) do |io|
|
||||
io.put_next_entry(period.archive_json_file)
|
||||
io.write(json_data)
|
||||
io.put_next_entry('checksum.sha256')
|
||||
io.write("#{current_archive_checksum}\t#{period.archive_json_file}")
|
||||
io.put_next_entry('chained.sha256')
|
||||
io.write("#{chained}\t#{date}")
|
||||
end
|
||||
|
||||
NotificationCenter.call type: :notify_admin_archive_complete,
|
||||
receiver: User.find(period.closed_by),
|
||||
attached_object: period
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def to_json_archive(period, invoices, previous_file, last_checksum)
|
||||
code_checksum = Checksum.code
|
||||
ApplicationController.new.view_context.render(
|
||||
partial: 'archive/accounting',
|
||||
locals: {
|
||||
invoices: period.invoices_with_vat(invoices),
|
||||
period_total: period.period_total,
|
||||
perpetual_total: period.perpetual_total,
|
||||
period_footprint: period.footprint,
|
||||
code_checksum: code_checksum,
|
||||
last_archive_checksum: last_checksum,
|
||||
previous_file: previous_file,
|
||||
software_version: Version.current,
|
||||
date: Time.now.iso8601
|
||||
},
|
||||
formats: [:json],
|
||||
handlers: [:jbuilder]
|
||||
)
|
||||
end
|
||||
end
|
@ -424,7 +424,7 @@ en:
|
||||
confirm_close_START_END: "Do you really want to close the accounting period between {{START}} and {{END}}? Any subsequent changes will be impossible."
|
||||
period_must_match_fiscal_year: "A closing must occur at the end of a minimum annual period, or per financial year when it is not calendar-based."
|
||||
this_may_take_a_while: "This operation will take some time to complete."
|
||||
period_START_END_closed_success: "The accounting period from {{START}} to {{END}} has been successfully closed"
|
||||
period_START_END_closed_success: "The accounting period from {{START}} to {{END}} has been successfully closed. Archive generation is running, you'll be notified when it's done."
|
||||
failed_to_close_period: "An error occurred, unable to close the accounting period"
|
||||
no_periods: "No closings for now"
|
||||
|
||||
|
@ -424,7 +424,7 @@ es:
|
||||
confirm_close_START_END: "Do you really want to close the accounting period between {{START}} and {{END}}? Any subsequent changes will be impossible." # translation_missing
|
||||
period_must_match_fiscal_year: "A closing must occur at the end of a minimum annual period, or per financial year when it is not calendar-based." # translation_missing
|
||||
this_may_take_a_while: "This operation will take some time to complete." # translation_missing
|
||||
period_START_END_closed_success: "The accounting period from {{START}} to {{END}} has been successfully closed" # translation_missing
|
||||
period_START_END_closed_success: "The accounting period from {{START}} to {{END}} has been successfully closed. Archive generation is running, you'll be notified when it's done." # translation_missing
|
||||
failed_to_close_period: "An error occurred, unable to close the accounting period" # translation_missing
|
||||
no_periods: "No closings for now" # translation_missing
|
||||
|
||||
|
@ -424,7 +424,7 @@ fr:
|
||||
confirm_close_START_END: "Êtes-vous sur de vouloir clôturer la période comptable du {{START}} au {{END}} ? Toute modification ultérieure sera impossible."
|
||||
period_must_match_fiscal_year: "Une clôture doit intervenir à l'issue d'une période au minimum annuelle, ou par exercice lorsque celui-ci n'est pas calé sur l'année civile."
|
||||
this_may_take_a_while: "Cette opération va prendre un certain temps."
|
||||
period_START_END_closed_success: "La période comptable du {{START}} au {{END}} a bien été clôturée"
|
||||
period_START_END_closed_success: "La période comptable du {{START}} au {{END}} a bien été clôturée. La génération de l'archive est en cours, vous serez prévenu lorsque celle-ci sera terminée."
|
||||
failed_to_close_period: "Une erreur est survenue, impossible de clôturer la période comptable"
|
||||
no_periods: "Aucune clôture pour le moment"
|
||||
|
||||
|
@ -424,7 +424,7 @@ pt:
|
||||
confirm_close_START_END: "Do you really want to close the accounting period between {{START}} and {{END}}? Any subsequent changes will be impossible" # translation_missing
|
||||
period_must_match_fiscal_year: "A closing must occur at the end of a minimum annual period, or per financial year when it is not calendar-based." # translation_missing
|
||||
this_may_take_a_while: "This operation will take some time to complete." # translation_missing
|
||||
period_START_END_closed_success: "The accounting period from {{START}} to {{END}} has been successfully closed" # translation_missing
|
||||
period_START_END_closed_success: "The accounting period from {{START}} to {{END}} has been successfully closed. Archive generation is running, you'll be notified when it's done." # translation_missing
|
||||
failed_to_close_period: "An error occurred, unable to close the accounting period" # translation_missing
|
||||
no_periods: "No closings for now" # translation_missing
|
||||
|
||||
|
@ -316,6 +316,8 @@ en:
|
||||
notify_admin_close_period_reminder:
|
||||
warning_last_closed_period_over_1_year: "Please remind to periodically close your accounting periods. Last closed period ended at %{LAST_END}"
|
||||
warning_no_closed_periods: "Please remind to periodically close your accounting periods. You have to close periods from %{FIRST_DATE}"
|
||||
notify_admin_archive_complete:
|
||||
archive_complete: "Data archiving from %{START} to %{END} is done. <a href='api/accounting_periods/%{ID}/archive' target='_blank'>click here to download</a>. Remember to save it on an external secured media."
|
||||
|
||||
statistics:
|
||||
# statistics tools for admins
|
||||
|
@ -316,6 +316,8 @@ es:
|
||||
notify_admin_close_period_reminder:
|
||||
warning_last_closed_period_over_1_year: "Please remind to periodically close your accounting periods. Last closed period ended at %{LAST_END}" # missing translation
|
||||
warning_no_closed_periods: "Please remind to periodically close your accounting periods. You have to close periods from %{FIRST_DATE}" # missing translation
|
||||
notify_admin_archive_complete: # missing translation
|
||||
archive_complete: "Data archiving from %{START} to %{END} is done. <a href='api/accounting_periods/%{ID}/archive' target='_blank'>click here to download</a>. Remember to save it on an external secured media." # missing translation
|
||||
|
||||
statistics:
|
||||
# statistics tools for admins
|
||||
|
@ -316,6 +316,8 @@ fr:
|
||||
notify_admin_close_period_reminder:
|
||||
warning_last_closed_period_over_1_year: "Pensez à clôturer régulièrement vos périodes comptables. Les comptes sont actuellement clôturés jusqu'au %{LAST_END}"
|
||||
warning_no_closed_periods: "Pensez à clôturer régulièrement vos périodes comptables. Vous devez clôturer des périodes depuis le %{FIRST_DATE}"
|
||||
notify_admin_archive_complete:
|
||||
archive_complete: "L'archivage des données du %{START} au %{END} est terminé. <a href='api/accounting_periods/%{ID}/archive' target='_blank'>Cliquez ici pour la télécharger</a>. Pensez à l'enregistrer sur un support externe sécurisé."
|
||||
|
||||
statistics:
|
||||
# outil de statistiques pour les administrateurs
|
||||
|
@ -286,5 +286,13 @@ en:
|
||||
warning_last_closed_period_over_1_year: "Please remind to periodically close your accounting periods. Last closed period ended at %{LAST_END}."
|
||||
warning_no_closed_periods: "Please remind to periodically close your accounting periods. You have to close periods from %{FIRST_DATE}."
|
||||
|
||||
notify_admin_archive_complete:
|
||||
subject: "Archiving completed"
|
||||
body:
|
||||
archive_complete: "You have closed the accounting period from %{START} to %{END}. Archiving of data is now complete."
|
||||
click_to_download: "To download the ZIP archive, click"
|
||||
here: "here."
|
||||
save_on_secured: "Remember that you must save this archive on a secured external support, which may be requested by the tax authorities during a check."
|
||||
|
||||
shared:
|
||||
hello: "Hello %{user_name}"
|
||||
|
@ -285,5 +285,13 @@ es:
|
||||
warning_last_closed_period_over_1_year: "Please remind to periodically close your accounting periods. Last closed period ended at %{LAST_END}."
|
||||
warning_no_closed_periods: "Please remind to periodically close your accounting periods. You have to close periods from %{FIRST_DATE}."
|
||||
|
||||
notify_admin_archive_complete: #translation_missing
|
||||
subject: "Archiving completed"
|
||||
body:
|
||||
archive_complete: "You have closed the accounting period from %{START} to %{END}. Archiving of data is now complete."
|
||||
click_to_download: "To download the ZIP archive, click"
|
||||
here: "here."
|
||||
save_on_secured: "Remember that you must save this archive on a secured external support, which may be requested by the tax authorities during a check."
|
||||
|
||||
shared:
|
||||
hello: "¡Hola %{user_name}!"
|
||||
|
@ -286,5 +286,13 @@ fr:
|
||||
warning_last_closed_period_over_1_year: "Pensez à clôturer régulièrement vos périodes comptables. Les comptes sont actuellement clôturés jusqu'au %{LAST_END}."
|
||||
warning_no_closed_periods: "Pensez à clôturer régulièrement vos périodes comptables. Vous devez clôturer des périodes depuis le %{FIRST_DATE}."
|
||||
|
||||
notify_admin_archive_complete:
|
||||
subject: "Archivage terminé"
|
||||
body:
|
||||
archive_complete: "Vous avez clôturé la période comptable du %{START} au %{END}. L'archivage des données est maintenant terminé."
|
||||
click_to_download: "Pour télécharger l'archive ZIP, cliquez"
|
||||
here: "ici."
|
||||
save_on_secured: "N'oubliez pas que vous devez obligatoirement enregistrer cette archive sur un support externe sécurisé, qui peut vous être demandé par l'administration fiscale lors d'un contrôle."
|
||||
|
||||
shared:
|
||||
hello: "Bonjour %{user_name}"
|
||||
|
@ -286,5 +286,13 @@ pt:
|
||||
warning_last_closed_period_over_1_year: "Please remind to periodically close your accounting periods. Last closed period ended at %{LAST_END}."
|
||||
warning_no_closed_periods: "Please remind to periodically close your accounting periods. You have to close periods from %{FIRST_DATE}."
|
||||
|
||||
notify_admin_archive_complete: #translation_missing
|
||||
subject: "Archiving completed"
|
||||
body:
|
||||
archive_complete: "You have closed the accounting period from %{START} to %{END}. Archiving of data is now complete."
|
||||
click_to_download: "To download the ZIP archive, click"
|
||||
here: "here."
|
||||
save_on_secured: "Remember that you must save this archive on a secured external support, which may be requested by the tax authorities during a check."
|
||||
|
||||
shared:
|
||||
hello: "Olá %{user_name}"
|
||||
|
@ -316,6 +316,8 @@ pt:
|
||||
notify_admin_close_period_reminder:
|
||||
warning_last_closed_period_over_1_year: "Please remind to periodically close your accounting periods. Last closed period ended at %{LAST_END}" # missing translation
|
||||
warning_no_closed_periods: "Please remind to periodically close your accounting periods. You have to close periods from %{FIRST_DATE}" # missing translation
|
||||
notify_admin_archive_complete: # missing translation
|
||||
archive_complete: "Data archiving from %{START} to %{END} is done. <a href='api/accounting_periods/%{ID}/archive' target='_blank'>click here to download</a>. Remember to save it on an external secured media." # missing translation
|
||||
|
||||
statistics:
|
||||
# statistics tools for admins
|
||||
|
Loading…
x
Reference in New Issue
Block a user