dorsale 3.9.3 → 3.9.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/app/controllers/dorsale/billing_machine/application_controller.rb +14 -0
- data/app/controllers/dorsale/billing_machine/invoices_controller.rb +16 -12
- data/app/controllers/dorsale/billing_machine/quotations_controller.rb +32 -2
- data/app/{commands/dorsale/flyboy/task_commands.rb → crons/dorsale/flyboy/task_crons.rb} +1 -1
- data/app/mailers/dorsale/generic_mailer.rb +9 -0
- data/app/models/dorsale/billing_machine/email.rb +33 -0
- data/app/models/dorsale/billing_machine/invoice.rb +7 -2
- data/app/models/dorsale/billing_machine/quotation.rb +7 -2
- data/app/models/dorsale/email.rb +80 -0
- data/app/policies/dorsale/billing_machine/invoice_policy_helper.rb +1 -5
- data/app/policies/dorsale/billing_machine/quotation_policy_helper.rb +3 -0
- data/app/services/dorsale/billing_machine/pdf_file_generator.rb +6 -2
- data/app/views/dorsale/_actions.html.slim +1 -1
- data/app/views/dorsale/billing_machine/commons/_details.html.slim +121 -0
- data/app/views/dorsale/billing_machine/commons/_email.html.slim +16 -0
- data/app/views/dorsale/billing_machine/commons/_form.html.slim +115 -0
- data/app/views/dorsale/billing_machine/commons/_header_infos.html.slim +9 -0
- data/app/views/dorsale/billing_machine/{invoices → commons}/_line_details.html.slim +0 -0
- data/app/views/dorsale/billing_machine/commons/_line_fields.html.slim +27 -0
- data/app/views/dorsale/billing_machine/commons/_preview_button.html.slim +25 -0
- data/app/views/dorsale/billing_machine/invoices/_details.html.slim +1 -126
- data/app/views/dorsale/billing_machine/invoices/_form.html.slim +1 -115
- data/app/views/dorsale/billing_machine/invoices/_header_infos.html.slim +1 -9
- data/app/views/dorsale/billing_machine/invoices/_line_fields.html.slim +1 -27
- data/app/views/dorsale/billing_machine/invoices/_show_title.html.slim +2 -7
- data/app/views/dorsale/billing_machine/invoices/email.html.slim +1 -51
- data/app/views/dorsale/billing_machine/quotations/_details.html.slim +6 -1
- data/app/views/dorsale/billing_machine/quotations/_form.html.slim +1 -1
- data/app/views/dorsale/billing_machine/quotations/_header_infos.html.slim +1 -1
- data/app/views/dorsale/billing_machine/quotations/_line_fields.html.slim +1 -1
- data/app/views/dorsale/billing_machine/quotations/_show_actions.html.slim +4 -1
- data/app/views/dorsale/billing_machine/quotations/_show_title.html.slim +7 -1
- data/app/views/dorsale/billing_machine/quotations/email.html.slim +1 -0
- data/app/views/dorsale/forms/_send_email_buttons.html.slim +10 -0
- data/config/locales/billing_machine.en.yml +10 -5
- data/config/locales/billing_machine.fr.yml +10 -4
- data/config/locales/dorsale.en.yml +11 -0
- data/config/locales/dorsale.fr.yml +11 -0
- data/config/routes.rb +11 -4
- data/config/schedule.rb +1 -1
- data/features/billing_machine_invoices.feature +7 -0
- data/features/billing_machine_quotations.feature +14 -0
- data/features/step_definitions/billing_machine_invoices_steps.rb +17 -0
- data/features/step_definitions/billing_machine_quotations_steps.rb +40 -0
- data/features/step_definitions/flyboy_tasks_steps.rb +1 -1
- data/lib/dorsale/version.rb +1 -1
- data/spec/controllers/dorsale/billing_machine/invoices_controller_spec.rb +16 -0
- data/spec/controllers/dorsale/billing_machine/quotations_controller_spec.rb +19 -0
- data/spec/models/dorsale/billing_machine/invoice_spec.rb +4 -0
- data/spec/models/dorsale/billing_machine/quotation_spec.rb +5 -1
- data/spec/models/dorsale/expense_gun/expense_line_spec.rb +1 -1
- data/spec/rails_helper.rb +1 -4
- data/spec/routing/dorsale/billing_machine/invoices_routing_spec.rb +5 -0
- data/spec/routing/dorsale/billing_machine/quotations_routing_spec.rb +15 -0
- metadata +15 -6
- data/app/mailers/dorsale/billing_machine/invoice_mailer.rb +0 -13
- data/app/views/dorsale/billing_machine/quotations/_line_details.html.slim +0 -1
@@ -1,27 +1 @@
|
|
1
|
-
|
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
|
-
|
7
|
-
.
|
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
|
-
|
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 @@
|
|
1
|
-
= render "dorsale/billing_machine/
|
1
|
+
= render "dorsale/billing_machine/commons/form", document: @quotation
|
@@ -1 +1 @@
|
|
1
|
-
= render "dorsale/billing_machine/
|
1
|
+
= render "dorsale/billing_machine/commons/header_infos"
|
@@ -1 +1 @@
|
|
1
|
-
= render "dorsale/billing_machine/
|
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).
|
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
|
-
=
|
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
|
@@ -72,9 +72,13 @@ en:
|
|
72
72
|
canceled: "Canceled"
|
73
73
|
not_canceled: "Not canceled"
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
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
|
-
|
76
|
-
|
77
|
-
|
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
|
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"
|
data/config/routes.rb
CHANGED
@@ -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
|
-
|
46
|
-
|
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
|
data/config/schedule.rb
CHANGED
@@ -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
|
data/lib/dorsale/version.rb
CHANGED
@@ -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
|
|