dorsale 3.9.3 → 3.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/app/controllers/dorsale/billing_machine/application_controller.rb +14 -0
  4. data/app/controllers/dorsale/billing_machine/invoices_controller.rb +16 -12
  5. data/app/controllers/dorsale/billing_machine/quotations_controller.rb +32 -2
  6. data/app/{commands/dorsale/flyboy/task_commands.rb → crons/dorsale/flyboy/task_crons.rb} +1 -1
  7. data/app/mailers/dorsale/generic_mailer.rb +9 -0
  8. data/app/models/dorsale/billing_machine/email.rb +33 -0
  9. data/app/models/dorsale/billing_machine/invoice.rb +7 -2
  10. data/app/models/dorsale/billing_machine/quotation.rb +7 -2
  11. data/app/models/dorsale/email.rb +80 -0
  12. data/app/policies/dorsale/billing_machine/invoice_policy_helper.rb +1 -5
  13. data/app/policies/dorsale/billing_machine/quotation_policy_helper.rb +3 -0
  14. data/app/services/dorsale/billing_machine/pdf_file_generator.rb +6 -2
  15. data/app/views/dorsale/_actions.html.slim +1 -1
  16. data/app/views/dorsale/billing_machine/commons/_details.html.slim +121 -0
  17. data/app/views/dorsale/billing_machine/commons/_email.html.slim +16 -0
  18. data/app/views/dorsale/billing_machine/commons/_form.html.slim +115 -0
  19. data/app/views/dorsale/billing_machine/commons/_header_infos.html.slim +9 -0
  20. data/app/views/dorsale/billing_machine/{invoices → commons}/_line_details.html.slim +0 -0
  21. data/app/views/dorsale/billing_machine/commons/_line_fields.html.slim +27 -0
  22. data/app/views/dorsale/billing_machine/commons/_preview_button.html.slim +25 -0
  23. data/app/views/dorsale/billing_machine/invoices/_details.html.slim +1 -126
  24. data/app/views/dorsale/billing_machine/invoices/_form.html.slim +1 -115
  25. data/app/views/dorsale/billing_machine/invoices/_header_infos.html.slim +1 -9
  26. data/app/views/dorsale/billing_machine/invoices/_line_fields.html.slim +1 -27
  27. data/app/views/dorsale/billing_machine/invoices/_show_title.html.slim +2 -7
  28. data/app/views/dorsale/billing_machine/invoices/email.html.slim +1 -51
  29. data/app/views/dorsale/billing_machine/quotations/_details.html.slim +6 -1
  30. data/app/views/dorsale/billing_machine/quotations/_form.html.slim +1 -1
  31. data/app/views/dorsale/billing_machine/quotations/_header_infos.html.slim +1 -1
  32. data/app/views/dorsale/billing_machine/quotations/_line_fields.html.slim +1 -1
  33. data/app/views/dorsale/billing_machine/quotations/_show_actions.html.slim +4 -1
  34. data/app/views/dorsale/billing_machine/quotations/_show_title.html.slim +7 -1
  35. data/app/views/dorsale/billing_machine/quotations/email.html.slim +1 -0
  36. data/app/views/dorsale/forms/_send_email_buttons.html.slim +10 -0
  37. data/config/locales/billing_machine.en.yml +10 -5
  38. data/config/locales/billing_machine.fr.yml +10 -4
  39. data/config/locales/dorsale.en.yml +11 -0
  40. data/config/locales/dorsale.fr.yml +11 -0
  41. data/config/routes.rb +11 -4
  42. data/config/schedule.rb +1 -1
  43. data/features/billing_machine_invoices.feature +7 -0
  44. data/features/billing_machine_quotations.feature +14 -0
  45. data/features/step_definitions/billing_machine_invoices_steps.rb +17 -0
  46. data/features/step_definitions/billing_machine_quotations_steps.rb +40 -0
  47. data/features/step_definitions/flyboy_tasks_steps.rb +1 -1
  48. data/lib/dorsale/version.rb +1 -1
  49. data/spec/controllers/dorsale/billing_machine/invoices_controller_spec.rb +16 -0
  50. data/spec/controllers/dorsale/billing_machine/quotations_controller_spec.rb +19 -0
  51. data/spec/models/dorsale/billing_machine/invoice_spec.rb +4 -0
  52. data/spec/models/dorsale/billing_machine/quotation_spec.rb +5 -1
  53. data/spec/models/dorsale/expense_gun/expense_line_spec.rb +1 -1
  54. data/spec/rails_helper.rb +1 -4
  55. data/spec/routing/dorsale/billing_machine/invoices_routing_spec.rb +5 -0
  56. data/spec/routing/dorsale/billing_machine/quotations_routing_spec.rb +15 -0
  57. metadata +15 -6
  58. data/app/mailers/dorsale/billing_machine/invoice_mailer.rb +0 -13
  59. data/app/views/dorsale/billing_machine/quotations/_line_details.html.slim +0 -1
