Skip to main content
Back to ScopeForged

ScopeForged Documentation

Technical documentation, guides, and feature references for the ScopeForged client portal.

Core Business Features/Invoicing

Invoicing Guide

Last Updated: 2026-01-13 Status: Implemented Plan Reference: 008-invoicing-system.md, 072-invoicing-improvement.md


Overview

The Invoicing system allows administrators to create, manage, and track invoices for clients. Invoices support multiple line items, status tracking, PDF generation, online payments via Stripe, recurring invoices, and automated payment reminders. Clients can view and pay their invoices through the portal.


Table of Contents

  1. Accessing Invoices
  2. Features
  3. Invoice Status Workflow
  4. How to Use
  5. Line Items
  6. Online Payments (Stripe)
  7. Recurring Invoices
  8. Payment Reminders
  9. Technical Architecture
  10. Related Features

Accessing Invoices

Access PointLocationURLRole
All InvoicesAdmin sidebar/admin/invoicesAdmin
Create InvoiceInvoices page/admin/invoices/createAdmin
View InvoiceInvoice row/admin/invoices/{id}Admin
Edit InvoiceInvoice detail/admin/invoices/{id}/editAdmin
My InvoicesPortal sidebar/portal/invoicesClient User
Invoice DetailPortal/portal/invoices/{id}Client User
Invoice PDFInvoice page/admin/invoices/{id}/pdfAdmin
Download PDFInvoice page/admin/invoices/{id}/pdf/downloadBoth

Permissions

ActionAdminClient User
View all invoices
View client's invoices
Create invoice
Edit invoice
Delete invoice
Mark as paid
Download PDF

Features

Invoice Details

  • Invoice number (auto-generated)
  • Client association
  • Issue date and due date
  • Status tracking
  • Notes for client
  • Internal notes

Line Items

  • Multiple line items per invoice
  • Description, quantity, rate
  • Automatic line total calculation
  • Automatic invoice total

Invoice Totals

  • Subtotal (sum of line items)
  • Tax calculation (optional)
  • Discount (optional)
  • Grand total

PDF Generation


Invoice Status Workflow

Status Enum

Location: app/Enums/InvoiceStatus.php

StatusValueDescriptionColor
DraftdraftNot sent to clientGray
SentsentSent, awaiting paymentBlue
PaidpaidPayment receivedGreen
OverdueoverduePast due dateRed

Status Transitions

Draft → Sent → Paid
           ↓
        Overdue (automatic after due date)

Status Methods

// Check status
$invoice->status->isDraft();
$invoice->status->isSent();
$invoice->status->isPaid();
$invoice->status->isOverdue();

// Get display values
$invoice->status->label();  // "Sent"
$invoice->status->color();  // "blue"

How to Use

Creating an Invoice

  1. Navigate to Admin → Invoices
  2. Click "Create Invoice"
  3. Select Client from dropdown
  4. Set Issue Date (defaults to today)
  5. Set Due Date
  6. Add Line Items:
    • Click "Add Line Item"
    • Enter description
    • Enter quantity
    • Enter unit rate
    • Line total calculates automatically
  7. Add optional notes
  8. Click "Create Invoice"

Adding Line Items

Each line item requires:

  • Description: What the charge is for
  • Quantity: Number of units (default: 1)
  • Rate: Price per unit
Line Total = Quantity × Rate
Invoice Total = Sum of all Line Totals

Viewing an Invoice

  1. Navigate to Admin → Invoices or Portal → Invoices
  2. Click invoice number or "View"
  3. See full invoice details:
    • Client information
    • Line items breakdown
    • Status and dates
    • Total amount

Editing an Invoice

  1. Navigate to Admin → Invoices → [Invoice]
  2. Click "Edit"
  3. Modify fields or line items
  4. Click "Update Invoice"

Note: Only draft invoices should be edited. Sent invoices should be voided and recreated.

Changing Invoice Status

Mark as Sent:

  1. View invoice
  2. Click "Mark as Sent"
  3. Status changes to "Sent"

