Skip to content

Commit

Permalink
feat(credit_notes): Expose credit notes in API
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent-pochet committed Oct 3, 2022
1 parent 2a45887 commit 6ff0b08
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ RSpec/MultipleMemoizedHelpers:
RSpec/NestedGroups:
Enabled: false

RSpec/FilePath:
Exclude:
- 'spec/requests/**/*'

Style/BlockDelimiters:
Description: >-
Avoid using {...} for multi-line blocks (multiline chaining is always ugly).
Expand Down
41 changes: 41 additions & 0 deletions app/controllers/api/v1/credit_notes_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

module Api
module V1
class CreditNotesController < Api::BaseController
def show
credit_note = current_organization.credit_notes.find_by(id: params[:id])
return not_found_error(resource: 'credit_note') unless credit_note

render(
json: ::V1::CreditNoteSerializer.new(
credit_note,
root_name: 'credit_note',
includes: %i[fees],
),
)
end

def index
credit_notes = current_organization.credit_notes

if params[:external_customer_id]
credit_notes = credit_notes.joins(:customer).where(customers: { external_id: params[:external_customer_id] })
end

credit_notes = credit_notes.order(created_at: :desc)
.page(params[:page])
.per(params[:per_page] || PER_PAGE)

render(
json: ::CollectionSerializer.new(
credit_notes,
::V1::CreditNoteSerializer,
collection_name: 'credit_notes',
meta: pagination_metadata(credit_notes),
),
)
end
end
end
end
1 change: 1 addition & 0 deletions app/models/customer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Customer < ApplicationRecord
has_many :invoices
has_many :applied_coupons
has_many :coupons, through: :applied_coupons
has_many :credit_notes
has_many :applied_add_ons
has_many :add_ons, through: :applied_add_ons
has_many :wallets
Expand Down
34 changes: 34 additions & 0 deletions app/serializers/v1/credit_note_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module V1
class CreditNoteSerializer < ModelSerializer
def serialize
payload = {
lago_id: model.id,
sequential_id: model.sequential_id,
number: model.number,
lago_invoice_id: model.invoice_id,
invoice_number: model.invoice.number,
status: model.status,
reason: model.reason,
amount_cents: model.amount_cents,
amount_currency: model.amount_currency,
remaining_amount_cents: model.remaining_amount_cents,
remaining_amount_currency: model.remaining_amount_currency,
created_at: model.created_at.iso8601,
updated_at: model.updated_at.iso8601,
file_url: nil, # TODO: Expose credit note document in API
}

payload = payload.merge(fees) if include?(:fees)

payload
end

private

def fees
::CollectionSerializer.new(model.fees, ::V1::FeeSerializer, collection_name: 'fees').serialize
end
end
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
resources :add_ons, param: :code
resources :billable_metrics, param: :code
resources :coupons, param: :code
resources :credit_notes, only: %i[show index]
resources :events, only: %i[create show]
resources :applied_coupons, only: %i[create]
resources :applied_add_ons, only: %i[create]
Expand Down
106 changes: 106 additions & 0 deletions spec/requests/api/v1/credit_notes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe Api::V1::CreditNotesController, type: :request do
let(:invoice) { create(:invoice) }
let(:organization) { invoice.organization }
let(:customer) { invoice.customer }
let(:credit_note) { create(:credit_note, invoice: invoice, customer: customer) }
let(:credit_note_items) { create_list(:credit_note_item, 2, credit_note: credit_note) }

describe 'GET /credit_notes/:id' do
before { credit_note_items }

it 'returns a credit note' do
get_with_token(organization, "/api/v1/credit_notes/#{credit_note.id}")

aggregate_failures do
expect(response).to have_http_status(:success)
expect(json[:credit_note][:lago_id]).to eq(credit_note.id)
expect(json[:credit_note][:sequential_id]).to eq(credit_note.sequential_id)
expect(json[:credit_note][:number]).to eq(credit_note.number)
expect(json[:credit_note][:lago_invoice_id]).to eq(invoice.id)
expect(json[:credit_note][:invoice_number]).to eq(invoice.number)
expect(json[:credit_note][:status]).to eq(credit_note.status)
expect(json[:credit_note][:reason]).to eq(credit_note.reason)
expect(json[:credit_note][:amount_cents]).to eq(credit_note.amount_cents)
expect(json[:credit_note][:amount_currency]).to eq(credit_note.amount_currency)
expect(json[:credit_note][:remaining_amount_cents]).to eq(credit_note.remaining_amount_cents)
expect(json[:credit_note][:remaining_amount_currency]).to eq(credit_note.remaining_amount_currency)
expect(json[:credit_note][:created_at]).to eq(credit_note.created_at.iso8601)
expect(json[:credit_note][:updated_at]).to eq(credit_note.updated_at.iso8601)

expect(json[:credit_note][:fees].count).to eq(2)
end
end

context 'when credit note does not exists' do
it 'returns not found' do
get_with_token(organization, '/api/v1/credit_notes/foo')

expect(response).to have_http_status(:not_found)
end
end

context 'when credit note belongs to another organization' do
let(:wrong_credit_note) { create(:credit_note) }

it 'returns not found' do
get_with_token(organization, "/api/v1/credit_notes/#{wrong_credit_note.id}")
end
end
end

describe 'GET /credits_notes' do
let(:second_customer) { create(:customer, organization: organization) }
let(:second_invoice) { create(:invoice, customer: second_customer) }
let(:second_credit_note) { create(:credit_note, invoice: second_invoice, customer: second_invoice.customer) }

before do
credit_note
second_credit_note
end

it 'returns a list of credit_notes' do
get_with_token(organization, '/api/v1/credit_notes')

aggregate_failures do
expect(response).to have_http_status(:success)
expect(json[:credit_notes].count).to eq(2)
expect(json[:credit_notes].first[:lago_id]).to eq(second_credit_note.id)
expect(json[:credit_notes].last[:lago_id]).to eq(credit_note.id)
end
end

context 'with pagination' do
it 'returns the metadata' do
get_with_token(organization, '/api/v1/credit_notes?page=1&per_page=1')

aggregate_failures do
expect(response).to have_http_status(:success)
expect(json[:credit_notes].count).to eq(1)

expect(json[:meta][:current_page]).to eq(1)
expect(json[:meta][:next_page]).to eq(2)
expect(json[:meta][:prev_page]).to eq(nil)
expect(json[:meta][:total_pages]).to eq(2)
expect(json[:meta][:total_count]).to eq(2)
end
end
end

context 'with external_customer_id filter' do
it 'returns credit notes of the customer' do
get_with_token(organization, "/api/v1/credit_notes?external_customer_id=#{customer.external_id}")

aggregate_failures do
expect(response).to have_http_status(:success)

expect(json[:credit_notes].count).to eq(1)
expect(json[:credit_notes].first[:lago_id]).to eq(credit_note.id)
end
end
end
end
end

0 comments on commit 6ff0b08

Please sign in to comment.