@@ -1,27 +1 @@
1
- tr.line
2
- td.actions
3
- a.delete href="#"
4
- span.fa.fa-fw.fa-trash
5
- = f.hidden_field :_destroy
6
-
7
- td.line-label
8
- = f.text_area :label, rows: 1
9
-
10
- td.line-quantity
11
- = f.text_field :quantity, class: "number"
12
-
13
- td.line-unit
14
- = f.text_field :unit
15
-
16
- - if ::Dorsale::BillingMachine.vat_mode == :multiple
17
- td.line-vat_rate
18
- = f.text_field :vat_rate, class: "number"
19
- span.unit = "%"
20
-
21
- td.line-unit_price
22
- = f.text_field :unit_price, class: "number"
23
- span.unit = Dorsale::BillingMachine.default_currency
24
-
25
- td.line-total
26
- = f.text_field :total, class: "number", disabled: true
27
- span.unit = Dorsale::BillingMachine.default_currency
1
+ = render "dorsale/billing_machine/commons/line_fields", f: f
@@ -3,10 +3,5 @@
3
3
  = " n° "
4
4
  = document.tracking_id
5
5
 
6
- - if document.is_a?(Dorsale::BillingMachine::Invoice)
7
- .label class="invoice #{document.payment_status}"
8
- = document.t("payment_status.#{document.payment_status}")
9
-
10
- - if document.is_a?(Dorsale::BillingMachine::Quotation)
11
- .label class="quotation #{quotation_state_classes(document)}"
12
- = document.t("state.#{document.state}")
6
+ .label class="invoice #{document.payment_status}"
7
+ = document.t("payment_status.#{document.payment_status}")
@@ -1,51 +1 @@
1
- .billing_machine
2
- .panel.panel-default
3
- .panel-heading: .panel-title = t("actions.email")
4
-
5
- .panel-body
6
- = horizontal_form_for :email do |f|
7
- = f.input(:subject,
8
- :as => :string,
9
- :label => t("attributes.email_subject"),
10
- :input_html => {value: @subject},
11
- )
12
-
13
- = f.input(:from,
14
- :disabled => true,
15
- :label => t("attributes.email_from"),
16
- :input_html => {value: "#{current_user} <#{current_user.email}>"},
17
- )
18
-
19
- = f.input(:to,
20
- :disabled => true,
21
- :label => t("attributes.email_to"),
22
- :input_html => {value: "#{@invoice.customer} <#{@invoice.customer.email}>"},
23
- )
24
-
25
- = f.input(:attachment,
26
- :disabled => true,
27
- :label => t("attributes.email_attachment"),
28
- :input_html => {value: "#{@invoice.class.t}_#{@invoice.tracking_id}.pdf"},
29
- )
30
-
31
- = f.input(:body,
32
- :as => :text,
33
- :label => t("attributes.email_body"),
34
- :input_html => {value: @body},
35
- )
36
-
37
-
38
- .actions
39
- button.btn.btn-success type="submit"
40
- = icon(:send)
41
- = " "
42
- = t("actions.send")
43
-
44
- a.btn.btn-default href=dorsale.billing_machine_invoice_path(@invoice)
45
- = icon(:times)
46
- = " "
47
- = t("actions.cancel")
48
-
49
- hr
50
-
51
- = render "dorsale/billing_machine/invoices/details", document: @invoice
1
+ = render "dorsale/billing_machine/commons/email", document: @invoice
@@ -1 +1,6 @@
1
- = render "dorsale/billing_machine/invoices/details", document: document
1
+ = render "dorsale/billing_machine/commons/details", document: document
2
+
3
+
4
+ - if action_name == "show"
5
+ .attachments-panel
6
+ = attachments_for(document)
@@ -1 +1 @@
1
- = render "dorsale/billing_machine/invoices/form", document: @quotation
1
+ = render "dorsale/billing_machine/commons/form", document: @quotation
@@ -1 +1 @@
1
- = render "dorsale/billing_machine/invoices/header_infos"
1
+ = render "dorsale/billing_machine/commons/header_infos"
@@ -1 +1 @@
1
- = render "dorsale/billing_machine/invoices/line_fields", f: f
1
+ = render "dorsale/billing_machine/commons/line_fields", f: f
@@ -7,7 +7,10 @@
7
7
  - if policy(@quotation).copy?
