Fms-Java版开发实录:30 Journal类删改及小补充

Fms-Java版开发实录:30 Journal类删改及小补充

Journal类的增删改查写完了,接近完工了。

这一次就一鼓作气把Journal类相关的内容全部写完。

展示日期的修改

由于JavaDate对象,其中的getYear()等方法都被删除了,所以在展示凭证号的时候,就要使用Thymeleaf提供的函数了,如下:

<td th:text="${#dates.format(journal.getNoteDate(), 'yyyy')  +'-'+ #dates.format(journal.getNoteDate(), 'MM')  + '-' + #numbers.formatDecimal(journal.getNoteNumber(),4,0,'POINT')}"></td>

修改Journal类的功能

这个也是套路了,表单可以复用,只要把其他的页面修改一下就可以了,只要记得给journal类加上一个生成自己的编辑URL的方法就行了:

    public String editUrl() {
        int cid = contract.getId();
        return "/pms/journal/" + cid + "/edit/" + id;
    }

之后就来编写控制器。

控制器

控制器也是套路了:

    // 显示编辑页面
    @GetMapping("/{cid}/edit/{id}")
    public String editJournalPage(@PathVariable("cid") int cid,
                                  @PathVariable("id") int jid, Model model) {
        Contract contract = contractService.getContractById(cid);
        Set<Budget> budgets = contract.getBudgets();
        Budget budget = (Budget) (budgets.toArray()[0]);
        Project project = budget.getProject();
        Journal journal = journalService.findById(jid);

        model.addAttribute("project", project);
        model.addAttribute("budgets", budgets);
        model.addAttribute("contract", contract);
        model.addAttribute("journal", journal);

        return "pms/journal/journalEdit";
    }

    // 接受编辑修改
    @PostMapping("/{cid}/edit/{id}")
    public String editJournal(
            @Valid @ModelAttribute("journal") Journal journal,
            BindingResult rs, @PathVariable("cid") int cid,
            @PathVariable("id") int jid, Model model,
            RedirectAttributes attributes) {
        Contract contract = contractService.getContractById(cid);
        journal.setContract(contract);
        Set<Budget> budgets = contract.getBudgets();
        Budget budget = (Budget) (budgets.toArray()[0]);
        Project project = budget.getProject();
        model.addAttribute("project", project);
        model.addAttribute("contract", contract);
        model.addAttribute("budgets", budgets);

        if (rs.hasErrors()) {
            return "pms/journal/journalEdit";
        }

        if (!journal.isValid()) {
            model.addAttribute("numberError", "借贷方不平,请检查数字。");
            return "pms/journal/journalEdit";
        }

        journal.setContract(contract);
        journal.setProject_id(project.getId());
        journalService.save(journal);
        attributes.addFlashAttribute("updated", "成功修改明细记录");
        return "redirect:" + journal.absoluteUrl();
    }

接受编辑修改的控制器其实和添加的控制器非常相似。然后是页面,其实也就是把添加页面拿过来修改一下:

