From 86ded2b8d2aecaa16de3534f267fdaa70a22ecb1 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 11 Jun 2019 16:56:11 +0200 Subject: [PATCH] [ongoing] fixes for invoices pdf files --- app/assets/javascripts/controllers/members.js | 12 ++++++-- .../templates/admin/invoices/index.html.erb | 4 ++- .../templates/dashboard/settings.html.erb | 2 +- app/controllers/api/members_controller.rb | 2 +- app/models/invoice.rb | 9 ++++-- app/models/reservation.rb | 4 +-- app/models/subscription.rb | 29 ++++++++++++++++--- app/models/user.rb | 12 -------- app/pdfs/pdf/invoice.rb | 6 ++-- app/services/members/members_service.rb | 7 +++++ app/services/subscriptions/subscribe.rb | 2 +- config/locales/app.logged.en.yml | 7 +++-- config/locales/app.logged.es.yml | 7 +++-- config/locales/app.logged.fr.yml | 7 +++-- config/locales/app.logged.pt.yml | 17 ++++++----- lib/tasks/fablab/maintenance.rake | 4 +-- lib/tasks/fablab/setup.rake | 17 +++++++++++ 17 files changed, 103 insertions(+), 45 deletions(-) diff --git a/app/assets/javascripts/controllers/members.js b/app/assets/javascripts/controllers/members.js index 6a5d44698..bfef747c2 100644 --- a/app/assets/javascripts/controllers/members.js +++ b/app/assets/javascripts/controllers/members.js @@ -72,8 +72,8 @@ Application.Controllers.controller('MembersController', ['$scope', 'Member', 'me /** * Controller used when editing the current user's profile */ -Application.Controllers.controller('EditProfileController', ['$scope', '$rootScope', '$state', '$window', 'Member', 'Auth', 'Session', 'activeProviderPromise', 'growl', 'dialogs', 'CSRF', 'memberPromise', 'groups', '_t', - function ($scope, $rootScope, $state, $window, Member, Auth, Session, activeProviderPromise, growl, dialogs, CSRF, memberPromise, groups, _t) { +Application.Controllers.controller('EditProfileController', ['$scope', '$rootScope', '$state', '$window', '$sce', 'Member', 'Auth', 'Session', 'activeProviderPromise', 'growl', 'dialogs', 'CSRF', 'memberPromise', 'groups', '_t', + function ($scope, $rootScope, $state, $window, $sce, Member, Auth, Session, activeProviderPromise, growl, dialogs, CSRF, memberPromise, groups, _t) { /* PUBLIC SCOPE */ // API URL where the form will be posted @@ -194,7 +194,13 @@ Application.Controllers.controller('EditProfileController', ['$scope', '$rootSco object () { return { title: _t('confirmation_required'), - msg: _t('do_you_really_want_to_delete_your_account') + ' ' + _t('all_data_relative_to_your_projects_will_be_lost') + msg: $sce.trustAsHtml( + _t('confirm_delete_your_account') + '
' + + '' + _t('all_data_will_be_lost') + '

' + + _t('invoicing_data_kept') + '
' + + _t('statistic_data_anonymized') + '
' + + _t('no_further_access_to_projects') + ) }; } } diff --git a/app/assets/templates/admin/invoices/index.html.erb b/app/assets/templates/admin/invoices/index.html.erb index 0f12a229b..6ab3ab14e 100644 --- a/app/assets/templates/admin/invoices/index.html.erb +++ b/app/assets/templates/admin/invoices/index.html.erb @@ -83,7 +83,9 @@ {{ invoice.date | amDateFormat:'L LTS' }} {{ invoice.date | amDateFormat:'L' }} {{ invoice.total | currency}} - {{ invoice.name }} + + {{ invoice.name }} + {{ invoice.name }}
diff --git a/app/assets/templates/dashboard/settings.html.erb b/app/assets/templates/dashboard/settings.html.erb index 87721459e..64f35d3b5 100644 --- a/app/assets/templates/dashboard/settings.html.erb +++ b/app/assets/templates/dashboard/settings.html.erb @@ -78,7 +78,7 @@
- +
diff --git a/app/controllers/api/members_controller.rb b/app/controllers/api/members_controller.rb index 47c32369a..039280418 100644 --- a/app/controllers/api/members_controller.rb +++ b/app/controllers/api/members_controller.rb @@ -65,7 +65,7 @@ class API::MembersController < API::ApiController def destroy authorize @member - @member.soft_destroy + @member.destroy sign_out(@member) head :no_content end diff --git a/app/models/invoice.rb b/app/models/invoice.rb index 3395536a9..0e8de8dbe 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -31,9 +31,9 @@ class Invoice < ActiveRecord::Base validates_with ClosedPeriodValidator def file - dir = "invoices/#{user.id}" + dir = "invoices/#{invoicing_profile.id}" - # create directories if they doesn't exists (invoice & user_id) + # create directories if they doesn't exists (invoice & invoicing_profile_id) FileUtils.mkdir_p dir "#{dir}/#{filename}" end @@ -142,7 +142,7 @@ class Invoice < ActiveRecord::Base # for debug & used by rake task "fablab:maintenance:regenerate_invoices" def regenerate_invoice_pdf - pdf = ::PDF::Invoice.new(self, nil).render + pdf = ::PDF::Invoice.new(self, subscription&.expiration_date).render File.binwrite(file, pdf) end @@ -211,9 +211,12 @@ class Invoice < ActiveRecord::Base ## # Check if the current invoice is about a training that was previously validated for the concerned user. # In that case refunding the invoice shouldn't be allowed. + # Moreover, an invoice cannot be refunded if the users' account was deleted # @return {Boolean} ## def prevent_refund? + return true if user.nil? + if invoiced_type == 'Reservation' && invoiced.reservable_type == 'Training' user.trainings.include?(invoiced.reservable_id) else diff --git a/app/models/reservation.rb b/app/models/reservation.rb index 3d46a9e7d..dbab3aeb8 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -227,7 +227,7 @@ class Reservation < ActiveRecord::Base def save_with_payment(operator_id, coupon_code = nil) begin clean_pending_strip_invoice_items - build_invoice(invoicing_profile: user.invoicing_profile, operator_id: operator_id) + build_invoice(invoicing_profile: user.invoicing_profile, statistic_profile: user.statistic_profile, operator_id: operator_id) invoice_items = generate_invoice_items(false, coupon_code) rescue StandardError => e logger.error e @@ -369,7 +369,7 @@ class Reservation < ActiveRecord::Base end def save_with_local_payment(operator_id, coupon_code = nil) - build_invoice(invoicing_profile: user.invoicing_profile, operator_id: operator_id) + build_invoice(invoicing_profile: user.invoicing_profile, statistic_profile: user.statistic_profile, operator_id: operator_id) generate_invoice_items(true, coupon_code) return false unless valid? diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 191eee43e..aba6e8b97 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -165,8 +165,22 @@ class Subscription < ActiveRecord::Base end end - invoice = Invoice.new(invoiced_id: id, invoiced_type: 'Subscription', invoicing_profile: user.invoicing_profile, total: total, stp_invoice_id: stp_invoice_id, coupon_id: coupon_id, operator_id: operator_id) - invoice.invoice_items.push InvoiceItem.new(amount: plan.amount, stp_invoice_item_id: stp_subscription_id, description: plan.name, subscription_id: self.id) + invoice = Invoice.new( + invoiced_id: id, + invoiced_type: 'Subscription', + invoicing_profile: user.invoicing_profile, + statistic_profile: user.statistic_profile, + total: total, + stp_invoice_id: stp_invoice_id, + coupon_id: coupon_id, + operator_id: operator_id + ) + invoice.invoice_items.push InvoiceItem.new( + amount: plan.amount, + stp_invoice_item_id: stp_subscription_id, + description: plan.name, + subscription_id: id + ) invoice end @@ -208,11 +222,18 @@ class Subscription < ActiveRecord::Base expiration_date end - def free_extend(expiration) + def free_extend(expiration, operator_id) return false if expiration <= expired_at od = offer_days.create(start_at: expired_at, end_at: expiration) - invoice = Invoice.new(invoiced_id: od.id, invoiced_type: 'OfferDay', invoicing_profile: user.invoicing_profile, total: 0) + invoice = Invoice.new( + invoiced_id: od.id, + invoiced_type: 'OfferDay', + invoicing_profile: user.invoicing_profile, + statistic_profile: user.statistic_profile, + operator_id: operator_id, + total: 0 + ) invoice.invoice_items.push InvoiceItem.new(amount: 0, description: plan.name, subscription_id: id) invoice.save diff --git a/app/models/user.rb b/app/models/user.rb index 64d75a29c..f40dc1996 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -38,8 +38,6 @@ class User < ActiveRecord::Base has_many :training_credits, through: :users_credits, source: :training_credit has_many :machine_credits, through: :users_credits, source: :machine_credit - has_many :operated_invoices, foreign_key: :operator_id, class_name: 'Invoice', dependent: :nullify - has_many :user_tags, dependent: :destroy has_many :tags, through: :user_tags accepts_nested_attributes_for :tags, allow_destroy: true @@ -139,16 +137,6 @@ class User < ActiveRecord::Base Stripe::Customer.retrieve stp_customer_id end - def soft_destroy - update_attribute(:is_active, false) - uninvolve_from_projects - end - - def uninvolve_from_projects - my_projects.destroy_all - project_users.destroy_all - end - def active_for_authentication? super && is_active? end diff --git a/app/pdfs/pdf/invoice.rb b/app/pdfs/pdf/invoice.rb index dcb89866e..f4ff16764 100644 --- a/app/pdfs/pdf/invoice.rb +++ b/app/pdfs/pdf/invoice.rb @@ -131,14 +131,16 @@ class PDF::Invoice < Prawn::Document else subscription_end_at = if subscription_expiration_date.is_a?(Time) subscription_expiration_date - else + elsif subscription_expiration_date.is_a?(String) DateTime.parse(subscription_expiration_date) + else + subscription.expiration_date end subscription_start_at = subscription_end_at - subscription.plan.duration details += I18n.t('invoices.subscription_NAME_from_START_to_END', NAME: item.description, START: I18n.l(subscription_start_at.to_date), - END: I18n.l(subscription_expiration_date.to_date)) + END: I18n.l(subscription_end_at.to_date)) end diff --git a/app/services/members/members_service.rb b/app/services/members/members_service.rb index 51e0d36fd..24a84b898 100644 --- a/app/services/members/members_service.rb +++ b/app/services/members/members_service.rb @@ -30,6 +30,13 @@ class Members::MembersService # if the user is created by an admin and the authentication is made through an SSO, generate a migration token @member.generate_auth_migration_token if current_user.admin? && AuthProvider.active.providable_type != DatabaseProvider.name + # setup the attached profiles (invoicing & statistics) + @member.invoicing_profile.email = params[:email] + @member.invoicing_profile.first_name = params[:profile_attributes][:first_name] + @member.invoicing_profile.last_name = params[:profile_attributes][:last_name] + @member.statistic_profile.group_id = params[:group_id] + @member.statistic_profile.role_id = Role.find_by(name: 'member').id + if @member.save @member.generate_subscription_invoice(current_user.id) @member.send_confirmation_instructions diff --git a/app/services/subscriptions/subscribe.rb b/app/services/subscriptions/subscribe.rb index dd5b5a065..776e0c8e6 100644 --- a/app/services/subscriptions/subscribe.rb +++ b/app/services/subscriptions/subscribe.rb @@ -21,7 +21,7 @@ class Subscriptions::Subscribe end def extend_subscription(subscription, new_expiration_date, free_days) - return subscription.free_extend(new_expiration_date) if free_days + return subscription.free_extend(new_expiration_date, @operator_id) if free_days new_sub = Subscription.create( plan_id: subscription.plan_id, diff --git a/config/locales/app.logged.en.yml b/config/locales/app.logged.en.yml index 4c4d18317..67776c1d8 100644 --- a/config/locales/app.logged.en.yml +++ b/config/locales/app.logged.en.yml @@ -48,8 +48,11 @@ en: edit_my_profile: "Edit my profile" your_group_has_been_successfully_changed: "Your group has been successfully changed." an_unexpected_error_prevented_your_group_from_being_changed: "An unexpected error prevented your group from being changed." - do_you_really_want_to_delete_your_account: "Do you really want to delete your account?" - all_data_relative_to_your_projects_will_be_lost: "All data relative to your projects will be lost." + confirm_delete_your_account: "Do you really want to delete your account?" + all_data_will_be_lost: "All your data will be destroyed and won't be recoverable." + invoicing_data_kept: "According to regulation, all data related to your invoices will be kept separately for 10 years." + statistic_data_anonymized: "Some data (sex, date of birth, group) will be anonymized and kept for statistical purposes." + no_further_access_to_projects: "Your published projects will be anonymized and you won't get any further ability to edit them." your_user_account_has_been_successfully_deleted_goodbye: "Your user account has been successfully deleted. Goodbye." an_error_occured_preventing_your_account_from_being_deleted: "An error occurred, preventing your account from being deleted." projects: diff --git a/config/locales/app.logged.es.yml b/config/locales/app.logged.es.yml index 0362644aa..439b7c82f 100644 --- a/config/locales/app.logged.es.yml +++ b/config/locales/app.logged.es.yml @@ -48,8 +48,11 @@ es: edit_my_profile: "Editar mi perfil" your_group_has_been_successfully_changed: "Su grupo ha sido cambiado con exito." an_unexpected_error_prevented_your_group_from_being_changed: "Un error inesperado impidió que su grupo fuese cambiado." - do_you_really_want_to_delete_your_account: "¿Está seguro de querer eliminar su cuenta?" - all_data_relative_to_your_projects_will_be_lost: "Todo dato relacionado con sus proyectos se perderá" + confirm_delete_your_account: "¿Está seguro de querer eliminar su cuenta?" + all_data_will_be_lost: "All your data will be destroyed and won't be recoverable." # missing translation + invoicing_data_kept: "According to regulation, all data related to your invoices will be kept separately for 10 years." # missing translation + statistic_data_anonymized: "Some data (sex, date of birth, group) will be anonymized and kept for statistical purposes." # missing translation + no_further_access_to_projects: "Your published projects will be anonymized and you won't get any further ability to edit them." # missing translation your_user_account_has_been_successfully_deleted_goodbye: "Su cuenta ha sido eliminada con éxito. Adiós" an_error_occured_preventing_your_account_from_being_deleted: "Un error inesperado impidió que su cuenta fuese eliminada." projects: diff --git a/config/locales/app.logged.fr.yml b/config/locales/app.logged.fr.yml index e18ae5b7a..c13a57805 100644 --- a/config/locales/app.logged.fr.yml +++ b/config/locales/app.logged.fr.yml @@ -48,8 +48,11 @@ fr: edit_my_profile: "Éditer votre profil" your_group_has_been_successfully_changed: "Votre groupe a bien été changé." an_unexpected_error_prevented_your_group_from_being_changed: "Une erreur inattendue a empêché votre changement de groupe." - do_you_really_want_to_delete_your_account: "Êtes-vous sûr de vouloir supprimer votre compte ?" - all_data_relative_to_your_projects_will_be_lost: "Toutes les données relatives à vos projets seront détruites." + confirm_delete_your_account: "Êtes-vous sûr de vouloir supprimer votre compte ?" + all_data_will_be_lost: "Toutes vos données seront détruites et ne pourront pas être récupérées." + invoicing_data_kept: "Conformément à la réglementation, les données relatives à vos facturations seront conservées de manière séparée pendant 10 ans." + statistic_data_anonymized: "Certaines données (sexe, date de naissance, groupe) seront anonymisées et conservées à des fins statistiques." + no_further_access_to_projects: "Vos projets publiés seront anonymisés et vous n'aurez plus de possibilité de les modifier." your_user_account_has_been_successfully_deleted_goodbye: "Votre compte utilisateur a bien été supprimé. Au revoir." an_error_occured_preventing_your_account_from_being_deleted: "Une erreur est survenue qui a empêché la suppression de votre compte." projects: diff --git a/config/locales/app.logged.pt.yml b/config/locales/app.logged.pt.yml index 2aeff49c3..edd9ddec9 100755 --- a/config/locales/app.logged.pt.yml +++ b/config/locales/app.logged.pt.yml @@ -48,8 +48,11 @@ pt: edit_my_profile: "Editar meu perfil" your_group_has_been_successfully_changed: "Seu grupo foi modificado com sucesso." an_unexpected_error_prevented_your_group_from_being_changed: "Um erro inesperado impediu o seu grupo de ser alterado." - do_you_really_want_to_delete_your_account: "Você realmente deseja deletar sua conta?" - all_data_relative_to_your_projects_will_be_lost: "Todos os dados relativos aos seus projectos serão perdidos." + confirm_delete_your_account: "Você realmente deseja deletar sua conta?" + all_data_will_be_lost: "All your data will be destroyed and won't be recoverable." # missing translation + invoicing_data_kept: "According to regulation, all data related to your invoices will be kept separately for 10 years." # missing translation + statistic_data_anonymized: "Some data (sex, date of birth, group) will be anonymized and kept for statistical purposes." # missing translation + no_further_access_to_projects: "Your published projects will be anonymized and you won't get any further ability to edit them." # missing translation your_user_account_has_been_successfully_deleted_goodbye: "Sua conta de usuário foi excluída com êxito. Até mais." an_error_occured_preventing_your_account_from_being_deleted: "Ocorreu um erro, impedindo que sua conta fosse excluída." projects: @@ -98,19 +101,19 @@ pt: machines_reserve: # book a machine machine_planning: "Planejamento de máquinas" - i_ve_reserved: "Eu tenho reserva" + i_ve_reserved: "Eu tenho reserva" not_available: "Não disponível" i_reserve: "Eu reservo" i_shift: "Eu mudo" i_change: "Eu altero" - + trainings_reserve: # book a training trainings_planning: "Planos de treinamento" planning_of: "Planejamento de" # followed by the training name (eg. "Planning of 3d printer training") all_trainings: "Todos treinamentos" - cancel_my_selection: "Cancelar minha seleção" - i_ve_reserved: "Eu reservei" + cancel_my_selection: "Cancelar minha seleção" + i_ve_reserved: "Eu reservei" space_reserve: # book a space @@ -118,7 +121,7 @@ pt: planning_of_space_NAME: "Plano de {{NAME}} espaço" # angular interpolation i_ve_reserved: "Eu reservei" i_shift: "Eu troco" - i_change: "Eu altero" + i_change: "Eu altero" notifications: notifications_center: "Centro de notificações" diff --git a/lib/tasks/fablab/maintenance.rake b/lib/tasks/fablab/maintenance.rake index cda136058..e285eed57 100644 --- a/lib/tasks/fablab/maintenance.rake +++ b/lib/tasks/fablab/maintenance.rake @@ -9,8 +9,8 @@ namespace :fablab do month = args.month || Time.now.month start_date = Time.new(year.to_i, month.to_i, 1) end_date = start_date.next_month - puts "-> Start regenerate the invoices PDF between #{I18n.l start_date, format: :long} in " \ - " #{I18n.l end_date - 1.minute, format: :long}" + puts "-> Start regenerate the invoices PDF between #{I18n.l start_date, format: :long} and " \ + "#{I18n.l end_date - 1.minute, format: :long}" invoices = Invoice.only_invoice .where('created_at >= :start_date AND created_at < :end_date', start_date: start_date, end_date: end_date) .order(created_at: :asc) diff --git a/lib/tasks/fablab/setup.rake b/lib/tasks/fablab/setup.rake index 5920a4d65..74363edca 100644 --- a/lib/tasks/fablab/setup.rake +++ b/lib/tasks/fablab/setup.rake @@ -79,5 +79,22 @@ namespace :fablab do ) end end + + desc 'migrate invoices PDF to folders by invoicing_profile' + task migrate_invoices_pdf_folders: :environment do + require 'fileutils' + Invoice.all.each do |i| + invoicing_profile = i.invoicing_profile + user_id = invoicing_profile.user_id + + src = "invoices/#{user_id}/#{i.filename}" + dest = "tmp/invoices/#{invoicing_profile.id}" + + FileUtils.mkdir_p dest + FileUtils.mv src, "dest/#{i.filename}", force: true if FileTest.exist?(src) + end + FileUtils.rm_rf 'invoices' + FileUtils.mv 'tmp/invoices', 'invoices' + end end end