From 359b60adb303defd5061ead432dc75d65573a41d Mon Sep 17 00:00:00 2001 From: akadusei Date: Thu, 22 Aug 2024 14:12:21 +0000 Subject: [PATCH] Add sales receipts Sales receipts are invoices paid for on the spot. --- CHANGELOG.md | 1 + docs/05-INVOICE.md | 33 +++++++++++ .../api/direct_sales_receipt/create_spec.cr | 34 ++++++++++++ .../api/direct_sales_receipt/update_spec.cr | 29 ++++++++++ .../actions/api/sales_receipt/create_spec.cr | 28 ++++++++++ .../actions/api/sales_receipt/update_spec.cr | 27 +++++++++ .../direct_sales_receipts/create_spec.cr | 34 ++++++++++++ .../direct_sales_receipts/edit_spec.cr | 14 +++++ .../actions/direct_sales_receipts/new_spec.cr | 9 +++ .../direct_sales_receipts/update_spec.cr | 29 ++++++++++ .../actions/sales_receipts/create_spec.cr | 28 ++++++++++ spec/bill/actions/sales_receipts/edit_spec.cr | 12 ++++ spec/bill/actions/sales_receipts/new_spec.cr | 9 +++ .../actions/sales_receipts/update_spec.cr | 27 +++++++++ .../create_direct_sales_receipt_spec.cr | 53 ++++++++++++++++++ .../operations/create_sales_receipt_spec.cr | 49 +++++++++++++++++ .../update_direct_sales_receipt_spec.cr | 55 +++++++++++++++++++ .../operations/update_sales_receipt_spec.cr | 51 +++++++++++++++++ .../api/direct_sales_receipts/create.cr | 7 +++ .../api/direct_sales_receipts/update.cr | 7 +++ .../src/actions/api/sales_receipts/create.cr | 7 +++ .../src/actions/api/sales_receipts/update.cr | 7 +++ .../actions/direct_sales_receipts/create.cr | 7 +++ .../src/actions/direct_sales_receipts/edit.cr | 12 ++++ .../src/actions/direct_sales_receipts/new.cr | 11 ++++ .../actions/direct_sales_receipts/update.cr | 7 +++ .../app/src/actions/sales_receipts/create.cr | 7 +++ .../app/src/actions/sales_receipts/edit.cr | 12 ++++ .../app/src/actions/sales_receipts/new.cr | 11 ++++ .../app/src/actions/sales_receipts/update.cr | 7 +++ .../operations/create_direct_sales_receipt.cr | 3 + .../operations/update_direct_sales_receipt.cr | 3 + .../pages/direct_sales_receipts/edit_page.cr | 7 +++ .../pages/direct_sales_receipts/new_page.cr | 7 +++ .../app/src/pages/sales_receipts/edit_page.cr | 7 +++ .../app/src/pages/sales_receipts/new_page.cr | 7 +++ .../api/direct_sales_receipts/create.cr | 35 ++++++++++++ .../api/direct_sales_receipts/update.cr | 40 ++++++++++++++ src/bill/actions/api/sales_receipts/create.cr | 35 ++++++++++++ src/bill/actions/api/sales_receipts/update.cr | 40 ++++++++++++++ .../actions/direct_sales_receipts/create.cr | 31 +++++++++++ .../actions/direct_sales_receipts/edit.cr | 16 ++++++ src/bill/actions/direct_sales_receipts/new.cr | 11 ++++ .../actions/direct_sales_receipts/update.cr | 36 ++++++++++++ src/bill/actions/sales_receipts/create.cr | 31 +++++++++++ src/bill/actions/sales_receipts/edit.cr | 16 ++++++ src/bill/actions/sales_receipts/new.cr | 11 ++++ src/bill/actions/sales_receipts/update.cr | 36 ++++++++++++ .../operations/create_direct_sales_receipt.cr | 26 +++++++++ src/bill/operations/create_sales_receipt.cr | 26 +++++++++ .../operations/update_direct_sales_receipt.cr | 26 +++++++++ src/bill/operations/update_sales_receipt.cr | 26 +++++++++ src/presets/invoice.cr | 18 ++++++ 53 files changed, 1118 insertions(+) create mode 100644 spec/bill/actions/api/direct_sales_receipt/create_spec.cr create mode 100644 spec/bill/actions/api/direct_sales_receipt/update_spec.cr create mode 100644 spec/bill/actions/api/sales_receipt/create_spec.cr create mode 100644 spec/bill/actions/api/sales_receipt/update_spec.cr create mode 100644 spec/bill/actions/direct_sales_receipts/create_spec.cr create mode 100644 spec/bill/actions/direct_sales_receipts/edit_spec.cr create mode 100644 spec/bill/actions/direct_sales_receipts/new_spec.cr create mode 100644 spec/bill/actions/direct_sales_receipts/update_spec.cr create mode 100644 spec/bill/actions/sales_receipts/create_spec.cr create mode 100644 spec/bill/actions/sales_receipts/edit_spec.cr create mode 100644 spec/bill/actions/sales_receipts/new_spec.cr create mode 100644 spec/bill/actions/sales_receipts/update_spec.cr create mode 100644 spec/bill/operations/create_direct_sales_receipt_spec.cr create mode 100644 spec/bill/operations/create_sales_receipt_spec.cr create mode 100644 spec/bill/operations/update_direct_sales_receipt_spec.cr create mode 100644 spec/bill/operations/update_sales_receipt_spec.cr create mode 100644 spec/support/app/src/actions/api/direct_sales_receipts/create.cr create mode 100644 spec/support/app/src/actions/api/direct_sales_receipts/update.cr create mode 100644 spec/support/app/src/actions/api/sales_receipts/create.cr create mode 100644 spec/support/app/src/actions/api/sales_receipts/update.cr create mode 100644 spec/support/app/src/actions/direct_sales_receipts/create.cr create mode 100644 spec/support/app/src/actions/direct_sales_receipts/edit.cr create mode 100644 spec/support/app/src/actions/direct_sales_receipts/new.cr create mode 100644 spec/support/app/src/actions/direct_sales_receipts/update.cr create mode 100644 spec/support/app/src/actions/sales_receipts/create.cr create mode 100644 spec/support/app/src/actions/sales_receipts/edit.cr create mode 100644 spec/support/app/src/actions/sales_receipts/new.cr create mode 100644 spec/support/app/src/actions/sales_receipts/update.cr create mode 100644 spec/support/app/src/operations/create_direct_sales_receipt.cr create mode 100644 spec/support/app/src/operations/update_direct_sales_receipt.cr create mode 100644 spec/support/app/src/pages/direct_sales_receipts/edit_page.cr create mode 100644 spec/support/app/src/pages/direct_sales_receipts/new_page.cr create mode 100644 spec/support/app/src/pages/sales_receipts/edit_page.cr create mode 100644 spec/support/app/src/pages/sales_receipts/new_page.cr create mode 100644 src/bill/actions/api/direct_sales_receipts/create.cr create mode 100644 src/bill/actions/api/direct_sales_receipts/update.cr create mode 100644 src/bill/actions/api/sales_receipts/create.cr create mode 100644 src/bill/actions/api/sales_receipts/update.cr create mode 100644 src/bill/actions/direct_sales_receipts/create.cr create mode 100644 src/bill/actions/direct_sales_receipts/edit.cr create mode 100644 src/bill/actions/direct_sales_receipts/new.cr create mode 100644 src/bill/actions/direct_sales_receipts/update.cr create mode 100644 src/bill/actions/sales_receipts/create.cr create mode 100644 src/bill/actions/sales_receipts/edit.cr create mode 100644 src/bill/actions/sales_receipts/new.cr create mode 100644 src/bill/actions/sales_receipts/update.cr create mode 100644 src/bill/operations/create_direct_sales_receipt.cr create mode 100644 src/bill/operations/create_sales_receipt.cr create mode 100644 src/bill/operations/update_direct_sales_receipt.cr create mode 100644 src/bill/operations/update_sales_receipt.cr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e74861..6769dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Allow paying for a given invoice +- Add sales receipts (invoices paid for on the spot) - Add `Api::DirectReceipts::Create` action - Add `Api::DirectReceipts::Edit` action - Add `Api::DirectReceipts::New` action diff --git a/docs/05-INVOICE.md b/docs/05-INVOICE.md index f129515..47d0e7b 100644 --- a/docs/05-INVOICE.md +++ b/docs/05-INVOICE.md @@ -145,6 +145,26 @@ See end ``` + ```crystal + # ->>> src/operations/create_sales_receipt.cr + + class CreateSalesReceipt < Invoice::SaveOperation + # ... + end + ``` + + A sales receipt is an invoice paid for on the spot. This operation creates an invoice and a corresponding receipt, and marks the invoice as paid. It is available only if `Receipt` model exists. If there is no `Receipt` model but `Transaction` exists, `CreateDirectSalesReceipt` is used instead. + + ```crystal + # ->>> src/operations/update_sales_receipt.cr + + class UpdateSalesReceipt < Invoice::SaveOperation + # ... + end + ``` + + This operation is available only if `Receipt` model exists. If there is no `Receipt` model but `Transaction` exists, `UpdateDirectSalesReceipt` is used instead. + 1. Set up actions: ```crystal @@ -152,9 +172,14 @@ See class Invoices::New < BrowserAction # ... + + # Use `Bill::DirectReceipts::New` instead if invoices + # are paid for on the spot. include Bill::Invoices::New get "/invoices/new" do + # Use `CreateDirectReceipt` instead if invoices + # are paid for on the spot. operation = CreateInvoice.new( # Uncomment after setting up invoice items #line_items: Array(Hash(String, String)).new @@ -184,6 +209,9 @@ See class Invoices::Create < BrowserAction # ... + + # Use `Bill::DirectReceipts::Create` instead if invoices + # are paid for on the spot. include Bill::Invoices::Create post "/invoices" do @@ -211,9 +239,14 @@ See class Invoices::Edit < BrowserAction # ... + + # Use `Bill::DirectReceipts::Edit` instead if invoices + # are paid for on the spot. include Bill::Invoices::Edit get "/invoices/:invoice_id/edit" do + # Use `UpdateDirectReceipt` instead if invoices + # are paid for on the spot. operation = UpdateInvoice.new( invoice, # Uncomment after setting up invoice items diff --git a/spec/bill/actions/api/direct_sales_receipt/create_spec.cr b/spec/bill/actions/api/direct_sales_receipt/create_spec.cr new file mode 100644 index 0000000..7532a0e --- /dev/null +++ b/spec/bill/actions/api/direct_sales_receipt/create_spec.cr @@ -0,0 +1,34 @@ +require "../../../../spec_helper" + +describe Bill::Api::DirectSalesReceipts::Create do + it "creates sales receipt" do + user = UserFactory.create + + response = ApiClient.exec( + Api::DirectSalesReceipts::Create, + invoice: { + user_id: user.id, + description: "New invoice", + due_at: 3.days.from_now, + status: :open + }, + line_items: [{ + description: "Item 1", + quantity: 2, + price_mu: 0.12 + }] + ) + + response.should send_json(200, message: "action.invoice.create.success") + + # ameba:disable Performance/AnyInsteadOfEmpty + InvoiceQuery.new.is_paid.any?.should be_true + + TransactionQuery.new + .user_id(user.id) + .type(:receipt) + .is_finalized + .none? + .should(be_false) + end +end diff --git a/spec/bill/actions/api/direct_sales_receipt/update_spec.cr b/spec/bill/actions/api/direct_sales_receipt/update_spec.cr new file mode 100644 index 0000000..e552bd0 --- /dev/null +++ b/spec/bill/actions/api/direct_sales_receipt/update_spec.cr @@ -0,0 +1,29 @@ +require "../../../../spec_helper" + +describe Bill::Api::DirectSalesReceipts::Update do + it "updates sales receipt" do + user = UserFactory.create + + invoice = InvoiceFactory.create &.user_id(user.id) + .description("New invoice") + .status(:draft) + + InvoiceItemFactory.create &.invoice_id(invoice.id) + + response = ApiClient.exec( + Api::DirectSalesReceipts::Update.with(invoice_id: invoice.id), + invoice: {status: :open} + ) + + response.should send_json(200, message: "action.invoice.update.success") + + invoice.reload.status.paid?.should be_true + + TransactionQuery.new + .user_id(user.id) + .type(:receipt) + .is_finalized + .none? + .should(be_false) + end +end diff --git a/spec/bill/actions/api/sales_receipt/create_spec.cr b/spec/bill/actions/api/sales_receipt/create_spec.cr new file mode 100644 index 0000000..b9bb2c2 --- /dev/null +++ b/spec/bill/actions/api/sales_receipt/create_spec.cr @@ -0,0 +1,28 @@ +require "../../../../spec_helper" + +describe Bill::Api::SalesReceipts::Create do + it "creates sales receipt" do + response = ApiClient.exec( + Api::SalesReceipts::Create, + invoice: { + user_id: UserFactory.create.id, + description: "New invoice", + due_at: 3.days.from_now, + status: :open + }, + line_items: [{ + description: "Item 1", + quantity: 2, + price_mu: 0.12 + }] + ) + + response.should send_json(200, message: "action.invoice.create.success") + + # ameba:disable Performance/AnyInsteadOfEmpty + InvoiceQuery.new.is_paid.any?.should be_true + + # ameba:disable Performance/AnyInsteadOfEmpty + ReceiptQuery.new.is_finalized.any?.should be_true + end +end diff --git a/spec/bill/actions/api/sales_receipt/update_spec.cr b/spec/bill/actions/api/sales_receipt/update_spec.cr new file mode 100644 index 0000000..d084c23 --- /dev/null +++ b/spec/bill/actions/api/sales_receipt/update_spec.cr @@ -0,0 +1,27 @@ +require "../../../../spec_helper" + +describe Bill::Api::SalesReceipts::Update do + it "updates sales receipt" do + user = UserFactory.create + + invoice = InvoiceFactory.create &.user_id(user.id) + .description("New invoice") + .status(:draft) + + InvoiceItemFactory.create &.invoice_id(invoice.id) + + ReceiptQuery.new.none?.should be_true + + response = ApiClient.exec( + Api::SalesReceipts::Update.with(invoice_id: invoice.id), + invoice: {status: :open} + ) + + response.should send_json(200, message: "action.invoice.update.success") + + invoice.reload.status.paid?.should be_true + + # ameba:disable Performance/AnyInsteadOfEmpty + ReceiptQuery.new.is_finalized.any?.should be_true + end +end diff --git a/spec/bill/actions/direct_sales_receipts/create_spec.cr b/spec/bill/actions/direct_sales_receipts/create_spec.cr new file mode 100644 index 0000000..fd317d5 --- /dev/null +++ b/spec/bill/actions/direct_sales_receipts/create_spec.cr @@ -0,0 +1,34 @@ +require "../../../spec_helper" + +describe Bill::DirectSalesReceipts::Create do + it "creates direct sales receipt" do + user = UserFactory.create + + response = ApiClient.exec( + DirectSalesReceipts::Create, + invoice: { + user_id: user.id, + description: "New invoice", + due_at: 3.days.from_now, + status: :open + }, + line_items: [{ + description: "Item 1", + quantity: 2, + price_mu: 0.12 + }] + ) + + response.status.should eq(HTTP::Status::FOUND) + + # ameba:disable Performance/AnyInsteadOfEmpty + InvoiceQuery.new.is_paid.any?.should be_true + + TransactionQuery.new + .user_id(user.id) + .type(:receipt) + .is_finalized + .none? + .should(be_false) + end +end diff --git a/spec/bill/actions/direct_sales_receipts/edit_spec.cr b/spec/bill/actions/direct_sales_receipts/edit_spec.cr new file mode 100644 index 0000000..4a11f32 --- /dev/null +++ b/spec/bill/actions/direct_sales_receipts/edit_spec.cr @@ -0,0 +1,14 @@ +require "../../../spec_helper" + +describe Bill::DirectSalesReceipts::Edit do + it "renders edit page" do + user = UserFactory.create + invoice = InvoiceFactory.create &.user_id(user.id) + + response = ApiClient.exec(DirectSalesReceipts::Edit.with( + invoice_id: invoice.id + )) + + response.body.should eq("DirectSalesReceipts::EditPage") + end +end diff --git a/spec/bill/actions/direct_sales_receipts/new_spec.cr b/spec/bill/actions/direct_sales_receipts/new_spec.cr new file mode 100644 index 0000000..b58157d --- /dev/null +++ b/spec/bill/actions/direct_sales_receipts/new_spec.cr @@ -0,0 +1,9 @@ +require "../../../spec_helper" + +describe Bill::DirectSalesReceipts::New do + it "renders new page" do + response = ApiClient.exec(DirectSalesReceipts::New) + + response.body.should eq("DirectSalesReceipts::NewPage") + end +end diff --git a/spec/bill/actions/direct_sales_receipts/update_spec.cr b/spec/bill/actions/direct_sales_receipts/update_spec.cr new file mode 100644 index 0000000..5b3977a --- /dev/null +++ b/spec/bill/actions/direct_sales_receipts/update_spec.cr @@ -0,0 +1,29 @@ +require "../../../spec_helper" + +describe Bill::DirectSalesReceipts::Update do + it "updates direct sales receipt" do + user = UserFactory.create + + invoice = InvoiceFactory.create &.user_id(user.id) + .description("New invoice") + .status(:draft) + + InvoiceItemFactory.create &.invoice_id(invoice.id) + + response = ApiClient.exec( + DirectSalesReceipts::Update.with(invoice_id: invoice.id), + invoice: {status: :open} + ) + + response.status.should eq(HTTP::Status::FOUND) + + invoice.reload.status.paid?.should be_true + + TransactionQuery.new + .user_id(user.id) + .type(:receipt) + .is_finalized + .none? + .should(be_false) + end +end diff --git a/spec/bill/actions/sales_receipts/create_spec.cr b/spec/bill/actions/sales_receipts/create_spec.cr new file mode 100644 index 0000000..d0c16d0 --- /dev/null +++ b/spec/bill/actions/sales_receipts/create_spec.cr @@ -0,0 +1,28 @@ +require "../../../spec_helper" + +describe Bill::SalesReceipts::Create do + it "creates sales receipt" do + response = ApiClient.exec( + SalesReceipts::Create, + invoice: { + user_id: UserFactory.create.id, + description: "New invoice", + due_at: 3.days.from_now, + status: :open + }, + line_items: [{ + description: "Item 1", + quantity: 2, + price_mu: 0.12 + }] + ) + + response.status.should eq(HTTP::Status::FOUND) + + # ameba:disable Performance/AnyInsteadOfEmpty + InvoiceQuery.new.is_paid.any?.should be_true + + # ameba:disable Performance/AnyInsteadOfEmpty + ReceiptQuery.new.is_finalized.any?.should be_true + end +end diff --git a/spec/bill/actions/sales_receipts/edit_spec.cr b/spec/bill/actions/sales_receipts/edit_spec.cr new file mode 100644 index 0000000..38d8ae9 --- /dev/null +++ b/spec/bill/actions/sales_receipts/edit_spec.cr @@ -0,0 +1,12 @@ +require "../../../spec_helper" + +describe Bill::SalesReceipts::Edit do + it "renders edit page" do + user = UserFactory.create + invoice = InvoiceFactory.create &.user_id(user.id) + + response = ApiClient.exec(SalesReceipts::Edit.with(invoice_id: invoice.id)) + + response.body.should eq("SalesReceipts::EditPage") + end +end diff --git a/spec/bill/actions/sales_receipts/new_spec.cr b/spec/bill/actions/sales_receipts/new_spec.cr new file mode 100644 index 0000000..c4456d9 --- /dev/null +++ b/spec/bill/actions/sales_receipts/new_spec.cr @@ -0,0 +1,9 @@ +require "../../../spec_helper" + +describe Bill::SalesReceipts::New do + it "renders new page" do + response = ApiClient.exec(SalesReceipts::New) + + response.body.should eq("SalesReceipts::NewPage") + end +end diff --git a/spec/bill/actions/sales_receipts/update_spec.cr b/spec/bill/actions/sales_receipts/update_spec.cr new file mode 100644 index 0000000..f6c5e7a --- /dev/null +++ b/spec/bill/actions/sales_receipts/update_spec.cr @@ -0,0 +1,27 @@ +require "../../../spec_helper" + +describe Bill::SalesReceipts::Update do + it "updates sales receipt" do + user = UserFactory.create + + invoice = InvoiceFactory.create &.user_id(user.id) + .description("New invoice") + .status(:draft) + + InvoiceItemFactory.create &.invoice_id(invoice.id) + + ReceiptQuery.new.none?.should be_true + + response = ApiClient.exec( + SalesReceipts::Update.with(invoice_id: invoice.id), + invoice: {status: :open} + ) + + response.status.should eq(HTTP::Status::FOUND) + + invoice.reload.status.paid?.should be_true + + # ameba:disable Performance/AnyInsteadOfEmpty + ReceiptQuery.new.is_finalized.any?.should be_true + end +end diff --git a/spec/bill/operations/create_direct_sales_receipt_spec.cr b/spec/bill/operations/create_direct_sales_receipt_spec.cr new file mode 100644 index 0000000..ae9a731 --- /dev/null +++ b/spec/bill/operations/create_direct_sales_receipt_spec.cr @@ -0,0 +1,53 @@ +require "../../spec_helper" + +describe Bill::CreateDirectSalesReceipt do + it "creates sales receipt" do + user = UserFactory.create + + invoice_2 = CreateInvoice.create!( + params( + user_id: user.id, + description: "Another invoice", + due_at: Time.utc, + status: :open + ), + line_items: [{ + "description" => "Item 1", + "quantity" => "1", + "price" => "12" + }] + ) + + TransactionQuery.new.type(:receipt).none?.should be_true + + CreateDirectSalesReceipt.create( + params( + user_id: user.id, + description: "New invoice", + due_at: 2.days.from_now, + status: :open + ), + line_items: [{ + "description" => "Item 1", + "quantity" => "2", + "price" => "12" + }] + ) do |_, invoice| + invoice.should be_a(Invoice) + + # ameba:disable Lint/ShadowingOuterLocalVar + invoice.try do |invoice| + invoice.status.paid?.should be_true + end + end + + invoice_2.reload.status.open?.should be_true + + TransactionQuery.new + .user_id(user.id) + .type(:receipt) + .is_finalized + .none? + .should(be_false) + end +end diff --git a/spec/bill/operations/create_sales_receipt_spec.cr b/spec/bill/operations/create_sales_receipt_spec.cr new file mode 100644 index 0000000..fce1044 --- /dev/null +++ b/spec/bill/operations/create_sales_receipt_spec.cr @@ -0,0 +1,49 @@ +require "../../spec_helper" + +describe Bill::CreateSalesReceipt do + it "creates sales receipt" do + user = UserFactory.create + + invoice_2 = CreateInvoice.create!( + params( + user_id: user.id, + description: "Another invoice", + due_at: Time.utc, + status: :open + ), + line_items: [{ + "description" => "Item 1", + "quantity" => "1", + "price" => "12" + }] + ) + + ReceiptQuery.new.none?.should be_true + + CreateSalesReceipt.create( + params( + user_id: user.id, + description: "New invoice", + due_at: 2.days.from_now, + status: :open + ), + line_items: [{ + "description" => "Item 1", + "quantity" => "2", + "price" => "12" + }] + ) do |_, invoice| + invoice.should be_a(Invoice) + + # ameba:disable Lint/ShadowingOuterLocalVar + invoice.try do |invoice| + invoice.status.paid?.should be_true + end + end + + invoice_2.reload.status.open?.should be_true + + # ameba:disable Performance/AnyInsteadOfEmpty + ReceiptQuery.new.is_finalized.any?.should be_true + end +end diff --git a/spec/bill/operations/update_direct_sales_receipt_spec.cr b/spec/bill/operations/update_direct_sales_receipt_spec.cr new file mode 100644 index 0000000..0cb65e9 --- /dev/null +++ b/spec/bill/operations/update_direct_sales_receipt_spec.cr @@ -0,0 +1,55 @@ +require "../../spec_helper" + +describe Bill::UpdateDirectSalesReceipt do + it "updates sales receipt" do + user = UserFactory.create + + invoice = CreateInvoice.create!( + params( + user_id: user.id, + description: "New invoice", + due_at: 2.days.from_now, + status: :draft + ), + line_items: [{ + "description" => "Item 1", + "quantity" => "2", + "price" => "12" + }] + ) + + invoice_2 = CreateInvoice.create!( + params( + user_id: user.id, + description: "Another invoice", + due_at: Time.utc, + status: :open + ), + line_items: [{ + "description" => "Item 1", + "quantity" => "1", + "price" => "12" + }] + ) + + TransactionQuery.new.type(:receipt).none?.should be_true + + UpdateDirectSalesReceipt.update( + InvoiceQuery.preload_line_items(invoice), + params(status: :open), + line_items: Array(Hash(String, String)).new + ) do |operation, updated_invoice| + operation.saved?.should be_true + updated_invoice.status.paid?.should be_true + end + + invoice_2.reload.status.open?.should be_true + + TransactionQuery.new + .user_id(user.id) + .type(:receipt) + .is_finalized + .none? + .should(be_false) + end +end diff --git a/spec/bill/operations/update_sales_receipt_spec.cr b/spec/bill/operations/update_sales_receipt_spec.cr new file mode 100644 index 0000000..8dfdeb8 --- /dev/null +++ b/spec/bill/operations/update_sales_receipt_spec.cr @@ -0,0 +1,51 @@ +require "../../spec_helper" + +describe Bill::UpdateSalesReceipt do + it "updates sales receipt" do + user = UserFactory.create + + invoice = CreateInvoice.create!( + params( + user_id: user.id, + description: "New invoice", + due_at: 2.days.from_now, + status: :draft + ), + line_items: [{ + "description" => "Item 1", + "quantity" => "2", + "price" => "12" + }] + ) + + invoice_2 = CreateInvoice.create!( + params( + user_id: user.id, + description: "Another invoice", + due_at: Time.utc, + status: :open + ), + line_items: [{ + "description" => "Item 1", + "quantity" => "1", + "price" => "12" + }] + ) + + ReceiptQuery.new.none?.should be_true + + UpdateSalesReceipt.update( + InvoiceQuery.preload_line_items(invoice), + params(status: :open), + line_items: Array(Hash(String, String)).new + ) do |operation, updated_invoice| + operation.saved?.should be_true + updated_invoice.status.paid?.should be_true + end + + invoice_2.reload.status.open?.should be_true + + # ameba:disable Performance/AnyInsteadOfEmpty + ReceiptQuery.new.is_finalized.any?.should be_true + end +end diff --git a/spec/support/app/src/actions/api/direct_sales_receipts/create.cr b/spec/support/app/src/actions/api/direct_sales_receipts/create.cr new file mode 100644 index 0000000..fadef5f --- /dev/null +++ b/spec/support/app/src/actions/api/direct_sales_receipts/create.cr @@ -0,0 +1,7 @@ +class Api::DirectSalesReceipts::Create < ApiAction + include Bill::Api::DirectSalesReceipts::Create + + post "/direct-sales-receipts" do + run_operation + end +end diff --git a/spec/support/app/src/actions/api/direct_sales_receipts/update.cr b/spec/support/app/src/actions/api/direct_sales_receipts/update.cr new file mode 100644 index 0000000..d69dcd9 --- /dev/null +++ b/spec/support/app/src/actions/api/direct_sales_receipts/update.cr @@ -0,0 +1,7 @@ +class Api::DirectSalesReceipts::Update < ApiAction + include Bill::Api::DirectSalesReceipts::Update + + patch "/direct-sales-receipts/:invoice_id" do + run_operation + end +end diff --git a/spec/support/app/src/actions/api/sales_receipts/create.cr b/spec/support/app/src/actions/api/sales_receipts/create.cr new file mode 100644 index 0000000..719d6e5 --- /dev/null +++ b/spec/support/app/src/actions/api/sales_receipts/create.cr @@ -0,0 +1,7 @@ +class Api::SalesReceipts::Create < ApiAction + include Bill::Api::SalesReceipts::Create + + post "/sales-receipts" do + run_operation + end +end diff --git a/spec/support/app/src/actions/api/sales_receipts/update.cr b/spec/support/app/src/actions/api/sales_receipts/update.cr new file mode 100644 index 0000000..f0fb728 --- /dev/null +++ b/spec/support/app/src/actions/api/sales_receipts/update.cr @@ -0,0 +1,7 @@ +class Api::SalesReceipts::Update < ApiAction + include Bill::Api::SalesReceipts::Update + + patch "/sales-receipts/:invoice_id" do + run_operation + end +end diff --git a/spec/support/app/src/actions/direct_sales_receipts/create.cr b/spec/support/app/src/actions/direct_sales_receipts/create.cr new file mode 100644 index 0000000..97f0bef --- /dev/null +++ b/spec/support/app/src/actions/direct_sales_receipts/create.cr @@ -0,0 +1,7 @@ +class DirectSalesReceipts::Create < BrowserAction + include Bill::DirectSalesReceipts::Create + + post "/direct-sales-receipts" do + run_operation + end +end diff --git a/spec/support/app/src/actions/direct_sales_receipts/edit.cr b/spec/support/app/src/actions/direct_sales_receipts/edit.cr new file mode 100644 index 0000000..dbc6a0f --- /dev/null +++ b/spec/support/app/src/actions/direct_sales_receipts/edit.cr @@ -0,0 +1,12 @@ +class DirectSalesReceipts::Edit < BrowserAction + include Bill::DirectSalesReceipts::Edit + + get "/direct-sales-receipts/:invoice_id/edit" do + operation = UpdateDirectSalesReceipt.new( + invoice, + line_items: Array(Hash(String, String)).new + ) + + html EditPage, operation: operation + end +end diff --git a/spec/support/app/src/actions/direct_sales_receipts/new.cr b/spec/support/app/src/actions/direct_sales_receipts/new.cr new file mode 100644 index 0000000..dc4558f --- /dev/null +++ b/spec/support/app/src/actions/direct_sales_receipts/new.cr @@ -0,0 +1,11 @@ +class DirectSalesReceipts::New < BrowserAction + include Bill::DirectSalesReceipts::New + + get "/direct-sales-receipts/new" do + operation = CreateDirectSalesReceipt.new( + line_items: Array(Hash(String, String)).new + ) + + html NewPage, operation: operation + end +end diff --git a/spec/support/app/src/actions/direct_sales_receipts/update.cr b/spec/support/app/src/actions/direct_sales_receipts/update.cr new file mode 100644 index 0000000..e0b4aff --- /dev/null +++ b/spec/support/app/src/actions/direct_sales_receipts/update.cr @@ -0,0 +1,7 @@ +class DirectSalesReceipts::Update < BrowserAction + include Bill::DirectSalesReceipts::Update + + patch "/direct-sales-receipts/:invoice_id" do + run_operation + end +end diff --git a/spec/support/app/src/actions/sales_receipts/create.cr b/spec/support/app/src/actions/sales_receipts/create.cr new file mode 100644 index 0000000..7ec4fb6 --- /dev/null +++ b/spec/support/app/src/actions/sales_receipts/create.cr @@ -0,0 +1,7 @@ +class SalesReceipts::Create < BrowserAction + include Bill::SalesReceipts::Create + + post "/sales-receipts" do + run_operation + end +end diff --git a/spec/support/app/src/actions/sales_receipts/edit.cr b/spec/support/app/src/actions/sales_receipts/edit.cr new file mode 100644 index 0000000..4c25a0d --- /dev/null +++ b/spec/support/app/src/actions/sales_receipts/edit.cr @@ -0,0 +1,12 @@ +class SalesReceipts::Edit < BrowserAction + include Bill::SalesReceipts::Edit + + get "/sales-receipts/:invoice_id/edit" do + operation = UpdateSalesReceipt.new( + invoice, + line_items: Array(Hash(String, String)).new + ) + + html EditPage, operation: operation + end +end diff --git a/spec/support/app/src/actions/sales_receipts/new.cr b/spec/support/app/src/actions/sales_receipts/new.cr new file mode 100644 index 0000000..d2c36b0 --- /dev/null +++ b/spec/support/app/src/actions/sales_receipts/new.cr @@ -0,0 +1,11 @@ +class SalesReceipts::New < BrowserAction + include Bill::SalesReceipts::New + + get "/sales-receipts/new" do + operation = CreateSalesReceipt.new( + line_items: Array(Hash(String, String)).new + ) + + html NewPage, operation: operation + end +end diff --git a/spec/support/app/src/actions/sales_receipts/update.cr b/spec/support/app/src/actions/sales_receipts/update.cr new file mode 100644 index 0000000..2755af9 --- /dev/null +++ b/spec/support/app/src/actions/sales_receipts/update.cr @@ -0,0 +1,7 @@ +class SalesReceipts::Update < BrowserAction + include Bill::SalesReceipts::Update + + patch "/sales-receipts/:invoice_id" do + run_operation + end +end diff --git a/spec/support/app/src/operations/create_direct_sales_receipt.cr b/spec/support/app/src/operations/create_direct_sales_receipt.cr new file mode 100644 index 0000000..724298c --- /dev/null +++ b/spec/support/app/src/operations/create_direct_sales_receipt.cr @@ -0,0 +1,3 @@ +class CreateDirectSalesReceipt < Invoice::SaveOperation + include Bill::CreateDirectSalesReceipt +end diff --git a/spec/support/app/src/operations/update_direct_sales_receipt.cr b/spec/support/app/src/operations/update_direct_sales_receipt.cr new file mode 100644 index 0000000..1d175d5 --- /dev/null +++ b/spec/support/app/src/operations/update_direct_sales_receipt.cr @@ -0,0 +1,3 @@ +class UpdateDirectSalesReceipt < Invoice::SaveOperation + include Bill::UpdateDirectSalesReceipt +end diff --git a/spec/support/app/src/pages/direct_sales_receipts/edit_page.cr b/spec/support/app/src/pages/direct_sales_receipts/edit_page.cr new file mode 100644 index 0000000..16494f9 --- /dev/null +++ b/spec/support/app/src/pages/direct_sales_receipts/edit_page.cr @@ -0,0 +1,7 @@ +struct DirectSalesReceipts::EditPage < MainLayout + needs operation : UpdateDirectSalesReceipt + + def content + text "DirectSalesReceipts::EditPage" + end +end diff --git a/spec/support/app/src/pages/direct_sales_receipts/new_page.cr b/spec/support/app/src/pages/direct_sales_receipts/new_page.cr new file mode 100644 index 0000000..a6216e4 --- /dev/null +++ b/spec/support/app/src/pages/direct_sales_receipts/new_page.cr @@ -0,0 +1,7 @@ +struct DirectSalesReceipts::NewPage < MainLayout + needs operation : CreateDirectSalesReceipt + + def content + text "DirectSalesReceipts::NewPage" + end +end diff --git a/spec/support/app/src/pages/sales_receipts/edit_page.cr b/spec/support/app/src/pages/sales_receipts/edit_page.cr new file mode 100644 index 0000000..b0a51f2 --- /dev/null +++ b/spec/support/app/src/pages/sales_receipts/edit_page.cr @@ -0,0 +1,7 @@ +struct SalesReceipts::EditPage < MainLayout + needs operation : UpdateSalesReceipt + + def content + text "SalesReceipts::EditPage" + end +end diff --git a/spec/support/app/src/pages/sales_receipts/new_page.cr b/spec/support/app/src/pages/sales_receipts/new_page.cr new file mode 100644 index 0000000..eac0ecc --- /dev/null +++ b/spec/support/app/src/pages/sales_receipts/new_page.cr @@ -0,0 +1,7 @@ +struct SalesReceipts::NewPage < MainLayout + needs operation : CreateSalesReceipt + + def content + text "SalesReceipts::NewPage" + end +end diff --git a/src/bill/actions/api/direct_sales_receipts/create.cr b/src/bill/actions/api/direct_sales_receipts/create.cr new file mode 100644 index 0000000..d569466 --- /dev/null +++ b/src/bill/actions/api/direct_sales_receipts/create.cr @@ -0,0 +1,35 @@ +module Bill::Api::DirectSalesReceipts::Create + macro included + # post "/invoices" do + # run_operation + # end + + def run_operation + CreateDirectSalesReceipt.create( + params, + line_items: params.many_nested?(:line_items) + ) do |operation, invoice| + if operation.saved? + do_run_operation_succeeded(operation, invoice.not_nil!) + else + response.status_code = 400 + do_run_operation_failed(operation) + end + end + end + + def do_run_operation_succeeded(operation, invoice) + json InvoiceSerializer.new( + invoice: InvoiceQuery.preload_line_items(invoice), + message: Rex.t(:"action.invoice.create.success") + ) + end + + def do_run_operation_failed(operation) + json FailureSerializer.new( + errors: operation.errors, + message: Rex.t(:"action.invoice.create.failure") + ) + end + end +end diff --git a/src/bill/actions/api/direct_sales_receipts/update.cr b/src/bill/actions/api/direct_sales_receipts/update.cr new file mode 100644 index 0000000..a3c25fe --- /dev/null +++ b/src/bill/actions/api/direct_sales_receipts/update.cr @@ -0,0 +1,40 @@ +module Bill::Api::DirectSalesReceipts::Update + macro included + # patch "/invoices/:invoice_id" do + # run_operation + # end + + def run_operation + UpdateDirectSalesReceipt.update( + invoice, + params, + line_items: params.many_nested?(:line_items) + ) do |operation, updated_invoice| + if operation.saved? + do_run_operation_succeeded(operation, updated_invoice) + else + response.status_code = 400 + do_run_operation_failed(operation) + end + end + end + + getter invoice : Invoice do + InvoiceQuery.new.preload_line_items.find(invoice_id) + end + + def do_run_operation_succeeded(operation, invoice) + json InvoiceSerializer.new( + invoice: InvoiceQuery.preload_line_items(invoice), + message: Rex.t(:"action.invoice.update.success") + ) + end + + def do_run_operation_failed(operation) + json FailureSerializer.new( + errors: operation.errors, + message: Rex.t(:"action.invoice.update.failure") + ) + end + end +end diff --git a/src/bill/actions/api/sales_receipts/create.cr b/src/bill/actions/api/sales_receipts/create.cr new file mode 100644 index 0000000..573095c --- /dev/null +++ b/src/bill/actions/api/sales_receipts/create.cr @@ -0,0 +1,35 @@ +module Bill::Api::SalesReceipts::Create + macro included + # post "/invoices" do + # run_operation + # end + + def run_operation + CreateSalesReceipt.create( + params, + line_items: params.many_nested?(:line_items) + ) do |operation, invoice| + if operation.saved? + do_run_operation_succeeded(operation, invoice.not_nil!) + else + response.status_code = 400 + do_run_operation_failed(operation) + end + end + end + + def do_run_operation_succeeded(operation, invoice) + json InvoiceSerializer.new( + invoice: InvoiceQuery.preload_line_items(invoice), + message: Rex.t(:"action.invoice.create.success") + ) + end + + def do_run_operation_failed(operation) + json FailureSerializer.new( + errors: operation.errors, + message: Rex.t(:"action.invoice.create.failure") + ) + end + end +end diff --git a/src/bill/actions/api/sales_receipts/update.cr b/src/bill/actions/api/sales_receipts/update.cr new file mode 100644 index 0000000..3e28fa7 --- /dev/null +++ b/src/bill/actions/api/sales_receipts/update.cr @@ -0,0 +1,40 @@ +module Bill::Api::SalesReceipts::Update + macro included + # patch "/invoices/:invoice_id" do + # run_operation + # end + + def run_operation + UpdateSalesReceipt.update( + invoice, + params, + line_items: params.many_nested?(:line_items) + ) do |operation, updated_invoice| + if operation.saved? + do_run_operation_succeeded(operation, updated_invoice) + else + response.status_code = 400 + do_run_operation_failed(operation) + end + end + end + + getter invoice : Invoice do + InvoiceQuery.new.preload_line_items.find(invoice_id) + end + + def do_run_operation_succeeded(operation, invoice) + json InvoiceSerializer.new( + invoice: InvoiceQuery.preload_line_items(invoice), + message: Rex.t(:"action.invoice.update.success") + ) + end + + def do_run_operation_failed(operation) + json FailureSerializer.new( + errors: operation.errors, + message: Rex.t(:"action.invoice.update.failure") + ) + end + end +end diff --git a/src/bill/actions/direct_sales_receipts/create.cr b/src/bill/actions/direct_sales_receipts/create.cr new file mode 100644 index 0000000..9c01197 --- /dev/null +++ b/src/bill/actions/direct_sales_receipts/create.cr @@ -0,0 +1,31 @@ +module Bill::DirectSalesReceipts::Create + macro included + # post "/invoices" do + # run_operation + # end + + def run_operation + CreateDirectSalesReceipt.create( + params, + line_items: params.many_nested?(:line_items) + ) do |operation, invoice| + if operation.saved? + do_run_operation_succeeded(operation, invoice.not_nil!) + else + response.status_code = 400 + do_run_operation_failed(operation) + end + end + end + + def do_run_operation_succeeded(operation, invoice) + flash.success = Rex.t(:"action.invoice.create.success") + redirect to: Invoices::Show.with(invoice_id: invoice.id) + end + + def do_run_operation_failed(operation) + flash.failure = Rex.t(:"action.invoice.create.failure") + html NewPage, operation: operation + end + end +end diff --git a/src/bill/actions/direct_sales_receipts/edit.cr b/src/bill/actions/direct_sales_receipts/edit.cr new file mode 100644 index 0000000..ee63e64 --- /dev/null +++ b/src/bill/actions/direct_sales_receipts/edit.cr @@ -0,0 +1,16 @@ +module Bill::DirectSalesReceipts::Edit + macro included + # get "/invoices/:invoice_id/edit" do + # operation = UpdateDirectSalesReceipt.new( + # invoice, + # line_items: Array(Hash(String, String)).new + # ) + # + # html EditPage, operation: operation + # end + + getter invoice : Invoice do + InvoiceQuery.new.preload_line_items.find(invoice_id) + end + end +end diff --git a/src/bill/actions/direct_sales_receipts/new.cr b/src/bill/actions/direct_sales_receipts/new.cr new file mode 100644 index 0000000..725fafd --- /dev/null +++ b/src/bill/actions/direct_sales_receipts/new.cr @@ -0,0 +1,11 @@ +module Bill::DirectSalesReceipts::New + macro included + # get "/invoices/new" do + # operation = CreateDirectSalesReceipt.new( + # line_items: Array(Hash(String, String)).new + # ) + # + # html NewPage, operation: operation + # end + end +end diff --git a/src/bill/actions/direct_sales_receipts/update.cr b/src/bill/actions/direct_sales_receipts/update.cr new file mode 100644 index 0000000..0289fa2 --- /dev/null +++ b/src/bill/actions/direct_sales_receipts/update.cr @@ -0,0 +1,36 @@ +module Bill::DirectSalesReceipts::Update + macro included + # patch "/invoices/:invoice_id" do + # run_operation + # end + + def run_operation + UpdateDirectSalesReceipt.update( + invoice, + params, + line_items: params.many_nested?(:line_items) + ) do |operation, updated_invoice| + if operation.saved? + do_run_operation_succeeded(operation, updated_invoice) + else + response.status_code = 400 + do_run_operation_failed(operation) + end + end + end + + getter invoice : Invoice do + InvoiceQuery.new.preload_line_items.find(invoice_id) + end + + def do_run_operation_succeeded(operation, invoice) + flash.success = Rex.t(:"action.invoice.update.success") + redirect to: Invoices::Show.with(invoice_id: invoice.id) + end + + def do_run_operation_failed(operation) + flash.failure = Rex.t(:"action.invoice.update.failure") + html EditPage, operation: operation + end + end +end diff --git a/src/bill/actions/sales_receipts/create.cr b/src/bill/actions/sales_receipts/create.cr new file mode 100644 index 0000000..24ccf0c --- /dev/null +++ b/src/bill/actions/sales_receipts/create.cr @@ -0,0 +1,31 @@ +module Bill::SalesReceipts::Create + macro included + # post "/invoices" do + # run_operation + # end + + def run_operation + CreateSalesReceipt.create( + params, + line_items: params.many_nested?(:line_items) + ) do |operation, invoice| + if operation.saved? + do_run_operation_succeeded(operation, invoice.not_nil!) + else + response.status_code = 400 + do_run_operation_failed(operation) + end + end + end + + def do_run_operation_succeeded(operation, invoice) + flash.success = Rex.t(:"action.invoice.create.success") + redirect to: Invoices::Show.with(invoice_id: invoice.id) + end + + def do_run_operation_failed(operation) + flash.failure = Rex.t(:"action.invoice.create.failure") + html NewPage, operation: operation + end + end +end diff --git a/src/bill/actions/sales_receipts/edit.cr b/src/bill/actions/sales_receipts/edit.cr new file mode 100644 index 0000000..638ebe7 --- /dev/null +++ b/src/bill/actions/sales_receipts/edit.cr @@ -0,0 +1,16 @@ +module Bill::SalesReceipts::Edit + macro included + # get "/invoices/:invoice_id/edit" do + # operation = UpdateSalesReceipt.new( + # invoice, + # line_items: Array(Hash(String, String)).new + # ) + # + # html EditPage, operation: operation + # end + + getter invoice : Invoice do + InvoiceQuery.new.preload_line_items.find(invoice_id) + end + end +end diff --git a/src/bill/actions/sales_receipts/new.cr b/src/bill/actions/sales_receipts/new.cr new file mode 100644 index 0000000..eeffd12 --- /dev/null +++ b/src/bill/actions/sales_receipts/new.cr @@ -0,0 +1,11 @@ +module Bill::SalesReceipts::New + macro included + # get "/invoices/new" do + # operation = CreateSalesReceipt.new( + # line_items: Array(Hash(String, String)).new + # ) + # + # html NewPage, operation: operation + # end + end +end diff --git a/src/bill/actions/sales_receipts/update.cr b/src/bill/actions/sales_receipts/update.cr new file mode 100644 index 0000000..4728d56 --- /dev/null +++ b/src/bill/actions/sales_receipts/update.cr @@ -0,0 +1,36 @@ +module Bill::SalesReceipts::Update + macro included + # patch "/invoices/:invoice_id" do + # run_operation + # end + + def run_operation + UpdateSalesReceipt.update( + invoice, + params, + line_items: params.many_nested?(:line_items) + ) do |operation, updated_invoice| + if operation.saved? + do_run_operation_succeeded(operation, updated_invoice) + else + response.status_code = 400 + do_run_operation_failed(operation) + end + end + end + + getter invoice : Invoice do + InvoiceQuery.new.preload_line_items.find(invoice_id) + end + + def do_run_operation_succeeded(operation, invoice) + flash.success = Rex.t(:"action.invoice.update.success") + redirect to: Invoices::Show.with(invoice_id: invoice.id) + end + + def do_run_operation_failed(operation) + flash.failure = Rex.t(:"action.invoice.update.failure") + html EditPage, operation: operation + end + end +end diff --git a/src/bill/operations/create_direct_sales_receipt.cr b/src/bill/operations/create_direct_sales_receipt.cr new file mode 100644 index 0000000..cc44354 --- /dev/null +++ b/src/bill/operations/create_direct_sales_receipt.cr @@ -0,0 +1,26 @@ +# A sales receipt is an invoice paid for on the spot. +# +# Creates invoice, and marks the invoice as paid. +# (No `Receipt` record is created, only a receipt `Transaction`) +module Bill::CreateDirectSalesReceipt # Invoice::SaveOperation + macro included + include Bill::InvoiceDescription + include Bill::CreateInvoice + + after_save create_receipt + + private def create_receipt(invoice : Bill::Invoice) + return unless InvoiceStatus.now_finalized?(status) + + CreateDirectReceipt.create!( + invoice_id: invoice.id, + user_id: invoice.user_id, + description: invoice_description(invoice), + amount: invoice.net_amount!, + status: TransactionStatus.new(:open) + ) + + self.record = self.record.try(&.reload) + end + end +end diff --git a/src/bill/operations/create_sales_receipt.cr b/src/bill/operations/create_sales_receipt.cr new file mode 100644 index 0000000..c530cef --- /dev/null +++ b/src/bill/operations/create_sales_receipt.cr @@ -0,0 +1,26 @@ +# A sales receipt is an invoice paid for on the spot. +# +# Creates invoice and a corresponding receipt, and marks the +# invoice as paid. +module Bill::CreateSalesReceipt # Invoice::SaveOperation + macro included + include Bill::InvoiceDescription + include Bill::CreateInvoice + + after_save create_receipt + + private def create_receipt(invoice : Bill::Invoice) + return unless InvoiceStatus.now_finalized?(status) + + CreateReceipt.create!( + invoice_id: invoice.id, + user_id: invoice.user_id, + description: invoice_description(invoice), + amount: invoice.net_amount!, + status: ReceiptStatus.new(:open) + ) + + self.record = self.record.try(&.reload) + end + end +end diff --git a/src/bill/operations/update_direct_sales_receipt.cr b/src/bill/operations/update_direct_sales_receipt.cr new file mode 100644 index 0000000..a21e971 --- /dev/null +++ b/src/bill/operations/update_direct_sales_receipt.cr @@ -0,0 +1,26 @@ +# A sales receipt is an invoice paid for on the spot. +# +# Creates invoice, and marks the invoice as paid. +# (No `Receipt` record is created, only a receipt `Transaction`) +module Bill::UpdateDirectSalesReceipt # Invoice::SaveOperation + macro included + include Bill::InvoiceDescription + include Bill::UpdateInvoice + + after_save create_receipt + + private def create_receipt(invoice : Bill::Invoice) + return unless InvoiceStatus.now_finalized?(status) + + CreateDirectReceipt.create!( + invoice_id: invoice.id, + user_id: invoice.user_id, + description: invoice_description(invoice), + amount: invoice.net_amount!, + status: TransactionStatus.new(:open) + ) + + self.record = self.record.try(&.reload) + end + end +end diff --git a/src/bill/operations/update_sales_receipt.cr b/src/bill/operations/update_sales_receipt.cr new file mode 100644 index 0000000..867b533 --- /dev/null +++ b/src/bill/operations/update_sales_receipt.cr @@ -0,0 +1,26 @@ +# A sales receipt is an invoice paid for on the spot. +# +# Creates invoice and a corresponding receipt, and marks the +# invoice as paid. +module Bill::UpdateSalesReceipt # Invoice::SaveOperation + macro included + include Bill::InvoiceDescription + include Bill::UpdateInvoice + + after_save create_receipt + + private def create_receipt(invoice : Bill::Invoice) + return unless InvoiceStatus.now_finalized?(status) + + CreateReceipt.create!( + invoice_id: invoice.id, + user_id: invoice.user_id, + description: invoice_description(invoice), + amount: invoice.net_amount!, + status: ReceiptStatus.new(:open) + ) + + self.record = self.record.try(&.reload) + end + end +end diff --git a/src/presets/invoice.cr b/src/presets/invoice.cr index 2eb8ba6..efa3e99 100644 --- a/src/presets/invoice.cr +++ b/src/presets/invoice.cr @@ -45,3 +45,21 @@ end include Bill::UpdateInvoiceTotalCreditNotes end {% end %} + +{% if Avram::Model.all_subclasses.find(&.name.== :Receipt.id) %} + class CreateSalesReceipt < Invoice::SaveOperation + include Bill::CreateSalesReceipt + end + + class UpdateSalesReceipt < Invoice::SaveOperation + include Bill::UpdateSalesReceipt + end +{% elsif Avram::Model.all_subclasses.find(&.name.== :Transaction.id) %} + class CreateDirectSalesReceipt < Invoice::SaveOperation + include Bill::CreateDirectSalesReceipt + end + + class UpdateDirectSalesReceipt < Invoice::SaveOperation + include Bill::UpdateDirectSalesReceipt + end +{% end %}