dorsale 3.14.8 → 3.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/app/assets/javascripts/dorsale/common/tags.coffee +1 -1
  4. data/app/assets/javascripts/dorsale/dependencies.coffee +1 -1
  5. data/app/assets/javascripts/dorsale/engines/billing_machine.coffee.erb +14 -0
  6. data/app/assets/stylesheets/dorsale/engines/billing_machine.sass +4 -1
  7. data/app/controllers/dorsale/billing_machine/invoices_controller.rb +14 -9
  8. data/app/controllers/dorsale/billing_machine/quotations_controller.rb +14 -9
  9. data/app/controllers/dorsale/customer_vault/events_controller.rb +0 -2
  10. data/app/controllers/dorsale/customer_vault/people_controller.rb +11 -5
  11. data/app/mailers/dorsale/flyboy/task_mailer.rb +2 -2
  12. data/app/models/dorsale/billing_machine/invoice.rb +2 -4
  13. data/app/models/dorsale/billing_machine/invoice_line.rb +1 -1
  14. data/app/models/dorsale/billing_machine/quotation.rb +2 -3
  15. data/app/models/dorsale/billing_machine/quotation_line.rb +1 -1
  16. data/app/models/dorsale/customer_vault/person.rb +7 -7
  17. data/app/pdfs/dorsale/billing_machine/invoice_single_vat_pdf.rb +2 -2
  18. data/app/policies/dorsale/application_policy.rb +1 -0
  19. data/app/services/dorsale/billing_machine/invoice/copy.rb +1 -0
  20. data/app/services/dorsale/billing_machine/pdf_file_generator.rb +1 -7
  21. data/app/services/dorsale/billing_machine/quotation/copy.rb +1 -0
  22. data/app/services/dorsale/billing_machine/quotation/to_invoice.rb +1 -0
  23. data/app/services/dorsale/expense_gun/expense/copy.rb +1 -0
  24. data/app/services/dorsale/flyboy/task/copy.rb +1 -0
  25. data/app/services/dorsale/flyboy/task/snoozer.rb +1 -0
  26. data/app/services/dorsale/tag_list_for_model.rb +1 -0
  27. data/app/uploaders/dorsale/image_uploader.rb +1 -1
  28. data/app/uploaders/dorsale/pdf_uploader.rb +1 -1
  29. data/app/views/dorsale/billing_machine/commons/_form.html.slim +2 -1
  30. data/app/views/dorsale/billing_machine/commons/_line_fields.html.slim +4 -0
  31. data/app/views/dorsale/billing_machine/invoices/show.pdf.ruby +1 -1
  32. data/app/views/dorsale/billing_machine/quotations/show.pdf.ruby +1 -1
  33. data/app/views/dorsale/customer_vault/people/_form.html.slim +9 -1
  34. data/app/views/dorsale/customer_vault/people/create_corporation_js.html.slim +7 -0
  35. data/app/views/dorsale/flyboy/task_mailer/new_task.html.slim +1 -1
  36. data/app/views/dorsale/flyboy/task_mailer/term_email.html.slim +1 -1
  37. data/config/locales/billing_machine.en.yml +1 -0
  38. data/config/locales/billing_machine.fr.yml +2 -0
  39. data/db/migrate/20210202100529_billing_machine_add_positions.rb +6 -0
  40. data/features/customer_vault_individuals.feature +9 -1
  41. data/features/step_definitions/billing_machine_payment_terms_steps.rb +1 -1
  42. data/features/step_definitions/billing_machine_quotations_steps.rb +1 -1
  43. data/features/step_definitions/customer_vault_activity_types_steps.rb +1 -1
  44. data/features/step_definitions/customer_vault_individuals_steps.rb +11 -0
  45. data/features/step_definitions/customer_vault_origins_steps.rb +1 -1
  46. data/features/step_definitions/customer_vault_people_steps.rb +1 -1
  47. data/features/step_definitions/customer_vault_search_steps.rb +10 -10
  48. data/features/step_definitions/expense_gun_categories_steps.rb +1 -1
  49. data/lib/dorsale/version.rb +1 -1
  50. data/spec/factories/customer_vault_individuals.rb +13 -13
  51. data/spec/factories/expense_gun_categories.rb +3 -3
  52. data/spec/factories/expense_gun_expense_lines.rb +7 -7
  53. data/spec/factories/expense_gun_expenses.rb +3 -3
  54. data/spec/models/dorsale/billing_machine/quotation_spec.rb +1 -1
  55. data/spec/pdfs/dorsale/billing_machine/invoice_multiple_vat_pdf_spec.rb +1 -1
  56. data/spec/pdfs/dorsale/billing_machine/invoice_single_vat_pdf_spec.rb +1 -1
  57. data/spec/pdfs/dorsale/billing_machine/quotation_multiple_vat_pdf_spec.rb +1 -1
  58. data/spec/pdfs/dorsale/billing_machine/quotation_single_vat_pdf_spec.rb +1 -1
  59. data/spec/rails_helper.rb +0 -1
  60. data/spec/services/dorsale/billing_machine/quotation/copy_spec.rb +1 -1
  61. metadata +8 -7
  62. data/app/assets/javascripts/url.min.js +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7fa35a5738f83f5a6ffe07e3357dd0d95e06a1e01e882d79e97a0b103cb6358
4
- data.tar.gz: fae747e4c22a18ae7efc3e683c014a1513d6cf5c798022f45959b053b0338b00
3
+ metadata.gz: 8c07aa163d58442697fc7cbbd50008cff4128218f6c28a567ec693fa028112a8
4
+ data.tar.gz: 137b46a639e3e82c637e001e6bcdf06e307f357fd07c4bc69fbcce69e7600bf5
5
5
  SHA512:
6
- metadata.gz: cac3603f69fbda59905d4c191af33ede03c001c537ad0e3c387c95429033838e9ec8a10028b350448e7c5be7f69cd19079b847c962e36c01f45b8c3d3545dfd5
7
- data.tar.gz: 95675692f74edd6c555c209857939c84b3628616c2b3cd144a3876c3a264de65c7df972a6daa957ef3d49e6f4efe46f76e23c289c09bd59cd9f57e455792c3a0
6
+ metadata.gz: 3cc2d98f933b68bb02d421f3f8ba132fa70d54c910292db9539b3c7e2bd8027692cf4d8e30ba333a0abfe6825bd34fee362a0f5d699c91a542ff7fcf0dac59ce
7
+ data.tar.gz: 00cc8a3a2a6ac15cdd523871ed2b8ea3ffeb7832006c6b643fe3e0c801aa17579de20e22d525f36a273bcf93fcb13142f0257bda5dcef56b0b5758e40bdccf04
data/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## Next version
4
4
 
5
+ ## 3.16.0
6
+ - Allow to reorder BM lines (require Sorting.js + reimport migrations)
7
+ - Remove unused url.js
8
+
9
+ ## 3.15.0
10
+ - Fix Ruby 2.7 warnings
11
+ - Faker 2
12
+
13
+ ## 3.14.11
14
+ - Fix BM missing PDFs
15
+
16
+ ## 3.14.10
17
+ - Change BillingMachine PDF filenames
18
+ - Fix CustomerVault layout
19
+
20
+ ## 3.14.9
21
+ - Create corporation from individual form
22
+ - Quotations: add draft state
23
+ - Fix carrierwave white lists
24
+
5
25
  ## 3.14.8
6
26
  - Rails 6
7
27
 
@@ -1,4 +1,4 @@
1
- $(document).on "turbolinks:load", ->
1
+ $(document).on "turbolinks:load modal:load", ->
2
2
  $("form:not([action*=filters]) select[multiple][name*=tag]").not(".select2-hidden-accessible").map ->
3
3
  placeholder = $(this).attr("placeholder")
4
4
 
@@ -4,11 +4,11 @@
4
4
  //= require bootstrap
5
5
  //= require cocoon
6
6
  //= require accounting
7
- //= require url.min
8
7
  //= require select2
9
8
  //= require select2_locale_fr
10
9
  //= require bootstrap-datepicker/core
11
10
  //= require bootstrap-datepicker/locales/bootstrap-datepicker.fr
12
11
  //= require Chart.bundle
13
12
  //= require chartkick
13
+ //= require Sortable
14
14
  //= require agilibox/all
@@ -77,6 +77,10 @@ BillingMachine.formatInputs = ->
77
77
  formatted_number = BillingMachine.num2str BillingMachine.str2num $(this).val()
78
78
  $(this).val formatted_number
79
79
 
80
+ BillingMachine.updatePositions = ->
81
+ i = 0
82
+ $("#billing_machine-form input[name*=position]").map -> this.value = (i = i + 1)
83
+
80
84
  # Empty number inputs on focus if value is 0
81
85
  $(document).on "focus", "#billing_machine-form input.number", ->
82
86
  $(this).val("") if BillingMachine.str2num($(this).val()) == 0
@@ -96,6 +100,7 @@ $(document).on "click", "#billing_machine-form a.delete", (e) ->
96
100
  $(document).on "turbolinks:load cocoon:after-insert", ->
97
101
  BillingMachine.formatInputs()
98
102
  BillingMachine.updateTotals()
103
+ BillingMachine.updatePositions()
99
104
 
100
105
  # Fix Cocoon bug
101
106
  $("#billing_machine-form .line textarea").map ->
@@ -106,3 +111,12 @@ $(document).on "keyup", "#billing_machine-form input.number", ->
106
111
 
107
112
  $(document).on "blur", "#billing_machine-form input.number", ->
108
113
  BillingMachine.formatInputs()
114
+
115
+ $(document).on "turbolinks:load", ->
116
+ $("#billing_machine-form tbody").map ->
117
+ container = this
118
+
119
+ new Sortable container,
120
+ handle: ".handle"
121
+ animation: 150
122
+ onSort: -> BillingMachine.updatePositions()
@@ -103,11 +103,14 @@
103
103
 
104
104
  th.actions,
105
105
  td.actions,
106
+ th.position,
107
+ td.position,
106
108
  padding: 0
107
109
  width: 2.5em
108
110
  text-align: center
109
111
 
110
- th.actions
112
+ th.actions,
113
+ th.position,
111
114
  border: none
112
115
 
113
116
  // disabled fields
@@ -157,15 +157,20 @@ class Dorsale::BillingMachine::InvoicesController < ::Dorsale::BillingMachine::A
157
157
  :advance,
158
158
  :due_date,
159
159
  :comments,
160
- :lines_attributes => [
161
- :_destroy,
162
- :id,
163
- :label,
164
- :quantity,
165
- :unit,
166
- :unit_price,
167
- :vat_rate,
168
- ],
160
+ :lines_attributes => line_permitted_params,
161
+ ]
162
+ end
163
+
164
+ def line_permitted_params
165
+ [
166
+ :_destroy,
167
+ :id,
168
+ :label,
169
+ :quantity,
170
+ :unit,
171
+ :unit_price,
172
+ :vat_rate,
173
+ :position,
169
174
  ]
170
175
  end
171
176
 
@@ -165,15 +165,20 @@ class Dorsale::BillingMachine::QuotationsController < ::Dorsale::BillingMachine:
165
165
  :comments,
166
166
  :vat_rate,
167
167
  :commercial_discount,
168
- :lines_attributes => [
169
- :_destroy,
170
- :id,
171
- :label,
172
- :quantity,
173
- :unit,
174
- :unit_price,
175
- :vat_rate,
176
- ],
168
+ :lines_attributes => line_permitted_params,
169
+ ]
170
+ end
171
+
172
+ def line_permitted_params
173
+ [
174
+ :_destroy,
175
+ :id,
176
+ :label,
177
+ :quantity,
178
+ :unit,
179
+ :unit_price,
180
+ :vat_rate,
181
+ :position,
177
182
  ]
