feat: add legacy PDF generation support and invoice discount fields

This commit is contained in:
I-Fun
2026-04-07 07:49:35 +07:00
parent 737ba24293
commit 0e6726b0a6
13 changed files with 710 additions and 45 deletions

View File

@@ -21,7 +21,7 @@ class Invoice extends Model
'subtotal', 'tax_rate', 'tax_amount', 'total', 'currency',
'status', 'issue_date', 'due_date', 'paid_date', 'notes',
'bank_name', 'bank_account', 'bank_holder', 'branch_name',
'signature_image', 'signer_name', 'signer_title',
'signature_image', 'signer_name', 'signer_title', 'discount_rate',
];
protected $casts = [
@@ -47,14 +47,32 @@ class Invoice extends Model
public function recalculate(): void
{
// Total of all items (what the creator receives)
// Total of all items
$this->subtotal = $this->items->sum('total');
// Tax is calculated only on taxable items, but paid by the client on top
$taxableSubtotal = $this->items->where('is_taxable', true)->sum('total');
$discountAmount = 0;
if ($this->discount_rate > 0) {
$discountableSubtotal = $this->items->where('is_discountable', true)->sum('total');
$discountAmount = round((float) $discountableSubtotal * ($this->discount_rate / 100), 2);
}
$this->subtotal = $this->subtotal - $discountAmount;
// Tax is calculated only on taxable items
$taxableSubtotal = 0;
foreach ($this->items as $item) {
if ($item->is_taxable) {
if ($this->discount_rate > 0 && $item->is_discountable) {
$taxableSubtotal += $item->total * (1 - $this->discount_rate / 100);
} else {
$taxableSubtotal += $item->total;
}
}
}
$this->tax_amount = round((float) $taxableSubtotal * ($this->tax_rate / 100), 2);
// Grand total = subtotal (creator's fee, pre-tax — tax is borne by client separately)
// Grand total = subtotal (tax is borne by client separately)
$this->total = $this->subtotal;
$this->save();
}
@@ -64,6 +82,14 @@ class Invoice extends Model
return 'Rp ' . number_format((float) $this->total, 0, ',', '.');
}
public function getDiscountAmountAttribute(): float
{
if ($this->discount_rate <= 0) return 0.0;
$discountableSubtotal = $this->items->where('is_discountable', true)->sum('total');
return round((float) $discountableSubtotal * ($this->discount_rate / 100), 2);
}
public static function generateNumber(): string
{
return InvoiceSettings::generateNumber();

View File

@@ -9,12 +9,13 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
class InvoiceItem extends Model
{
protected $fillable = ['invoice_id', 'description', 'quantity', 'unit_price', 'total', 'is_taxable'];
protected $fillable = ['invoice_id', 'description', 'quantity', 'unit_price', 'total', 'is_taxable', 'is_discountable'];
protected $casts = [
'unit_price' => 'decimal:2',
'total' => 'decimal:2',
'is_taxable' => 'boolean',
'unit_price' => 'decimal:2',
'total' => 'decimal:2',
'is_taxable' => 'boolean',
'is_discountable' => 'boolean',
];
public function invoice(): BelongsTo