重构采购合同流程预算采购明细的数量记录,查询符合规定的项目

master
Harry Yang 2023-01-05 16:36:50 +08:00
parent 035a5f68bc
commit c09d100111
9 changed files with 191 additions and 131 deletions

View File

@ -41,6 +41,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
@ -442,9 +443,7 @@ public class ProcessController {
// 草稿模式也不允许为空 (基础数据)
Assert.notNull(amountForm.amount, "合同明细填写不完整");
Assert.notNull(amountForm.budgetCostId, "合同明细填写不完整");
// 已经采购的数量 要么是数据库存在的值要么是0 ,数据库获取已经采购数目
BigDecimal amountAlready = processService.getAmountAlready(amountForm.budgetCostId);
Assert.notNull(amountForm.amountCurrent, "合同明细填写不完整");
// 新建
BudgetPurchaseAmount purchaseAmount = new BudgetPurchaseAmount();
@ -453,20 +452,10 @@ public class ProcessController {
purchaseAmount.setAmount(amountForm.amount);
purchaseAmount.setBudgetCostId(amountForm.budgetCostId);
purchaseAmount.setAmountCurrent(amountForm.amountCurrent);
// 更新到数据库
purchaseAmount.setAmountAlready(amountAlready);
// TODO 提交模式才计算 剩余
if (form.getStatus() == ProcessStatus.to_be_audit) {
// 当前的必须填写
Assert.notNull(amountForm.amountCurrent, "合同明细填写不完整");
// 提交的时候 更新(计算)已经采购的数量 = 已经采购的数量 + 本次采购数量
amountAlready = amountAlready.add(amountForm.amountCurrent);
purchaseAmount.setAmountAlready(amountAlready); // 更新数据库
// amountForm.amount 和数据库的一致 剩余的未采购数量 = 总数 - 已经采购数量
BigDecimal amountLeft = amountForm.amount.subtract(amountAlready);
purchaseAmount.setAmountLeft(amountLeft);
// 提交时该次数量条目生效,撤回的时候 取消该状态
purchaseAmount.setSubmit(true);
}
entityManager.persist(purchaseAmount);
@ -556,13 +545,10 @@ public class ProcessController {
if (!CollectionUtils.isEmpty(form.purchaseAmount)) {
for (BudgetPurchaseAmountModel amountForm : form.purchaseAmount) {
// 草稿模式也不允许为空 (基础数据), 更新ID不能为空
Assert.notNull(amountForm.amountId, "合同明细填写不完整");
Assert.notNull(amountForm.amount, "合同明细填写不完整");
Assert.notNull(amountForm.amountId, "合同明细填写不完整");
Assert.notNull(amountForm.budgetCostId, "合同明细填写不完整");
// 已经采购的数量 要么是数据库存在的值要么是0 ,数据库获取已经采购数目
BigDecimal amountAlready = processService.getAmountAlready(amountForm.budgetCostId);
// 新建
BudgetPurchaseAmount purchaseAmount = new BudgetPurchaseAmount();
purchaseAmount.setId(amountForm.amountId);
@ -571,20 +557,12 @@ public class ProcessController {
purchaseAmount.setAmount(amountForm.amount);
purchaseAmount.setBudgetCostId(amountForm.budgetCostId);
purchaseAmount.setAmountCurrent(amountForm.amountCurrent);
// 更新到数据库
purchaseAmount.setAmountAlready(amountAlready);
// TODO 提交模式才计算 剩余
if (form.getStatus() == ProcessStatus.to_be_audit) {
// 当前的必须填写
Assert.notNull(amountForm.amountCurrent, "合同明细填写不完整");
// 提交的时候 更新(计算)已经采购的数量 = 已经采购的数量 + 本次采购数量
amountAlready = amountAlready.add(amountForm.amountCurrent);
purchaseAmount.setAmountAlready(amountAlready); // 更新数据库
// amountForm.amount 和数据库的一致 剩余的未采购数量 = 总数 - 已经采购数量
BigDecimal amountLeft = amountForm.amount.subtract(amountAlready);
purchaseAmount.setAmountLeft(amountLeft);
purchaseAmount.setSubmit(true);
}
entityManager.merge(purchaseAmount);
@ -733,13 +711,42 @@ public class ProcessController {
@ResponseBody
@DeleteMapping("/{id}")
@Transactional
public void delete(@PathVariable("id") int id) {
Admin admin = getLoginUser();
Integer applyUserId = admin.getId();
int update = jdbcTemplate.update("delete from project_process where id =? and apply_person_id=?", id, applyUserId);
if (update != 1) {
ProjectProcess process = obtainProjectProcess(id);
Integer applyUserId = getLoginUser().getId();
if (!Objects.equals(process.getApplyPersonId(), applyUserId)) {
throw ErrorMessageException.failed("删除的流程不存在或者不属于自己");
}
if (process.getProcessType() == ProcessType.sale_contract) {
Query query = entityManager.createQuery("delete from SaleContract where processId=:processId");
query.setParameter("processId", id);
query.executeUpdate();
}
else if (process.getProcessType() == ProcessType.procurement_contract) {
Query query = entityManager.createQuery("delete from ProcurementContract where processId=:processId");
query.setParameter("processId", id);
query.executeUpdate();
query = entityManager.createQuery("delete from SupplierMaterial where processId=:processId");
query.setParameter("processId", id);
query.executeUpdate();
query = entityManager.createQuery("delete from BudgetPurchaseAmount where processId=:processId");
query.setParameter("processId", id);
query.executeUpdate();
query = entityManager.createQuery("delete from BudgetPurchaseDetail where processId=:processId");
query.setParameter("processId", id);
query.executeUpdate();
}
Query query = entityManager.createQuery("delete from ProjectProcess where id=:processId");
query.setParameter("processId", id);
query.executeUpdate();
}
/**

View File

@ -10,6 +10,7 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import lombok.Data;
@ -17,6 +18,9 @@ import lombok.Data;
*
* <p>
* BudgetPurchaseAmount BudgetPurchaseDetail
* <p>
* budgetCostId amountAlready amountLeft ,
* amountCurrent
*
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
* @since 2.0 2022/12/28 14:15
@ -36,14 +40,11 @@ public class BudgetPurchaseAmount implements Serializable {
// 所有的
private BigDecimal amount;
// 已采购数量
private BigDecimal amountAlready;
// '本次采购数量'
private BigDecimal amountCurrent;
// 采购数量
private BigDecimal amountLeft;
// 该流程是否提交,提交了才计算到已采购数量
private Boolean submit;
// 流程ID
private Integer processId;
@ -54,4 +55,9 @@ public class BudgetPurchaseAmount implements Serializable {
// 成本ID
private Integer budgetCostId;
@Transient
public boolean isSubmitted() {
return submit != null && submit;
}
}

View File

@ -63,7 +63,7 @@ public class ProjectProcess implements Serializable {
// 申请部门 部门1,部门2,部门3 (逗号分割)
private String applyDept;
// 申请部门ID(逗号分割)
// 申请部门ID(逗号分割) find_in_set
private String applyDeptId;
// 申请部门领导ID

View File

@ -17,4 +17,5 @@ public class QueryProject {
private ProcessType processType;
private Integer creatorId;
}

View File

@ -5,7 +5,10 @@ import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
@ -16,13 +19,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import cn.palmte.work.ErrorMessageException;
import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.config.activiti.ActProjectTypeEnum;
import cn.palmte.work.model.Admin;
@ -30,7 +33,6 @@ import cn.palmte.work.model.AdminRepository;
import cn.palmte.work.model.Dept;
import cn.palmte.work.model.DeptRepository;
import cn.palmte.work.model.ProcurementType;
import cn.palmte.work.model.ProcurementTypeRepository;
import cn.palmte.work.model.Project;
import cn.palmte.work.model.ProjectBudgetCostDetail;
import cn.palmte.work.model.ProjectRepository;
@ -59,13 +61,12 @@ public class ProjectProcessService {
private final JdbcTemplate jdbcTemplate;
private final EntityManager entityManager;
private final ProjectInstanceService projectInstanceService;
private final ProjectRepository projectRepository;
private final ProjectService projectService;
private final ProjectInstanceService projectInstanceService;
private final DeptRepository deptRepository;
private final AdminRepository userRepository;
private final ProcurementTypeRepository procurementTypeRepository;
private final ProjectRepository projectRepository;
public List<Project> queryProjects(String q, ProcessType processType, Admin loginUser) {
List<Project> projects = Collections.emptyList();
@ -73,43 +74,40 @@ public class ProjectProcessService {
projects = projectRepository.findBudgetPassedProjects(q);
}
else if (processType == ProcessType.sale_contract) {
List<QueryProject> queryProjects = jdbcTemplate.query("select distinct p.id, p.name, p.project_no projectNo, pp.process_type processType " +
List<QueryProject> queryProjects = jdbcTemplate.query(
"select distinct p.id, p.name, p.project_no projectNo, pp.process_type processType,p.creator_id creatorId " +
" from project p left join project_process pp on p.id = pp.project_id " +
"where (p.`status` > 5 or (p.`status` = 5 and p.approve_status_budget = 2)) " +
" and (p.`project_no` like concat('%', ?, '%') or p.`name` like concat('%', ?, '%'))",
new Object[] { q, q },
BeanPropertyRowMapper.newInstance(QueryProject.class));
MultiValueMap<Integer, QueryProject> idMap = new LinkedMultiValueMap<>();
for (QueryProject queryProject : queryProjects) {
idMap.computeIfAbsent(queryProject.getId(), s -> new ArrayList<>())
.add(queryProject);
}
projects = queryProjects.stream().filter(queryProject -> {
ProcessType processTypeToTest = queryProject.getProcessType();
if (processTypeToTest == null) {
return true;
}
else {
// 如果已经存在销售合同,就排除
if (processTypeToTest == ProcessType.sale_contract) {
return false;
}
else {
// 一个项目可能有采购合同,但是要去判断是不是存在销售合同,存在就排除
Predicate<QueryProject> predicate = query -> {
return Objects.equals(query.getId(), queryProject.getId())
&& query.getProcessType() == ProcessType.sale_contract;
};
return queryProjects.stream().noneMatch(predicate);
}
}
List<QueryProject> projectList = idMap.get(queryProject.getId());
return projectList.stream().noneMatch(this::isSaleContract);
}).map(queryProject -> {
Project project = new Project();
project.setId(queryProject.getId());
project.setName(queryProject.getName());
project.setProjectNo(queryProject.getProjectNo());
project.setCreatorId(queryProject.getCreatorId());
return project;
}).collect(Collectors.toList());
}
return projectService.visibleProjects(projects, loginUser);
}
private boolean isSaleContract(QueryProject queryProject) {
return queryProject.getProcessType() == ProcessType.sale_contract;
}
@Data
static class DeptReturnValue {
@ -229,9 +227,19 @@ public class ProjectProcessService {
*
* @param processId ID
*/
@Transactional
public void revoke(int processId) {
jdbcTemplate.update("update project_process set current_audit=?, current_audit_id=?, `status`=? where id=?",
null, null, ProcessStatus.draft.getValue(), processId);
ProjectProcess process = obtain(processId);
if (process.getProcessType() == ProcessType.procurement_contract) {
// 撤回的时候要删除对应的采购数量
List<BudgetPurchaseAmount> purchaseAmount = getProcessPurchaseAmount(processId);
for (BudgetPurchaseAmount amount : purchaseAmount) {
amount.setSubmit(false);
entityManager.merge(amount);
}
}
}
/**
@ -289,13 +297,15 @@ public class ProjectProcessService {
detail.setPurchaseDetails(purchaseDetails);
detail.setBudgetCostId(costDetail.getId());
detail.setCategory(getCategory(costDetail));
detail.setAmountLeft(amount.getAmountLeft());
BigDecimal amountAlready = amount.getAmountAlready();
if (amountAlready == null) {
amountAlready = getAmountAlready(amount.getBudgetCostId());
}
detail.setAmountId(amountId);
BigDecimal amountAlready = getAmountAlready(amount.getBudgetCostId());
detail.setAmountAlready(amountAlready);
BigDecimal allAmount = amount.getAmount();
if (allAmount == null) {
allAmount = costDetail.getAmount();
}
detail.setAmountLeft(allAmount.subtract(amountAlready));
detail.setAmountCurrent(amount.getAmountCurrent());
ret.add(detail);
}
@ -309,9 +319,12 @@ public class ProjectProcessService {
BigDecimal amountAlready = getAmountAlready(costDetail.getId());
detail.setAmountAlready(amountAlready);
BigDecimal allAmount = costDetail.getAmount();
detail.setAmountLeft(allAmount.subtract(amountAlready));
detail.setAmount(allAmount);
// TODO 查询太频繁
detail.setCategory(getCategory(costDetail));
detail.setBudgetCostId(costDetail.getId());
ret.add(detail);
}
@ -389,9 +402,13 @@ public class ProjectProcessService {
return amountQuery.getResultList();
}
/**
*
*/
public BigDecimal getAmountAlready(int budgetCostId) {
return getPurchaseAmountList(budgetCostId).stream()
.map(BudgetPurchaseAmount::getAmountAlready)
.filter(BudgetPurchaseAmount::isSubmitted)
.map(BudgetPurchaseAmount::getAmountCurrent)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
@ -400,6 +417,14 @@ public class ProjectProcessService {
return entityManager.find(ProjectProcess.class, id);
}
public ProjectProcess obtain(int id) {
ProjectProcess projectProcess = getById(id);
if (projectProcess == null) {
throw ErrorMessageException.failed("流程不存在");
}
return projectProcess;
}
/**
*
*/

View File

@ -72,14 +72,12 @@ create table procurement_contract_supplier_material
create table procurement_contract_budget_purchase_amount
(
id int auto_increment primary key comment 'ID',
amount decimal(11, 2) comment '总共要采购数量',
amount_current decimal(11, 2) comment '本次采购数量',
amount_already decimal(11, 2) comment '已采购数量',
amount_left decimal(11, 2) comment '未采购数量',
process_id int not null comment '流程ID',
contract_id int not null comment '采购合同ID',
budget_cost_id int not null comment '成本ID',
amount decimal(11, 2) not null comment '总共要采购数量',
amount_current decimal(11, 2) not null comment '本次采购数量',
submit bit default 0 not null comment '该流程是否提交,提交了才计算到已采购数量',
process_id int not null comment '流程ID',
contract_id int not null comment '采购合同ID',
budget_cost_id int not null comment '成本ID',
UNIQUE key (process_id, budget_cost_id)
) comment '采购合同流程预算采购明细的数量记录';
@ -103,4 +101,11 @@ create table procurement_contract_budget_purchase_detail
amount_id int not null comment '记录数量表的ID',
budget_cost_id int not null comment '成本ID'
) comment '采购合同流程预算采购明细的详情';
) comment '采购合同流程预算采购明细的详情';
alter table procurement_contract_budget_purchase_amount
drop amount_left;
alter table procurement_contract_budget_purchase_amount
add submit bit default 0 not null comment '该流程是否提交,提交了才计算到已采购数量'
;

View File

@ -126,23 +126,23 @@
<el-descriptions-item label="收款条件" v-if="isSaleContract">
<el-popover placement="top-start" title="收款条件" width="400" trigger="hover">
<span slot="reference">{{contract.paymentTerms|ellipsis}}</span>
<div class="popover-overflow">{{contract.paymentTerms}}</div>
<el-input type="textarea" readonly :autosize="{ minRows: 5, maxRows: 10}" cols="50" :value="contract.paymentTerms"></el-input>
</el-popover>
</el-popover>
</el-descriptions-item>
<el-descriptions-item label="付款条件" v-if="isProcurementContract">
<el-popover placement="top-start" title="付款条件" width="400" trigger="hover">
<span slot="reference">{{contract.paymentTerms|ellipsis}}</span>
<div class="popover-overflow">{{contract.paymentTerms}}</div>
<el-input type="textarea" readonly :autosize="{ minRows: 5, maxRows: 10}" cols="50" :value="contract.paymentTerms"></el-input>
</el-popover>
</el-descriptions-item>
<el-descriptions-item label="备注">
<el-popover placement="top-end" title="备注" width="400" trigger="hover">
<span slot="reference">{{process.remark|ellipsis}}</span>
<div class="popover-overflow">{{process.remark}}</div>
<el-input type="textarea" readonly :autosize="{ minRows: 5, maxRows: 10}" cols="50" :value="process.remark"></el-input>
</el-popover>
</el-descriptions-item>
<el-descriptions-item label="附件">
<span v-if="isNotEmpty(attachments) && attachments.length===1" title="点击查看或下载">
<a :href="attachments[0].uri" target="_blank">
<el-tag size="small">

View File

@ -402,15 +402,15 @@
<template slot-scope="scope">
<span v-if="scope.row.newRow"></span>
<el-input-number v-else :precision="2" :step="0.1" :min="0" v-model="scope.row.amountCurrent"
:disabled="scope.row.amount - scope.row.amountAlready===0" size="mini"
:max="scope.row.amount - scope.row.amountAlready" controls-position="right">
:disabled="scope.row.amountLeft===0" size="mini"
:max="scope.row.amountLeft" controls-position="right">
</el-input-number>
</template>
</el-table-column>
<el-table-column prop="amountLeft" label="未采购数量" width="100">
<template slot-scope="scope">
<span v-if="scope.row.amount && scope.row.amountAlready">{{scope.row.amount - scope.row.amountAlready}}</span>
<span v-if="scope.row.amountLeft">{{scope.row.amountLeft}}</span>
</template>
</el-table-column>
@ -504,6 +504,15 @@
"procurementAmount"
]
const supplierMaterialProperties = [
"companyName",
"totalAmount",
"serviceTerms",
"paymentTerms",
"taxRate",
"attachment",
]
const fileUploadSuffix = [
".pdf",
".doc", ".docx",
@ -887,30 +896,32 @@
return false
}
const isPropertyEmpty = value => {
if (typeof value === 'string') {
return isBlank(value)
}
else if (value instanceof Array) {
return value.length === 0
}
return value != null || value !== undefined
}
const containsEmpty = detail => {
return isNotEmpty(supplierMaterialProperties.filter(property => isPropertyEmpty(detail[property])))
}
let idx = 0
for (const item in supplierMaterialsForm) {
for (const item of supplierMaterialsForm) {
idx++
if (isEmptyObject(item)) {
this.$message.error("供应商比选材料第'" + idx + "'行未填写,请检查表单")
return false
}
for (const [key, value] of Object.entries(item)) {
if (value) {
if (typeof value === 'string') {
if (isBlank(value)) {
this.$message.error("供应商比选材料第'" + idx + "'行有数据未填写,请检查表单")
return false
}
}
}
else {
// 没有值
if (key !== 'remark') {
this.$message.error("供应商比选材料第'" + idx + "'行有数据未填写,请检查表单")
}
}
}
if (containsEmpty(item)) {
this.$message.error("供应商比选材料第'" + idx + "'行有数据未填写,请检查表单")
return false
}
}
}
}

View File

@ -132,15 +132,15 @@
<el-table-column prop="amountCurrent" label="本次采购数量" width="180">
<template slot-scope="scope">
<el-input-number :precision="2" :step="0.1" :min="0" v-model="scope.row.amountCurrent"
:disabled="scope.row.amount - scope.row.amountAlready===0" size="mini"
:max="scope.row.amount - scope.row.amountAlready" controls-position="right">
:disabled="scope.row.amountLeft===0" size="mini"
:max="scope.row.amountLeft" controls-position="right">
</el-input-number>
</template>
</el-table-column>
<el-table-column prop="amountLeft" label="未采购数量" width="100">
<template slot-scope="scope">
<span>{{scope.row.amount - scope.row.amountAlready}}</span>
<span>{{scope.row.amountLeft}}</span>
</template>
</el-table-column>
@ -521,15 +521,15 @@
<template slot-scope="scope">
<span v-if="scope.row.newRow"></span>
<el-input-number v-else :precision="2" :step="0.1" :min="0" v-model="scope.row.amountCurrent"
:disabled="scope.row.amount - scope.row.amountAlready===0" size="mini"
:max="scope.row.amount - scope.row.amountAlready" controls-position="right">
:disabled="scope.row.amountLeft===0" size="mini"
:max="scope.row.amountLeft" controls-position="right">
</el-input-number>
</template>
</el-table-column>
<el-table-column prop="amountLeft" label="未采购数量" width="100">
<template slot-scope="scope">
<span v-if="!scope.row.newRow">{{scope.row.amount - scope.row.amountAlready}}</span>
<span v-if="!scope.row.newRow">{{scope.row.amountLeft}}</span>
</template>
</el-table-column>
@ -622,6 +622,15 @@
"procurementAmount"
]
const supplierMaterialProperties = [
"companyName",
"totalAmount",
"serviceTerms",
"paymentTerms",
"taxRate",
"attachment",
]
const fileUploadSuffix = [
".pdf",
".doc", ".docx",
@ -1043,36 +1052,32 @@
return false
}
const isPropertyEmpty = value => {
if (typeof value === 'string') {
return isBlank(value)
}
else if (value instanceof Array) {
return value.length === 0
}
return value != null || value !== undefined
}
const containsEmpty = detail => {
return isNotEmpty(supplierMaterialProperties.filter(property => isPropertyEmpty(detail[property])))
}
let idx = 0
for (const item in supplierMaterialsForm) {
for (const item of supplierMaterialsForm) {
idx++
if (isEmptyObject(item)) {
this.$message.error("供应商比选材料第'" + idx + "'行未填写,请检查表单")
return false
}
for (const [key, value] of Object.entries(item)) {
if (value) {
if (typeof value === 'string') {
if (isBlank(value)) {
this.$message.error("供应商比选材料第'" + idx + "'行有数据未填写,请检查表单")
return false
}
}
else if (value instanceof Array) {
if (value.length === 0) {
this.$message.error("供应商比选材料第'" + idx + "'行有数据未填写,请检查表单")
return false
}
}
}
else {
// 没有值
if (key !== 'remark') {
this.$message.error("供应商比选材料第'" + idx + "'行有数据未填写,请检查表单")
}
}
}
if (containsEmpty(item)) {
this.$message.error("供应商比选材料第'" + idx + "'行有数据未填写,请检查表单")
return false
}
}
}
}