RailsEventStore/cqrs-es-sample-with-res

View on GitHub
ecommerce/invoicing/lib/invoicing/invoice.rb

Summary

Maintainability
A
1 hr
Test Coverage
module Invoicing
  class Invoice
    InvoiceAlreadyIssued = Class.new(StandardError)
    InvoiceNumberInUse = Class.new(StandardError)
    BillingAddressNotSpecified = Class.new(StandardError)
    include AggregateRoot

    def initialize(invoice_id)
      @invoice_id = invoice_id
      @invoice_items = []
    end

    def issue(issue_date, invoice_number)
      raise BillingAddressNotSpecified unless @postal_address
      raise InvoiceAlreadyIssued unless draft?
      disposal_date = [@payment_date, issue_date].compact.min
      apply(
        InvoiceIssued.new(
          data: {
            invoice_id: @invoice_id,
            issue_date: issue_date,
            disposal_date: disposal_date,
            invoice_number: invoice_number
          }
        )
      )
    end

    def set_payment_date(payment_date)
      apply(
        InvoicePaymentDateSet.new(
          data: {
            invoice_id: @invoice_id,
            payment_date: payment_date
          }
        )
      )
    end

    def add_item(product_id, title, unit_price, vat_rate, quantity)
      raise InvoiceAlreadyIssued unless draft?
      apply(
        InvoiceItemAdded.new(
          data: {
            invoice_id: @invoice_id,
            product_id: product_id,
            title: title,
            quantity: quantity,
            unit_price: unit_price,
            vat_rate: vat_rate
          }
        )
      )
    end

    def set_billing_address(tax_id_number, postal_address)
      raise InvoiceAlreadyIssued unless draft?
      apply BillingAddressSet.new(
        data: {
          invoice_id: @invoice_id,
          postal_address: postal_address,
          tax_id_number: tax_id_number
        }
      )
    end

    private

    def draft?
      @issue_date.nil?
    end

    on InvoiceItemAdded do |event|
      @invoice_items << InvoiceItem.new(
        event.data[:product_id],
        event.data[:title],
        event.data[:unit_price],
        event.data[:vat_rate],
        event.data[:quantity]
      )
    end

    on InvoicePaymentDateSet do |event|
      @payment_date = event.data[:payment_date]
    end

    on InvoiceIssued do |event|
      @state = :issued
      @issue_date = event.data[:issue_date]
      @disposal_date = event.data[:disposal_date]
    end

    on BillingAddressSet do |event|
      @tax_id_number = event.data.fetch(:tax_id_number)
      @postal_address = event.data.fetch(:postal_address)
    end
  end

  class InvoiceItem
    attr_reader :product_id, :quantity, :unit_price, :vat_rate, :title

    def initialize(product_id, title, unit_price, vat_rate, quantity)
      @product_id = product_id
      @title = title
      @unit_price = unit_price
      @vat_rate = vat_rate
      @quantity = quantity
    end
  end
end