178
183
  end
179
184
 
@@ -1,8 +1,6 @@
1
1
  class Dorsale::CustomerVault::EventsController < ::Dorsale::CustomerVault::ApplicationController
2
2
  before_action :set_objects
3
3
 
4
- layout -> { action_name == "index" ? nil : false }
5
-
6
4
  def index
7
5
  authorize model, :list?
8
6
 
@@ -45,13 +45,19 @@ class Dorsale::CustomerVault::PeopleController < ::Dorsale::CustomerVault::Appli
45
45
 
46
46
  @person ||= scope.new(person_params_for_create)
47
47
 
48
- if @person.save
49
- generate_event!("create")
50
- flash[:notice] = t("messages.#{person_type.to_s.pluralize}.create_ok")
51
- redirect_to back_url
52
- else
48
+ unless @person.save
53
49
  render :new
50
+ return
54
51
  end
52
+
53
+ if request.xhr? && @person.corporation?
54
+ render :create_corporation_js
55
+ return
56
+ end
57
+
58
+ generate_event!("create")
59
+ flash[:notice] = t("messages.#{person_type.to_s.pluralize}.create_ok")
60
+ redirect_to back_url
55
61
  end
56
62
 
57
63
  def show
@@ -11,7 +11,7 @@ class Dorsale::Flyboy::TaskMailer < ::Dorsale::ApplicationMailer
11
11
 
12
12
  mail(
13
13
  :to => task.owner.email,
14
- :subject => t("task_mailer.new_task.subject", @locals),
14
+ :subject => t("task_mailer.new_task.subject", **@locals),
15
15
  )
16
16
  end
17
17
 
@@ -26,7 +26,7 @@ class Dorsale::Flyboy::TaskMailer < ::Dorsale::ApplicationMailer
26
26
 
27
27
  mail(
28
28
  :to => task.owner.email,
29
- :subject => t("task_mailer.term_email.subject", @locals),
29
+ :subject => t("task_mailer.term_email.subject", **@locals),
30
30
  )
31
31
  end
32
32
  end
@@ -22,6 +22,8 @@ class Dorsale::BillingMachine::Invoice < ::Dorsale::ApplicationRecord
22
22
  :invoice
23
23
  end
24
24
 
25
+ after_initialize :assign_default_dates
26
+ before_save :update_totals
25
27
  before_create :assign_unique_index
26
28
  before_create :assign_tracking_id
27
29
 
@@ -43,15 +45,11 @@ class Dorsale::BillingMachine::Invoice < ::Dorsale::ApplicationRecord
43
45
  assign_default :paid, false
44
46
  end
45
47
 
46
- after_initialize :assign_default_dates
47
-
48
48
  def assign_default_dates
49
49
  assign_default :date, Date.current
50
50
  assign_default :due_date, Date.current + 30.days
51
51
  end
52
52
 
53
- before_save :update_totals
54
-
55
53
  def update_totals
56
54
  assign_default_values
57
55
  lines.each(&:update_total)
@@ -6,7 +6,7 @@ class Dorsale::BillingMachine::InvoiceLine < ::Dorsale::ApplicationRecord
6
6
  validates :invoice, presence: true
7
7
 
8
8
  default_scope -> {
9
- order(created_at: :asc)
9
+ order(position: :asc, created_at: :asc, id: :asc)
10
10
  }
11
11
 
12
12
  before_validation :update_total
@@ -1,7 +1,7 @@
1
1
  class Dorsale::BillingMachine::Quotation < ::Dorsale::ApplicationRecord
2
2
  self.table_name = "dorsale_billing_machine_quotations"
3
3
 
4
- STATES = %w(pending accepted refused canceled)
4
+ STATES = %w(draft pending accepted refused canceled)
5
5
 
6
6
  belongs_to :customer, polymorphic: true
7
7
  belongs_to :payment_term
@@ -33,6 +33,7 @@ class Dorsale::BillingMachine::Quotation < ::Dorsale::ApplicationRecord
33
33
  :quotation
34
34
  end
35
35
 
36
+ before_save :update_totals
36
37
  before_create :assign_unique_index
37
38
  before_create :assign_tracking_id
38
39
 
@@ -55,8 +56,6 @@ class Dorsale::BillingMachine::Quotation < ::Dorsale::ApplicationRecord
55
56
  assign_default :total_excluding_taxes, 0
56
57
  end
57
58
 
58
- before_save :update_totals
59
-
60
59
  def update_totals
61
60
  assign_default_values
62
61
  lines.each(&:update_total)
@@ -6,7 +6,7 @@ class Dorsale::BillingMachine::QuotationLine < ::Dorsale::ApplicationRecord
6
6
  validates :quotation, presence: true
7
7
 
8
8
  default_scope -> {
9
- order(:created_at => :asc)
9
+ order(position: :asc, created_at: :asc, id: :asc)
10
10
  }
11
11
 
12
12
  before_validation :update_total
@@ -9,8 +9,6 @@ class Dorsale::CustomerVault::Person < ::Dorsale::ApplicationRecord
9
9
  Dorsale::CustomerVault::PersonPolicy
10
10
  end
11
11
 
12
- after_initialize :verify_class
13
-
14
12
  def verify_class
15
13
  if self.class == ::Dorsale::CustomerVault::Person
16
14
  # self.abstract_class does not work with STI
@@ -30,6 +28,13 @@ class Dorsale::CustomerVault::Person < ::Dorsale::ApplicationRecord
30
28
  belongs_to :activity_type, class_name: "Dorsale::CustomerVault::ActivityType"