<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="${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>
            <li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
                                                               th:href="${journal.editUrl()}"
                                                               class="link-secondary">编辑明细记录</a>
            </li>
        </ol>
    </nav>
    <div class="py-1 text-center">
        <h2>编辑明细记录</h2>

    </div>
    <div class="row py-1">
        <div class="col-sm-4"></div>
        <div th:if="${numberError}" class="text-center alert alert-danger col-12 col-sm-4" role="alert">
            [[${numberError}]]
        </div>
        <div class="col-sm-4"></div>
    </div>
    <div class="row g-5">
        <div class="col-2"></div>
        <div class="col-md-8 col-lg-8">
            <p class="text-secondary">不需填写的数值请保留0.00字样,不要删除</p>
            <form class="needs-validation" action="/pms/journal/add" th:action="${journal.editUrl()}" method="post"
                  th:object="${journal}">
                <div class="row g-3 mb-3">
                    <h4 class="mb-1">基础信息</h4>

                    <div class="col-sm-6">
                        <p class="mb-0">所属预算条目</p>
                        <select class="form-select mt-2" aria-label="budget" name="budget" th:field="*{budget}">
                            <option th:each="budget, control:${budgets}" th:value="${budget.getId()}" >
                                [[${budget.budgetName}]]
                            </option>
                        </select>
                    </div>
                    
                    <div class="col-sm-6">
                        <label for="description" class="form-label">说明</label>
                        <input type="text" class="form-control" id="description" th:field="*{description}"
                               th:classappend="${#fields.hasErrors('description')}? 'is-invalid'" required>
                        <div id="descriptionError" th:if="${#fields.hasErrors('description')}"
                             th:errors="*{description}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-6">
                        <label for="noteDate" class="form-label">凭证日期</label>
                        <input type="date" class="form-control" id="noteDate" th:field="*{noteDate}"
                               th:classappend="${#fields.hasErrors('noteDate')}? 'is-invalid'" required>
                        <div id="noteDateError" th:if="${#fields.hasErrors('noteDate')}"
                             th:errors="*{noteDate}"
                             class="invalid-feedback"></div>
                    </div>
                    
                    <div class="col-sm-6">
                        <label for="noteNumber" class="form-label">凭证号</label>
                        <input type="number" step="1" class="form-control" id="noteNumber" th:field="*{noteNumber}"
                               th:classappend="${#fields.hasErrors('noteNumber')}? 'is-invalid'" required>
                        <div id="noteNumberError" th:if="${#fields.hasErrors('noteNumber')}"
                             th:errors="*{noteNumber}"
                             class="invalid-feedback"></div>
                    </div>
                    <hr>
                    <h4 class="mb-1">损益</h4>

                    <div class="col-sm-6">
                        <label for="revenuePL" class="form-label">营业收入</label>
                        <input type="number" step="0.01" class="form-control" id="revenuePL" th:field="*{revenuePL}"
                               th:classappend="${#fields.hasErrors('revenuePL')}? 'is-invalid'" required>
                        <div id="revenuePLError" th:if="${#fields.hasErrors('revenuePL')}"
                             th:errors="*{revenuePL}"
                             class="invalid-feedback"></div>
                    </div>
                    <div class="col-sm-6">
                        <label for="costPL" class="form-label">营业成本</label>
                        <input type="number" step="0.01" class="form-control" id="costPL" th:field="*{costPL}"
                               th:classappend="${#fields.hasErrors('costPL')}? 'is-invalid'" required>
                        <div id="costPLError" th:if="${#fields.hasErrors('costPL')}"
                             th:errors="*{costPL}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-sm-6">
                        <label for="otherPL" class="form-label">其他损益</label>
                        <input type="number" step="0.01" class="form-control" id="otherPL" th:field="*{otherPL}"
                               th:classappend="${#fields.hasErrors('otherPL')}? 'is-invalid'" required>
                        <div id="otherPLError" th:if="${#fields.hasErrors('otherPL')}"
                             th:errors="*{otherPL}"
                             class="invalid-feedback"></div>
                    </div>
               
                    <hr>
                    <h4 class="mb-1">资产</h4>
                    <div class="col-sm-6">
                        <label for="cash" class="form-label">货币资金</label>
                        <input type="number" step="0.01" class="form-control" id="cash" th:field="*{cash}"
                               th:classappend="${#fields.hasErrors('cash')}? 'is-invalid'" required>
                        <div id="cashError" th:if="${#fields.hasErrors('cash')}"
                             th:errors="*{cash}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-sm-6">
                        <label for="contractAsset" class="form-label">合同资产</label>
                        <input type="number" step="0.01" class="form-control" id="contractAsset" th:field="*{contractAsset}"
                               th:classappend="${#fields.hasErrors('contractAsset')}? 'is-invalid'" required>
                        <div id="contractAssetError" th:if="${#fields.hasErrors('contractAsset')}"
                             th:errors="*{contractAsset}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-sm-6">
                        <label for="contractCost" class="form-label">合同履约成本</label>
                        <input type="number" step="0.01" class="form-control" id="contractCost" th:field="*{contractCost}"
                               th:classappend="${#fields.hasErrors('contractCost')}? 'is-invalid'" required>
                        <div id="contractCostError" th:if="${#fields.hasErrors('contractCost')}"
                             th:errors="*{contractCost}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-sm-6">
                        <label for="contractAcquireCost" class="form-label">合同取得成本</label>
                        <input type="number" step="0.01" class="form-control" id="contractAcquireCost" th:field="*{contractAcquireCost}"
                               th:classappend="${#fields.hasErrors('contractAcquireCost')}? 'is-invalid'" required>
                        <div id="contractAcquireCostError" th:if="${#fields.hasErrors('contractAcquireCost')}"
                             th:errors="*{contractAcquireCost}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-sm-6">
                        <label for="accountReceivable" class="form-label">应收账款</label>
                        <input type="number" step="0.01" class="form-control" id="accountReceivable" th:field="*{accountReceivable}"
                               th:classappend="${#fields.hasErrors('accountReceivable')}? 'is-invalid'" required>
                        <div id="accountReceivableError" th:if="${#fields.hasErrors('accountReceivable')}"
                             th:errors="*{accountReceivable}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-sm-6">
                        <label for="prepaid" class="form-label">预付账款</label>
                        <input type="number" step="0.01" class="form-control" id="prepaid" th:field="*{prepaid}"
                               th:classappend="${#fields.hasErrors('prepaid')}? 'is-invalid'" required>
                        <div id="prepaidError" th:if="${#fields.hasErrors('prepaid')}"
                             th:errors="*{prepaid}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-sm-6">
                        <label for="inputVAT" class="form-label">进项税金</label>
                        <input type="number" step="0.01" class="form-control" id="inputVAT" th:field="*{inputVAT}"
                               th:classappend="${#fields.hasErrors('inputVAT')}? 'is-invalid'" required>
                        <div id="inputVATError" th:if="${#fields.hasErrors('inputVAT')}"
                             th:errors="*{inputVAT}"
                             class="invalid-feedback"></div>
                    </div>

                    <div class="col-sm-6">
                        <label for="otherAsset" class="form-label">其他资产</label>
                        <input type="number" step="0.01" class="form-control" id="otherAsset" th:field="*{otherAsset}"
                               th:classappend="${#fields.hasErrors('otherAsset')}? 'is-invalid'" required>
                        <div id="otherAssetError" th:if="${#fields.hasErrors('otherAsset')}"
                             th:errors="*{otherAsset}"
                             class="invalid-feedback"></div>
                    </div>

                    <hr>
                    <h4 class="mb-1">负债</h4>
                    <div class="col-sm-6">
                        <label for="contractLiability" class="form-label">合同负债</label>
                        <input type="number" step="0.01" class="form-control" id="contractLiability" th:field="*{contractLiability}"
                               th:classappend="${#fields.hasErrors('contractLiability')}? 'is-invalid'" required>
                        <div id="contractLiabilityError" th:if="${#fields.hasErrors('contractLiability')}"
                             th:errors="*{contractLiability}"
                             class="invalid-feedback"></div>
                    </div>
                    <div class="col-sm-6">
                        <label for="accountPayable" class="form-label">应付账款</label>
                        <input type="number" step="0.01" class="form-control" id="accountPayable" th:field="*{accountPayable}"
                               th:classappend="${#fields.hasErrors('accountPayable')}? 'is-invalid'" required>
                        <div id="accountPayableError" th:if="${#fields.hasErrors('accountPayable')}"
                             th:errors="*{accountPayable}"
                             class="invalid-feedback"></div>
                    </div>
                    <div class="col-sm-6">
                        <label for="outputVAT" class="form-label">销项税金</label>
                        <input type="number" step="0.01" class="form-control" id="outputVAT" th:field="*{outputVAT}"
                               th:classappend="${#fields.hasErrors('outputVAT')}? 'is-invalid'" required>
                        <div id="outputVATError" th:if="${#fields.hasErrors('outputVAT')}"
                             th:errors="*{outputVAT}"
                             class="invalid-feedback"></div>
                    </div>
                    <div class="col-sm-6">
                        <label for="otherLiability" class="form-label">其他负债</label>
                        <input type="number" step="0.01" class="form-control" id="otherLiability" th:field="*{otherLiability}"
                               th:classappend="${#fields.hasErrors('otherLiability')}? 'is-invalid'" required>
                        <div id="otherLiabilityError" th:if="${#fields.hasErrors('otherLiability')}"
                             th:errors="*{otherLiability}"
                             class="invalid-feedback"></div>
                    </div>
                </div>
                <div class="text-center mb-4">
                    <button class="btn btn-primary btn-lg" type="submit"><i class="fas fa-check"></i> 提交</button>
                    <a class="btn btn-secondary btn-lg" th:href="${journal.absoluteUrl()}" href="/pms/project/list"><i class="fas fa-undo-alt"></i> 返回</a>
                </div>
            </form>
        </div>
        <div class="col-2"></div>
    </div>

