Files
PowderCoatingLogix/src/PowderCoating.Web/Views/Reports/ProfitAndLoss.cshtml
T
2026-04-23 21:38:24 -04:00

217 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@model PowderCoating.Application.DTOs.Accounting.ProfitAndLossDto
@{
ViewData["Title"] = "Profit & Loss";
ViewData["PageIcon"] = "bi-graph-up-arrow";
var today = DateTime.Today;
var ytdFrom = new DateTime(today.Year, 1, 1).ToString("yyyy-MM-dd");
var ytdTo = today.ToString("yyyy-MM-dd");
var q1From = new DateTime(today.Year, 1, 1).ToString("yyyy-MM-dd");
var q1To = new DateTime(today.Year, 3, 31).ToString("yyyy-MM-dd");
var lastYrFrom = new DateTime(today.Year - 1, 1, 1).ToString("yyyy-MM-dd");
var lastYrTo = new DateTime(today.Year - 1, 12, 31).ToString("yyyy-MM-dd");
var thisMonthFrom = new DateTime(today.Year, today.Month, 1).ToString("yyyy-MM-dd");
var thisMonthTo = today.ToString("yyyy-MM-dd");
}
<style>
@@media print {
.no-print { display: none !important; }
.card { border: 1px solid #dee2e6 !important; box-shadow: none !important; }
body { font-size: 12px; }
}
.report-section-header { background: #f8f9fa; font-weight: 600; }
.report-total-row { border-top: 2px solid #dee2e6; font-weight: 700; }
.report-subtotal-row { border-top: 1px solid #dee2e6; font-weight: 600; }
.report-net-row { background: #e8f5e9; font-weight: 700; font-size: 1.05em; }
.report-net-negative { background: #fdecea; }
</style>
<!-- Header -->
<div class="d-flex align-items-center gap-2 mb-3 no-print">
<a asp-action="Index" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i></a>
<p class="text-muted mb-0">Income Statement — @Model.From.ToString("MMM d") @Model.To.ToString("MMM d, yyyy")</p>
<div class="ms-auto d-flex gap-2">
<a href="@Url.Action("ProfitAndLossPdf", new { from = Model.From.ToString("yyyy-MM-dd"), to = Model.To.ToString("yyyy-MM-dd") })"
class="btn btn-sm btn-outline-danger no-print" target="_blank">
<i class="bi bi-file-pdf me-1"></i>Download PDF
</a>
<a href="@Url.Action("ProfitAndLossPdf", new { from = Model.From.ToString("yyyy-MM-dd"), to = Model.To.ToString("yyyy-MM-dd"), inline = true })"
class="btn btn-sm btn-outline-secondary no-print" target="_blank">
<i class="bi bi-printer me-1"></i>Print
</a>
</div>
</div>
<!-- Date filter -->
<div class="card shadow-sm mb-4 no-print">
<div class="card-body py-3">
<form method="get" class="row g-2 align-items-end">
<div class="col-auto">
<label class="form-label form-label-sm mb-1">From</label>
<input type="date" name="from" class="form-control form-control-sm" value="@Model.From.ToString("yyyy-MM-dd")" />
</div>
<div class="col-auto">
<label class="form-label form-label-sm mb-1">To</label>
<input type="date" name="to" class="form-control form-control-sm" value="@Model.To.ToString("yyyy-MM-dd")" />
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-funnel me-1"></i>Run Report</button>
</div>
<div class="col-auto ms-2">
<div class="btn-group btn-group-sm">
<a href="@Url.Action("ProfitAndLoss", new { from = thisMonthFrom, to = thisMonthTo })" class="btn btn-outline-secondary">This Month</a>
<a href="@Url.Action("ProfitAndLoss", new { from = ytdFrom, to = ytdTo })" class="btn btn-outline-secondary">YTD</a>
<a href="@Url.Action("ProfitAndLoss", new { from = lastYrFrom, to = lastYrTo })" class="btn btn-outline-secondary">Last Year</a>
</div>
</div>
</form>
</div>
</div>
<!-- Print header -->
<div class="text-center mb-4 d-none d-print-block">
<h4 class="fw-bold">@Model.CompanyName</h4>
<h5>Profit &amp; Loss</h5>
<p class="text-muted">@Model.From.ToString("MMMM d, yyyy") @Model.To.ToString("MMMM d, yyyy")</p>
</div>
<!-- KPI Summary -->
<div class="row g-3 mb-4 no-print">
<div class="col-6 col-md-3">
<div class="card shadow-sm text-center h-100">
<div class="card-body py-3">
<div class="h5 text-success mb-1">@Model.TotalRevenue.ToString("C")</div>
<div class="text-muted small">Total Revenue</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card shadow-sm text-center h-100">
<div class="card-body py-3">
<div class="h5 text-warning mb-1">@Model.TotalCogs.ToString("C")</div>
<div class="text-muted small">Cost of Goods</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card shadow-sm text-center h-100">
<div class="card-body py-3">
<div class="h5 text-danger mb-1">@Model.TotalExpenses.ToString("C")</div>
<div class="text-muted small">Operating Expenses</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card shadow-sm text-center h-100 @(Model.NetIncome >= 0 ? "border-success border-opacity-50" : "border-danger border-opacity-50")">
<div class="card-body py-3">
<div class="h5 fw-bold @(Model.NetIncome >= 0 ? "text-success" : "text-danger") mb-1">@Model.NetIncome.ToString("C")</div>
<div class="text-muted small">Net Income</div>
</div>
</div>
</div>
</div>
<!-- P&L Statement -->
<div class="card shadow-sm">
<div class="card-header d-flex justify-content-between align-items-center">
<span class="fw-semibold"><i class="bi bi-file-earmark-bar-graph me-1"></i>Income Statement</span>
<span class="text-muted small">@Model.From.ToString("MMM d") @Model.To.ToString("MMM d, yyyy")</span>
</div>
<div class="table-responsive">
<table class="table table-sm align-middle mb-0">
<thead class="table-light">
<tr>
<th>Account</th>
<th class="text-end" style="width:160px">Amount</th>
<th class="text-end" style="width:120px">% of Revenue</th>
</tr>
</thead>
<tbody>
<!-- Revenue -->
<tr class="report-section-header">
<td colspan="3" class="py-2"><i class="bi bi-graph-up-arrow text-success me-2"></i>Revenue</td>
</tr>
@if (!Model.RevenueLines.Any())
{
<tr><td colspan="3" class="text-muted ps-4 small">No revenue recorded for this period.</td></tr>
}
@foreach (var line in Model.RevenueLines)
{
<tr>
<td class="ps-4">@line.AccountNumber <span class="text-muted">@line.AccountName</span></td>
<td class="text-end">@line.Amount.ToString("C")</td>
<td class="text-end text-muted small">@(Model.TotalRevenue == 0 ? "—" : (line.Amount / Model.TotalRevenue * 100).ToString("F1") + "%")</td>
</tr>
}
<tr class="report-subtotal-row">
<td class="ps-4 fw-semibold">Total Revenue</td>
<td class="text-end fw-semibold text-success">@Model.TotalRevenue.ToString("C")</td>
<td class="text-end text-muted small">100%</td>
</tr>
<!-- COGS -->
@if (Model.CogsLines.Any())
{
<tr><td colspan="3" class="py-1"></td></tr>
<tr class="report-section-header">
<td colspan="3" class="py-2"><i class="bi bi-box-seam text-warning me-2"></i>Cost of Goods Sold</td>
</tr>
@foreach (var line in Model.CogsLines)
{
<tr>
<td class="ps-4">@line.AccountNumber <span class="text-muted">@line.AccountName</span></td>
<td class="text-end">@line.Amount.ToString("C")</td>
<td class="text-end text-muted small">@(Model.TotalRevenue == 0 ? "—" : (line.Amount / Model.TotalRevenue * 100).ToString("F1") + "%")</td>
</tr>
}
<tr class="report-subtotal-row">
<td class="ps-4 fw-semibold">Total COGS</td>
<td class="text-end fw-semibold text-warning">(@Model.TotalCogs.ToString("C"))</td>
<td class="text-end text-muted small">@(Model.TotalRevenue == 0 ? "—" : (Model.TotalCogs / Model.TotalRevenue * 100).ToString("F1") + "%")</td>
</tr>
<tr class="report-subtotal-row">
<td class="ps-2 fw-semibold">Gross Profit</td>
<td class="text-end fw-semibold @(Model.GrossProfit >= 0 ? "text-success" : "text-danger")">@Model.GrossProfit.ToString("C")</td>
<td class="text-end text-muted small">@(Model.TotalRevenue == 0 ? "—" : Model.GrossMarginPercent.ToString("F1") + "%")</td>
</tr>
}
<!-- Expenses -->
<tr><td colspan="3" class="py-1"></td></tr>
<tr class="report-section-header">
<td colspan="3" class="py-2"><i class="bi bi-receipt-cutoff text-danger me-2"></i>Operating Expenses</td>
</tr>
@if (!Model.ExpenseLines.Any())
{
<tr><td colspan="3" class="text-muted ps-4 small">No expenses recorded for this period.</td></tr>
}
@foreach (var line in Model.ExpenseLines)
{
<tr>
<td class="ps-4">@line.AccountNumber <span class="text-muted">@line.AccountName</span></td>
<td class="text-end">@line.Amount.ToString("C")</td>
<td class="text-end text-muted small">@(Model.TotalRevenue == 0 ? "—" : (line.Amount / Model.TotalRevenue * 100).ToString("F1") + "%")</td>
</tr>
}
<tr class="report-subtotal-row">
<td class="ps-4 fw-semibold">Total Expenses</td>
<td class="text-end fw-semibold text-danger">(@Model.TotalExpenses.ToString("C"))</td>
<td class="text-end text-muted small">@(Model.TotalRevenue == 0 ? "—" : (Model.TotalExpenses / Model.TotalRevenue * 100).ToString("F1") + "%")</td>
</tr>
</tbody>
<tfoot>
<tr class="report-net-row @(Model.NetIncome < 0 ? "report-net-negative" : "")">
<td class="ps-2">Net Income</td>
<td class="text-end @(Model.NetIncome >= 0 ? "text-success" : "text-danger")">@Model.NetIncome.ToString("C")</td>
<td class="text-end text-muted small">@(Model.TotalRevenue == 0 ? "—" : (Model.NetIncome / Model.TotalRevenue * 100).ToString("F1") + "%")</td>
</tr>
</tfoot>
</table>
</div>
</div>
<div class="text-muted small mt-2 no-print">
<i class="bi bi-info-circle me-1"></i>
Generated @DateTime.Now.ToString("MMM d, yyyy h:mm tt") · Accrual basis · Revenue from sent/paid invoices; expenses from bills and direct expenses.
</div>