diff --git a/app/controllers/admin/invoices_controller.rb b/app/controllers/admin/invoices_controller.rb index 39bf06152..10610983d 100644 --- a/app/controllers/admin/invoices_controller.rb +++ b/app/controllers/admin/invoices_controller.rb @@ -33,13 +33,8 @@ module Admin end def cancel - if @invoice.cancel - flash[:notice] = t(:record_updated) - redirect_to([:admin, @invoice]) - else - flash.now[:alert] = t(:failed_to_update_record) - render :show - end + @invoice.cancel + redirect_to [:admin, @invoice], notice: t('.cancelled') end def forward diff --git a/app/controllers/registrar/invoices_controller.rb b/app/controllers/registrar/invoices_controller.rb index c29558e0f..3736bfa6a 100644 --- a/app/controllers/registrar/invoices_controller.rb +++ b/app/controllers/registrar/invoices_controller.rb @@ -6,8 +6,7 @@ class Registrar def index params[:q] ||= {} - invoices = current_registrar_user.registrar.invoices - .includes(:invoice_items, :account_activity) + invoices = current_registrar_user.registrar.invoices.includes(:items, :account_activity) normalize_search_parameters do @q = invoices.search(params[:q]) @@ -35,13 +34,8 @@ class Registrar end def cancel - if @invoice.cancel - flash[:notice] = t(:record_updated) - redirect_to([:registrar, @invoice]) - else - flash.now[:alert] = t(:failed_to_update_record) - render :show - end + @invoice.cancel + redirect_to [:registrar, @invoice], notice: t('.cancelled') end def download_pdf @@ -58,18 +52,7 @@ class Registrar def normalize_search_parameters params[:q][:total_gteq].gsub!(',', '.') if params[:q][:total_gteq] params[:q][:total_lteq].gsub!(',', '.') if params[:q][:total_lteq] - - ca_cache = params[:q][:due_date_lteq] - begin - end_time = params[:q][:due_date_lteq].try(:to_date) - params[:q][:due_date_lteq] = end_time.try(:end_of_day) - rescue - logger.warn('Invalid date') - end - yield - - params[:q][:due_date_lteq] = ca_cache end end end diff --git a/app/controllers/registrar/payments_controller.rb b/app/controllers/registrar/payments_controller.rb index 466f5a865..5be0d6562 100644 --- a/app/controllers/registrar/payments_controller.rb +++ b/app/controllers/registrar/payments_controller.rb @@ -29,7 +29,7 @@ class Registrar if @payment.valid_response_from_intermediary? && @payment.settled_payment? @payment.complete_transaction - if invoice.binded? + if invoice.paid? flash[:notice] = t(:pending_applied) else flash[:alert] = t(:something_wrong) diff --git a/app/models/bank_transaction.rb b/app/models/bank_transaction.rb index 274c15b64..aa4348262 100644 --- a/app/models/bank_transaction.rb +++ b/app/models/bank_transaction.rb @@ -41,9 +41,7 @@ class BankTransaction < ActiveRecord::Base return unless registrar return unless invoice_num return unless invoice - - return if invoice.binded? - return if invoice.cancelled? + return unless invoice.payable? return if invoice.total != sum create_activity(registrar, invoice) @@ -62,7 +60,7 @@ class BankTransaction < ActiveRecord::Base return end - if invoice.binded? + if invoice.paid? errors.add(:base, I18n.t('invoice_is_already_binded')) return end diff --git a/app/models/concerns/invoice/cancellable.rb b/app/models/concerns/invoice/cancellable.rb new file mode 100644 index 000000000..2eb0f8e2e --- /dev/null +++ b/app/models/concerns/invoice/cancellable.rb @@ -0,0 +1,28 @@ +module Concerns + module Invoice + module Cancellable + extend ActiveSupport::Concern + + included do + scope :non_cancelled, -> { where(cancelled_at: nil) } + end + + def cancellable? + unpaid? && not_cancelled? + end + + def cancel + raise 'Invoice cannot be cancelled' unless cancellable? + update!(cancelled_at: Time.zone.now) + end + + def cancelled? + cancelled_at + end + + def not_cancelled? + !cancelled? + end + end + end +end diff --git a/app/models/concerns/invoice/payable.rb b/app/models/concerns/invoice/payable.rb new file mode 100644 index 000000000..1a1c3e269 --- /dev/null +++ b/app/models/concerns/invoice/payable.rb @@ -0,0 +1,28 @@ +module Concerns + module Invoice + module Payable + extend ActiveSupport::Concern + + included do + scope :unpaid, -> { where('id NOT IN (SELECT invoice_id FROM account_activities WHERE' \ + ' invoice_id IS NOT NULL)') } + end + + def payable? + unpaid? && not_cancelled? + end + + def paid? + account_activity + end + + def receipt_date + account_activity.created_at.to_date + end + + def unpaid? + !paid? + end + end + end +end diff --git a/app/models/directo.rb b/app/models/directo.rb index 3fc51bb40..f062912f9 100644 --- a/app/models/directo.rb +++ b/app/models/directo.rb @@ -3,7 +3,7 @@ class Directo < ActiveRecord::Base belongs_to :item, polymorphic: true def self.send_receipts - new_trans = Invoice.where(in_directo: false).where(cancelled_at: nil) + new_trans = Invoice.where(in_directo: false).non_cancelled total = new_trans.count counter = 0 Rails.logger.info("[DIRECTO] Will try to send #{total} invoices") @@ -26,7 +26,7 @@ class Directo < ActiveRecord::Base xml.invoice( "SalesAgent" => Setting.directo_sales_agent, "Number" => num, - "InvoiceDate" => invoice.created_at.strftime("%Y-%m-%d"), + "InvoiceDate" => invoice.issue_date.strftime("%Y-%m-%d"), "PaymentTerm" => Setting.directo_receipt_payment_term, "Currency" => invoice.currency, "CustomerCode"=> invoice.buyer.accounting_customer_code diff --git a/app/models/invoice.rb b/app/models/invoice.rb index a2e469ca0..6942ce5b8 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -1,16 +1,16 @@ class Invoice < ActiveRecord::Base include Versions + include Concerns::Invoice::Cancellable + include Concerns::Invoice::Payable + belongs_to :seller, class_name: 'Registrar' belongs_to :buyer, class_name: 'Registrar' has_one :account_activity - has_many :invoice_items + has_many :items, class_name: 'InvoiceItem', dependent: :destroy has_many :directo_records, as: :item, class_name: 'Directo' - accepts_nested_attributes_for :invoice_items + accepts_nested_attributes_for :items - scope :unbinded, lambda { - where('id NOT IN (SELECT invoice_id FROM account_activities where invoice_id IS NOT NULL)') - } scope :all_columns, ->{select("invoices.*")} scope :sort_due_date_column, ->{all_columns.select("CASE WHEN invoices.cancelled_at is not null THEN (invoices.cancelled_at + interval '100 year') ELSE @@ -24,11 +24,14 @@ class Invoice < ActiveRecord::Base scope :sort_by_sort_receipt_date_asc, ->{sort_receipt_date_column.order("sort_receipt_date ASC")} scope :sort_by_sort_receipt_date_desc, ->{sort_receipt_date_column.order("sort_receipt_date DESC")} + scope :overdue, -> { unpaid.non_cancelled.where('due_date < ?', Time.zone.today) } + attr_accessor :billing_email validates :billing_email, email_format: { message: :invalid }, allow_blank: true + validates :issue_date, presence: true validates :due_date, :currency, :seller_name, - :seller_iban, :buyer_name, :invoice_items, presence: true + :seller_iban, :buyer_name, :items, presence: true validates :vat_rate, numericality: { greater_than_or_equal_to: 0, less_than: 100 }, allow_nil: true @@ -56,35 +59,6 @@ class Invoice < ActiveRecord::Base false end - class << self - def cancel_overdue_invoices - STDOUT << "#{Time.zone.now.utc} - Cancelling overdue invoices\n" unless Rails.env.test? - - cr_at = Time.zone.now - Setting.days_to_keep_overdue_invoices_active.days - invoices = Invoice.unbinded.where( - 'due_date < ? AND cancelled_at IS NULL', cr_at - ) - - unless Rails.env.test? - invoices.each do |m| - STDOUT << "#{Time.zone.now.utc} Invoice.cancel_overdue_invoices: ##{m.id}\n" - end - end - - count = invoices.update_all(cancelled_at: Time.zone.now) - - STDOUT << "#{Time.zone.now.utc} - Successfully cancelled #{count} overdue invoices\n" unless Rails.env.test? - end - end - - def binded? - account_activity.present? - end - - def receipt_date - account_activity.try(:created_at) - end - def to_s I18n.t('invoice_no', no: number) end @@ -119,25 +93,6 @@ class Invoice < ActiveRecord::Base "invoice-#{number}.pdf" end - def cancel - if binded? - errors.add(:base, I18n.t('cannot_cancel_paid_invoice')) - return false - end - - if cancelled? - errors.add(:base, I18n.t('cannot_cancel_cancelled_invoice')) - return false - end - - self.cancelled_at = Time.zone.now - save - end - - def cancelled? - cancelled_at.present? - end - def forward(html) return false unless valid? return false unless billing_email.present? @@ -146,12 +101,8 @@ class Invoice < ActiveRecord::Base true end - def items - invoice_items - end - def subtotal - invoice_items.map(&:item_sum_without_vat).reduce(:+) + items.map(&:item_sum_without_vat).reduce(:+) end def vat_amount @@ -164,6 +115,10 @@ class Invoice < ActiveRecord::Base read_attribute(:total) end + def each + items.each { |item| yield item } + end + private def apply_default_vat_rate diff --git a/app/models/invoice_item.rb b/app/models/invoice_item.rb index 8ab3ac736..2c30cacdc 100644 --- a/app/models/invoice_item.rb +++ b/app/models/invoice_item.rb @@ -3,6 +3,6 @@ class InvoiceItem < ActiveRecord::Base belongs_to :invoice def item_sum_without_vat - (amount * price).round(2) + (price * quantity).round(2) end end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index cb633acb1..d1da1c317 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -51,8 +51,8 @@ class Registrar < ActiveRecord::Base def issue_prepayment_invoice(amount, description = nil) invoices.create( - due_date: (Time.zone.now.to_date + Setting.days_to_keep_invoices_active.days).end_of_day, - payment_term: 'prepayment', + issue_date: Time.zone.today, + due_date: (Time.zone.now + Setting.days_to_keep_invoices_active.days).to_date, description: description, currency: 'EUR', seller_name: Setting.registry_juridical_name, @@ -82,11 +82,11 @@ class Registrar < ActiveRecord::Base buyer_url: website, buyer_email: email, reference_no: reference_no, - invoice_items_attributes: [ + items_attributes: [ { description: 'prepayment', unit: 'piece', - amount: 1, + quantity: 1, price: amount } ] diff --git a/app/services/overdue_invoice_canceller.rb b/app/services/overdue_invoice_canceller.rb new file mode 100644 index 000000000..17401c9dd --- /dev/null +++ b/app/services/overdue_invoice_canceller.rb @@ -0,0 +1,33 @@ +class OverdueInvoiceCanceller + attr_reader :invoices + attr_reader :delay + + def initialize(invoices: Invoice.overdue, delay: self.class.delay) + @invoices = invoices + @delay = delay + end + + def self.default_delay + 30.days + end + + def self.delay + Setting.days_to_keep_overdue_invoices_active&.days || default_delay + end + + def cancel + invoices.each do |invoice| + next unless cancellable?(invoice) + + invoice.cancel + yield invoice if block_given? + end + end + + private + + def cancellable?(invoice) + due_date_with_delay = invoice.due_date + delay + due_date_with_delay.past? + end +end \ No newline at end of file diff --git a/app/views/admin/account_activities/index.haml b/app/views/admin/account_activities/index.haml index d8d5abcbe..1f201f255 100644 --- a/app/views/admin/account_activities/index.haml +++ b/app/views/admin/account_activities/index.haml @@ -61,7 +61,7 @@ %th{class: 'col-xs-2'} = sort_link(@q, 'activity_type') %th{class: 'col-xs-3'} - = sort_link(@q, 'created_at', t(:receipt_date)) + = sort_link(@q, 'created_at', AccountActivity.human_attribute_name(:created_at)) %th{class: 'col-xs-2'} = sort_link(@q, 'sum') %tbody diff --git a/app/views/admin/invoices/index.haml b/app/views/admin/invoices/index.haml index 4b34dba94..903eaf819 100644 --- a/app/views/admin/invoices/index.haml +++ b/app/views/admin/invoices/index.haml @@ -16,18 +16,18 @@ %th{class: 'col-xs-3'} = sort_link(@q, :sort_receipt_date, "Receipt date") %tbody - - @invoices.each do |x| + - @invoices.each do |invoice| %tr - %td= link_to(x, [:admin, x]) - %td= link_to(x.buyer_name, admin_registrar_path(x.buyer_id)) - - if x.cancelled? + %td= link_to(invoice, [:admin, invoice]) + %td= link_to(invoice.buyer_name, admin_registrar_path(invoice.buyer_id)) + - if invoice.cancelled? %td.text-grey= t(:cancelled) - else - %td= l(x.due_date, format: :date_long) + %td= l invoice.due_date - - if x.binded? - %td= l(x.receipt_date, format: :date_long) - - elsif x.cancelled? + - if invoice.paid? + %td= l invoice.receipt_date + - elsif invoice.cancelled? %td.text-grey= t(:cancelled) - else %td.text-danger= t(:unpaid) diff --git a/app/views/admin/invoices/show.haml b/app/views/admin/invoices/show.haml index 33fc5a5d1..bdeb69d6a 100644 --- a/app/views/admin/invoices/show.haml +++ b/app/views/admin/invoices/show.haml @@ -4,11 +4,11 @@ = @invoice .col-sm-8 %h1.text-right.text-center-xs - - unless @invoice.binded? + - if @invoice.unpaid? = link_to(t(:payment_received), new_admin_bank_statement_path(invoice_id: @invoice.id), class: 'btn btn-default') = link_to(t(:download), admin_invoice_download_pdf_path(@invoice), class: 'btn btn-default') = link_to(t(:forward), admin_invoice_forward_path(@invoice), class: 'btn btn-default') - - if !@invoice.cancelled? && !@invoice.binded? + - if @invoice.cancellable? = link_to(t(:cancel), cancel_admin_invoice_path(@invoice), method: :patch, class: 'btn btn-warning') = link_to(t(:back), admin_invoices_path, class: 'btn btn-default') %hr diff --git a/app/views/registrar/account_activities/index.haml b/app/views/registrar/account_activities/index.haml index 857f117d0..3cc6b34e4 100644 --- a/app/views/registrar/account_activities/index.haml +++ b/app/views/registrar/account_activities/index.haml @@ -44,7 +44,7 @@ %th{class: 'col-xs-2'} = sort_link(@q, 'activity_type') %th{class: 'col-xs-3'} - = sort_link(@q, 'created_at', t(:receipt_date)) + = sort_link(@q, 'created_at', AccountActivity.human_attribute_name(:created_at)) %th{class: 'col-xs-2'} = sort_link(@q, 'sum') %tbody diff --git a/app/views/registrar/invoices/index.haml b/app/views/registrar/invoices/index.haml index 47270f2a6..9ed8b91d5 100644 --- a/app/views/registrar/invoices/index.haml +++ b/app/views/registrar/invoices/index.haml @@ -52,22 +52,22 @@ %thead %tr %th{class: 'col-xs-3'}= t(:invoice) - %th{class: 'col-xs-3'}= t(:receipt_date) + %th{class: 'col-xs-3'}= Invoice.human_attribute_name :receipt_date %th{class: 'col-xs-3'}= t(:due_date) %th{class: 'col-xs-3'}= t(:total) %tbody - - @invoices.each do |x| - %tr - %td= link_to(x, [:registrar, x]) - - if x.receipt_date - %td= l(x.receipt_date, format: :date_long) - - elsif x.cancelled? + - @invoices.each do |invoice| + %tr.invoice + %td= link_to(invoice, [:registrar, invoice]) + - if invoice.paid? + %td= l invoice.receipt_date + - elsif invoice.cancelled? %td.text-grey= t(:cancelled) - else %td{class: 'text-danger'}= t(:unpaid) - %td= l(x.due_date, format: :date_long) - %td= currency(x.total) + %td= l invoice.due_date + %td= currency(invoice.total) .row .col-md-12 = paginate @invoices diff --git a/app/views/registrar/invoices/partials/_details.haml b/app/views/registrar/invoices/partials/_details.haml index 69248b457..c5e6193a4 100644 --- a/app/views/registrar/invoices/partials/_details.haml +++ b/app/views/registrar/invoices/partials/_details.haml @@ -2,28 +2,28 @@ %hr %dl.dl-horizontal %dt= t(:issue_date) - %dd= l(@invoice.created_at, format: :date_long) + %dd= l @invoice.issue_date - if @invoice.cancelled? - %dt= t(:cancel_date) - %dd= l(@invoice.cancelled_at, format: :date_long) + %dt= Invoice.human_attribute_name :cancelled_at + %dd= l @invoice.cancelled_at %dt= t(:due_date) - if @invoice.cancelled? %dd.text-grey= t(:cancelled) - else - %dd= l(@invoice.due_date, format: :date_long) + %dd= l @invoice.due_date - %dt= t(:receipt_date) - - if @invoice.binded? - %dd= l(@invoice.receipt_date, format: :date_long) + %dt= Invoice.human_attribute_name :receipt_date + - if @invoice.paid? + %dd= l @invoice.receipt_date - elsif @invoice.cancelled? %dd.text-grey= t(:cancelled) - else %dd{class: 'text-danger'}= t(:unpaid) %dt= t(:payment_term) - %dd= t(@invoice.payment_term) + %dd Prepayment %dt= t(:invoice_number) %dd= @invoice.number diff --git a/app/views/registrar/invoices/partials/_items.haml b/app/views/registrar/invoices/partials/_items.haml index ebd94be53..26985b1c1 100644 --- a/app/views/registrar/invoices/partials/_items.haml +++ b/app/views/registrar/invoices/partials/_items.haml @@ -6,17 +6,17 @@ %tr %th{class: 'col-xs-4'}= t(:description) %th{class: 'col-xs-2'}= t(:unit) - %th{class: 'col-xs-2'}= t(:amount) + %th{class: 'col-xs-2'}= InvoiceItem.human_attribute_name :quantity %th{class: 'col-xs-2'}= t(:price) %th{class: 'col-xs-2'}= t(:total) %tbody - - @invoice.items.each do |x| + - @invoice.each do |invoice_item| %tr - %td= t(x.description) - %td= x.unit - %td= currency(x.amount) - %td= currency(x.price) - %td= currency(x.item_sum_without_vat) + %td= invoice_item.description + %td= invoice_item.unit + %td= invoice_item.quantity + %td= currency(invoice_item.price) + %td= currency(invoice_item.item_sum_without_vat) %tfoot %tr %th{colspan: 3} diff --git a/app/views/registrar/invoices/partials/_total.haml b/app/views/registrar/invoices/partials/_total.haml index 5ed1db4f2..7eac6cc88 100644 --- a/app/views/registrar/invoices/partials/_total.haml +++ b/app/views/registrar/invoices/partials/_total.haml @@ -5,7 +5,7 @@ / %dd= t(@invoice.document_name) %dt= t(:issue_date) - %dd= l(@invoice.created_at) + %dd= l(@invoice.date) %dt= t(:due_date) %dd= l(@invoice.due_date) diff --git a/app/views/registrar/invoices/pdf.haml b/app/views/registrar/invoices/pdf.haml index 3d6e111ef..831279675 100644 --- a/app/views/registrar/invoices/pdf.haml +++ b/app/views/registrar/invoices/pdf.haml @@ -149,21 +149,21 @@ %hr %dl.dl-horizontal %dt= t(:issue_date) - %dd= l(@invoice.created_at, format: :date_long) + %dd= l @invoice.issue_date - if @invoice.cancelled? - %dt= t(:cancel_date) - %dd= l(@invoice.cancelled_at, format: :date_long) + %dt= Invoice.human_attribute_name :cancelled_at + %dd= l @invoice.cancelled_at, %dt= t(:due_date) - if @invoice.cancelled? %dd= t(:cancelled) - else - %dd= l(@invoice.due_date, format: :date_long) + %dd= l @invoice.due_date - %dt= t(:receipt_date) - - if @invoice.binded? - %dd= l(@invoice.receipt_date, format: :date_long) + %dt= Invoice.human_attribute_name :receipt_date + - if @invoice.paid? + %dd= l @invoice.receipt_date - elsif @invoice.cancelled? %dd= t(:cancelled) - else @@ -173,7 +173,7 @@ %dd= @invoice.seller_contact_name %dt= t(:payment_term) - %dd= t(@invoice.payment_term) + %dd Prepayment %dt= t(:invoice_number) %dd= @invoice.number @@ -224,17 +224,17 @@ %tr %th{class: 'col-xs-4'}= t(:description) %th{class: 'col-xs-2'}= t(:unit) - %th{class: 'col-xs-1'}= t(:amount) + %th{class: 'col-xs-1'}= InvoiceItem.human_attribute_name :quantity %th{class: 'col-xs-3'}= t(:price) %th{class: 'col-xs-2'}= t(:total) %tbody - - @invoice.items.each do |x| + - @invoice.each do |invoice_item| %tr - %td= t(x.description) - %td= x.unit - %td= currency(x.amount) - %td= currency(x.price) - %td= "#{currency(x.item_sum_without_vat)} #{@invoice.currency}" + %td= invoice_item.description + %td= invoice_item.unit + %td= invoice_item.quantity + %td= currency(invoice_item.price) + %td= "#{currency(invoice_item.item_sum_without_vat)} #{@invoice.currency}" %tfoot %tr %th{colspan: 3} diff --git a/app/views/registrar/invoices/show.haml b/app/views/registrar/invoices/show.haml index c2c5331c2..f44660c9a 100644 --- a/app/views/registrar/invoices/show.haml +++ b/app/views/registrar/invoices/show.haml @@ -1,7 +1,7 @@ - content_for :actions do = link_to(t(:download), download_pdf_registrar_invoice_path(@invoice), class: 'btn btn-default') = link_to(t(:forward), forward_registrar_invoice_path(@invoice), class: 'btn btn-default') - - if !@invoice.cancelled? && !@invoice.binded? + - if @invoice.cancellable? = link_to(t(:cancel), cancel_registrar_invoice_path(@invoice), method: :patch, class: 'btn btn-warning') = link_to(t(:back), registrar_invoices_path, class: 'btn btn-default') = render 'shared/title', name: @invoice.to_s @@ -15,6 +15,6 @@ .row .col-md-12= render 'registrar/invoices/partials/items' -- if !@invoice.cancelled? && !@invoice.binded? +- if @invoice.payable? .row.semifooter .col-md-6-offset-6.text-right= render 'registrar/invoices/partials/banklinks', locals: { payment_channels: PaymentOrders::PAYMENT_METHODS } diff --git a/config/initializers/initial_settings.rb b/config/initializers/initial_settings.rb index f507a74fa..95ad7049f 100644 --- a/config/initializers/initial_settings.rb +++ b/config/initializers/initial_settings.rb @@ -40,7 +40,6 @@ if con.present? && con.table_exists?('settings') Setting.save_default(:directo_monthly_number_max, 309999) Setting.save_default(:directo_monthly_number_last, 309901) Setting.save_default(:days_to_keep_invoices_active, 30) - Setting.save_default(:days_to_keep_overdue_invoices_active, 30) Setting.save_default(:minimum_deposit, 0.0) Setting.save_default(:directo_receipt_payment_term, "R") Setting.save_default(:directo_receipt_product_name, "ETTEM06") diff --git a/config/locales/account_activities.en.yml b/config/locales/account_activities.en.yml new file mode 100644 index 000000000..41863219f --- /dev/null +++ b/config/locales/account_activities.en.yml @@ -0,0 +1,5 @@ +en: + activerecord: + attributes: + account_activity: + created_at: Receipt date \ No newline at end of file diff --git a/config/locales/admin/invoices.en.yml b/config/locales/admin/invoices.en.yml new file mode 100644 index 000000000..fba6ea26d --- /dev/null +++ b/config/locales/admin/invoices.en.yml @@ -0,0 +1,5 @@ +en: + admin: + invoices: + cancel: + cancelled: Invoice has been cancelled \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index 978b7e7c3..eeda4caf1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -523,12 +523,10 @@ en: domain_not_found: 'Domain was not found' new_contact: 'New contact' add_deposit: 'Add deposit' - amount: 'Amount' please_pay_the_following_invoice: 'Please pay the following invoice' invoice_no: 'Invoice no. %{no}' invoice_number: Invoice no. seller: 'Seller' - prepayment: 'Prepayment' unpaid: 'Unpaid' your_current_account_balance_is: 'Your current account balance is %{balance} %{currency}' billing: 'Billing' @@ -588,7 +586,6 @@ en: refers to the computer that saves and forwards notices related to the Domain Names and their corresponding IP addresses in the Internet. account_activity: 'Account activity' account_activities: 'Account activities' - receipt_date: 'Receipt date' manual_binding: 'Manual binding' transaction_is_already_binded: 'Transaction is already binded' invoice_was_not_found: 'Invoice was not found' @@ -614,9 +611,6 @@ en: certificates: 'Certificates' cancel: 'Cancel' cancelled: 'Cancelled' - cancel_date: 'Cancel date' - cannot_cancel_paid_invoice: 'Cannot cancel paid invoice' - cannot_cancel_cancelled_invoice: 'Cannot cancel cancelled invoice' cannot_bind_cancelled_invoice: 'Cannot bind cancelled invoice' minimum_invoice_no: 'Miminum invoice no' maximum_invoice_no: 'Maximum invoice no' diff --git a/config/locales/registrar/invoices.en.yml b/config/locales/registrar/invoices.en.yml index 8415c3d61..8e79ee2e0 100644 --- a/config/locales/registrar/invoices.en.yml +++ b/config/locales/registrar/invoices.en.yml @@ -9,3 +9,6 @@ en: payment_complete: Credit Card payment Complete index: reset_btn: Reset + + cancel: + cancelled: Invoice has been cancelled \ No newline at end of file diff --git a/config/schedule.rb b/config/schedule.rb index 6ad7328b2..f58e5b07b 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -22,7 +22,7 @@ if @cron_group == 'registry' end every :day, at: '12:10am' do - runner 'Invoice.cancel_overdue_invoices' + rake 'invoices:cancel_overdue' end # TODO diff --git a/db/migrate/20181017092829_rename_invoice_items_amount_to_quantity.rb b/db/migrate/20181017092829_rename_invoice_items_amount_to_quantity.rb new file mode 100644 index 000000000..823305ef2 --- /dev/null +++ b/db/migrate/20181017092829_rename_invoice_items_amount_to_quantity.rb @@ -0,0 +1,5 @@ +class RenameInvoiceItemsAmountToQuantity < ActiveRecord::Migration + def change + rename_column :invoice_items, :amount, :quantity + end +end diff --git a/db/migrate/20181017153658_add_invoice_items_invoice_id_fk.rb b/db/migrate/20181017153658_add_invoice_items_invoice_id_fk.rb new file mode 100644 index 000000000..aa656f61b --- /dev/null +++ b/db/migrate/20181017153658_add_invoice_items_invoice_id_fk.rb @@ -0,0 +1,5 @@ +class AddInvoiceItemsInvoiceIdFk < ActiveRecord::Migration + def change + add_foreign_key :invoice_items, :invoices, name: 'invoice_items_invoice_id_fk' + end +end diff --git a/db/migrate/20181017153812_change_invoice_items_invoice_id_to_not_null.rb b/db/migrate/20181017153812_change_invoice_items_invoice_id_to_not_null.rb new file mode 100644 index 000000000..162b507cf --- /dev/null +++ b/db/migrate/20181017153812_change_invoice_items_invoice_id_to_not_null.rb @@ -0,0 +1,5 @@ +class ChangeInvoiceItemsInvoiceIdToNotNull < ActiveRecord::Migration + def change + change_column_null :invoice_items, :invoice_id, false + end +end diff --git a/db/migrate/20181017153935_change_invoice_items_quantity_to_not_null.rb b/db/migrate/20181017153935_change_invoice_items_quantity_to_not_null.rb new file mode 100644 index 000000000..409979f31 --- /dev/null +++ b/db/migrate/20181017153935_change_invoice_items_quantity_to_not_null.rb @@ -0,0 +1,5 @@ +class ChangeInvoiceItemsQuantityToNotNull < ActiveRecord::Migration + def change + change_column_null :invoice_items, :quantity, false + end +end diff --git a/db/migrate/20181017154038_change_invoice_items_unit_to_not_null.rb b/db/migrate/20181017154038_change_invoice_items_unit_to_not_null.rb new file mode 100644 index 000000000..f9ffad263 --- /dev/null +++ b/db/migrate/20181017154038_change_invoice_items_unit_to_not_null.rb @@ -0,0 +1,5 @@ +class ChangeInvoiceItemsUnitToNotNull < ActiveRecord::Migration + def change + change_column_null :invoice_items, :unit, false + end +end diff --git a/db/migrate/20181017154143_change_invoice_items_price_to_not_null.rb b/db/migrate/20181017154143_change_invoice_items_price_to_not_null.rb new file mode 100644 index 000000000..84949faee --- /dev/null +++ b/db/migrate/20181017154143_change_invoice_items_price_to_not_null.rb @@ -0,0 +1,5 @@ +class ChangeInvoiceItemsPriceToNotNull < ActiveRecord::Migration + def change + change_column_null :invoice_items, :price, false + end +end diff --git a/db/migrate/20181017205123_change_invoices_due_date_to_date.rb b/db/migrate/20181017205123_change_invoices_due_date_to_date.rb new file mode 100644 index 000000000..4cebc64c9 --- /dev/null +++ b/db/migrate/20181017205123_change_invoices_due_date_to_date.rb @@ -0,0 +1,5 @@ +class ChangeInvoicesDueDateToDate < ActiveRecord::Migration + def change + change_column :invoices, :due_date, :date, null: false + end +end diff --git a/db/migrate/20181022100114_add_invoices_issue_date.rb b/db/migrate/20181022100114_add_invoices_issue_date.rb new file mode 100644 index 000000000..ca713c5fb --- /dev/null +++ b/db/migrate/20181022100114_add_invoices_issue_date.rb @@ -0,0 +1,5 @@ +class AddInvoicesIssueDate < ActiveRecord::Migration + def change + add_column :invoices, :issue_date, :date + end +end diff --git a/db/migrate/20190311111718_remove_invoices_paid_at.rb b/db/migrate/20190311111718_remove_invoices_paid_at.rb new file mode 100644 index 000000000..8e7735da4 --- /dev/null +++ b/db/migrate/20190311111718_remove_invoices_paid_at.rb @@ -0,0 +1,5 @@ +class RemoveInvoicesPaidAt < ActiveRecord::Migration + def change + remove_column :invoices, :paid_at + end +end diff --git a/db/migrate/20190312211614_remove_invoices_payment_term.rb b/db/migrate/20190312211614_remove_invoices_payment_term.rb new file mode 100644 index 000000000..41ab648dc --- /dev/null +++ b/db/migrate/20190312211614_remove_invoices_payment_term.rb @@ -0,0 +1,5 @@ +class RemoveInvoicesPaymentTerm < ActiveRecord::Migration + def change + remove_column :invoices, :payment_term + end +end diff --git a/db/structure.sql b/db/structure.sql index db86621d8..2b73c36c6 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1039,11 +1039,11 @@ ALTER SEQUENCE public.epp_sessions_id_seq OWNED BY public.epp_sessions.id; CREATE TABLE public.invoice_items ( id integer NOT NULL, - invoice_id integer, + invoice_id integer NOT NULL, description character varying NOT NULL, - unit character varying, - amount integer, - price numeric(10,2), + unit character varying NOT NULL, + quantity integer NOT NULL, + price numeric(10,2) NOT NULL, created_at timestamp without time zone, updated_at timestamp without time zone, creator_str character varying, @@ -1078,13 +1078,11 @@ CREATE TABLE public.invoices ( id integer NOT NULL, created_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL, - due_date timestamp without time zone NOT NULL, - payment_term character varying, + due_date date NOT NULL, currency character varying NOT NULL, description character varying, reference_no character varying NOT NULL, vat_rate numeric(4,3), - paid_at timestamp without time zone, seller_id integer, seller_name character varying NOT NULL, seller_reg_no character varying, @@ -1118,7 +1116,8 @@ CREATE TABLE public.invoices ( cancelled_at timestamp without time zone, total numeric(10,2) NOT NULL, in_directo boolean DEFAULT false, - buyer_vat_no character varying + buyer_vat_no character varying, + issue_date date ); @@ -4234,6 +4233,14 @@ ALTER TABLE ONLY public.account_activities ADD CONSTRAINT fk_rails_d2cc3c2fa9 FOREIGN KEY (price_id) REFERENCES public.prices(id); +-- +-- Name: invoice_items_invoice_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoice_items + ADD CONSTRAINT invoice_items_invoice_id_fk FOREIGN KEY (invoice_id) REFERENCES public.invoices(id); + + -- -- Name: messages_registrar_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -4926,6 +4933,22 @@ INSERT INTO schema_migrations (version) VALUES ('20181001090536'); INSERT INTO schema_migrations (version) VALUES ('20181002090319'); +INSERT INTO schema_migrations (version) VALUES ('20181017092829'); + +INSERT INTO schema_migrations (version) VALUES ('20181017153658'); + +INSERT INTO schema_migrations (version) VALUES ('20181017153812'); + +INSERT INTO schema_migrations (version) VALUES ('20181017153935'); + +INSERT INTO schema_migrations (version) VALUES ('20181017154038'); + +INSERT INTO schema_migrations (version) VALUES ('20181017154143'); + +INSERT INTO schema_migrations (version) VALUES ('20181017205123'); + +INSERT INTO schema_migrations (version) VALUES ('20181022100114'); + INSERT INTO schema_migrations (version) VALUES ('20181108154921'); INSERT INTO schema_migrations (version) VALUES ('20181129150515'); @@ -4962,3 +4985,7 @@ INSERT INTO schema_migrations (version) VALUES ('20190102115333'); INSERT INTO schema_migrations (version) VALUES ('20190102144032'); +INSERT INTO schema_migrations (version) VALUES ('20190311111718'); + +INSERT INTO schema_migrations (version) VALUES ('20190312211614'); + diff --git a/lib/tasks/data_migrations/populate_invoice_issue_date.rake b/lib/tasks/data_migrations/populate_invoice_issue_date.rake new file mode 100644 index 000000000..90a6bd4cb --- /dev/null +++ b/lib/tasks/data_migrations/populate_invoice_issue_date.rake @@ -0,0 +1,16 @@ +namespace :data_migrations do + task populate_invoice_issue_date: [:environment] do + processed_invoice_count = 0 + + Invoice.transaction do + Invoice.find_each do |invoice| + invoice_issue_date = invoice.created_at.to_date + invoice.update!(issue_date: invoice_issue_date) + + processed_invoice_count += 1 + end + end + + puts "Invoices processed: #{processed_invoice_count}" + end +end diff --git a/lib/tasks/invoices/cancel_overdue.rake b/lib/tasks/invoices/cancel_overdue.rake new file mode 100644 index 000000000..5b049d853 --- /dev/null +++ b/lib/tasks/invoices/cancel_overdue.rake @@ -0,0 +1,13 @@ +namespace :invoices do + task cancel_overdue: :environment do + cancelled_invoice_count = 0 + + canceller = OverdueInvoiceCanceller.new + canceller.cancel do |invoice| + puts "Invoice ##{invoice.id} is cancelled" + cancelled_invoice_count += 1 + end + + puts "Cancelled total: #{cancelled_invoice_count}" + end +end \ No newline at end of file diff --git a/spec/factories/invoice.rb b/spec/factories/invoice.rb deleted file mode 100644 index 06293c7ad..000000000 --- a/spec/factories/invoice.rb +++ /dev/null @@ -1,18 +0,0 @@ -FactoryBot.define do - factory :invoice do - buyer_name 'Registrar 1' - currency { 'EUR' } - due_date { Time.zone.now.to_date + 1.day } - seller_iban { '123' } - seller_name { 'EIS' } - seller_city { 'Tallinn' } - seller_street { 'Paldiski mnt. 123' } - vat_rate 0.2 - buyer { FactoryBot.create(:registrar) } - sequence(:reference_no) { |n| "1234#{n}" } - - after :build do |invoice| - invoice.invoice_items << FactoryBot.create_pair(:invoice_item) - end - end -end diff --git a/spec/factories/invoice_item.rb b/spec/factories/invoice_item.rb deleted file mode 100644 index 3dee95319..000000000 --- a/spec/factories/invoice_item.rb +++ /dev/null @@ -1,8 +0,0 @@ -FactoryBot.define do - factory :invoice_item do - description { 'add money' } - unit 1 - amount 1 - price 150 - end -end diff --git a/spec/models/invoice_spec.rb b/spec/models/invoice_spec.rb deleted file mode 100644 index 14747c9c1..000000000 --- a/spec/models/invoice_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'rails_helper' - -describe Invoice do - context 'with invalid attribute' do - before :all do - @invoice = Invoice.new - end - - it 'should not be valid' do - @invoice.valid? - @invoice.errors.full_messages.should match_array([ - "Buyer name is missing", - "Currency is missing", - "Due date is missing", - "Invoice items is missing", - "Seller iban is missing", - "Seller name is missing", - ]) - end - - it 'should not have any versions' do - @invoice.versions.should == [] - end - end - - context 'with valid attributes' do - before :all do - @invoice = create(:invoice) - end - - it 'should be valid' do - @invoice.valid? - @invoice.errors.full_messages.should match_array([]) - end - - it 'should be valid twice' do - @invoice = create(:invoice) - @invoice.valid? - @invoice.errors.full_messages.should match_array([]) - end - - it 'should be valid twice' do - @invoice = create(:invoice) - @invoice.valid? - @invoice.errors.full_messages.should match_array([]) - end - - it 'should return correct addresses' do - @invoice = create(:invoice) - @invoice.seller_address.should == 'Paldiski mnt. 123, Tallinn' - end - - it 'should cancel overdue invoices' do - create(:invoice, created_at: Time.zone.now - 35.days, due_date: Time.zone.now - 30.days) - Invoice.cancel_overdue_invoices - Invoice.where(cancelled_at: nil).count.should == 1 - end - - it 'should have one version' do - with_versioning do - @invoice.versions.should == [] - @invoice.buyer_name = 'New name' - @invoice.save - @invoice.errors.full_messages.should match_array([]) - @invoice.versions.size.should == 1 - end - end - end -end diff --git a/test/fixtures/account_activities.yml b/test/fixtures/account_activities.yml index 06a5b4006..dbe1dc2aa 100644 --- a/test/fixtures/account_activities.yml +++ b/test/fixtures/account_activities.yml @@ -1,7 +1,5 @@ -DEFAULTS: &DEFAULTS - account_id: 1 - one: - <<: *DEFAULTS - invoice: paid + account: cash + invoice: one bank_transaction: one + created_at: <%= Time.zone.parse('2010-07-05 10:00') %> \ No newline at end of file diff --git a/test/fixtures/accounts.yml b/test/fixtures/accounts.yml index a7060c249..04b515dc4 100644 --- a/test/fixtures/accounts.yml +++ b/test/fixtures/accounts.yml @@ -4,6 +4,12 @@ cash: currency: EUR registrar: bestnames +two: + account_type: cash + balance: 100 + currency: EUR + registrar: goodnames + not_in_use_cash: account_type: cash balance: 0 diff --git a/test/fixtures/invoice_items.yml b/test/fixtures/invoice_items.yml index 9c37ece9a..19409df81 100644 --- a/test/fixtures/invoice_items.yml +++ b/test/fixtures/invoice_items.yml @@ -1,13 +1,6 @@ one: description: Acme services price: 5 - amount: 1 + quantity: 1 unit: pc - invoice: valid - -two: - description: Acme services - price: 5 - amount: 2 - unit: pc - invoice: valid + invoice: one diff --git a/test/fixtures/invoices.yml b/test/fixtures/invoices.yml index 52d7cb06e..d163ba732 100644 --- a/test/fixtures/invoices.yml +++ b/test/fixtures/invoices.yml @@ -1,5 +1,5 @@ -DEFAULTS: &DEFAULTS - created_at: <%= Date.parse '2010-07-05' %> +one: + issue_date: <%= Date.parse '2010-07-05' %> due_date: <%= Date.parse '2010-07-06' %> currency: EUR seller_name: John Doe @@ -10,30 +10,15 @@ DEFAULTS: &DEFAULTS total: 16.50 reference_no: 13 -valid: - <<: *DEFAULTS - -exported: - <<: *DEFAULTS - in_directo: true - -cancelled: - <<: *DEFAULTS - cancelled_at: <%= Date.parse '2010-07-05' %> - -paid: - <<: *DEFAULTS - total: 1 - -outstanding: - <<: *DEFAULTS - due_date: <%= Date.parse '2010-07-04' %> - -overdue: - <<: *DEFAULTS - due_date: <%= Date.parse '2010-07-03' %> - for_payments_test: - <<: *DEFAULTS - total: 12.00 number: 1 + issue_date: <%= Date.parse '2010-07-05' %> + due_date: <%= Date.parse '2010-07-06' %> + currency: EUR + seller_name: John Doe + seller_iban: 1234 + buyer: bestnames + buyer_name: Jane Doe + vat_rate: 0.1 + reference_no: 13 + total: 12.00 \ No newline at end of file diff --git a/test/integration/tasks/data_migrations/regenerate_registrar_reference_numbers_test.rb b/test/integration/tasks/data_migrations/regenerate_registrar_reference_numbers_test.rb index c496cd95b..946c6b898 100644 --- a/test/integration/tasks/data_migrations/regenerate_registrar_reference_numbers_test.rb +++ b/test/integration/tasks/data_migrations/regenerate_registrar_reference_numbers_test.rb @@ -35,7 +35,7 @@ class RegenerateRegistrarReferenceNumbersTaskTest < ActiveSupport::TestCase def test_keeps_iso_reference_number_on_the_invoice_unchanged registrar = registrars(:bestnames) registrar.update_column(:reference_no, 'RF1111') - invoice = registrar.invoices.first + invoice = invoices(:one) invoice.update!(reference_no: 'RF2222') capture_io { run_task } diff --git a/test/models/account_activity_test.rb b/test/models/account_activity_test.rb new file mode 100644 index 000000000..df0d76c05 --- /dev/null +++ b/test/models/account_activity_test.rb @@ -0,0 +1,11 @@ +require 'test_helper' + +class AccountActivityTest < ActiveSupport::TestCase + setup do + @account_activity = account_activities(:one) + end + + def test_fixture_is_valid + assert @account_activity.valid? + end +end \ No newline at end of file diff --git a/test/models/bank_transaction_test.rb b/test/models/bank_transaction_test.rb index d6f32bb10..4955b020d 100644 --- a/test/models/bank_transaction_test.rb +++ b/test/models/bank_transaction_test.rb @@ -2,7 +2,7 @@ require 'test_helper' class BankTransactionTest < ActiveSupport::TestCase def test_matches_against_invoice_reference_number - invoices(:valid).update!(number: '2222', total: 10, reference_no: '1111') + invoices(:one).update!(account_activity: nil, number: '2222', total: 10, reference_no: '1111') transaction = BankTransaction.new(description: 'invoice #2222', sum: 10, reference_no: '1111') assert_difference 'AccountActivity.count' do @@ -20,7 +20,7 @@ class BankTransactionTest < ActiveSupport::TestCase end def test_underpayment_is_not_matched_with_invoice - invoices(:valid).update!(number: '2222', total: 10) + invoices(:one).update!(account_activity: nil, number: '2222', total: 10) transaction = BankTransaction.new(sum: 9) assert_no_difference 'AccountActivity.count' do @@ -30,7 +30,7 @@ class BankTransactionTest < ActiveSupport::TestCase end def test_overpayment_is_not_matched_with_invoice - invoices(:valid).update!(number: '2222', total: 10) + invoices(:one).update!(account_activity: nil, number: '2222', total: 10) transaction = BankTransaction.new(sum: 11) assert_no_difference 'AccountActivity.count' do @@ -40,7 +40,7 @@ class BankTransactionTest < ActiveSupport::TestCase end def test_cancelled_invoice_is_not_matched - invoices(:valid).update!(number: '2222', total: 10, cancelled_at: '2010-07-05') + invoices(:one).update!(account_activity: nil, number: '2222', total: 10, cancelled_at: '2010-07-05') transaction = BankTransaction.new(sum: 10) assert_no_difference 'AccountActivity.count' do diff --git a/test/models/invoice/cancellable_test.rb b/test/models/invoice/cancellable_test.rb new file mode 100644 index 000000000..866fd19b2 --- /dev/null +++ b/test/models/invoice/cancellable_test.rb @@ -0,0 +1,69 @@ +require 'test_helper' + +class CancellableInvoiceTest < ActiveSupport::TestCase + setup do + @invoice = invoices(:one) + end + + def test_non_cancelled_scope_returns_non_cancelled_invoices + @invoice.update!(cancelled_at: nil) + assert Invoice.non_cancelled.include?(@invoice), 'Should return cancelled invoice' + end + + def test_non_cancelled_scope_does_not_return_cancelled_invoices + @invoice.update!(cancelled_at: '2010-07-05') + assert_not Invoice.non_cancelled.include?(@invoice), 'Should not return cancelled invoice' + end + + def test_cancellable_when_unpaid_and_not_yet_cancelled + @invoice.account_activity = nil + @invoice.cancelled_at = nil + assert @invoice.cancellable? + end + + def test_not_cancellable_when_paid + assert @invoice.paid? + assert_not @invoice.cancellable? + end + + def test_not_cancellable_when_already_cancelled + @invoice.cancelled_at = '2010-07-05' + assert_not @invoice.cancellable? + end + + def test_cancels_an_invoice + travel_to Time.zone.parse('2010-07-05 08:00') + @invoice.account_activity = nil + assert @invoice.cancellable? + assert_nil @invoice.cancelled_at + + @invoice.cancel + @invoice.reload + + assert @invoice.cancelled? + assert_equal Time.zone.parse('2010-07-05 08:00'), @invoice.cancelled_at + end + + def test_throws_an_exception_when_trying_to_cancel_already_cancelled_invoice + @invoice.cancelled_at = '2010-07-05' + + e = assert_raise do + @invoice.cancel + end + assert_equal 'Invoice cannot be cancelled', e.message + end + + def test_not_cancelled + @invoice.cancelled_at = nil + + assert @invoice.not_cancelled? + assert_not @invoice.cancelled? + end + + def test_cancelled + @invoice.cancelled_at = '2010-07-05' + + assert @invoice.cancelled? + assert_not @invoice.not_cancelled? + end +end \ No newline at end of file diff --git a/test/models/invoice/payable_test.rb b/test/models/invoice/payable_test.rb new file mode 100644 index 000000000..b35ae9a0e --- /dev/null +++ b/test/models/invoice/payable_test.rb @@ -0,0 +1,53 @@ +require 'test_helper' + +class InvoiceTest < ActiveSupport::TestCase + setup do + @invoice = invoices(:one) + end + + def test_unpaid_scope_returns_unpaid_invoices + @invoice.account_activity = nil + assert Invoice.unpaid.include?(@invoice), 'Should return unpaid invoice' + end + + def test_unpaid_scope_does_not_return_paid_invoices + assert @invoice.paid? + assert_not Invoice.unpaid.include?(@invoice), 'Should not return paid invoice' + end + + def test_paid_when_there_is_an_account_activity + assert @invoice.account_activity + + assert @invoice.paid? + assert_not @invoice.unpaid? + end + + def test_unpaid_when_there_is_no_account_activity + @invoice.account_activity = nil + + assert @invoice.unpaid? + assert_not @invoice.paid? + end + + def test_payable_when_unpaid_and_not_cancelled + @invoice.account_activity = nil + @invoice.cancelled_at = nil + + assert @invoice.payable? + end + + def test_not_payable_when_already_paid + assert @invoice.paid? + assert_not @invoice.payable? + end + + def test_not_payable_when_cancelled + @invoice.cancelled_at = '2010-07-05' + assert_not @invoice.payable? + end + + def test_returns_receipt_date + assert_equal Time.zone.parse('2010-07-05 10:00'), @invoice.account_activity.created_at + assert_equal Date.parse('2010-07-05'), @invoice.receipt_date + end +end \ No newline at end of file diff --git a/test/models/invoice_item_test.rb b/test/models/invoice_item_test.rb new file mode 100644 index 000000000..15008718f --- /dev/null +++ b/test/models/invoice_item_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class InvoiceItemTest < ActiveSupport::TestCase + def test_calculates_sum_without_vat + invoice_item = InvoiceItem.new(price: 5, quantity: 2) + assert_equal 10, invoice_item.item_sum_without_vat + end +end diff --git a/test/models/invoice_test.rb b/test/models/invoice_test.rb index 45ca57417..ea580082a 100644 --- a/test/models/invoice_test.rb +++ b/test/models/invoice_test.rb @@ -2,11 +2,40 @@ require 'test_helper' class InvoiceTest < ActiveSupport::TestCase setup do - @invoice = invoices(:valid) + @invoice = invoices(:one) end - def test_valid - assert @invoice.valid? + def test_fixture_is_valid + assert @invoice.valid?, proc { @invoice.errors.full_messages } + end + + def test_overdue_scope_returns_unpaid_uncancelled_invoices_with_past_due_date + travel_to Time.zone.parse('2010-07-05') + @invoice.update!(account_activity: nil, cancelled_at: nil, due_date: '2010-07-04') + + assert Invoice.overdue.include?(@invoice), 'Should return overdue invoice' + end + + def test_overdue_scope_does_not_return_paid_invoices + assert @invoice.paid? + assert_not Invoice.overdue.include?(@invoice), 'Should not return paid invoice' + end + + def test_overdue_scope_does_not_return_cancelled_invoices + @invoice.update!(cancelled_at: '2010-07-05') + assert_not Invoice.overdue.include?(@invoice), 'Should not return cancelled invoice' + end + + def test_overdue_scope_does_not_return_invoices_with_due_due_of_today_or_in_the_future + travel_to Time.zone.parse('2010-07-05') + @invoice.update!(due_date: '2010-07-05') + + assert_not Invoice.overdue.include?(@invoice), 'Should not return non-overdue invoice' + end + + def test_invalid_without_issue_date + @invoice.issue_date = nil + assert @invoice.invalid? end def test_optional_vat_rate @@ -30,7 +59,7 @@ class InvoiceTest < ActiveSupport::TestCase def test_serializes_and_deserializes_vat_rate invoice = @invoice.dup - invoice.invoice_items = @invoice.invoice_items + invoice.items = @invoice.items invoice.vat_rate = BigDecimal('25.5') invoice.save! invoice.reload @@ -42,7 +71,7 @@ class InvoiceTest < ActiveSupport::TestCase invoice = @invoice.dup invoice.vat_rate = nil invoice.buyer = registrar - invoice.invoice_items = @invoice.invoice_items + invoice.items = @invoice.items registrar.stub(:effective_vat_rate, BigDecimal(55)) do invoice.save! @@ -59,7 +88,9 @@ class InvoiceTest < ActiveSupport::TestCase end def test_calculates_vat_amount - assert_equal BigDecimal('1.5'), @invoice.vat_amount + invoice_item = InvoiceItem.new(price: 25, quantity: 2) + invoice = Invoice.new(vat_rate: 10, items: [invoice_item, invoice_item.dup]) + assert_equal 10, invoice.vat_amount end def test_vat_amount_is_zero_when_vat_rate_is_blank @@ -69,7 +100,7 @@ class InvoiceTest < ActiveSupport::TestCase def test_calculates_subtotal line_item = InvoiceItem.new - invoice = Invoice.new(invoice_items: [line_item, line_item]) + invoice = Invoice.new(items: [line_item, line_item]) line_item.stub(:item_sum_without_vat, BigDecimal('2.5')) do assert_equal BigDecimal(5), invoice.subtotal @@ -84,7 +115,7 @@ class InvoiceTest < ActiveSupport::TestCase line_item = InvoiceItem.new invoice = Invoice.new invoice.vat_rate = 10 - invoice.invoice_items = [line_item, line_item] + invoice.items = [line_item, line_item] line_item.stub(:item_sum_without_vat, BigDecimal('2.5')) do assert_equal BigDecimal('5.50'), invoice.total @@ -102,8 +133,25 @@ class InvoiceTest < ActiveSupport::TestCase invoice = @invoice.dup invoice.buyer_vat_no = nil invoice.buyer = registrar - invoice.invoice_items = @invoice.invoice_items + invoice.items = @invoice.items invoice.save! assert_equal 'US1234', invoice.buyer_vat_no end -end + + def test_invalid_without_invoice_items + @invoice.items.clear + assert @invoice.invalid? + end + + def test_iterates_over_invoice_items + invoice = Invoice.new(items: [InvoiceItem.new(description: 'test')]) + + iteration_count = 0 + invoice.each do |invoice_item| + assert_equal 'test', invoice_item.description + iteration_count += 1 + end + + assert_equal 1, iteration_count + end +end \ No newline at end of file diff --git a/test/models/payment_orders/bank_link_test.rb b/test/models/payment_orders/bank_link_test.rb index 16f790d13..d62c3c4c1 100644 --- a/test/models/payment_orders/bank_link_test.rb +++ b/test/models/payment_orders/bank_link_test.rb @@ -10,8 +10,8 @@ class BankLinkTest < ActiveSupport::TestCase @invoice = invoices(:for_payments_test) invoice_item = invoice_items(:one) - @invoice.invoice_items << invoice_item - @invoice.invoice_items << invoice_item + @invoice.items << invoice_item + @invoice.items << invoice_item travel_to '2018-04-01 00:30 +0300' create_new_bank_link diff --git a/test/models/payment_orders/every_pay_test.rb b/test/models/payment_orders/every_pay_test.rb index 2be3401dc..c8d45a84c 100644 --- a/test/models/payment_orders/every_pay_test.rb +++ b/test/models/payment_orders/every_pay_test.rb @@ -7,8 +7,8 @@ class EveryPayTest < ActiveSupport::TestCase @invoice = invoices(:for_payments_test) invoice_item = invoice_items(:one) - @invoice.invoice_items << invoice_item - @invoice.invoice_items << invoice_item + @invoice.items << invoice_item + @invoice.items << invoice_item params = { response: diff --git a/test/models/registrar_test.rb b/test/models/registrar_test.rb index 242e19c89..312822a8f 100644 --- a/test/models/registrar_test.rb +++ b/test/models/registrar_test.rb @@ -83,4 +83,17 @@ class RegistrarTest < ActiveSupport::TestCase registrars(:goodnames).update!(reference_no: '1234') end end + + def test_issues_new_invoice + travel_to Time.zone.parse('2010-07-05') + @original_days_to_keep_invoices_active_setting = Setting.days_to_keep_invoices_active + Setting.days_to_keep_invoices_active = 10 + + invoice = @registrar.issue_prepayment_invoice(100) + + assert_equal Date.parse('2010-07-05'), invoice.issue_date + assert_equal Date.parse('2010-07-15'), invoice.due_date + + Setting.days_to_keep_invoices_active = @original_days_to_keep_invoices_active_setting + end end diff --git a/test/services/overdue_invoice_canceller_test.rb b/test/services/overdue_invoice_canceller_test.rb new file mode 100644 index 000000000..6dd2ea3b4 --- /dev/null +++ b/test/services/overdue_invoice_canceller_test.rb @@ -0,0 +1,62 @@ +require 'test_helper' + +class OverdueInvoiceCancellerTest < ActiveSupport::TestCase + setup do + @invoice = invoices(:one) + end + + def test_default_delay + assert_equal 30.days, OverdueInvoiceCanceller.default_delay + end + + def test_uses_default_delay_when_not_configured + Setting.days_to_keep_overdue_invoices_active = nil + canceller = OverdueInvoiceCanceller.new + assert_equal OverdueInvoiceCanceller.default_delay, canceller.delay + end + + def test_uses_configured_delay + Setting.days_to_keep_overdue_invoices_active = 1 + canceller = OverdueInvoiceCanceller.new + assert_equal 1.day, canceller.delay + end + + def test_cancels_overdue_invoices + travel_to Time.zone.parse('2010-07-05') + @invoice.update!(account_activity: nil, cancelled_at: nil, due_date: '2010-07-03') + assert @invoice.cancellable? + + canceller = OverdueInvoiceCanceller.new(delay: 1.day) + canceller.cancel + @invoice.reload + + assert @invoice.cancelled? + end + + def test_yields_cancelled_invoices + travel_to Time.zone.parse('2010-07-05') + @invoice.update!(account_activity: nil, cancelled_at: nil, due_date: '2010-07-03') + assert @invoice.cancellable? + + canceller = OverdueInvoiceCanceller.new(delay: 1.day) + + iteration_count = 0 + canceller.cancel do |invoice| + assert_equal @invoice, invoice + iteration_count += 1 + end + assert_equal 1, iteration_count + end + + def test_keeps_not_overdue_invoices_intact + travel_to Time.zone.parse('2010-07-05') + @invoice.update!(account_activity: nil, cancelled_at: nil, due_date: '2010-07-04') + assert @invoice.cancellable? + + canceller = OverdueInvoiceCanceller.new(delay: 1.day) + canceller.cancel + @invoice.reload + + assert @invoice.not_cancelled? + end +end \ No newline at end of file diff --git a/test/system/admin_area/invoices_test.rb b/test/system/admin_area/invoices_test.rb new file mode 100644 index 000000000..081bbb850 --- /dev/null +++ b/test/system/admin_area/invoices_test.rb @@ -0,0 +1,20 @@ +require 'test_helper' + +class AdminAreaInvoicesTest < ApplicationSystemTestCase + setup do + sign_in users(:admin) + @invoice = invoices(:one) + end + + def test_cancels_an_invoice + @invoice.account_activity = nil + assert @invoice.cancellable? + + visit admin_invoice_url(@invoice) + click_on 'Cancel' + @invoice.reload + + assert @invoice.cancelled? + assert_text 'Invoice has been cancelled' + end +end \ No newline at end of file diff --git a/test/system/registrar_area/invoices/list_test.rb b/test/system/registrar_area/invoices/list_test.rb index be344d034..e63d8c1a4 100644 --- a/test/system/registrar_area/invoices/list_test.rb +++ b/test/system/registrar_area/invoices/list_test.rb @@ -1,12 +1,12 @@ require 'test_helper' class ListInvoicesTest < ApplicationSystemTestCase - def setup - super - + setup do @user = users(:api_bestnames) - @registrar_invoices = @user.registrar.invoices sign_in @user + + @invoice = invoices(:one) + eliminate_effect_of_other_invoices end def test_show_balance @@ -14,15 +14,31 @@ class ListInvoicesTest < ApplicationSystemTestCase assert_text "Your current account balance is 100,00 EUR" end - def test_show_multiple_invoices - @invoices = invoices - @registrar_invoices = [] - @invoices.each do |invoice| - @registrar_invoices << invoice - end + def test_show_invoices_of_current_registrar + registrar = registrars(:bestnames) + @user.update!(registrar: registrar) + @invoice.update!(seller: registrar) - visit registrar_invoices_path - assert_text "Unpaid", count: 5 - assert_text "Invoice no.", count: 7 + visit registrar_invoices_url + + assert_css '.invoice' end -end + + def test_do_not_show_invoices_of_other_registrars + registrar = registrars(:goodnames) + @user.update!(registrar: registrar) + @invoice.update!(seller: registrar) + + visit registrar_invoices_url + + assert_no_css '.invoice' + end + + private + + def eliminate_effect_of_other_invoices + Invoice.connection.disable_referential_integrity do + Invoice.delete_all("id != #{@invoice.id}") + end + end +end \ No newline at end of file diff --git a/test/system/registrar_area/invoices/payment_callback_test.rb b/test/system/registrar_area/invoices/payment_callback_test.rb index c1920995d..81f5f6ef6 100644 --- a/test/system/registrar_area/invoices/payment_callback_test.rb +++ b/test/system/registrar_area/invoices/payment_callback_test.rb @@ -12,8 +12,8 @@ class PaymentCallbackTest < ApplicationSystemTestCase @invoice = invoices(:for_payments_test) invoice_item = invoice_items(:one) - @invoice.invoice_items << invoice_item - @invoice.invoice_items << invoice_item + @invoice.items << invoice_item + @invoice.items << invoice_item @user.registrar.invoices << @invoice end diff --git a/test/system/registrar_area/invoices/payment_return_test.rb b/test/system/registrar_area/invoices/payment_return_test.rb index 45e85118f..ebf556673 100644 --- a/test/system/registrar_area/invoices/payment_return_test.rb +++ b/test/system/registrar_area/invoices/payment_return_test.rb @@ -12,8 +12,8 @@ class PaymentReturnTest < ApplicationSystemTestCase @invoice = invoices(:for_payments_test) invoice_item = invoice_items(:one) - @invoice.invoice_items << invoice_item - @invoice.invoice_items << invoice_item + @invoice.items << invoice_item + @invoice.items << invoice_item @user.registrar.invoices << @invoice end diff --git a/test/system/registrar_area/invoices_test.rb b/test/system/registrar_area/invoices_test.rb new file mode 100644 index 000000000..a8fa6891f --- /dev/null +++ b/test/system/registrar_area/invoices_test.rb @@ -0,0 +1,20 @@ +require 'test_helper' + +class RegistrarAreaInvoicesTest < ApplicationSystemTestCase + setup do + sign_in users(:api_bestnames) + @invoice = invoices(:one) + end + + def test_cancels_an_invoice + @invoice.account_activity = nil + assert @invoice.cancellable? + + visit registrar_invoice_url(@invoice) + click_on 'Cancel' + @invoice.reload + + assert @invoice.cancelled? + assert_text 'Invoice has been cancelled' + end +end \ No newline at end of file diff --git a/test/tasks/data_migrations/populate_invoice_issue_date_test.rb b/test/tasks/data_migrations/populate_invoice_issue_date_test.rb new file mode 100644 index 000000000..549647963 --- /dev/null +++ b/test/tasks/data_migrations/populate_invoice_issue_date_test.rb @@ -0,0 +1,37 @@ +require 'test_helper' + +class PopulateInvoiceIssueDateTaskTest < ActiveSupport::TestCase + setup do + @invoice = invoices(:one) + end + + def test_populates_invoice_issue_date + eliminate_effect_of_other_invoices + @invoice.update_columns(issue_date: nil, created_at: Time.zone.parse('2010-07-05')) + assert_nil @invoice.read_attribute(:issue_date) + + capture_io { run_task } + @invoice.reload + + assert_equal Date.parse('2010-07-05'), @invoice.issue_date + end + + def test_outputs_results + eliminate_effect_of_other_invoices + @invoice.update_columns(issue_date: nil, created_at: Time.zone.parse('2010-07-05')) + + assert_output("Invoices processed: 1\n") { run_task } + end + + private + + def eliminate_effect_of_other_invoices + Invoice.connection.disable_referential_integrity do + Invoice.delete_all("id != #{@invoice.id}") + end + end + + def run_task + Rake::Task['data_migrations:populate_invoice_issue_date'].execute + end +end diff --git a/test/tasks/invoices/cancel_overdue_test.rb b/test/tasks/invoices/cancel_overdue_test.rb new file mode 100644 index 000000000..19ef8e0bf --- /dev/null +++ b/test/tasks/invoices/cancel_overdue_test.rb @@ -0,0 +1,41 @@ +require 'test_helper' + +class CancelOverdueInvoicesTaskTest < ActiveSupport::TestCase + setup do + @invoice = invoices(:one) + eliminate_effect_of_other_invoices + end + + def test_cancels_overdue_invoices + @invoice.update!(account_activity: nil, cancelled_at: nil, due_date: '2010-07-05') + assert @invoice.cancellable? + + capture_io do + run_task + end + @invoice.reload + + assert @invoice.cancelled? + end + + def test_output + @invoice.update!(account_activity: nil, cancelled_at: nil, due_date: '2010-07-05') + assert @invoice.cancellable? + + assert_output "Invoice ##{@invoice.id} is cancelled\nCancelled total: 1\n" do + run_task + end + end + + private + + def eliminate_effect_of_other_invoices + Invoice.connection.disable_referential_integrity do + Invoice.delete_all("id != #{@invoice.id}") + end + end + + def run_task + Rake::Task['invoices:cancel_overdue'].execute + end +end \ No newline at end of file