作为重构的第一步,就是要把各个Entity
重新整理并且做好关联映射。
由于是重构,结构更清晰,这一次就从最底层的Journal
类开始做起。
Journal
类重构
Journal
类相比原来工程项目简化了不少,主要是没有了利润的烦恼。使用的主要科目如下:
- 货币资金
- 开发成本
- 预付账款
- 进项税金
- 其他资产
- 应付账款
- 其他应付款
- 其他负债
- 其他损益
可以看到,其实核心的就是开发成本,进项税金和货币资金了,毕竟房地产的成本归集还是比较简单的。
至于类的方法,依然保留有检查借贷方合计是否相等的方法,至于查询URL
,我打算把数据结构重新整理之后,写一个服务用来生成对应的URL
,这样就可以把原来分散在各个entity
类中的方法聚合起来,让整个程序的结构互相分离的更加明显。
重构之后的类如下:
@Entity
@Table(name = "journal")
public class Journal implements Serializable {
private static final long serialVersionUID = 7L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
// 说明
@NotEmpty(message = "说明")
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "description", nullable = false)
private String description;
// 付款日期
@Temporal(TemporalType.DATE)
@Column(name = "payment_date")
@DateTimeFormat(pattern = "yyyy-MM-dd", iso = DateTimeFormat.ISO.DATE)
private Date noteDate;
// 凭证号
@Digits(integer = 4, fraction = 0, message = "必须为0-9999的整数")
@Column(name = "note_number")
private int noteNumber;
// 以下为会计科目部分
// 资产-货币资金
@NotNull(message = "不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal cash = new BigDecimal("0.00");
// 资产-开发成本
@NotNull(message = "不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal developmentCost = new BigDecimal("0.00");
// 资产-预付账款
@NotNull(message = "不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal prepaid = new BigDecimal("0.00");
// 资产-进项税金
@NotNull(message = "不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal inputVAT = new BigDecimal("0.00");
// 资产-其他资产
@NotNull(message = "不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal otherAsset = new BigDecimal("0.00");
// 负债-应付账款
@NotNull(message = "不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal accountPayable = new BigDecimal("0.00");
// 负债-其他应付款
@NotNull(message = "不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal otherPayable = new BigDecimal("0.00");
// 负债-其他负债
@NotNull(message = "不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal otherLiability = new BigDecimal("0.00");
// 损益-其他损益
@NotNull(message = "成本不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal otherPL = new BigDecimal("0.00");
// 以下为时间戳
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_time", updatable = false)
@CreationTimestamp
private Date createTime;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "update_time")
@UpdateTimestamp
protected Date updateTime;
//计算合计的借方金额
public BigDecimal totalDebitSide() {
return cash
.add(developmentCost)
.add(prepaid)
.add(inputVAT)
.add(otherAsset);
}
//计算合计的贷方金额
public BigDecimal totalCreditSide() {
return accountPayable
.add(otherLiability)
.add(otherPayable)
.add(otherPL);
}
// 当前记录是否有效,即总借方是否等于总贷方
public boolean isValid() {
return totalDebitSide().equals(totalCreditSide());
}
}
常见的equals
和hashcode
还有getter setter
和toString
方法就都省略了。而且对于id
是不设置setter
的。
很显然,目前还没有设置关系映射,没有关系,马上就开始下一个层级的数据,也就是合同类Contract
。
Contract
类重构
合同所需要的信息倒是与原来差不多,因为很多合同也涉及到结算金额,只不过我们是甲方,没有那么复杂,但是为了清晰起见,还是把基本信息都列出来:
- 必填 - 合同编号,这个需要唯一,我就打算用用印单的号码即可。
- 必填 - 合同名称
- 必填 - 合同主要条款
- 必填 - 合同主要对手方
- 必填 - 合同总价
- 必填 - 开口或闭口合同
- 选填 - 不含税价格
- 选填 - 增值税额
- 选填 - 结算价格
- 选填 - 签订时间
- 选填 - 终止时间
- 选填 - 次要对手方1
- 选填 - 次要对手方2
- 选填 - 次要对手方3
- 选填 - 合同标的
- 选填 - 联系人
- 选填 - 联系电话
目前重构的类如下:
@Entity
@Table(name = "contract")
public class Contract implements Serializable {
private static final long serialVersionUID = 9L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
// 合同编号
@NotEmpty(message = "合同编号不能为空")
@Length(max = 255, message = "合同编号最长255个字符")
@Column(name = "number", nullable = false, unique = true)
private String number;
//关联到印花税条目的外键,每个合同只对应一种印花税
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "stamp_id")
private Stamp stamp;
// 合同名称
@NotEmpty(message = "合同名称不能为空")
@Length(max = 255, message = "合同名称最长255个字符")
@Column(name = "name", nullable = false)
private String name;
// 合同主要条款
@NotEmpty(message = "主要条款不能为空")
@Length(max = 1000, message = "长度不能超过1000个字符")
@Column(name = "terms", nullable = false, length = 1000)
private String terms;
// 主要合同方
@NotEmpty(message = "主要合同方不能为空")
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "supplier", nullable = false)
private String supplier;
// 合同总价
@NotNull(message = "合同总价不能为空")
@Digits(integer = 14, fraction = 2)
private BigDecimal totalPrice;
//是否单价合同或开口合同
@Column(name = "is_open")
private boolean isOpen;
//以上为必填项目,以下为可留空项目
// 不含税价格
@Digits(integer = 14, fraction = 2)
private BigDecimal priceWithoutVAT;
// 增值税额
@Digits(integer = 14, fraction = 2)
private BigDecimal VAT;
// 结算价格
@Digits(integer = 14, fraction = 2)
private BigDecimal finalPrice;
// 签订时间
@Temporal(TemporalType.DATE)
@Column(name = "sign_date")
@DateTimeFormat(pattern = "yyyy-MM-dd", iso = DateTimeFormat.ISO.DATE)
private Date signDate;
// 终止时间
@Temporal(TemporalType.DATE)
@Column(name = "end_date")
@DateTimeFormat(pattern = "yyyy-MM-dd", iso = DateTimeFormat.ISO.DATE)
private Date endDate;
// 次要供应商1
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "secondary_supplier1")
private String secondarySupplier1;
// 次要供应商2
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "secondary_supplier2")
private String secondarySupplier2;
// 次要供应商3
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "secondary_supplier3")
private String secondarySupplier3;
// 合同标的
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "target")
private String target;
// 联系人
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "contact")
private String contact;
// 联系电话
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "phone")
private String phone;
// 以下为时间戳
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_time", updatable = false)
@CreationTimestamp
private Date createTime;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "update_time")
@UpdateTimestamp
protected Date updateTime;
// 以下为关系映射
// 对应到Journal类的关联关系
@OneToMany(mappedBy = "contract", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Journal> journals;
}
由于设置了关系映射,所以在Journal
类中添加一条对应的:
@ManyToOne(optional = false, fetch = FetchType.EAGER)
@JoinColumn(name = "contract_id", nullable = false)
private Contract contract;
接下来继续编写合同类型的类,以及项目类。
Budget
重构为Subject
现在的预算就不叫预算了,实际上就是类似于开发成本下边的大类一样,将合同进行分类。
其实这里的类型,还可以再进行区分,比如审计事务所的合同等等,这些进管理费用的合同,我这里都不纳入范围内,仅仅只是开发项目的合同台账。其他那些合同,其实就直接装在凭证后边就可以。
根据我原来的经验和科目设置,要分类为如下的类型:
- 土地征用及拆迁补偿
- 前期工程
- 基础设施
- 建筑安装
- 公共配套
- 资金成本
- 开发间接费
基本上前边这些科目,都有着对应的科目。当然这些分类比科目还是要少的,科目里还会有暂估和结转,但是不会有对应的合同分类。合同分类也是在建立科目的时候就要想好,否则一旦更改起来就比较麻烦。
这个类非常简单了,在我的规划里,其实就包含一个约束unique
的名称就行了。
@Entity
@Table(name = "subject")
public class Subject implements Serializable {
private static final long serialVersionUID = 9L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@NotEmpty(message = "合同类型名称不能为空")
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "name", nullable = false)
private String budgetName;
// 是否虚拟类别,用于进行展示
@Column(name = "virtual")
@ColumnDefault("false")
private boolean virtual;
// 备注
@Length(max = 255, message = "备注最长255个字符")
@Column(name = "description")
private String description;
// 时间戳类型
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_time", updatable = false)
@CreationTimestamp
private Date createTime;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "update_time")
@UpdateTimestamp
protected Date updateTime;
//关联关系映射
//关联到合同的多对多映射
@OneToMany(mappedBy = "subject",cascade = CascadeType.REMOVE)
private Set<Contract> contracts;
}
当然,还需要在Contract
类中添加对应的映射:
// 对应到Subject类的关联关系
@ManyToOne(optional = false, fetch = FetchType.EAGER)
@JoinColumn(name = "subject_id", nullable = false)
private Subject subject;
Project
类重构
这个类就简单多了,也不存在项目经理这种说法了,基本就只剩下项目名称,地址,和简要描述,关联关系就是关联到Subject
类的一对多关系,类的代码如下:
@Entity
@Table(name = "project")
public class Project implements Serializable {
private static final long serialVersionUID = 10L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@NotEmpty(message = "项目名称不能为空")
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "name", nullable = false)
private String projectName;
@NotEmpty(message = "地址不能为空")
@Length(max = 255, message = "长度不能超过255个字符")
@Column(name = "address", nullable = false)
private String projectAddress;
@NotEmpty(message = "项目描述不能为空")
@Length(max = 1000, message = "长度不能超过1000个字符")
@Column(name = "description", nullable = false, length = 1000)
private String projectDescription;
// 时间戳部分
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_time", updatable = false)
@CreationTimestamp
private Date createTime;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "update_time")
@UpdateTimestamp
protected Date updateTime;
// 关联关系
// 映射到Subject类的一对多
@OneToMany(mappedBy = "project", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Subject> subjects;
}
这样四个PO
类就搞定了,接下来设计一下BO
类,用于装载一个PO
数据和对应的汇总计算数据。之后就是慢慢改写页面了。现在工作节奏也不快,合同也比较少,慢慢写吧。