</main>

感觉这里有点纯粹灌水的感觉了,其实代码都差不多。

详情页与删除功能

这里就继续灌水,写到这里大家如果一路看下来,对于套路也都熟悉了,控制器如下:

    // 详情页面
    @GetMapping("/detail/{id}")
    public String detailPage(@PathVariable("id") int jid, Model model) {
        Journal journal = journalService.findById(jid);
        Contract contract = journal.getContract();
        Set<Budget> budgets = contract.getBudgets();
        Budget budget = (Budget) (budgets.toArray()[0]);
        Project project = budget.getProject();
        model.addAttribute("project", project);
        model.addAttribute("contract", contract);
        model.addAttribute("journal", journal);

        return "pms/journal/journalDetail";
    }

详情页面也是类似的,用一个表格来展示:

<main class="col-lg-8 mx-auto 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>
            <li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
                                                               th:href="${journal.absoluteUrl()}"
                                                               class="link-secondary">明细记录</a>
            </li>
        </ol>
    </nav>
    <h1 th:text="${journal.description}">明细记录</h1>
    <div class="row py-1" >
        <div class="col-sm-4"></div>
        <div th:if="${updated}" class="text-center alert alert-success col-12 col-sm-4" role="alert">
            [[${updated}]]
        </div>
        <div class="col-sm-4"></div>
    </div>
    <hr>

    <div class="row g-5">
        <div class="col-md-9">
            <table class="table table-sm table-striped" th:object="${journal}">
                <thead>
                <tr>
                    <th>属性</th>
                    <th>内容</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td>说明</td>
                    <td th:text="*{description}"></td>
                </tr>
                <tr>
                    <td>凭证号</td>
                    <td th:text="${#dates.format(journal.getNoteDate(), 'yyyy')  +'-'+ #dates.format(journal.getNoteDate(), 'MM')  + '-' + #numbers.formatDecimal(journal.getNoteNumber(),4,0,'POINT')}"></td>
                </tr>
                <tr>
                    <td>损益-营业收入</td>
                    <td th:text="${#numbers.formatDecimal(journal.revenuePL,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>损益-营业成本</td>
                    <td th:text="${#numbers.formatDecimal(journal.costPL,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>损益-其他</td>
                    <td th:text="${#numbers.formatDecimal(journal.otherPL,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>资产-货币资金</td>
                    <td th:text="${#numbers.formatDecimal(journal.cash,1,'COMMA',2,'POINT')}"></td>
                </tr>

                <tr>
                    <td>资产-合同资产</td>
                    <td th:text="${#numbers.formatDecimal(journal.contractAsset,1,'COMMA',2,'POINT')}"></td>
                </tr>

                <tr>
                    <td>资产-合同履约成本</td>
                    <td th:text="${#numbers.formatDecimal(journal.contractCost,1,'COMMA',2,'POINT')}"></td>
                </tr>

                <tr>
                    <td>资产-合同取得成本</td>
                    <td th:text="${#numbers.formatDecimal(journal.contractAcquireCost,1,'COMMA',2,'POINT')}"></td>
                </tr>

                <tr>
                    <td>资产-应收账款</td>
                    <td th:text="${#numbers.formatDecimal(journal.accountReceivable,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>资产-预付账款</td>
                    <td th:text="${#numbers.formatDecimal(journal.prepaid,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>资产-进项税金</td>
                    <td th:text="${#numbers.formatDecimal(journal.inputVAT,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>资产-其他资产</td>
                    <td th:text="${#numbers.formatDecimal(journal.otherAsset,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>负债-合同负债</td>
                    <td th:text="${#numbers.formatDecimal(journal.contractLiability,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>负债-应付账款</td>
                    <td th:text="${#numbers.formatDecimal(journal.accountPayable,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>负债-销项税金</td>
                    <td th:text="${#numbers.formatDecimal(journal.outputVAT,1,'COMMA',2,'POINT')}"></td>
                </tr>
                <tr>
                    <td>负债-其他负债</td>
                    <td th:text="${#numbers.formatDecimal(journal.otherLiability,1,'COMMA',2,'POINT')}"></td>
                </tr>
                </tbody>
            </table>
        </div>

        <div class="col-md-3">
            <h2>功能</h2>
            <ul class="list-group list-group-flush">
                <li class="list-group-item"><a href="/pms/project/detail" th:href="${journal.editUrl()}"><i
                        class="fas fa-pencil-alt"></i> 编辑本记录</a></li>
                <li class="list-group-item"><a href="#" class="link-danger" data-bs-toggle="modal"
                                               data-bs-target="#deleteModal"><i class="fas fa-trash-alt"></i> 删除本记录</a>
                </li>
            </ul>
        </div>
    </div>

    <div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="deleteLabel">删除付款结算记录</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    将删除[[${journal.description}]]。
                </div>
                <form class="modal-footer" method="post" action="/pms/project/delete" th:action="@{/pms/journal/delete}">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><i class="fas fa-undo"></i>
                        返回
                    </button>
                    <input type="hidden" name="deleteId" th:value="${journal.getId()}">
                    <input type="hidden" name="contract" th:value="${contract.id}">
                    <input type="hidden" name="name" th:value="${journal.getDescription()}">
                    <button type="submit" class="btn btn-danger"><i class="fas fa-info-circle"></i>
                        确定删除
                    </button>
                </form>
            </div>
        </div>
    </div>
</main>

这里因为已经把删除功能写好了,所以再添加一个删除控制器:

    // 删除Journal
    @PostMapping("/delete")
    public String deleteJournal(@RequestParam("deleteId")int jid,
                                @RequestParam("name")String name,
                                @RequestParam("contract")int cid,
                                RedirectAttributes attributes) {
        journalService.deleteJournal(jid);
        Contract contract = contractService.getContractById(cid);
        attributes.addFlashAttribute("deleted", "成功删除明细记录:" + name);

        return "redirect:" + contract.journalListUrl();
    }

之后还是老样子,在Spring Security中加入对应的权限保护:

.antMatchers("/pms/journal/delete").hasAnyAuthority("ADMIN", "SUPERUSER")

这样就完成了删除功能的编写。最后就是把各种图标调整一下。

之后就是最后的功能,就是编写BudgetProject类中用来展示其汇总的所有明细记录的数据了。

LICENSED UNDER CC BY-NC-SA 4.0
Comment