31
29
  belongs_to :origin, class_name: "Dorsale::CustomerVault::Origin"
32
30
 
31
+ validate :validate_taken_emails
32
+
33
+ after_initialize :verify_class
34
+ after_initialize :build_address, if: proc { new_record? && address.nil? }
35
+
36
+ before_validation :build_address, if: proc { address.nil? }
37
+
33
38
  after_destroy :destroy_links
34
39
 
35
40
  default_scope -> {
@@ -50,9 +55,6 @@ class Dorsale::CustomerVault::Person < ::Dorsale::ApplicationRecord
50
55
 
51
56
  scope :having_email, -> (email) { where("email = :e OR :e = ANY (secondary_emails)", e: email) }
52
57
 
53
- after_initialize :build_address, if: proc { new_record? && address.nil? }
54
- before_validation :build_address, if: proc { address.nil? }
55
-
56
58
  def taken_emails
57
59
  taken_emails = {}
58
60
  ([email] + secondary_emails).select(&:present?).each do |e|
@@ -62,8 +64,6 @@ class Dorsale::CustomerVault::Person < ::Dorsale::ApplicationRecord
62
64
  taken_emails
63
65
  end
64
66
 
65
- validate :validate_taken_emails
66
-
67
67
  def validate_taken_emails
68
68
  return if taken_emails.empty?
69
69
 
@@ -177,11 +177,11 @@ class Dorsale::BillingMachine::InvoiceSingleVatPdf < Dorsale::ApplicationPdf
177
177
  end
178
178
 
179
179
  def has_advance
180
- main_document.try(:advance) && main_document.advance != 0.0
180
+ main_document.try(:advance) && main_document.advance.to_d != 0.0.to_d
181
181
  end
182
182
 
183
183
  def has_discount
184
- main_document.try(:commercial_discount) && main_document.commercial_discount != 0.0
184
+ main_document.try(:commercial_discount) && main_document.commercial_discount.to_d != 0.0.to_d
185
185
  end
186
186
 
187
187
  def build_table
@@ -19,6 +19,7 @@ class Dorsale::ApplicationPolicy
19
19
  end
20
20
 
21
21
  def self.inherited(klass)
22
+ super(klass)
22
23
  klass.define_subject_accessor!
23
24
  end
24
25
 
@@ -2,6 +2,7 @@ class Dorsale::BillingMachine::Invoice::Copy < ::Dorsale::Service
2
2
  attr_accessor :invoice, :copy
3
3
 
4
4
  def initialize(invoice)
5
+ super()
5
6
  @invoice = invoice
6
7
  end
7
8
 
@@ -2,14 +2,8 @@ class Dorsale::BillingMachine::PdfFileGenerator < Dorsale::Service
2
2
  attr_reader :document
3
3
 
4
4
  def initialize(document)
5
+ super()
5
6
  @document = document
6
-
7
- # I have no idea why I need to do that,
8
- # if I don't do that, CarrierWare do not stores the file.
9
- # The reload() method don't work either.
10
- # The problem appears only on server, not in console.
11
- # I think CarrierWave do not work anymore after first save.
12
- @document = document.class.find(document.id) if document.persisted?
13
7
  end
14
8
 
15
9
  def call
@@ -2,6 +2,7 @@ class Dorsale::BillingMachine::Quotation::Copy < ::Dorsale::Service
2
2
  attr_accessor :quotation, :copy
3
3
 
4
4
  def initialize(quotation)
5
+ super()
5
6
  @quotation = quotation
6
7
  end
7
8
 
@@ -2,6 +2,7 @@ class Dorsale::BillingMachine::Quotation::ToInvoice < ::Dorsale::Service
2
2
  attr_accessor :quotation, :invoice
3
3
 
4
4
  def initialize(quotation)
5
+ super()
5
6
  @quotation = quotation
6
7
  end
7
8
 
@@ -2,6 +2,7 @@ class Dorsale::ExpenseGun::Expense::Copy < ::Dorsale::Service
2
2
  attr_accessor :expense, :copy
3
3
 
4
4
  def initialize(expense)
5
+ super()
5
6
  @expense = expense
6
7
  end
7
8
 
@@ -2,6 +2,7 @@ class Dorsale::Flyboy::Task::Copy < ::Dorsale::Service
2
2
  attr_accessor :task, :copy
3
3
 
4
4
  def initialize(task)
5
+ super()
5
6
  @task = task
6
7
  end
7
8
 
@@ -2,6 +2,7 @@ class Dorsale::Flyboy::Task::Snoozer < ::Dorsale::Service
2
2
  attr_reader :task
3
3
 
4
4
  def initialize(task)
5
+ super()
5
6
  @task = task
6
7
  end
7
8
 
@@ -2,6 +2,7 @@ class Dorsale::TagListForModel < ::Dorsale::Service
2
2
  attr_reader :model
3
3
 
4
4
  def initialize(model)
5
+ super()
5
6
  @model = model
6
7
  end
7
8
 
@@ -1,5 +1,5 @@
1
1
  class Dorsale::ImageUploader < ::Dorsale::ApplicationUploader
2
- def extension_white_list
2
+ def extension_whitelist
3
3
  %w(jpg jpeg gif png)
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  class Dorsale::PdfUploader < ::Dorsale::ApplicationUploader
2
- def extension_white_list
2
+ def extension_whitelist
3
3
  %w(pdf)
4
4
  end
5
5
  end
@@ -21,6 +21,7 @@
21
21
  table#lines-table
22
22
  thead
23
23
  tr
24
+ th.position
24
25
  th.actions
25
26
  th.line-label = Dorsale::BillingMachine::InvoiceLine.t(:label)
26
27
  th.line-quantity = Dorsale::BillingMachine::InvoiceLine.t(:quantity)
@@ -32,7 +33,7 @@
32
33
 
33
34
  tbody
34
35
  = f.simple_fields_for :lines do |lf|
35
- = render "dorsale/billing_machine/invoices/line_fields", f: lf
36
+ = render "dorsale/billing_machine/#{document.document_type}s/line_fields", f: lf
36
37
 
37
38
  .row
38
39
  .col-sm-6
@@ -1,4 +1,8 @@
1
1
  tr.line
2
+ td.position
3
+ = icon(:arrows_alt, class: "handle")
4
+ = f.hidden_field :position
5
+
2
6
  td.actions
3
7
  a.delete href="#"
4
8
  span.fa.fa-fw.fa-trash
@@ -1,7 +1,7 @@
1
1
  filename = [
2
2
  @invoice.t.capitalize,
3
3
  @invoice.tracking_id,
4
- @invoice.customer.try(:short_name),
4
+ @invoice.customer.to_s.tr(" ", "_"),
5
5
  ].join("_").concat(".pdf")
6
6
 
7
7
  response.headers["Content-Disposition"] = %(inline; filename="#{filename}")
@@ -1,7 +1,7 @@
1
1
  filename = [
2
2
  @quotation.t.capitalize,
3
3
  @quotation.tracking_id,
4
- @quotation.customer.try(:short_name),
4
+ @quotation.customer.to_s.tr(" ", "_"),
5
5
  ].join("_").concat(".pdf")
6
6
 
7
7
  response.headers["Content-Disposition"] = %(inline; filename="#{filename}")
@@ -30,8 +30,16 @@
30
30
 
31
31
  - if @person.individual?
32
32
  = f.input :title
33
- = f.association :corporation, collection: policy_scope(Dorsale::CustomerVault::Corporation), input_html: {class: 'select2'}
33
+ .row.form-group
34
+ = f.label :corporation, class: "control-label col-sm-3"
35
+ .col-sm-9
36
+ div style="display:flex"
37
+ = f.collection_select :corporation_id, policy_scope(Dorsale::CustomerVault::Corporation), :id, :to_s, {include_blank: true}, {class: "select2", style: "width:100%"}
38
+ a.btn.btn-success.btn-xs href=new_customer_vault_corporation_path data-modal="nested" style="margin-left:1em;padding-top:5px"
39
+ = icon(:plus)
40
+
34
41
  = f.input :context, as: :text, input_html: {rows: 4}
42
+
35
43
  .panel.panel-default
36
44
  .panel-heading: .panel-title = t("customer_vault.contact_informations")
37
45
  .panel-body
@@ -0,0 +1,7 @@
1
+ javascript:
2
+ option = document.createElement("option")
3
+ option.value = #{@person.id.to_json.html_safe}
4
+ option.innerHTML = #{@person.to_s.to_json.html_safe}
5
+ option.selected = true
6
+ $("select[id$=corporation_id]").append(option)
7
+ modal.close()
@@ -1 +1 @@
1
- p == lf2br t("task_mailer.new_task.body", @locals)
1
+ p == lf2br t("task_mailer.new_task.body", **@locals)
@@ -1 +1 @@
1
- p == lf2br t("task_mailer.term_email.body", @locals)
1
+ p == lf2br t("task_mailer.term_email.body", **@locals)
@@ -66,6 +66,7 @@ en:
66
66
 
67
67
  dorsale/billing_machine/quotation/state:
68
68
  all: "All states"
69
+ draft: Draft
69
70
  pending: "Pending"
70
71
  accepted: "Accepted"
71
72
  refused: "Refused"
@@ -25,6 +25,7 @@ fr:
25
25
  expires_at: "Date d'expiration"
26
26
  balance: "Reste à payer"
27
27
  pdf_file: "Fichier PDF"
28
+ position: "Position"
28
29
 
29
30
  activerecord:
30
31
  models:
@@ -66,6 +67,7 @@ fr:
66
67
 
67
68
  dorsale/billing_machine/quotation/state:
68
69
  all: "Tous les états"
70
+ draft: Brouillon
69
71
  pending: "En attente"
70
72
  accepted: "Accepté"
71
73
  refused: "Refusé"
@@ -0,0 +1,6 @@
1
+ class BillingMachineAddPositions < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :dorsale_billing_machine_invoice_lines, :position, :integer, null: false, default: 0
4
+ add_column :dorsale_billing_machine_quotation_lines, :position, :integer, null: false, default: 0
5
+ end
6
+ end
@@ -14,7 +14,7 @@ Feature: Manage individuals
14
14
  And I validate the new individual
15
15
  Then the individual is created
16
16
 
17
- Scenario: New individual without individual
17
+ Scenario: New individual errors
18
18
  When I create an new individual
19
19
  And I fill the address
20
20
  And I validate the new individual
@@ -33,3 +33,11 @@ Feature: Manage individuals
33
33
  And I remove tags to this individual
34
34
  And I submit this individual
35
35
  Then tags are removed
36
+
37
+ Scenario: Create corporation from individual
38
+ When I create an new individual
39
+ And I create corporation from individual
40
+ And I add his first_name, last_name and email
41
+ And I validate the new individual
42
+ Then the individual is created
43
+ And the new corporation is associated to individual
@@ -28,7 +28,7 @@ When(/^the user edits the payment_term$/) do
28
28
  find(".link_update").click
29
29
  end
30
30
 
31
- Then(/^the current payment_term's label should be pre\-filled$/) do
31
+ Then(/^the current payment_term's label should be pre-filled$/) do
32
32
  expect(page).to have_field("billing_machine_payment_term_label", with: @payment_term.label)
33
33
  end
34
34
 
@@ -58,7 +58,7 @@ When(/^the quotation line shows the right date$/) do
58
58
  expect(page).to have_selector ".date", text: I18n.l(@quotation.date)
59
59
  end
60
60
 
61
- When(/^the quotation line shows the right traking\-id$/) do
61
+ When(/^the quotation line shows the right traking-id$/) do
62
62
  expect(page).to have_selector ".tracking_id", text: @quotation.tracking_id
63
63
  end
64
64
 
@@ -28,7 +28,7 @@ When(/^I edit the activity type$/) do
28
28
  find(".link_update").click
29
29
  end
30
30
 
31
- Then(/^the current activity type's name should be pre\-filled$/) do
31
+ Then(/^the current activity type's name should be pre-filled$/) do
32
32
  expect(page).to have_field("activity_type_name", with: @activity_type.name)
33
33
  end
34
34
 
@@ -65,3 +65,14 @@ end
65
65
  When(/^I go on this individual$/) do
66
66
  visit dorsale.customer_vault_individual_path(@individual)
67
67
  end
68
+
69
+ When("I create corporation from individual") do
70
+ find("a[href*=corporation][href$=new]").click
71
+ fill_in :person_corporation_name, with: "agilidée"
72
+ find("#modal [type=submit]").click
73
+ end
74
+
75
+ Then("the new corporation is associated to individual") do
76
+ expect(@individual.corporation).to be_present
77
+ expect(@individual.corporation.name).to eq "agilidée"
78
+ end
@@ -28,7 +28,7 @@ When(/^I edit the origin$/) do
28
28
  find(".link_update").click
29
29
  end
30
30
 
31
- Then(/^the current origin's name should be pre\-filled$/) do
31
+ Then(/^the current origin's name should be pre-filled$/) do
32
32
  expect(page).to have_field("origin_name", with: @origin.name)
33
33
  end
34
34
 
@@ -7,7 +7,7 @@ Then(/^the file is downloaded$/) do
7
7
  end
8
8
 
9
9
  Given(/^a very long comment on this person$/) do
10
- text = Faker::Lorem.paragraph(30)
10
+ text = Faker::Lorem.paragraph(sentence_count: 30)
11
11
  @comment = create(:customer_vault_event_comment, person: @person, text: text)
12
12
  end
13
13
 
@@ -1,48 +1,48 @@
1
- Lorsqu(/^he go to the people list$/) do
1
+ When(/^he go to the people list$/) do
2
2
  visit dorsale.customer_vault_people_path
3
3
  end
4
4
 
5
- Etantdonné(/^existing individuals$/) do
5
+ Given(/^existing individuals$/) do
6
6
  @individual1 = create(:customer_vault_individual, first_name: "Jean", last_name: "DUPONT")
7
7
  @individual2 = create(:customer_vault_individual, first_name: "Laurent", last_name: "DURAND")
8
8
  end
9
9
 
10
- Etantdonné(/^existing corporations$/) do
10
+ Given(/^existing corporations$/) do
11
11
  @corporation1 = create(:customer_vault_corporation, name: "aaa", email: "contact@aaa.com")
12
12
  @corporation2 = create(:customer_vault_corporation, name: "zzz", email: "contact@zzz.com")
13
13
  end
14
14
 
15
- Lorsqu(/^he search an individual by first name$/) do
15
+ When(/^he search an individual by first name$/) do
16
16
  fill_in "q", with: "Jean"
17
17
  find(".search-submit").click
18
18
  end
19
19
 
20
- Alors(/^this individual appear in search results$/) do
20
+ Then(/^this individual appear in search results$/) do
21
21
  expect(page).to have_content "Jean"
22
22
  expect(page).to have_content "DUPONT"
23
23
  end
24
24
 
25
- Alors(/^other individuals do not appear in search results$/) do
25
+ Then(/^other individuals do not appear in search results$/) do
26
26
  expect(page).to have_no_content "Laurent"
27
27
  expect(page).to have_no_content "DURAND"
28
28
  end
29
29
 
30
- Lorsqu(/^he search an individual by last name$/) do
30
+ When(/^he search an individual by last name$/) do
31
31
  fill_in "q", with: "DUPONT"
32
32
  find(".search-submit").click
33
33
  end
34
34
 
35
- Lorsqu(/^he search a corporation by name$/) do
35
+ When(/^he search a corporation by name$/) do
36
36
  fill_in "q", with: "aaa"
37
37
  find(".search-submit").click
38
38
  end
39
39
 
40
- Alors(/^this corporation appear in search results$/) do
40
+ Then(/^this corporation appear in search results$/) do
41
41
  expect(page).to have_content "aaa"
42
42
  expect(page).to have_content "contact@aaa.com"
43
43
  end
44
44
 
45
- Alors(/^other corporations do not appear in search results$/) do
45
+ Then(/^other corporations do not appear in search results$/) do
46
46
  expect(page).to have_no_content "zzz"
47
47
  expect(page).to have_no_content "contact@zzz.com"
48
48
  end
@@ -28,7 +28,7 @@ When(/^I edit the expense category$/) do
28
28
  find(".link_update").click
29
29
  end
30
30
 
31
- Then(/^the current expense category's label should be pre\-filled$/) do
31
+ Then(/^the current expense category's label should be pre-filled$/) do
32
32
  expect(page).to have_field("category_name", with: @category.name)
33
33
  end
34
34
 
@@ -1,3 +1,3 @@
1
1
  module Dorsale
2
- VERSION = "3.14.8"
2
+ VERSION = "3.16.0"
3
3
  end
@@ -1,17 +1,17 @@
1
1
  FactoryBot.define do
2
2
  factory :customer_vault_individual, class: ::Dorsale::CustomerVault::Individual do
3
- origin { create(:customer_vault_origin) }
4
- first_name { Faker::Name.first_name }
5
- last_name { Faker::Name.last_name }
6
- short_name { "SN" }
7
- email { Faker::Internet.email("#{first_name} #{last_name}") }
8
- title { "Individual-Title" }
9
- twitter { "#{first_name}#{last_name}" }
10
- www { Faker::Internet.url }
11
- context { "Individual-Context" }
12
- phone { "01 23 xx xx xx" }
13
- fax { "09 xx xx xx xx" }
14
- mobile { "06 xx xx xx xx" }
15
- address { build(:dorsale_address) }
3
+ origin { create(:customer_vault_origin) }
4
+ first_name { Faker::Name.first_name }
5
+ last_name { Faker::Name.last_name }
6
+ short_name { "SN" }
7
+ email { Faker::Internet.email(name: name) }
8
+ title { "Individual-Title" }
9
+ twitter { "#{first_name}#{last_name}" }
10
+ www { Faker::Internet.url }
11
+ context { "Individual-Context" }
12
+ phone { "01 23 xx xx xx" }
13
+ fax { "09 xx xx xx xx" }
14
+ mobile { "06 xx xx xx xx" }
15
+ address { build(:dorsale_address) }
16
16
  end
17
17
  end
@@ -1,7 +1,7 @@
1
1
  FactoryBot.define do
2
2
  factory :expense_gun_category, class: ::Dorsale::ExpenseGun::Category do
3
- name { Faker::Lorem.word }
4
- code { Faker::Number.number(4) }
5
- vat_deductible { [true, false].sample }
3
+ name { Faker::Lorem.word }
4
+ code { Faker::Number.number(digits: 4) }
5
+ vat_deductible { [true, false].sample }
6
6
  end
7
7
  end
@@ -1,11 +1,11 @@
1
1
  FactoryBot.define do
2
2
  factory :expense_gun_expense_line, class: ::Dorsale::ExpenseGun::ExpenseLine do
3
- expense { build(:expense_gun_expense) }
4
- name { Faker::Lorem.sentence(3) }
5
- category { build(:expense_gun_category) }
6
- date { Faker::Date.backward(30) }
7
- total_all_taxes { rand(100..1000) }
8
- vat { rand(1..(total_all_taxes/5)) }
9
- company_part { [25, 50, 75, 100].sample }
3
+ expense { build(:expense_gun_expense) }
4
+ name { Faker::Lorem.sentence(word_count: 3) }
5
+ category { build(:expense_gun_category) }
6
+ date { Faker::Date.backward(days: 30) }
7
+ total_all_taxes { rand(100..1000) }
8
+ vat { rand(1..(total_all_taxes/5)) }
9
+ company_part { [25, 50, 75, 100].sample }
10
10
  end
11
11
  end
@@ -1,8 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :expense_gun_expense, class: ::Dorsale::ExpenseGun::Expense do
3
- name { Faker::Lorem.sentence(3) }
4
- date { Date.current }
5
- user { create(:user) }
3
+ name { Faker::Lorem.sentence(word_count: 3) }
4
+ date { Date.current }
5
+ user { create(:user) }
6
6
 
7
7
  after(:create) { |expense|
8
8
  rand(2..5).times {
@@ -36,7 +36,7 @@ describe Dorsale::BillingMachine::Quotation do
36
36
  end
37
37
 
38
38
  it "default state should be pending" do
39
- expect(described_class.new.state).to eq "pending"
39
+ expect(described_class.new.state).to eq "draft"
40
40
  end
41
41
  end
42
42
 
@@ -23,7 +23,7 @@ describe ::Dorsale::BillingMachine::InvoiceMultipleVatPdf, pdfs: true do
23
23
 
24
24
  let(:content) {
25
25
  generate!
26
- Yomu.new(invoice.pdf_file.path).text
26
+ PDF::Reader.new(invoice.pdf_file.path).pages.map(&:text).join("\n")
27
27
  }
28
28
 
29
29
  it "should not display global vat rate" do
@@ -23,7 +23,7 @@ describe ::Dorsale::BillingMachine::InvoiceSingleVatPdf, pdfs: true do
23
23
 
24
24
  let(:content) {
25
25
  generate!
26
- Yomu.new(invoice.pdf_file.path).text
26
+ PDF::Reader.new(invoice.pdf_file.path).pages.map(&:text).join("\n")
27
27
  }
28
28
 
29
29
  it "should display global vat rate" do
@@ -23,7 +23,7 @@ describe ::Dorsale::BillingMachine::QuotationMultipleVatPdf, pdfs: true do
23
23
 
24
24
  let(:content) {
25
25
  generate!
26
- Yomu.new(quotation.pdf_file.path).text
26
+ PDF::Reader.new(quotation.pdf_file.path).pages.map(&:text).join("\n")
27
27
  }
28
28
 
29
29
  it "should not display global vat rate" do
@@ -23,7 +23,7 @@ describe ::Dorsale::BillingMachine::QuotationSingleVatPdf, pdfs: true do
23
23
 
24
24
  let(:content) {
25
25
  generate!
26
- Yomu.new(quotation.pdf_file.path).text
26
+ PDF::Reader.new(quotation.pdf_file.path).pages.map(&:text).join("\n")
27
27
  }
28
28
 
29
29
  it "should display global vat rate" do
data/spec/rails_helper.rb CHANGED
@@ -4,7 +4,6 @@ require 'spec_helper'
4
4
  require File.expand_path("../dummy/config/environment", __FILE__)
5
5
  require 'rspec/rails'
6
6
  require "agilibox/rspec"
7
- require "yomu"
8
7
  # Add additional requires below this line. Rails is not loaded until this point!
9
8
  # Requires supporting ruby files with custom matchers and macros, etc,
10
9
  # in spec/support/ and its subdirectories.
@@ -50,6 +50,6 @@ RSpec.describe Dorsale::BillingMachine::Quotation::Copy do
50
50
 
51
51
  it "should reset state to pending" do
52
52
  expect(quotation.reload.state).to eq "canceled"
53
- expect(copy.reload.state).to eq "pending"
53
+ expect(copy.reload.state).to eq "draft"
54
54
  end
55
55
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dorsale
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.14.8
4
+ version: 3.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - agilidée
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-11 00:00:00.000000000 Z
11
+ date: 2021-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -468,7 +468,6 @@ files:
468
468
  - app/assets/javascripts/dorsale/engines/billing_machine.coffee.erb
469
469
  - app/assets/javascripts/dorsale/engines/customer_vault.coffee
470
470
  - app/assets/javascripts/dorsale/engines/flyboy.coffee
471
- - app/assets/javascripts/url.min.js
472
471
  - app/assets/stylesheets/dorsale/all.sass
473
472
  - app/assets/stylesheets/dorsale/common.sass
474
473
  - app/assets/stylesheets/dorsale/common/comments.sass
@@ -714,6 +713,7 @@ files:
714
713
  - app/views/dorsale/customer_vault/people/_list_item.html.slim
715
714
  - app/views/dorsale/customer_vault/people/_show_layout.html.slim
716
715
  - app/views/dorsale/customer_vault/people/_show_tabs.html.slim
716
+ - app/views/dorsale/customer_vault/people/create_corporation_js.html.slim
717
717
  - app/views/dorsale/customer_vault/people/edit.html.slim
718
718
  - app/views/dorsale/customer_vault/people/index.html.slim
719
719
  - app/views/dorsale/customer_vault/people/index.xlsx.ruby
@@ -834,6 +834,7 @@ files:
834
834
  - db/migrate/20171023133219_customer_vault_events_add_contact_type.rb
835
835
  - db/migrate/20171024075514_customer_vault_contact_type_default_value.rb
836
836
  - db/migrate/20171115171425_dorsale_customer_vault_people_add_secondary_emails.rb
837
+ - db/migrate/20210202100529_billing_machine_add_positions.rb
837
838
  - features/access.feature
838
839
  - features/billing_machine_invoices.feature
839
840
  - features/billing_machine_multiple_vat.feature
@@ -973,7 +974,7 @@ homepage: https://github.com/agilidee/dorsale
973
974
  licenses:
974
975
  - Nonstandard
975
976
  metadata: {}
976
- post_install_message:
977
+ post_install_message:
977
978
  rdoc_options: []
978
979
  require_paths:
979
980
  - lib
@@ -988,8 +989,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
988
989
  - !ruby/object:Gem::Version
989
990
  version: '0'
990
991
  requirements: []
991
- rubygems_version: 3.0.3
992
- signing_key:
992
+ rubygems_version: 3.1.4
993
+ signing_key:
993
994
  specification_version: 4
994
995
  summary: Modular ERP made with Ruby on Rails
995
996
  test_files:
@@ -1 +0,0 @@
1
- /*! js-url - v2.3.0 - 2016-03-10 */window.url=function(){function a(){}function b(a){return decodeURIComponent(a.replace(/\+/g," "))}function c(a,b){var c=a.charAt(0),d=b.split(c);return c===a?d:(a=parseInt(a.substring(1),10),d[0>a?d.length+a:a-1])}function d(a,c){for(var d=a.charAt(0),e=c.split("&"),f=[],g={},h=[],i=a.substring(1),j=0,k=e.length;k>j;j++)if(f=e[j].match(/(.*?)=(.*)/),f||(f=[e[j],e[j],""]),""!==f[1].replace(/\s/g,"")){if(f[2]=b(f[2]||""),i===f[1])return f[2];h=f[1].match(/(.*)\[([0-9]+)\]/),h?(g[h[1]]=g[h[1]]||[],g[h[1]][h[2]]=f[2]):g[f[1]]=f[2]}return d===a?g:g[i]}return function(b,e){var f,g={};if("tld?"===b)return a();if(e=e||window.location.toString(),!b)return e;if(b=b.toString(),f=e.match(/^mailto:([^\/].+)/))g.protocol="mailto",g.email=f[1];else{if((f=e.match(/(.*?)\/#\!(.*)/))&&(e=f[1]+f[2]),(f=e.match(/(.*?)#(.*)/))&&(g.hash=f[2],e=f[1]),g.hash&&b.match(/^#/))return d(b,g.hash);if((f=e.match(/(.*?)\?(.*)/))&&(g.query=f[2],e=f[1]),g.query&&b.match(/^\?/))return d(b,g.query);if((f=e.match(/(.*?)\:?\/\/(.*)/))&&(g.protocol=f[1].toLowerCase(),e=f[2]),(f=e.match(/(.*?)(\/.*)/))&&(g.path=f[2],e=f[1]),g.path=(g.path||"").replace(/^([^\/])/,"/$1").replace(/\/$/,""),b.match(/^[\-0-9]+$/)&&(b=b.replace(/^([^\/])/,"/$1")),b.match(/^\//))return c(b,g.path.substring(1));if(f=c("/-1",g.path.substring(1)),f&&(f=f.match(/(.*?)\.(.*)/))&&(g.file=f[0],g.filename=f[1],g.fileext=f[2]),(f=e.match(/(.*)\:([0-9]+)$/))&&(g.port=f[2],e=f[1]),(f=e.match(/(.*?)@(.*)/))&&(g.auth=f[1],e=f[2]),g.auth&&(f=g.auth.match(/(.*)\:(.*)/),g.user=f?f[1]:g.auth,g.pass=f?f[2]:void 0),g.hostname=e.toLowerCase(),"."===b.charAt(0))return c(b,g.hostname);a()&&(f=g.hostname.match(a()),f&&(g.tld=f[3],g.domain=f[2]?f[2]+"."+f[3]:void 0,g.sub=f[1]||void 0)),g.port=g.port||("https"===g.protocol?"443":"80"),g.protocol=g.protocol||("443"===g.port?"https":"http")}return b in g?g[b]:"{}"===b?g:void 0}}(),"undefined"!=typeof jQuery&&jQuery.extend({url:function(a,b){return window.url(a,b)}});