8
8
  = copy_button dorsale.copy_billing_machine_quotation_path(@quotation), method: :post, confirm: true
9
9
 
10
- - if policy(@quotation).create?
10
+ - if policy(@quotation).email?
11
+ = bs_button dorsale.email_billing_machine_quotation_path(@quotation), action: :email, icon: :send
12
+
13
+ - if policy(@quotation).create_invoice?
11
14
  = bs_button dorsale.create_invoice_billing_machine_quotation_path(@quotation), action: :create_invoice, icon: :magic
12
15
 
13
16
  - if policy(@quotation).delete?
@@ -1 +1,7 @@
1
- = render "dorsale/billing_machine/invoices/show_title", document: document
1
+ = document.t
2
+ - if document.tracking_id.present?
3
+ = " n° "
4
+ = document.tracking_id
5
+
6
+ .label class="quotation #{quotation_state_classes(document)}"
7
+ = document.t("state.#{document.state}")
@@ -0,0 +1 @@
1
+ = render "dorsale/billing_machine/commons/email", document: @quotation
@@ -0,0 +1,10 @@
1
+ .actions
2
+ button.btn.btn-success type="submit"
3
+ = icon(:send)
4
+ = " "
5
+ = t("actions.send")
6
+
7
+ a.btn.btn-default href=back_url
8
+ = icon(:times)
9
+ = " "
10
+ = t("actions.cancel")
@@ -72,9 +72,13 @@ en:
72
72
  canceled: "Canceled"
73
73
  not_canceled: "Not canceled"
74
74
 
75
- emails:
76
- invoices:
77
- send_invoice_to_customer: ""
75
+ billing_machine:
76
+ emails:
77
+ invoice:
78
+ body: ""
79
+
80
+ quotation:
81
+ body: ""
78
82
 
79
83
  messages:
80
84
  invoices:
@@ -85,7 +89,7 @@ en:
85
89
  pay_ok : "This invoice has been marked payed."
86
90
  pay_error : "Impossible to mark this invoice as payed."
87
91
  email_ok : "Invoice successfully sent to customer."
88
- email_error : "Unable to send invoice to customer, please verify customer email."
92
+ email_error : "Unable to send invoice to customer."
89
93
 
90
94
  quotations:
91
95
  create_ok : "Quotation successfully created."
@@ -94,4 +98,5 @@ en:
94
98
  update_error : "Impossible to update this quotation."
95
99
  copy_ok : "Quotation successfully duplicated."
96
100
  create_invoice_ok : "Invoice successfully created from quotation."
97
-
101
+ email_ok : "Quotation successfully sent to customer."
102
+ email_error : "Unable to send quotation to customer."
@@ -72,9 +72,13 @@ fr:
72
72
  canceled: "Annulé"
73
73
  not_canceled: "Non annulé"
74
74
 
75
- emails:
76
- invoices:
77
- send_invoice_to_customer: "Bonjour,\n\nVeuillez-trouver ci-joint votre facture.\n\nCordialement,\n%{from}"
75
+ billing_machine:
76
+ emails:
77
+ invoice:
78
+ body: "Bonjour,\n\nVeuillez-trouver ci-joint votre facture.\n\nCordialement,\n%{from}"
79
+
80
+ quotation:
81
+ body: "Bonjour,\n\nVeuillez-trouver ci-joint votre devis.\n\nCordialement,\n%{from}"
78
82
 
79
83
  messages:
80
84
  invoices:
@@ -85,7 +89,7 @@ fr:
85
89
  pay_ok : "La facture a été marquée comme payée."
86
90
  pay_error : "Impossible de marquer la facture comme payée."
87
91
  email_ok : "La facture a été envoyée au client."
88
- email_error : "Impossible d'envoyer la facture au client, vérifiez son adresse email."
92
+ email_error : "Impossible d'envoyer la facture au client."
89
93
 
90
94
  quotations:
91
95
  create_ok : "Le devis a été créé."
@@ -94,3 +98,5 @@ fr:
94
98
  update_error : "Erreur dans la modification du devis."
95
99
  copy_ok : "Le devis a été dupliqué."