Mark as Paid:

  1. View invoice
  2. Click "Mark as Paid"
  3. Status changes to "Paid"
  4. Paid date recorded

Generating PDF

  1. View any invoice
  2. Click "View PDF" to open in browser
  3. Click "Download PDF" to save file

See PDF Generation Guide for details.

Deleting an Invoice

  1. Navigate to Admin → Invoices → [Invoice]
  2. Click "Delete"
  3. Confirm deletion

Warning: Only delete draft invoices. Consider voiding sent invoices instead.


Line Items

Structure

Each invoice has multiple line items:

// InvoiceItem model
$item->description;  // "Web Development Services"
$item->quantity;     // 10
$item->unit_price;   // 150.00
$item->total;        // 1500.00 (calculated)

Adding Items Dynamically

The invoice form uses JavaScript to add/remove line items:

// Add new line item row
addLineItem();

// Remove line item row
removeLineItem(index);

// Recalculate totals
updateTotals();

Calculations

// Line item total
$lineTotal = $quantity * $unitPrice;

// Invoice subtotal
$subtotal = $invoice->items->sum('total');

// With tax (if applicable)
$tax = $subtotal * ($taxRate / 100);
$total = $subtotal + $tax;

Online Payments (Stripe)

Overview

Clients can pay invoices online via Stripe Checkout. When Stripe is configured, a "Pay Now" button appears on unpaid invoices in the client portal.

Configuration

Add these environment variables to .env:

STRIPE_KEY=pk_test_...
STRIPE_SECRET=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

Getting Stripe Credentials

  1. Create a Stripe account if you don't have one
  2. Go to Stripe Dashboard → Developers → API keys
  3. Copy the Publishable keySTRIPE_KEY
  4. Copy the Secret keySTRIPE_SECRET

Setting Up the Webhook

  1. Go to Stripe Dashboard → Developers → Webhooks
  2. Click "Add endpoint"
  3. Enter your endpoint URL: https://your-domain.com/webhooks/stripe
  4. Select events to listen to:
    • checkout.session.completed
    • payment_intent.succeeded
    • payment_intent.payment_failed
  5. Click "Add endpoint"
  6. Copy the Signing secretSTRIPE_WEBHOOK_SECRET

Note: For local development, use Stripe CLI to forward webhooks:

stripe listen --forward-to localhost:8000/webhooks/stripe

Features

  • Stripe Checkout: Secure, hosted payment page
  • Multiple Payment Methods: Credit cards supported
  • Automatic Status Update: Invoice marked as paid after successful payment
  • Payment Recording: All payments tracked in the database
  • Webhook Support: Real-time payment confirmation

Payment Flow

  1. Client clicks "Pay Now" on invoice
  2. Redirected to Stripe Checkout
  3. Completes payment
  4. Webhook confirms payment
  5. Invoice status updated to "Paid"
  6. Client sees success page

Payment Model

Location: app/Models/Payment.php

$payment->invoice_id;        // Related invoice
$payment->amount;            // Payment amount
$payment->payment_method;    // 'stripe', 'manual', etc.
$payment->transaction_id;    // Stripe payment intent ID
$payment->status;            // 'completed', 'pending', 'failed'
$payment->paid_at;           // Payment timestamp

Routes

RouteMethodDescription
portal.invoices.payment.checkoutPOSTInitiate Stripe Checkout
portal.invoices.payment.successGETPayment success page
webhooks.stripePOSTStripe webhook handler

Recurring Invoices

Overview

Set up recurring invoices that automatically generate on a schedule. Useful for retainers, subscriptions, and maintenance contracts.

Access

LocationURLRole
Recurring Invoices List/admin/recurring-invoicesAdmin
Create Recurring Invoice/admin/recurring-invoices/createAdmin
View Details/admin/recurring-invoices/{id}Admin
Edit/admin/recurring-invoices/{id}/editAdmin

Frequencies

FrequencyLabelDays Between
weeklyWeekly7
biweeklyBi-weekly14
monthlyMonthly30
quarterlyQuarterly90
annuallyAnnually365

