InvoicePlane:在 Invoice 中列出所有 Payment 交易
结论
问题在于传给模板的数据被截断了:
'payment' => (isset($payment[0]) ? $payment[0] : null),
get()->result() 返回对象数组,而 $payment[0] 只取了第一条记录。应把完整结果集传给模板,再通过 foreach 为每条记录生成表格行。
修改 pdf_helper.php
建议使用复数变量名,表明变量中包含多条付款记录:
$CI->load->model('quotes/mdl_quotes');
$quote = $CI->mdl_quotes
->where('ip_quotes.invoice_id', $invoice_id)
->get()
->result();
$CI->load->model('payments/mdl_payments');
$payments = $CI->mdl_payments
->where('ip_payments.invoice_id', $invoice_id)
->get()
->result();
$data = array(
'invoice' => $invoice,
'invoice_tax_rates' => $CI->mdl_invoice_tax_rates
->where('invoice_id', $invoice_id)
->get()
->result(),
'quote' => isset($quote[0]) ? $quote[0] : null,
'payments' => $payments,
'items' => $items,
'payment_method' => $payment_method,
'output_type' => 'pdf',
'show_discounts' => $show_discounts,
);
这里不要再对 $payments 取 [0]。即使没有付款记录,result() 通常也会返回空数组,模板可以直接通过 empty($payments) 判断。
如果需要按日期排列付款记录,可以在查询中加入排序:
$payments = $CI->mdl_payments
->where('ip_payments.invoice_id', $invoice_id)
->order_by('ip_payments.payment_date', 'asc')
->order_by('ip_payments.payment_id', 'asc')
->get()
->result();
第二个排序条件可以让同一天的多笔付款保持稳定顺序。前提是当前版本的付款表以 payment_id 为主键。如果字段名不同,需要根据实际数据库结构调整。
修改 Invoice PDF Template
在 application/views/invoice_templates/pdf/InvoicePlane.php 中使用 foreach 遍历 $payments:
<?php if (!empty($payments)) { ?>
<table class="item-table">
<thead>
<tr>
<th>Date of Payment</th>
<th>Payment Method</th>
<th>Amount Paid</th>
<th>Payment Journal</th>
</tr>
</thead>
<tbody>
<?php foreach ($payments as $payment) { ?>
<tr>
<td>
<?php echo htmlsc($payment->payment_date); ?>
</td>
<td>
<?php echo htmlsc($payment->payment_method_name); ?>
</td>
<td>
<?php echo htmlsc($payment->payment_amount); ?>
</td>
<td>
<?php echo htmlsc($payment->payment_note); ?>
</td>
</tr>
<?php } ?>
</tbody>
</table>
<?php } ?>
也就是把原来固定的一行:
<tr>
...
</tr>
放入下面的循环:
<?php foreach ($payments as $payment) { ?>
...
<?php } ?>
查询得到的每一笔 Payment 都会生成一个 <tr>。
保持原变量名的最小改法
如果不想把模板变量从 $payment 改成 $payments,也可以保留原名称,但传递的必须是完整数组:
'payment' => $payment,
模板可以写成:
<?php if (!empty($payment)) { ?>
<?php foreach ($payment as $payment_row) { ?>
<tr>
<td><?php echo htmlsc($payment_row->payment_date); ?></td>
<td><?php echo htmlsc($payment_row->payment_method_name); ?></td>
<td><?php echo htmlsc($payment_row->payment_amount); ?></td>
<td><?php echo htmlsc($payment_row->payment_note); ?></td>
</tr>
<?php } ?>
<?php } ?>
这种写法同样有效,不过用 $payments 表示集合、用 $payment 表示单条记录,会更便于长期维护。
日期和金额格式化
直接输出数据库值可以正常工作,但日期和金额的格式可能与 InvoicePlane 其他区域不一致。如果当前版本提供了相关辅助函数,可以沿用现有模板中的格式化方式。例如,InvoicePlane 1.x 模板中可能会使用:
<td><?php echo date_from_mysql($payment->payment_date); ?></td>
<td><?php echo format_currency($payment->payment_amount); ?></td>
完整示例如下:
<?php if (!empty($payments)) { ?>
<table class="item-table">
<thead>
<tr>
<th>Date of Payment</th>
<th>Payment Method</th>
<th>Amount Paid</th>
<th>Payment Journal</th>
</tr>
</thead>
<tbody>
<?php foreach ($payments as $payment) { ?>
<tr>
<td><?php echo date_from_mysql($payment->payment_date); ?></td>
<td><?php echo htmlsc($payment->payment_method_name); ?></td>
<td><?php echo format_currency($payment->payment_amount); ?></td>
<td><?php echo nl2br(htmlsc($payment->payment_note)); ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<?php } ?>
能否直接使用 date_from_mysql()、format_currency() 和 htmlsc(),取决于当前安装版本以及现有 PDF 模板已经加载的辅助函数。如果模板其他位置已经在使用这些函数,通常可以直接复用。
注意事项
payment_method_name通常依赖付款模型对 Payment Method 表的关联查询。当前第一笔记录既然已经能显示该字段,现有模型查询大概率包含了所需关联。如果修改后该字段为空,需要检查mdl_payments的查询和关联配置。- Payment Note 是用户输入内容,建议通过
htmlsc()或htmlspecialchars()转义,避免特殊字符破坏 PDF HTML。 - 没有付款记录时,最好隐藏整个表格,以免生成只有表头的空表。
- 系统自带的 PDF 模板和 helper 文件可能在升级时被覆盖。升级前应备份改动,升级后也要重新核对模型字段和模板接口。
- 不要同时保留原来读取单条
$payment的代码,否则模板仍可能把数组当作对象访问,引发Trying to get property of non-object或数组属性访问错误。
备注:内容仅供参考。
