Skip to content

Commit

Permalink
Allow paying for a given invoice
Browse files Browse the repository at this point in the history
  • Loading branch information
akadusei committed Aug 20, 2024
1 parent 57699ab commit 19b6f24
Show file tree
Hide file tree
Showing 15 changed files with 482 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased] -

### Added
- Allow paying for a given invoice
- Add `Api::DirectReceipts::Create` action
- Add `Api::DirectReceipts::Edit` action
- Add `Api::DirectReceipts::New` action
Expand Down
2 changes: 2 additions & 0 deletions docs/09-RECEIPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ See <https://en.wikipedia.org/wiki/Receipt>

The form should be `POST`ed to `Receipts::Create`, with the following parameters:

- `invoice_id` (if payment for specific invoice supported)
- `user_id`
- `amount : Amount`
- `description : String`
Expand Down Expand Up @@ -216,6 +217,7 @@ See <https://en.wikipedia.org/wiki/Receipt>

The form should be `POST`ed to `Receipts::Update`, with the following parameters:

- `invoice_id` (if payment for specific invoice supported)
- `user_id`
- `amount : Amount`
- `description : String`
Expand Down
2 changes: 2 additions & 0 deletions docs/10-I18N.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,13 @@ en:
invoice_items_empty: Invoice has no line items
invoice_not_finalized: Invoice is not finalized
invoice_not_found: Invoice does not exist
invoice_not_open: Invoice is not open
notes_too_long: Notes cannot be longer than %{max} characters
price_lte_zero: Price must be greater than zero
price_required: Price is required
quantity_lte_zero: Quantity must be greater than zero
receipt_finalized: Receipt is finalized
receipt_not_equal_invoice: Receipt amount must equal invoice amount
receipt_not_finalized: Receipt is not finalized
reference_exists: Reference is already used
refund_exceeds_receipt: Amount cannot exceed %{receipt_amount_mu}
Expand Down
49 changes: 49 additions & 0 deletions spec/bill/operations/create_direct_receipt_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,53 @@ describe Bill::CreateDirectReceipt do
end
end
end

it "pays for a given invoice" do
amount = 90
user = UserFactory.create