Creating a Recurring Invoice

  1. Navigate to Admin → Recurring Invoices
  2. Click "New Recurring Invoice"
  3. Fill in details:
    • Name: Descriptive name (e.g., "Monthly Retainer")
    • Client: Select client
    • Frequency: Select billing frequency
    • Start Date: When to begin
    • End Date: Optional end date
    • Line Items: Template items for generated invoices
  4. Click "Create Recurring Invoice"

Actions

ActionDescription
Generate NowManually generate an invoice immediately
Toggle StatusActivate/deactivate the recurring invoice
EditModify template details
DeleteRemove the recurring invoice

Scheduled Processing

Recurring invoices are processed daily at 7:00 AM. The ProcessRecurringInvoicesJob checks for due recurring invoices and generates them automatically.

Configuration

// config/invoicing.php
'recurring' => [
    'auto_send' => true,           // Auto-send when generated
    'default_payment_terms' => 30, // Days until due
],

Payment Reminders

Overview

Automated email reminders are sent for upcoming and overdue invoices. Reminders help improve collection rates without manual follow-up.

Reminder Schedule

Before Due Date (configurable):

  • 7 days before due
  • 3 days before due
  • 1 day before due

After Due Date (overdue):

  • 1 day overdue
  • 7 days overdue
  • 14 days overdue
  • 30 days overdue

Configuration

// config/invoicing.php
'reminders' => [
    'enabled' => true,
    'days_before_due' => [7, 3, 1],
    'days_after_due' => [1, 7, 14, 30],
],

Enable/disable via environment:

INVOICE_REMINDERS_ENABLED=true

Scheduled Job

Reminders are sent daily at 9:00 AM via the SendInvoiceRemindersJob.

Notification Content

Upcoming Reminders:

  • Subject: "Payment Reminder: Invoice INV-2026-0001"
  • Tone: Friendly reminder

Overdue Reminders:

  • Subject: "Overdue Invoice: INV-2026-0001"
  • Tone: More urgent, includes days overdue

Technical Architecture

Models

Invoice Model: app/Models/Invoice.php

class Invoice extends Model
{
    protected $fillable = [
        'client_id',
        'project_id',
        'invoice_number',
        'status',
        'issue_date',
        'due_date',
        'paid_date',
        'subtotal',
        'tax',
        'total',
        'notes',
        'internal_notes',
    ];

    protected $casts = [
        'status' => InvoiceStatus::class,
        'issue_date' => 'date',
        'due_date' => 'date',
        'paid_date' => 'date',
        'subtotal' => 'decimal:2',
        'tax' => 'decimal:2',
        'total' => 'decimal:2',
    ];
}

InvoiceItem Model: app/Models/InvoiceItem.php

class InvoiceItem extends Model
{
    protected $fillable = [
        'invoice_id',
        'description',
        'quantity',
        'unit_price',
        'total',
    ];

    protected $casts = [
        'quantity' => 'decimal:2',
        'unit_price' => 'decimal:2',
        'total' => 'decimal:2',
    ];
}

Relationships

// Invoice model
public function client(): BelongsTo
{
    return $this->belongsTo(Client::class);
}

public function project(): BelongsTo
{
    return $this->belongsTo(Project::class);
}

public function items(): HasMany
{
    return $this->hasMany(InvoiceItem::class);
}

// InvoiceItem model
public function invoice(): BelongsTo
{
    return $this->belongsTo(Invoice::class);
}

Controller

Location: app/Http/Controllers/Admin/InvoiceController.php

MethodRouteDescription
index()GET /admin/invoicesList invoices
create()GET /admin/invoices/createCreate form
store()POST /admin/invoicesCreate invoice
show()GET /admin/invoices/{invoice}View invoice
edit()GET /admin/invoices/{invoice}/editEdit form
update()PUT /admin/invoices/{invoice}Update invoice
destroy()DELETE /admin/invoices/{invoice}Delete invoice
pdf()GET /admin/invoices/{invoice}/pdfView PDF
downloadPdf()GET /admin/invoices/{invoice}/pdf/downloadDownload PDF