96
100
  create_invoice_ok : "La facture a été créée depuis le devis"
101
+ email_ok : "Le devis a été envoyée au client."
102
+ email_error : "Impossible d'envoyer le devis au client."
@@ -40,3 +40,14 @@ en:
40
40
  dorsale/comment:
41
41
  title: "Title (optional)"
42
42
  text: "Comment"
43
+
44
+ activemodel:
45
+ attributes:
46
+ dorsale/email:
47
+ from: "From"
48
+ to: "To"
49
+ cc: "Copy"
50
+ subject: "Subject"
51
+ body: "Message"
52
+ attachments: "Attachments"
53
+ attachment_names: "Attachments"
@@ -40,3 +40,14 @@ fr:
40
40
  dorsale/comment:
41
41
  title: "Titre (facultatif)"
42
42
  text: "Commentaire"
43
+
44
+ activemodel:
45
+ attributes:
46
+ dorsale/email:
47
+ from: "Expéditeur"
48
+ to: "Destinataire"
49
+ cc: "Copie cachée"
50
+ subject: "Objet"
51
+ body: "Message"
52
+ attachments: "Pièces jointes"
53
+ attachment_names: "Pièces jointes"
@@ -34,6 +34,9 @@ Dorsale::Engine.routes.draw do
34
34
  resources :payment_terms, except: [:destroy, :show]
35
35
 
36
36
  resources :invoices, except: [:destroy] do
37
+ collection do
38
+ post :preview
39
+ end
37
40
  member do
38
41
  get :copy
39
42
  patch :pay
@@ -42,8 +45,14 @@ Dorsale::Engine.routes.draw do
42
45
  end
43
46
 
44
47
  resources :quotations do
45
- post :copy, on: :member
46
- get :create_invoice, on: :member
48
+ collection do
49
+ post :preview
50
+ end
51
+ member do
52
+ post :copy
53
+ get :create_invoice
54
+ match :email, via: [:get, :post]
55
+ end
47
56
  end
48
57
  end
49
58
 
@@ -53,7 +62,6 @@ Dorsale::Engine.routes.draw do
53
62
  get "customer_vault/individuals" => "customer_vault/people#individuals"
54
63
 
55
64
  namespace :customer_vault do
56
-
57
65
  resources :activity_types, except: [:destroy, :show]
58
66
  resources :origins, except: [:destroy, :show]
59
67
 
@@ -97,5 +105,4 @@ Dorsale::Engine.routes.draw do
97
105
 
98
106
  get "/" => redirect{ ExpenseGun::Engine.routes.url_helpers.expenses_path }
99
107
  end
100
-
101
108
  end
@@ -1,5 +1,5 @@
1
1
  if ENV["RAILS_ENV"] == "production"
2
2
  every :day, at: "7 am" do
3
- runner "Dorsale::Flyboy::TaskCommands.send_daily_term_emails"
3
+ runner "Dorsale::Flyboy::TaskCrons.send_daily_term_emails!"
4
4
  end
5
5
  end
@@ -48,6 +48,13 @@ Feature: Invoice Management
48
48
  When the user goes to the invoice details
49
49
  Then he can see all the informations
50
50
 
51
+ Scenario: Invoice preview
52
+ When the user goes to the invoices page
53
+ And he creates a new invoice
54
+ And he click on the preview invoice button
55
+ Then he see the invoice preview
56
+ And no invoice is created
57
+
51
58
  Scenario: New invoice for existing customer
52
59
  And an existing customer
53
60
  And an existing payment term
@@ -35,12 +35,26 @@ Feature: Quotation Management
35
35
  When the user goes to the quotation details
36
36
  Then he can see all the quotation informations
37
37
 
38
+ Scenario: Quotation preview
39
+ When the user goes to the quotations page
40
+ And he creates a new quotation
41
+ And he click on the preview quotation button
42
+ Then he see the quotation preview
43
+ And no quotation is created
44
+
38
45
  Scenario: Quotation copy
39
46
  Given an existing quotation
40
47
  When the user goes to the quotation details
41
48
  And he copy the quotation
42
49
  Then he is on the created quotation edit page
43
50
 
51
+ Scenario: Send quotation by email
52
+ Given an existing customer
53
+ And an existing quotation
54
+ When the user goes to the quotation details
55
+ And he send quotation to customer by email
56
+ Then an quotation is sent to customer
57
+
44
58
  Scenario: Quotation to invoice
