feat: add legacy PDF generation support and invoice discount fields
This commit is contained in:
@@ -56,6 +56,8 @@ class InvoiceController extends Controller
|
||||
'items.*.quantity' => 'required|integer|min:1',
|
||||
'items.*.unit_price' => 'required|numeric|min:0',
|
||||
'items.*.is_taxable' => 'nullable|boolean',
|
||||
'items.*.is_discountable' => 'nullable|boolean',
|
||||
'discount_rate' => 'nullable|integer|min:0|max:100',
|
||||
]);
|
||||
|
||||
$items = $validated['items'];
|
||||
@@ -118,6 +120,8 @@ class InvoiceController extends Controller
|
||||
'items.*.quantity' => 'required|integer|min:1',
|
||||
'items.*.unit_price' => 'required|numeric|min:0',
|
||||
'items.*.is_taxable' => 'nullable|boolean',
|
||||
'items.*.is_discountable' => 'nullable|boolean',
|
||||
'discount_rate' => 'nullable|integer|min:0|max:100',
|
||||
]);
|
||||
|
||||
$items = $validated['items'];
|
||||
@@ -151,4 +155,9 @@ class InvoiceController extends Controller
|
||||
{
|
||||
return $this->invoiceService->generatePdf($invoice);
|
||||
}
|
||||
|
||||
public function pdfLegacy(Invoice $invoice): Response
|
||||
{
|
||||
return $this->invoiceService->generateLegacyPdf($invoice);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -59,4 +59,15 @@ final class InvoiceService
|
||||
|
||||
return $pdf->download("invoice-{$invoice->invoice_number}.pdf");
|
||||
}
|
||||
|
||||
public function generateLegacyPdf(Invoice $invoice): Response
|
||||
{
|
||||
$invoice->load('client', 'items');
|
||||
$invoiceSettings = InvoiceSettings::instance();
|
||||
|
||||
$pdf = Pdf::loadView('invoices.pdf-legacy', compact('invoice', 'invoiceSettings'));
|
||||
$pdf->setPaper('A4', 'portrait');
|
||||
|
||||
return $pdf->download("invoice-{$invoice->invoice_number}-legacy.pdf");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user