由于现在已经有了Journal
类,现在可以给Contract
添加一些方法和判断,然后修改合同的详情页和展示列表,让合同对象把所属明细记录都汇总起来,进行一些展示和判断。
Journal
类列表
由于目前Journal
类是挂在合同下边,所以根据合同列出对应的明细记录是我要要继续编写的功能,控制器比较简单:
// 明细记录列表
@GetMapping("/contract/{cid}/list")
public String journalList(@PathVariable("cid") int cid, Model model) {
Contract contract = contractService.getContractById(cid);
Set<Budget> budgets = contract.getBudgets();
Budget budget = (Budget) (budgets.toArray()[0]);
Project project = budget.getProject();
Set<Journal> journals = contract.getJournals();
model.addAttribute("project", project);
model.addAttribute("contract", contract);
model.addAttribute("journals", journals);
model.addAttribute("count", journals.size());
model.addAttribute("contractData", contractService.getContractData(cid));
return "pms/journal/journalList";
}
页面也不复杂,如下:
<main class="container">
<nav style="--bs-breadcrumb-divider: '>';" aria-label="breadcrumb" class="mt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list" th:href="@{/pms/project/list}"
class="link-secondary">项目管理</a></li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${project.absoluteUrl()}"
class="link-secondary">[[${project.projectName}]]</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${project.contractListUrl()}"
class="link-secondary">合同列表</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${contract.absoluteUrl()}"
class="link-secondary">[[${contract.name}]]</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${contract.journalListUrl()}"
class="link-secondary">明细记录列表</a>
</li>
</ol>
</nav>
<div class="row py-1">
<div class="col-sm-4"></div>
<div th:if="${created}" class="text-center alert alert-success col-12 col-sm-4" role="alert">
[[${created}]]
</div>
<div th:if="${updated}" class="text-center alert alert-success col-12 col-sm-4" role="alert">
[[${updated}]]
</div>
<div th:if="${deleted}" class="text-center alert alert-success col-12 col-sm-4" role="alert">
[[${deleted}]]
</div>
<div class="col-sm-4"></div>
</div>
<p>
<a class="btn btn-primary" href="#" th:href="${contract.addJournalUrl()}"><i class="fas fa-plus"></i> 新增明细记录</a>
</p>
<table class="table table-hover table-responsive" th:if="${count != 0}">
<thead>
<tr>
<th>序号</th>
<th>结算说明</th>
<th>凭证号</th>
<th>净现金流</th>
<th>确认收入</th>
<th>履约成本</th>
</tr>
</thead>
<tbody>
<tr th:each="journal, control:${journals}">
<td th:text="${control.count}"></td>
<td><a href="#" th:href="${journal.absoluteUrl()}" th:text="${journal.getDescription()}"></a></td>
<td th:text="${journal.getNoteDate().getYear() +'-'+ journal.getNoteDate().getMonth() + '-' + #numbers.formatDecimal(journal.getNoteNumber(),4,0,'POINT')}"></td>
<td th:text="${#numbers.formatDecimal(journal.getCash(),1,'COMMA',2,'POINT')}"></td>
<td th:text="${#numbers.formatDecimal(journal.getRevenuePL(),1,'COMMA',2,'POINT')}"></td>
<td><span th:text="${#numbers.formatDecimal(journal.getContractCost(),1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td class="text-center" th:text="合计" colspan="3"></td>
<td class="text-primary"
th:text="${#numbers.formatDecimal(contractData.totalCash,1,'COMMA',2,'POINT')}"></td>
<td class="text-primary"
th:text="${#numbers.formatDecimal(contractData.totalRevenue,1,'COMMA',2,'POINT')}"></td>
<td class="text-primary"
th:text="${#numbers.formatDecimal(contractData.totalContractCost,1,'COMMA',2,'POINT')}"></td>
</tr>
</tbody>
</table>
</main>
对于每个合同,我认为比较重要的是货币资金、合同履约成本、确认收入、应付账款这四个项目,在其明细记录列表中展示前三个,但是对于合同来说需要汇总合同下所有明细记录的合计。接下来就来修改这方面的内容。
修改Contract
类
为Contract
类添加一些新的方法,用于返回其汇总的结果:
public BigDecimal totalCash() {
BigDecimal result = new BigDecimal("0.00");
for (Journal journal : journals) {
result = result.add(journal.getCash());
}
return result;
}
public BigDecimal totalRevenue() {
BigDecimal result = new BigDecimal("0.00");
for (Journal journal : journals) {
result = result.add(journal.getRevenuePL());
}
return result;
}
public BigDecimal totalContractCost() {
BigDecimal result = new BigDecimal("0.00");
for (Journal journal : journals) {
result = result.add(journal.getContractCost());
}
return result;
}
public BigDecimal totalAccountPayable() {
BigDecimal result = new BigDecimal("0.00");
for (Journal journal : journals) {
result = result.add(journal.getAccountPayable());
}
return result;
}
// 判断是否超过合同价
public boolean isOverPaid() {
// 如果是开口合同,就返回false
if (isOpen) {
return false;
}
if (finalPrice == null) {
return totalPrice.compareTo(totalCash().abs()) < 0;
} else {
return finalPrice.compareTo(totalCash().abs()) < 0;
}
}
前边四个方法用于计算合同所有合并的现金流,收入,履约成本和应付账款的余额,未来也会用于在合同列表页中展示。
最后一个方法是用来判断合同是否超付,其判断原理是如果合同为开口合同,则永远不会出现超付。如果还没有结算价,则判断总价和付款的绝对值大小,如果总价小,那就属于超付或者超出了合同价的收款。如果有结算价,则不使用合同价,而使用结算价来进行判断。
修改ContractData
在ContractData
中,需要添加与Journal
类相关的内容,如下:
// 以下是明细记录合计
// 现金收付
private BigDecimal totalCash= new BigDecimal("0.00");
// 合同履约成本
private BigDecimal totalContractCost = new BigDecimal("0.00");
// 累计收入
private BigDecimal totalRevenue = new BigDecimal("0.00");
// 累计应付账款
private BigDecimal totalAccountPayable = new BigDecimal("0.00");
public BigDecimal getTotalCash() {
return totalCash;
}
public void setTotalCash(BigDecimal totalCash) {
this.totalCash = totalCash;
}
public BigDecimal getTotalContractCost() {
return totalContractCost;
}
public void setTotalContractCost(BigDecimal totalContractCost) {
this.totalContractCost = totalContractCost;
}
public BigDecimal getTotalRevenue() {
return totalRevenue;
}
public void setTotalRevenue(BigDecimal totalRevenue) {
this.totalRevenue = totalRevenue;
}
public BigDecimal getTotalAccountPayable() {
return totalAccountPayable;
}
public void setTotalAccountPayable(BigDecimal totalAccountPayable) {
this.totalAccountPayable = totalAccountPayable;
}
// 收款与结算不符
public boolean isPotentialReceiveError() {
System.out.println(totalReceived);
System.out.println(contract.totalCash());
if (receiptSettlements.size() == 0) {
return false;
}
return !totalReceived.equals(contract.totalCash());
}
// 付款与结算不符
public boolean isPotentialPaidError() {
if (paymentSettlements.size() == 0) {
return false;
}
return !totalPaid.equals(contract.totalCash().abs());
}
前边几个比较简单,就是计算几个合计,最后两个方法是判断如果存在收款结算或者付款结算记录,会比较一下相关的金额是否相等,如果不相等,就会显示一个警告标志,提示用户进行检查。由于这些不一定是硬性错误,所以不在合同列表页进行提示,仅在合同详情页内进行提示。
修改ContractService
上边两个类都准备好之后,最后就可以来修改ContractService
,组装新的ContractData
:
contractData.setJournals(contract.getJournals());
// 以下是明细记录部分
contract.getJournals().forEach(journal -> {
contractData.setTotalCash(contractData.getTotalCash().add(journal.getCash()));
});
contract.getJournals().forEach(journal -> {
contractData.setTotalContractCost(contractData.getTotalContractCost().add(journal.getContractCost()));
});
contract.getJournals().forEach(journal -> {
contractData.setTotalRevenue(contractData.getTotalRevenue().add(journal.getRevenuePL()));
});
contract.getJournals().forEach(journal -> {
contractData.setTotalAccountPayable(contractData.getTotalAccountPayable().add(journal.getAccountPayable()));
});
这样就添加了新的信息,在合同详情页就可以使用这个新的ContractData
对象了。
修改页面
需要修改的页面包括合同的详情页,添加一个新的表:
<h2 th:if="${contractData.getJournals().size()!=0}" class="mt-2">合同执行情况</h2>
<table th:if="${contractData.getJournals().size()!=0}" class="table table-sm table-striped"
th:object="${contractData}">
<thead>
<tr>
<th>属性</th>
<th>金额</th>
</tr>
</thead>
<tbody>
<tr>
<td>现金流</td>
<td th:text="${#numbers.formatDecimal(contractData.totalCash,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>累计确认收入</td>
<td th:text="${#numbers.formatDecimal(contractData.totalRevenue,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>累计履约成本</td>
<td th:text="${#numbers.formatDecimal(contractData.totalContractCost,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>应付账款余额</td>
<td th:text="${#numbers.formatDecimal(contractData.totalAccountPayable,1,'COMMA',2,'POINT')}"></td>
</tr>
</tbody>
</table>
这个表就是展示刚刚编写的合计数,之后还需要在项目合同和预算合同列表,添加累计收付款和应付账款余额两列:
<table class="table table-hover" th:if="${count != 0}">
<thead>
<tr>
<th>序号</th>
<th>合同编号</th>
<th>合同名称</th>
<th>合同总价</th>
<th>累计收付款</th>
<th>应付账款</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="contract, control:${contracts}">
<td th:text="${control.count}"></td>
<td th:text="${contract.number}"></td>
<td><a href="" th:href="${contract.absoluteUrl()}">[[${contract.name}]]</a></td>
<td th:text="${#numbers.formatDecimal(contract.totalPrice,1,'COMMA',2,'POINT')}"></td>
<td >[[${#numbers.formatDecimal(contract.totalCash(),1,'COMMA',2,'POINT')}]] <span th:if="${contract.isOverPaid()}"><i class="fas fa-exclamation-triangle text-danger"></i></span></td>
<td th:text="${#numbers.formatDecimal(contract.totalAccountPayable(),1,'COMMA',2,'POINT')}"></td>
<td>
<a href="#" th:href="${contract.addJournalUrl()}">新增明细</a>
</td>
</tr>
</tbody>
</table>
由于Journal
类还有一个外键连接到预算,所以很显然,对于Budget
类也需要添加类似的方法,对于一个预算类,我实际上只需要知道合同履约成本,和资金支付就可以了。这里代码就不再具体放了。
接下来,就把Journal
类的修改和删除补完,然后来补上最后一块,就是项目汇总数据。