45
59
  Given an existing quotation
46
60
  When the user goes to the quotation details
@@ -358,3 +358,20 @@ When(/^he filters invoices between two date$/) do
358
358
  fill_in :filters_bm_date_end, with: I18n.l(Date.current)
359
359
  find(".filter-submit").click
360
360
  end
361
+
362
+ When(/^he click on the preview invoice button$/) do
363
+ @invoices_count = Dorsale::BillingMachine::Invoice.count
364
+ controller = Dorsale::BillingMachine::InvoicesController
365
+ expect_any_instance_of(controller).to receive(:preview).and_call_original
366
+ expect_any_instance_of(controller).to_not receive(:create)
367
+ expect_any_instance_of(controller).to_not receive(:update)
368
+ find("#preview-button").click
369
+ end
370
+
371
+ Then(/^he see the invoice preview$/) do
372
+ expect(windows.count).to eq 2
373
+ end
374
+
375
+ Then(/^no invoice is created$/) do
376
+ expect(Dorsale::BillingMachine::Invoice.count).to eq @invoices_count
377
+ end
@@ -208,3 +208,43 @@ Then(/^only the "(.*?)" quotations appear$/) do |state|
208
208
  expect(page).to have_selector("tr.quotation", count: 1)
209
209
  expect(find("tr.quotation")).to have_content(state_i18n)
210
210
  end
211
+
212
+ When(/^he send quotation to customer by email$/) do
213
+ ActionMailer::Base.deliveries.clear
214
+
215
+ Dorsale::BillingMachine::PdfFileGenerator.(@quotation)
216
+ @quotation.customer = create(:customer_vault_corporation, email: "aaa@example.org")
217
+ @quotation.save!
218
+
219
+ find("[href$=email]").click
220
+ fill_in :email_subject, with: "abc"
221
+ fill_in :email_body, with: "def"
222
+ find("[type=submit]").click
223
+ end
224
+
225
+ Then(/^an quotation is sent to customer$/) do
226
+ expect(ActionMailer::Base.deliveries.count).to eq 1
227
+ email = ActionMailer::Base.deliveries.first
228
+
229
+ expect(email.to).to include "aaa@example.org"
230
+ expect(email.subject).to eq "abc"
231
+ expect(email.parts.first.body).to eq "def"
232
+ expect(email.attachments.count).to eq 1
233
+ end
234
+
235
+ When(/^he click on the preview quotation button$/) do
236
+ @invoices_count = Dorsale::BillingMachine::Quotation.count
237
+ controller = Dorsale::BillingMachine::QuotationsController
238
+ expect_any_instance_of(controller).to receive(:preview).and_call_original
239
+ expect_any_instance_of(controller).to_not receive(:create)
240
+ expect_any_instance_of(controller).to_not receive(:update)
241
+ find("#preview-button").click
242
+ end
243
+
244
+ Then(/^he see the quotation preview$/) do
245
+ expect(windows.count).to eq 2
246
+ end
247
+
248
+ Then(/^no quotation is created$/) do
249
+ expect(Dorsale::BillingMachine::Quotation.count).to eq @invoices_count
250
+ end
@@ -55,7 +55,7 @@ end
55
55
 
56
56
  When(/^the flyboy daily crons run$/) do
57
57
  ActionMailer::Base.deliveries.clear
58
- Dorsale::Flyboy::TaskCommands.send_daily_term_emails!
58
+ Dorsale::Flyboy::TaskCrons.send_daily_term_emails!
59
59
  end
60
60
 
61
61
  When(/^I create a task$/) do
@@ -1,3 +1,3 @@
1
1
  module Dorsale
2
- VERSION = "3.9.3"
2
+ VERSION = "3.9.4"
3
3
  end
@@ -6,6 +6,22 @@ describe Dorsale::BillingMachine::InvoicesController, type: :controller do
6
6
  let(:user) { create(:user) }
7
7
  before(:each) { sign_in(user) }
8
8
 
9
+ describe "#preview" do
10
+ render_views
11
+
12
+ it "should render show" do
13
+ post :preview
14
+ expect(response).to render_template(:show)
15
+ end
16
+
17
+ it "should not save" do
18
+ post :preview
19
+ @invoice = assigns(:invoice)
20
+ expect(@invoice).to be_valid
21
+ expect(@invoice).to_not be_persisted
22
+ end
23
+ end # describe "#preview" do
24
+
9
25
  describe "XLSX export" do
10
26
  render_views
11
27