invoice = CreateInvoice.create!(
params(
user_id: user.id,
description: "New invoice",
due_at: 3.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

invoice_2 = CreateInvoice.create!(
params(
user_id: user.id,
description: "Another invoice",
due_at: 2.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

invoice.status.paid?.should be_false
invoice_2.status.open?.should be_true

CreateDirectReceipt.create(params(
invoice_id: invoice.id,
user_id: user.id,
description: "New receipt",
amount: amount,
status: :open
)) do |_, transaction|
transaction.should be_a(Transaction)
end

invoice.reload.status.paid?.should be_true
invoice_2.reload.status.open?.should be_true
end
end
49 changes: 49 additions & 0 deletions spec/bill/operations/create_receipt_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,53 @@ describe Bill::CreateReceipt do
.none?
.should(be_false)
end

it "pays for a given invoice" do
amount = 90
user = UserFactory.create

invoice = CreateInvoice.create!(
params(
user_id: user.id,
description: "New invoice",
due_at: 3.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

invoice_2 = CreateInvoice.create!(
params(
user_id: user.id,
description: "Another invoice",
due_at: 2.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

invoice.status.paid?.should be_false
invoice_2.status.open?.should be_true

CreateReceipt.create(params(
invoice_id: invoice.id,
user_id: user.id,
description: "New receipt",
amount: amount,
status: :open
)) do |_, receipt|
receipt.should be_a(Receipt)
end

invoice.reload.status.paid?.should be_true
invoice_2.reload.status.open?.should be_true
end
end
64 changes: 64 additions & 0 deletions spec/bill/operations/mixins/receive_direct_invoice_payment_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require "../../../spec_helper"

describe Bill::ReceiveDirectInvoicePayment do
it "requires open invoice" do
amount = 90
user = UserFactory.create

invoice = CreateInvoice.create!(
params(
user_id: user.id,
description: "New invoice",
due_at: 3.days.from_now,
status: :draft
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

CreateDirectReceipt.create(params(
invoice_id: invoice.id,
user_id: user.id,
description: "New invoice",
amount: amount,
status: :open
)) do |operation, receipt|
receipt.should be_nil
operation.invoice_id.should have_error("operation.error.invoice_not_open")
end
end

it "ensures receipt amount equals invoice amount" do
user = UserFactory.create

invoice = CreateInvoice.create!(
params(
user_id: user.id,
description: "New invoice",
due_at: 3.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "90"
}]
)

CreateDirectReceipt.create(params(
invoice_id: invoice.id,
user_id: user.id,
description: "New invoice",
amount: 100,
status: :open
)) do |operation, receipt|
receipt.should be_nil

operation.amount
.should(have_error "operation.error.receipt_not_equal_invoice")
end
end
end
64 changes: 64 additions & 0 deletions spec/bill/operations/mixins/receive_invoice_payment_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require "../../../spec_helper"

describe Bill::ReceiveInvoicePayment do
it "requires open invoice" do
amount = 90
user = UserFactory.create

invoice = CreateInvoice.create!(
params(
user_id: user.id,
description: "New invoice",
due_at: 3.days.from_now,
status: :draft
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

CreateReceipt.create(params(
invoice_id: invoice.id,
user_id: user.id,
description: "New invoice",
amount: amount,
status: :open
)) do |operation, receipt|
receipt.should be_nil
operation.invoice_id.should have_error("operation.error.invoice_not_open")
end
end

it "ensures receipt amount equals invoice amount" do
user = UserFactory.create

invoice = CreateInvoice.create!(
params(
user_id: user.id,
description: "New invoice",
due_at: 3.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "90"
}]
)

CreateReceipt.create(params(
invoice_id: invoice.id,
user_id: user.id,
description: "New invoice",
amount: 100,
status: :open
)) do |operation, receipt|
receipt.should be_nil

operation.amount
.should(have_error "operation.error.receipt_not_equal_invoice")
end
end
end
50 changes: 50 additions & 0 deletions spec/bill/operations/update_direct_receipt_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,54 @@ describe Bill::UpdateDirectReceipt do
updated_transaction.user_id.should eq(new_user.id)
end
end

it "pays for a given invoice" do
amount = 90
user = UserFactory.create

transaction = TransactionFactory.create &.user_id(user.id)
.amount(-amount)
.type(:receipt)

invoice = CreateInvoice.create!(
params(
user_id: user.id,
description: "New invoice",
due_at: 3.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

invoice_2 = CreateInvoice.create!(
params(
user_id: user.id,
description: "Another invoice",
due_at: 2.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

invoice.status.paid?.should be_false
invoice_2.status.open?.should be_true

UpdateDirectReceipt.update(transaction, params(
invoice_id: invoice.id,
status: :open,
)) do |operation, _|
operation.saved?.should be_true
end

invoice.reload.status.paid?.should be_true
invoice_2.reload.status.open?.should be_true
end
end
48 changes: 48 additions & 0 deletions spec/bill/operations/update_receipt_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,52 @@ describe Bill::UpdateReceipt do
operation.status.should have_error("operation.error.receipt_finalized")
end
end

it "pays for a given invoice" do
amount = 90

user = UserFactory.create
receipt = ReceiptFactory.create &.user_id(user.id).amount(amount)

invoice = CreateInvoice.create!(
params(
user_id: user.id,
description: "New invoice",
due_at: 3.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

invoice_2 = CreateInvoice.create!(
params(
user_id: user.id,
description: "Another invoice",
due_at: 2.days.from_now,
status: :open
),
line_items: [{
"description" => "Item 1",
"quantity" => "1",
"price" => "#{amount}"
}]
)

invoice.status.paid?.should be_false
invoice_2.status.open?.should be_true

UpdateReceipt.update(receipt, params(
invoice_id: invoice.id,
status: :open
)) do |operation, _|
operation.saved?.should be_true
end

invoice.reload.status.paid?.should be_true
invoice_2.reload.status.open?.should be_true
end
end
4 changes: 4 additions & 0 deletions src/bill/operations/create_direct_receipt.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ module Bill::CreateDirectReceipt # Transaction::SaveOperation
set_credit
end

{% if Avram::Model.all_subclasses.find(&.name.== :Invoice.id) %}
include Bill::ReceiveDirectInvoicePayment
{% end %}

include Bill::CreateTransaction

private def set_type
Expand Down
Loading

0 comments on commit 19b6f24

Please sign in to comment.