Routes

// Admin routes
Route::prefix('admin')->middleware(['auth', 'admin'])->group(function () {
    Route::resource('invoices', Admin\InvoiceController::class);
    Route::get('invoices/{invoice}/pdf', [Admin\InvoiceController::class, 'pdf'])->name('admin.invoices.pdf');
    Route::get('invoices/{invoice}/pdf/download', [Admin\InvoiceController::class, 'downloadPdf'])->name('admin.invoices.pdf.download');
});

// Portal routes
Route::prefix('portal')->middleware(['auth'])->group(function () {
    Route::get('invoices', [Portal\InvoiceController::class, 'index'])->name('portal.invoices.index');
    Route::get('invoices/{invoice}', [Portal\InvoiceController::class, 'show'])->name('portal.invoices.show');
    Route::get('invoices/{invoice}/pdf', [Portal\InvoiceController::class, 'pdf'])->name('portal.invoices.pdf');
    Route::get('invoices/{invoice}/pdf/download', [Portal\InvoiceController::class, 'downloadPdf'])->name('portal.invoices.pdf.download');
});

Database Schema

Table: invoices

ColumnTypeDescription
idbigintPrimary key
client_idbigintForeign key to clients
project_idbigintOptional foreign key to projects
invoice_numberstringUnique invoice number
statusstringStatus enum value
issue_datedateDate issued
due_datedatePayment due date
paid_datedateDate payment received
subtotaldecimalSum of line items
taxdecimalTax amount
totaldecimalFinal total
notestextClient-visible notes
internal_notestextAdmin-only notes
created_attimestampCreation date
updated_attimestampLast update

Table: invoice_items

ColumnTypeDescription
idbigintPrimary key
invoice_idbigintForeign key to invoices
descriptionstringLine item description
quantitydecimalQuantity
unit_pricedecimalPrice per unit
totaldecimalLine total
created_attimestampCreation date
updated_attimestampLast update

Views

ViewPathPurpose
Indexresources/views/admin/invoices/index.blade.phpInvoice list
Createresources/views/admin/invoices/create.blade.phpCreate form
Showresources/views/admin/invoices/show.blade.phpInvoice details
Editresources/views/admin/invoices/edit.blade.phpEdit form
Portal Indexresources/views/portal/invoices/index.blade.phpClient's invoices
Portal Showresources/views/portal/invoices/show.blade.phpClient's view
PDF Templateresources/views/pdf/invoice.blade.phpPDF template

Scopes and Query Helpers

// By status
Invoice::status(InvoiceStatus::Sent)->get();

// Overdue invoices
Invoice::overdue()->get();

// For a client
Invoice::forClient($client)->get();

// Unpaid invoices
Invoice::unpaid()->get();

// Date range
Invoice::issuedBetween($start, $end)->get();

// With totals
Invoice::withItemsTotal()->get();

Dependencies

FeatureRelationship
Client ManagementInvoices belong to clients
Project ManagementOptional project association
AuthorizationInvoicePolicy controls access

Complementary Features

FeatureDescription
PDF GenerationInvoice PDF output
NotificationsInvoice email notifications
ReportsFinancial reporting
Activity LoggingInvoice change history

Best Practices

For Administrators

  1. Use descriptive line items for clarity
  2. Set realistic due dates (typically Net 30)
  3. Send invoices promptly after work is completed
  4. Mark paid immediately when payment received
  5. Use notes for payment instructions
  6. Use internal notes for accounting references

For Developers

  1. Use InvoiceStatus enum for status values
  2. Calculate totals server-side for accuracy
  3. Validate line items ensure at least one item
  4. Use transactions when creating invoice with items

Troubleshooting

IssueSolution
Total not calculatingCheck line item quantities and rates
PDF not generatingVerify dompdf is installed
Client can't see invoiceCheck client association
Invoice number duplicateAuto-generation handles this

See Also