buildPdfTotalsBlock function

Widget buildPdfTotalsBlock({
  1. required List<InvoiceItem> items,
  2. required InvoiceStyle style,
  3. InvoiceInfo? info,
  4. List<InvoiceAdjustment> adjustments = const [],
  5. double amountPaid = 0,
  6. bool taxInclusive = false,
  7. MonetaryPolicy? monetaryPolicy,
  8. InvoiceTotals? totals,
})

Builds the financial summary from the centralized calculation engine.

Implementation

pw.Widget buildPdfTotalsBlock({
  required List<InvoiceItem> items,
  required InvoiceStyle style,
  InvoiceInfo? info,
  List<InvoiceAdjustment> adjustments = const [],
  double amountPaid = 0,
  bool taxInclusive = false,
  MonetaryPolicy? monetaryPolicy,
  InvoiceTotals? totals,
}) {
  final policy = monetaryPolicy ?? style.monetaryPolicy;
  final resolvedTotals =
      totals ??
      InvoiceCalculator(policy: policy, taxInclusive: taxInclusive).calculate(
        items: items,
        adjustments: adjustments,
        amountPaid: amountPaid,
      );
  final formatter = info != null
      ? style.currencyFormatter(info)
      : const CurrencyFormatter();

  pw.Widget buildRow(String label, double value, {bool isTotal = false}) {
    final textStyle = isTotal
        ? style.boldBodyTextStyle.copyWith(fontSize: style.subtitleFontSize)
        : style.bodyTextStyle;
    return pw.Padding(
      padding: const pw.EdgeInsets.symmetric(vertical: 4),
      child: pw.Row(
        mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
        children: [
          pw.Expanded(
            child: pw.Text(
              label,
              style: isTotal
                  ? textStyle
                  : textStyle.copyWith(color: style.mutedTextColor),
            ),
          ),
          pw.SizedBox(width: 12),
          pw.Text(formatter.format(value), style: textStyle),
        ],
      ),
    );
  }

  return pw.Container(
    width: 270,
    alignment: pw.Alignment.centerRight,
    child: pw.Column(
      crossAxisAlignment: pw.CrossAxisAlignment.stretch,
      children: [
        buildRow(style.label('subtotal', 'Subtotal'), resolvedTotals.subtotal),
        if (resolvedTotals.discount != 0)
          buildRow(
            style.label('discount', 'Discount'),
            -resolvedTotals.discount.abs(),
          ),
        if (resolvedTotals.tax != 0)
          buildRow(style.label('tax', 'Tax'), resolvedTotals.tax),
        for (final entry in resolvedTotals.adjustments)
          buildRow(entry.adjustment.label, switch (entry.adjustment.type) {
            InvoiceAdjustmentType.discount ||
            InvoiceAdjustmentType.withholding => -entry.amount.abs(),
            InvoiceAdjustmentType.shipping ||
            InvoiceAdjustmentType.fee => entry.amount,
          }),
        pw.Divider(color: PdfColors.grey300, thickness: 0.5),
        buildRow(
          style.label('total', 'Total'),
          resolvedTotals.grandTotal,
          isTotal: true,
        ),
        if (resolvedTotals.amountPaid != 0)
          buildRow(
            style.label('amountPaid', 'Amount Paid'),
            -resolvedTotals.amountPaid.abs(),
          ),
        if (resolvedTotals.amountPaid != 0)
          buildRow(
            style.label('balanceDue', 'Balance Due'),
            resolvedTotals.balanceDue,
            isTotal: true,
          ),
      ],
    ),
  );
}