Class: Invoice
Overview
Invoice model representing a financial document for goods or services.
Handles billing, payments, line items, and various invoice types including
sales orders, credit memos, and consignment invoices.
Defined Under Namespace
Classes: CaptureFundsHandler, TaxjarSubmissionHandler
Constant Summary
collapse
- SO =
'SO'
- ST =
'ST'
- MI =
'MI'
- MO =
'MO'
- TO =
'TO'
- CI =
'CI'
- SS =
'SS'
- INVOICE_TYPES =
[SO, ST, MI, MO, TO, CI, SS].freeze
- REFERENCE_NUMBER_PATTERN =
/^INV\d+$/i
- LINE_ITEM_CATEGORIES =
[{ name: 'Coupon (Goods)', account_number: COUPONS_ACCOUNT },
{ name: 'Coupon (Freight)', account_number: FREIGHT_COUPONS_ACCOUNT },
{ name: 'Freight', account_number: FREIGHT_ACCOUNT },
{ name: 'Misc', account_number: PRODUCT_SALES_ACCOUNT },
{ name: 'Item', account_number: PRODUCT_SALES_ACCOUNT },
{ name: 'Fee', account_number: nil }].freeze
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
#min_profit_markup
#force_total_reset, #total_reset
#resource_tax_rate
#account_specialist, #local_sales_rep, #primary_sales_rep, #secondary_sales_rep
#creator, #updater
#coupons, #discounts
Delegated Instance Attributes
collapse
Class Method Summary
collapse
Instance Method Summary
collapse
#can_be_transmitted?, #fallback_notification_channel_type, #notification_channel_sort_order, #notification_channel_types, #notification_channels, #own_notification_channel_type, #post_communication_exception_hook, #post_communication_sent_hook, #primary_transmission_contact, #primary_transmission_contact_point_id, #transmission_contact_points
#customer_sync_instance, #delete_from_taxjar, #evaluate_taxjar_submission, #record_already_exists_on_taxjar?, #resubmit_to_taxjar, #should_be_submitted_to_taxjar?, #should_sync_customer_with_taxjar?, #submit_to_taxjar, #sync_customer_with_taxjar, #taxjar_customer_id, #taxjar_submission_instance
#default_sales_markup, #profit_margins_met?, #profitable_line_items, #profitable_status, #profitable_total_discounted, #profitable_total_estimated_cost, #profitable_total_estimated_line_cost, #profitable_total_profit, #profitable_total_profit_margin, #profitable_total_profit_markup, #track_profit?, #validate_min_profit_markup?
#apply_tax_rate_to_line_items, #build_tax_params, #calculate_tax_for_all_lines, #copy_tax_rate, #effective_date, #get_rates_for_line, #get_tax_rate, #manual_rate_goods, #manual_rate_services, #manual_rate_shipping, #origin_address, #refresh_tax_rate, #resource_not_taxable?, #set_initial_tax_rate, #should_refresh_tax_rate?, #state_code, #state_code_sym, #taxes_grouped_by_rate, #taxes_grouped_by_type
#add_line_item, #additional_items, #assign_sequence, #breakdown_of_prices, #calculate_actual_insured_value, #calculate_discounts, #calculate_shipping_cost, #coupon_search, #customer_applied_coupons, #customer_can_apply_coupon?, #discounts_changed?, #discounts_grouped_by_coupon, #discounts_subtotal, #effective_discount, #effective_shipping_discount, #has_kits?, #has_kits_or_serial_numbers?, #has_serial_numbers?, #is_credit_order?, #line_items_requiring_serial_number, #line_items_with_counters, #line_total_plus_tax, #main_rep, #perform_db_total, #purge_empty_quoting_deliveries, #purge_shipping_when_no_other_lines, #remove_line_item, #require_total_reset?, #reset_discount, #set_for_recalc, #set_signature_confirmation_on_shipping_address_change, #set_totals, #shipping_conditions_changed?, #shipping_discounted, #shipping_method_changed?, #should_recalculate_shipping?, #smartinstall_data, #smartsupport_data, #subtotal_cogs, #sync_shipping_line, #total_cogs
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
#quick_note
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
#publish_event
Instance Attribute Details
#allow_duplicate_delivery_for_testing ⇒ Object
Returns the value of attribute allow_duplicate_delivery_for_testing.
163
164
165
|
# File 'app/models/invoice.rb', line 163
def allow_duplicate_delivery_for_testing
@allow_duplicate_delivery_for_testing
end
|
#billing_address_id ⇒ Object
198
|
# File 'app/models/invoice.rb', line 198
validates :due_date, :terms, :invoice_type, :billing_address_id, :customer_id, :document_date, :gl_date, :gl_offset_account_id, presence: true
|
#customer_id ⇒ Object
198
|
# File 'app/models/invoice.rb', line 198
validates :due_date, :terms, :invoice_type, :billing_address_id, :customer_id, :document_date, :gl_date, :gl_offset_account_id, presence: true
|
#delivery_id ⇒ Object
203
|
# File 'app/models/invoice.rb', line 203
validates :delivery_id, uniqueness: { allow_nil: true }, unless: :allow_duplicate_delivery_for_testing?
|
#disable_auto_coupon ⇒ Object
Returns the value of attribute disable_auto_coupon.
163
164
165
|
# File 'app/models/invoice.rb', line 163
def disable_auto_coupon
@disable_auto_coupon
end
|
#do_not_detect_shipping ⇒ Object
Returns the value of attribute do_not_detect_shipping.
163
164
165
|
# File 'app/models/invoice.rb', line 163
def do_not_detect_shipping
@do_not_detect_shipping
end
|
#do_not_set_totals ⇒ Object
Returns the value of attribute do_not_set_totals.
163
164
165
|
# File 'app/models/invoice.rb', line 163
def do_not_set_totals
@do_not_set_totals
end
|
#document_date ⇒ Object
198
|
# File 'app/models/invoice.rb', line 198
validates :due_date, :terms, :invoice_type, :billing_address_id, :customer_id, :document_date, :gl_date, :gl_offset_account_id, presence: true
|
#due_date ⇒ Object
198
|
# File 'app/models/invoice.rb', line 198
validates :due_date, :terms, :invoice_type, :billing_address_id, :customer_id, :document_date, :gl_date, :gl_offset_account_id, presence: true
|
#enter_new_address ⇒ Object
Returns the value of attribute enter_new_address.
163
164
165
|
# File 'app/models/invoice.rb', line 163
def enter_new_address
@enter_new_address
end
|
#gl_date ⇒ Object
198
|
# File 'app/models/invoice.rb', line 198
validates :due_date, :terms, :invoice_type, :billing_address_id, :customer_id, :document_date, :gl_date, :gl_offset_account_id, presence: true
|
#gl_offset_account_id ⇒ Object
198
|
# File 'app/models/invoice.rb', line 198
validates :due_date, :terms, :invoice_type, :billing_address_id, :customer_id, :document_date, :gl_date, :gl_offset_account_id, presence: true
|
#gl_offset_account_ref ⇒ Object
Returns the value of attribute gl_offset_account_ref.
163
164
165
|
# File 'app/models/invoice.rb', line 163
def gl_offset_account_ref
@gl_offset_account_ref
end
|
#invoice_type ⇒ Object
198
|
# File 'app/models/invoice.rb', line 198
validates :due_date, :terms, :invoice_type, :billing_address_id, :customer_id, :document_date, :gl_date, :gl_offset_account_id, presence: true
|
#order_id ⇒ Object
199
|
# File 'app/models/invoice.rb', line 199
validates :order_id, presence: { if: proc { |i| [MI, CI].exclude?(i.invoice_type) } }
|
#original_order_ref ⇒ Object
Returns the value of attribute original_order_ref.
163
164
165
|
# File 'app/models/invoice.rb', line 163
def original_order_ref
@original_order_ref
end
|
#skip_initial_state_check ⇒ Object
Flag to bypass the initial state check (use only if you have a legitimate reason to create
an invoice in a non-draft state, which should be extremely rare)
240
241
242
|
# File 'app/models/invoice.rb', line 240
def skip_initial_state_check
@skip_initial_state_check
end
|
#skip_line_item_integrity_check ⇒ Object
Flag to skip integrity check during initial creation (set by CreateInvoiceFromDelivery)
236
237
238
|
# File 'app/models/invoice.rb', line 236
def skip_line_item_integrity_check
@skip_line_item_integrity_check
end
|
#skip_pdf ⇒ Object
Returns the value of attribute skip_pdf.
163
164
165
|
# File 'app/models/invoice.rb', line 163
def skip_pdf
@skip_pdf
end
|
#store_id ⇒ Object
200
|
# File 'app/models/invoice.rb', line 200
validates :store_id, presence: { if: proc { |i| [MI, CI].include?(i.invoice_type) } }
|
#tax_date ⇒ Object
201
|
# File 'app/models/invoice.rb', line 201
validates :tax_date, :store_id, presence: { if: proc { |i| [MI, CI].include?(i.invoice_type) } }
|
#terms ⇒ Object
198
|
# File 'app/models/invoice.rb', line 198
validates :due_date, :terms, :invoice_type, :billing_address_id, :customer_id, :document_date, :gl_date, :gl_offset_account_id, presence: true
|
Class Method Details
.awaiting_transmission ⇒ ActiveRecord::Relation<Invoice>
A relation of Invoices that are awaiting transmission. Active Record Scope
247
|
# File 'app/models/invoice.rb', line 247
scope :awaiting_transmission, -> { where(state: %w[unpaid paid], transmission_state: %w[awaiting_transmission in_transmission_queue]) }
|
.calculate_due_date(order, delivery) ⇒ Object
440
441
442
443
|
# File 'app/models/invoice.rb', line 440
def self.calculate_due_date(order, delivery)
shipped_date = delivery.shipped_date.to_datetime.to_date
shipped_date + order.billing_entity.terms_in_days.days
end
|
.calculate_terms(order) ⇒ Object
458
459
460
461
462
|
# File 'app/models/invoice.rb', line 458
def self.calculate_terms(order)
terms = order.billing_entity.terms
terms += ' (COD)' if order.funded_by_cod?
terms
end
|
.included_in_notifications ⇒ ActiveRecord::Relation<Invoice>
A relation of Invoices that are included in notifications. Active Record Scope
250
|
# File 'app/models/invoice.rb', line 250
scope :included_in_notifications, -> { where(exclude_fund_capture_notification: false) }
|
.like_lookup ⇒ ActiveRecord::Relation<Invoice>
A relation of Invoices that are like lookup. Active Record Scope
253
|
# File 'app/models/invoice.rb', line 253
scope :like_lookup, ->(q) { left_joins(:order).where(Invoice[:reference_number].matches("%#{q}%")).or(Order.where(Order[:reference_number].matches("%#{q}%"))) }
|
.lookup ⇒ ActiveRecord::Relation<Invoice>
A relation of Invoices that are lookup. Active Record Scope
252
|
# File 'app/models/invoice.rb', line 252
scope :lookup, ->(q) { where(reference_number: q) }
|
.missing_edi_810 ⇒ ActiveRecord::Relation<Invoice>
A relation of Invoices that are missing edi 810. Active Record Scope
254
255
256
257
258
|
# File 'app/models/invoice.rb', line 254
scope :missing_edi_810, -> {
joins(customer: :notification_channels)
.where(notification_channels: { notification_type: NotificationChannel::INVOICES, transmission_type: NotificationChannel::EDI })
.where(transmission_state: 'awaiting_transmission')
}
|
.overdue ⇒ ActiveRecord::Relation<Invoice>
A relation of Invoices that are overdue. Active Record Scope
251
|
# File 'app/models/invoice.rb', line 251
scope :overdue, -> { unpaid.where(due_date: ...Date.current) }
|
.sales_orders ⇒ ActiveRecord::Relation<Invoice>
A relation of Invoices that are sales orders. Active Record Scope
248
|
# File 'app/models/invoice.rb', line 248
scope :sales_orders, -> { where(invoice_type: 'SO') }
|
.unpaid ⇒ ActiveRecord::Relation<Invoice>
A relation of Invoices that are unpaid. Active Record Scope
249
|
# File 'app/models/invoice.rb', line 249
scope :unpaid, -> { where(state: 'unpaid') }
|
Instance Method Details
#activities ⇒ ActiveRecord::Relation<Activity>
189
|
# File 'app/models/invoice.rb', line 189
has_many :activities, as: :resource, dependent: :nullify, inverse_of: :resource
|
#allow_duplicate_delivery_for_testing? ⇒ Boolean
205
206
207
|
# File 'app/models/invoice.rb', line 205
def allow_duplicate_delivery_for_testing?
allow_duplicate_delivery_for_testing == true
end
|
#amount_due ⇒ Object
428
429
430
|
# File 'app/models/invoice.rb', line 428
def amount_due
total
end
|
#balance ⇒ Object
412
413
414
|
# File 'app/models/invoice.rb', line 412
def balance
total - receipts_total
end
|
#balance_is_zero? ⇒ Boolean
408
409
410
|
# File 'app/models/invoice.rb', line 408
def balance_is_zero?
balance.zero?
end
|
#balance_positive? ⇒ Object
Alias for Balance#positive?
270
|
# File 'app/models/invoice.rb', line 270
delegate :positive?, to: :balance, prefix: true, allow_nil: true
|
#billing_address ⇒ Address
166
|
# File 'app/models/invoice.rb', line 166
belongs_to :billing_address, class_name: 'Address', optional: true, inverse_of: :billing_invoices
|
#billing_customer ⇒ Customer
170
|
# File 'app/models/invoice.rb', line 170
belongs_to :billing_customer, class_name: 'Customer', optional: true, inverse_of: :invoices
|
#billing_entity ⇒ Object
786
787
788
|
# File 'app/models/invoice.rb', line 786
def billing_entity
billing_address.party
end
|
#build_activity ⇒ Object
358
359
360
|
# File 'app/models/invoice.rb', line 358
def build_activity
activities.build resource: self, party: primary_party
end
|
176
|
# File 'app/models/invoice.rb', line 176
belongs_to :business_unit, optional: true, inverse_of: :invoices
|
172
|
# File 'app/models/invoice.rb', line 172
belongs_to :buying_group, optional: true, inverse_of: :invoices
|
#calculate_all_cogs ⇒ Object
366
367
368
|
# File 'app/models/invoice.rb', line 366
def calculate_all_cogs
BigDecimal(line_items.where(cm_category: 'Item').sum('unit_cogs * quantity'))
end
|
#calculate_cogs(tax_class = %w[g svc shp])) ⇒ Object
362
363
364
|
# File 'app/models/invoice.rb', line 362
def calculate_cogs(tax_class = %w[g svc shp])
line_items.where(cm_category: 'Item').select { |li| tax_class.include?(li.calculated_tax_class) }.sum { |li| li.unit_cogs * li.quantity }
end
|
#capture_funds? ⇒ Boolean
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
|
# File 'app/models/invoice.rb', line 571
def capture_funds?
logger.info("#{Time.current}: Capturing funds for invoice id: #{id}, ref: #{reference_number}, delivery id: #{delivery_id}")
copy_payments unless delivery.nil?
invoice_total = total
captured_balance = payments.all_captured.sum(:amount)
pending_balance = invoice_total - captured_balance
payments_total = payments.all_authorized.sum(:amount)
check_total = payments.all_check_captured.sum(:amount)
applied_store_credit_total = delivery.nil? ? 0 : delivery.payments.where(state: 'authorized', category: Payment::STORE_CREDIT, currency: currency).sum(:amount)
unapplied_credit_memos = billing_customer.credit_memos.available_to_apply.order(:document_date)
logger.info("#{Time.current}: Invoice total: #{invoice_total}")
logger.info("#{Time.current}: Payments not captured available: #{payments_total}")
logger.info("#{Time.current}: Checks already captured but receipt needed: #{check_total}")
capture_problem = false
if pending_balance <= 0
create_receipts_for_captured_payments
paid! if can_paid?
if pending_balance.negative?
overpaid_amount = pending_balance.abs
currency_sym = Money::Currency.new(currency).symbol
Mailer.generic_mailer(
from: ADMINISTRATOR_EMAIL,
to: "#{ADMINISTRATOR_EMAIL},#{ACCOUNTS_RECEIVABLE_EMAIL}",
subject: "ORDER OVERPAYMENT — Invoice ##{reference_number} (Order ##{order&.reference_number})",
message: "Invoice ##{reference_number} (Order ##{order&.reference_number}) has been overpaid by #{currency_sym}#{'%.2f' % overpaid_amount}.\n\n" \
"Invoice total: #{currency_sym}#{'%.2f' % invoice_total}\n" \
"Total captured: #{currency_sym}#{'%.2f' % captured_balance}\n" \
"Overpaid amount: #{currency_sym}#{'%.2f' % overpaid_amount}\n\n" \
"Customer: #{customer&.full_name} (ID: #{customer_id})\n" \
"CRM link: #{crm_link}\n\n" \
"A manual refund or credit memo needs to be issued for the excess amount.",
no_verbage: true
).deliver
logger.warn("#{Time.current}: OVERPAYMENT on invoice #{id}: captured #{captured_balance} exceeds total #{invoice_total} by #{overpaid_amount}")
elsif invoice_type == 'SO'
Mailer.generic_mailer(from: ADMINISTRATOR_EMAIL,
to: "#{ADMINISTRATOR_EMAIL},#{ACCOUNTS_RECEIVABLE_EMAIL}",
subject: "BALANCE ALREADY PAID FOR INVOICE ID ##{id}",
message: "Invoice ##{id} had all the payments already captured before starting the invoicing process. This might be ok, but check all payments are correctly captured.",
no_verbage: true).deliver
end
else
create_receipts_for_captured_payments
if applied_store_credit_total.positive?
unapplied_credit_memos.each do |cm|
next if pending_balance.zero?
cm_balance = cm.balance * -1
amount = [pending_balance, cm_balance].min
new_receipt = Receipt.new(company: company,
customer: customer,
category: 'Non-Cash',
amount: 0,
reference: cm.reference_number,
currency: currency,
gl_date: Date.current,
receipt_date: Date.current)
new_receipt.receipt_details << ReceiptDetail.new(category: 'Invoice', invoice: self, amount: amount, gl_date: Date.current)
new_receipt.receipt_details << ReceiptDetail.new(category: 'Credit Memo', credit_memo: cm, amount: amount * -1, gl_date: Date.current)
begin
new_receipt.save!
logger.info("#{Time.current}: Created new receipt id: #{new_receipt.id}")
pending_balance -= amount
rescue StandardError => e
msg = "#{Time.current}: Unable to create new receipt for Credit Memo ID: #{cm.id} (store credit), Exception: #{e}"
logger.error(msg)
ErrorReporting.error(e, credit_memo_id: cm.id)
capture_problem = true
end
end
end
payments.all_authorized.cc_paypal_bread_amazon.each do |payment|
next if pending_balance.zero?
amount = [pending_balance, payment.amount].min
res = payment.gateway_class.new(payment).capture(amount, { order_id: reference_number, currency: payment.currency })
if res.success
pending_balance -= amount
capture_problem = true if payment.receipts.empty?
else
capture_problem = true
end
end
end
if capture_problem
authorized_without_receipts = payments.all_authorized.select { |p| p.receipts.empty? }
only_manual_payments = authorized_without_receipts.all? { |p| Payment::CATEGORIES_NOT_ALLOWING_CAPTURE.include?(p.category) }
if only_manual_payments && authorized_without_receipts.any?
logger.info("#{Time.current}: Authorized payments exist that require manual processing (#{authorized_without_receipts.map(&:category).uniq.join(', ')}). This is expected.")
capture_problem = false
end
end
if capture_problem == true
Mailer.generic_mailer(
from: ADMINISTRATOR_EMAIL,
to: "#{ADMINISTRATOR_EMAIL},#{ACCOUNTS_RECEIVABLE_EMAIL}",
subject: "INVOICE ##{reference_number} FUNDS CAPTURE ERROR",
message: "There has been a problem with the funds capture on invoice id #{id}, ref #{reference_number}, delivery id: #{delivery_id}. Please take action to ensure all funds are captured or applied.",
no_verbage: true
).deliver
logger.error("#{Time.current}: CAPTURE ERROR: Problem with funds capture")
else
if pending_balance.zero?
logger.info("#{Time.current}: Funds captured successfully.")
else
logger.info("#{Time.current}: Funds captured successfully, but balance has not been completely paid.")
end
InvoicePdfGenerationWorker.perform_async(id)
end
true
end
|
#chosen_shipping_cost ⇒ Object
468
469
470
471
472
473
474
475
476
|
# File 'app/models/invoice.rb', line 468
def chosen_shipping_cost
cost = BigDecimal('0.00')
begin
cost = chosen_shipping_method.cost unless chosen_shipping_method.shipping_account_number
rescue StandardError => e
Rails.logger.warn "Could not get shipping cost for invoice #{id}: #{e.message}"
end
cost
end
|
#chosen_shipping_method ⇒ Object
464
465
466
|
# File 'app/models/invoice.rb', line 464
def chosen_shipping_method
shipping_costs.first
end
|
#combined_terms ⇒ Object
754
755
756
757
758
759
760
|
# File 'app/models/invoice.rb', line 754
def combined_terms
if early_payment_discount && early_payment_timescale
"#{terms} - #{early_payment_discount}%/#{early_payment_timescale}"
else
terms
end
end
|
#communications ⇒ ActiveRecord::Relation<Communication>
191
|
# File 'app/models/invoice.rb', line 191
has_many :communications, -> { order(:id).reverse_order }, as: :resource, dependent: :nullify, inverse_of: :resource
|
171
|
# File 'app/models/invoice.rb', line 171
belongs_to :company, inverse_of: :invoices
|
#copy_payments ⇒ Object
402
403
404
405
406
|
# File 'app/models/invoice.rb', line 402
def copy_payments
delivery.payments.where(currency: currency).find_each do |pp|
pp.update!(invoice_id: id) if pp.authorized? || (pp.authorization_type.in?(%w[credit_card check paypal_invoice amazon_pay]) && pp.captured?)
end
end
|
#create_receipts_for_captured_payments ⇒ Object
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
|
# File 'app/models/invoice.rb', line 712
def create_receipts_for_captured_payments
payments.all_captured.each do |payment|
next if payment.receipts.present?
next if payment.skip_auto_receipt
res = payment.gateway_class.new(payment).create_receipt(self, payment.amount, payment.amount)
res.receipt.apply
end
payments.all_cc_captured.each do |payment|
receipts_with_no_details = payment.receipts.where.missing(:receipt_details)
receipts_with_no_details.each do |receipt|
receipt.create_receipt_details(payment.invoice, payment.amount) if payment.invoice.present?
end
end
end
|
#credit_memos ⇒ ActiveRecord::Relation<CreditMemo>
186
|
# File 'app/models/invoice.rb', line 186
has_many :credit_memos, foreign_key: 'original_invoice_id', dependent: :destroy, inverse_of: :original_invoice
|
#crm_link ⇒ Object
533
534
535
|
# File 'app/models/invoice.rb', line 533
def crm_link
UrlHelper.instance.invoice_path(self)
end
|
#currency_symbol ⇒ Object
416
417
418
|
# File 'app/models/invoice.rb', line 416
def currency_symbol
Money::Currency.new(currency).symbol
end
|
169
|
# File 'app/models/invoice.rb', line 169
belongs_to :customer, optional: true, inverse_of: :invoices
|
#customer_name ⇒ Object
394
395
396
|
# File 'app/models/invoice.rb', line 394
def customer_name
customer.try(:full_name)
end
|
177
|
# File 'app/models/invoice.rb', line 177
belongs_to :delivery, optional: true, inverse_of: :invoices
|
#disable_auto_coupon? ⇒ Boolean
782
783
784
|
# File 'app/models/invoice.rb', line 782
def disable_auto_coupon?
true
end
|
#discount_applied ⇒ Object
774
775
776
|
# File 'app/models/invoice.rb', line 774
def discount_applied
receipt_details.sum(:discount)
end
|
#discount_days_due ⇒ Object
452
453
454
455
456
|
# File 'app/models/invoice.rb', line 452
def discount_days_due
return unless early_payment_due_date
(early_payment_due_date - gl_date).to_i
end
|
#do_not_detect_shipping? ⇒ Boolean
778
779
780
|
# File 'app/models/invoice.rb', line 778
def do_not_detect_shipping?
true
end
|
#drop_ship_purchase_orders ⇒ ActiveRecord::Relation<DropShipPurchaseOrder>
192
|
# File 'app/models/invoice.rb', line 192
has_many :drop_ship_purchase_orders, -> { order(:id) }, through: :delivery, dependent: :destroy
|
#early_payment_amount ⇒ Object
730
731
732
733
734
735
736
|
# File 'app/models/invoice.rb', line 730
def early_payment_amount
if early_payment_discount.blank?
BigDecimal(0)
else
((early_payment_discount * total) / 100).round(2)
end
end
|
#early_payment_due_date ⇒ Object
738
739
740
741
742
743
744
|
# File 'app/models/invoice.rb', line 738
def early_payment_due_date
if early_payment_timescale.blank?
nil
else
shipped_date + early_payment_timescale.days
end
end
|
#early_payment_total ⇒ Object
746
747
748
749
750
751
752
|
# File 'app/models/invoice.rb', line 746
def early_payment_total
if early_payment_discount.zero?
total
else
total - early_payment_amount
end
end
|
#edi_communication_logs ⇒ ActiveRecord::Relation<EdiCommunicationLog>
194
|
# File 'app/models/invoice.rb', line 194
has_many :edi_communication_logs, through: :edi_documents, dependent: :destroy
|
#edi_documents ⇒ ActiveRecord::Relation<EdiDocument>
193
|
# File 'app/models/invoice.rb', line 193
has_many :edi_documents, dependent: :destroy, inverse_of: :invoice
|
#editing_locked? ⇒ Boolean
382
383
384
|
# File 'app/models/invoice.rb', line 382
def editing_locked?
unpaid? || paid?
end
|
#effective_store ⇒ Object
342
343
344
|
# File 'app/models/invoice.rb', line 342
def effective_store
store || order&.store || company.stores.first
end
|
#file_name(with_extension: true) ⇒ Object
515
516
517
|
# File 'app/models/invoice.rb', line 515
def file_name(with_extension: true)
"invoice_#{reference_number}#{'.pdf' if with_extension}"
end
|
#friendly_shipping_method(_show_customer_pays_info: false) ⇒ Object
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
|
# File 'app/models/invoice.rb', line 486
def friendly_shipping_method(_show_customer_pays_info: false)
shipping_method_name = ''
method_cod = ''
if begin
shipping_address.is_warehouse
rescue StandardError => e
Rails.logger.warn "Could not check if shipping address is warehouse for invoice #{id}: #{e.message}"
false
end
shipping_method_name = 'Warehouse Pickup'
elsif chosen_shipping_method
shipping_method_name = chosen_shipping_method.description
method_cod = ' (inc. COD charge)' if chosen_shipping_method.cod
end
"#{shipping_method_name} #{method_cod}".strip
end
|
#fully_funded_by_rma? ⇒ Boolean
563
564
565
|
# File 'app/models/invoice.rb', line 563
def fully_funded_by_rma?
order.present? && order.fully_funded_by_advance_replacement?
end
|
#funded_by_cod? ⇒ Boolean
567
568
569
|
# File 'app/models/invoice.rb', line 567
def funded_by_cod?
terms.include?('COD')
end
|
#funded_by_rma? ⇒ Boolean
559
560
561
|
# File 'app/models/invoice.rb', line 559
def funded_by_rma?
order.present? && order.funded_by_advance_replacement?
end
|
#generate_pdf ⇒ Object
519
520
521
522
523
524
525
526
527
|
# File 'app/models/invoice.rb', line 519
def generate_pdf
combined_pdf_result = Invoicing::CombinedPdfGenerator.new.process(self, output_to_file: true)
upload = Upload.uploadify(combined_pdf_result.pdf_file_path,
'invoice_pdf',
self,
combined_pdf_result.file_name)
uploads << upload
upload
end
|
#get_or_regen_pdf(logger = nil) ⇒ Object
503
504
505
506
507
508
509
510
511
512
513
|
# File 'app/models/invoice.rb', line 503
def get_or_regen_pdf(logger = nil)
logger ||= Rails.logger
pdf = uploads.in_category('invoice_pdf').first
logger.info "Retrieving Invoice #{id} pdf, record exists: #{!pdf.nil?}"
unless pdf&.file_exists?
logger.error ' * Pdf nil or file does not exist, attempting regen'
pdf = generate_pdf
logger.info "Pdf regenerated with upload id #{pdf.id}"
end
pdf
end
|
175
|
# File 'app/models/invoice.rb', line 175
belongs_to :gl_offset_account, class_name: 'LedgerCompanyAccount', optional: true, inverse_of: :invoices
|
#item_ledger_entries ⇒ ActiveRecord::Relation<ItemLedgerEntry>
184
|
# File 'app/models/invoice.rb', line 184
has_many :item_ledger_entries, dependent: :destroy, inverse_of: :invoice
|
#ledger_transactions ⇒ ActiveRecord::Relation<LedgerTransaction>
183
|
# File 'app/models/invoice.rb', line 183
has_many :ledger_transactions, dependent: :destroy, inverse_of: :invoice
|
#line_discounts ⇒ ActiveRecord::Relation<LineDiscount>
181
|
# File 'app/models/invoice.rb', line 181
has_many :line_discounts, through: :line_items
|
#line_items ⇒ ActiveRecord::Relation<LineItem>
180
|
# File 'app/models/invoice.rb', line 180
has_many :line_items, as: :resource, inverse_of: :resource, dependent: :destroy, extend: LineItemExtension, autosave: true
|
Alias for Customer#marketplace_invoice_format
269
|
# File 'app/models/invoice.rb', line 269
delegate :marketplace_invoice_format, to: :customer, allow_nil: true
|
#name ⇒ Object
478
479
480
|
# File 'app/models/invoice.rb', line 478
def name
reference_number
end
|
#non_service_line_items ⇒ Object
370
371
372
|
# File 'app/models/invoice.rb', line 370
def non_service_line_items
line_items.where(cm_category: 'Item').reject { |li| li.item.tax_class == 'svc' }
end
|
#non_voided_receipt_details ⇒ Object
420
421
422
|
# File 'app/models/invoice.rb', line 420
def non_voided_receipt_details
receipt_details.non_voided
end
|
#not_rma? ⇒ Boolean
555
556
557
|
# File 'app/models/invoice.rb', line 555
def not_rma?
!(order.present? && order.funded_by_advance_replacement?)
end
|
#online_payment_options ⇒ Object
398
399
400
|
# File 'app/models/invoice.rb', line 398
def online_payment_options
[Payment::CREDIT_CARD]
end
|
165
|
# File 'app/models/invoice.rb', line 165
belongs_to :order, optional: true, inverse_of: :invoices
|
#order_ref ⇒ Object
386
387
388
|
# File 'app/models/invoice.rb', line 386
def order_ref
order.try(:reference_number)
end
|
#order_ref=(ref) ⇒ Object
390
391
392
|
# File 'app/models/invoice.rb', line 390
def order_ref=(ref)
self.order = Order.find_by(reference_number: ref) if ref.present?
end
|
#payments ⇒ ActiveRecord::Relation<Payment>
190
|
# File 'app/models/invoice.rb', line 190
has_many :payments, dependent: :nullify, inverse_of: :invoice
|
#po_numbers ⇒ Object
482
483
484
|
# File 'app/models/invoice.rb', line 482
def po_numbers
payments.where.not(po_number: nil).distinct.pluck(:po_number)
end
|
#prevent_recalculate_shipping? ⇒ Boolean
378
379
380
|
# File 'app/models/invoice.rb', line 378
def prevent_recalculate_shipping?
true
end
|
#pricing_program_discount_factor ⇒ Object
432
433
434
435
436
437
438
|
# File 'app/models/invoice.rb', line 432
def pricing_program_discount_factor
if order.present?
order.pricing_program_discount_factor
else
customer.pricing_program_discount
end
end
|
#primary_party ⇒ Object
529
530
531
|
# File 'app/models/invoice.rb', line 529
def primary_party
order&.primary_party || customer
end
|
173
|
# File 'app/models/invoice.rb', line 173
belongs_to :profile, optional: true, inverse_of: :invoices
|
#public_pay_link ⇒ Object
537
538
539
540
541
542
543
544
545
546
|
# File 'app/models/invoice.rb', line 537
def public_pay_link
return nil unless order
order.public_payment_link
end
|
#public_pay_link_has_auth_token? ⇒ Boolean
548
549
550
551
552
553
|
# File 'app/models/invoice.rb', line 548
def public_pay_link_has_auth_token?
false
end
|
#receipt_details ⇒ ActiveRecord::Relation<ReceiptDetail>
182
|
# File 'app/models/invoice.rb', line 182
has_many :receipt_details, dependent: :nullify, inverse_of: :invoice
|
#receipts_total ⇒ Object
424
425
426
|
# File 'app/models/invoice.rb', line 424
def receipts_total
non_voided_receipt_details.sum('amount') + non_voided_receipt_details.sum('write_off') + non_voided_receipt_details.sum('discount')
end
|
#rma_awaiting_return? ⇒ Boolean
354
355
356
|
# File 'app/models/invoice.rb', line 354
def rma_awaiting_return?
order.try(:rma).try(:state) == 'awaiting_return'
end
|
#rma_number ⇒ Object
350
351
352
|
# File 'app/models/invoice.rb', line 350
def rma_number
order.try(:rma).try(:rma_number) || "RMA # #{order.rma_reference}"
end
|
#rmas ⇒ ActiveRecord::Relation<Rma>
185
|
# File 'app/models/invoice.rb', line 185
has_many :rmas, foreign_key: 'original_invoice_id', dependent: :destroy, inverse_of: :original_invoice
|
#selection_name ⇒ Object
762
763
764
|
# File 'app/models/invoice.rb', line 762
def selection_name
"#{reference_number} #{customer.full_name}"
end
|
#selection_name_for_rmas ⇒ Object
766
767
768
769
770
771
772
|
# File 'app/models/invoice.rb', line 766
def selection_name_for_rmas
if order.nil?
"#{reference_number} #{customer.full_name}"
else
"#{reference_number} #{customer.full_name} (#{order&.reference_number})"
end
end
|
#service_line_items ⇒ Object
374
375
376
|
# File 'app/models/invoice.rb', line 374
def service_line_items
line_items.where(cm_category: 'Item').select { |li| li.item.tax_class == 'svc' }
end
|
#set_consolidated_amount ⇒ Object
790
791
792
793
794
795
796
797
798
799
800
801
|
# File 'app/models/invoice.rb', line 790
def set_consolidated_amount
if currency && gl_date
if currency == CONSOLIDATED_CURRENCY
self.consolidated_exchange_rate = 1.0
else
exchange_rate = ExchangeRate.get_exchange_rate(currency, CONSOLIDATED_CURRENCY, gl_date)
self.consolidated_exchange_rate = exchange_rate
end
else
self.consolidated_exchange_rate = nil
end
end
|
#shipping_address ⇒ Address
Also known as:
destination_address
Validations:
168
|
# File 'app/models/invoice.rb', line 168
belongs_to :shipping_address, class_name: 'Address', optional: true, inverse_of: :shipping_invoices
|
#shipping_costs ⇒ ActiveRecord::Relation<ShippingCost>
187
|
# File 'app/models/invoice.rb', line 187
has_many :shipping_costs, -> { order(:cost) }, through: :delivery
|
#show_tax_info? ⇒ Boolean
346
347
348
|
# File 'app/models/invoice.rb', line 346
def show_tax_info?
tax_info.present?
end
|
#sold_to_billing_address ⇒ Address
167
|
# File 'app/models/invoice.rb', line 167
belongs_to :sold_to_billing_address, class_name: 'Address', foreign_key: 'sold_to_billing_address', optional: true, inverse_of: :billing_invoices
|
178
|
# File 'app/models/invoice.rb', line 178
belongs_to :source, optional: true, inverse_of: :invoices
|
174
|
# File 'app/models/invoice.rb', line 174
belongs_to :store, optional: true, inverse_of: :invoices
|
#tax_info ⇒ Object
327
328
329
330
331
332
333
334
335
336
337
338
339
340
|
# File 'app/models/invoice.rb', line 327
def tax_info
destination_country = shipping_address&.country
return unless destination_country
if destination_country.eu_country?
if destination_country.eu_vat_number.present?
"VAT: #{destination_country.eu_vat_number}"
else
company.tax_info
end
elsif company.canada?
company.tax_info
end
end
|
#technical_support_rep ⇒ Employee
179
|
# File 'app/models/invoice.rb', line 179
belongs_to :technical_support_rep, class_name: 'Employee', optional: true, inverse_of: :technical_support_invoices
|
#terms_in_days ⇒ Object
calculate net due date in days
446
447
448
449
450
|
# File 'app/models/invoice.rb', line 446
def terms_in_days
return unless due_date
(due_date - gl_date).to_i
end
|
#to_s ⇒ Object
803
804
805
806
807
808
809
|
# File 'app/models/invoice.rb', line 803
def to_s
if respond_to?(:reference_number)
"Invoice # #{reference_number}"
else
"Invoice ID #{id}"
end
end
|
#uploads ⇒ ActiveRecord::Relation<Upload>
188
|
# File 'app/models/invoice.rb', line 188
has_many :uploads, -> { order(:updated_at).reverse_order }, as: :resource, dependent: :destroy, inverse_of: :resource
|