diff --git a/client/pom.xml b/client/pom.xml
index 28831e4..1356444 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -6,12 +6,12 @@
com.ecep.contract
Contract-Manager
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
com.ecep.contract
client
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
${java.version}
@@ -22,7 +22,7 @@
com.ecep.contract
common
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
org.springframework.boot
diff --git a/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceManagerSkin.java b/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceManagerSkin.java
new file mode 100644
index 0000000..7afe183
--- /dev/null
+++ b/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceManagerSkin.java
@@ -0,0 +1,24 @@
+package com.ecep.contract.controller.contract;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ecep.contract.controller.AbstEntityManagerSkin;
+import com.ecep.contract.vm.ContractInvoiceViewModel;
+import com.ecep.contract.vo.ContractInvoiceVo;
+
+public class ContractInvoiceManagerSkin extends
+ AbstEntityManagerSkin {
+
+ private static final Logger logger = LoggerFactory.getLogger(ContractInvoiceManagerSkin.class);
+
+ public ContractInvoiceManagerSkin(ContractInvoiceManagerWindowController controller) {
+ super(controller);
+ }
+
+ @Override
+ public void initializeTable() {
+ // TODO Auto-generated method stub
+ throw new UnsupportedOperationException("Unimplemented method 'initializeTable'");
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceManagerWindowController.java b/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceManagerWindowController.java
new file mode 100644
index 0000000..ae73187
--- /dev/null
+++ b/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceManagerWindowController.java
@@ -0,0 +1,48 @@
+package com.ecep.contract.controller.contract;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import com.ecep.contract.controller.AbstManagerWindowController;
+import com.ecep.contract.service.ContractInvoiceService;
+import com.ecep.contract.util.FxmlPath;
+import com.ecep.contract.vm.ContractInvoiceViewModel;
+import com.ecep.contract.vo.ContractInvoiceVo;
+
+import javafx.scene.control.TableColumn;
+import javafx.stage.Stage;
+
+@Lazy
+@Scope("prototype")
+@Component
+@FxmlPath("/ui/contract/contract-invoice-manager.fxml")
+public class ContractInvoiceManagerWindowController extends AbstManagerWindowController {
+
+ public TableColumn idColumn;
+ public TableColumn codeColumn;
+ public TableColumn nameColumn;
+ public TableColumn contractColumn;
+ public TableColumn contractItemColumn;
+
+ @Autowired
+ private ContractInvoiceService contractInvoiceService;
+
+ @Override
+ public ContractInvoiceService getViewModelService() {
+ return contractInvoiceService;
+ }
+
+ @Override
+ protected ContractInvoiceManagerSkin createDefaultSkin() {
+ ContractInvoiceManagerSkin skin = new ContractInvoiceManagerSkin(this);
+ return skin;
+ }
+
+ @Override
+ public void show(Stage stage) {
+ super.show(stage);
+ getTitle().set("合同发票管理");
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceWindowController.java b/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceWindowController.java
new file mode 100644
index 0000000..0728685
--- /dev/null
+++ b/client/src/main/java/com/ecep/contract/controller/contract/ContractInvoiceWindowController.java
@@ -0,0 +1,50 @@
+package com.ecep.contract.controller.contract;
+
+import org.springframework.stereotype.Component;
+
+import com.ecep.contract.controller.AbstEntityController;
+import com.ecep.contract.controller.tab.ContractInvoiceTabSkinBase;
+import com.ecep.contract.service.ContractInvoiceService;
+import com.ecep.contract.util.FxmlPath;
+import com.ecep.contract.vm.ContractInvoiceViewModel;
+import com.ecep.contract.vo.ContractInvoiceVo;
+
+import javafx.collections.ObservableList;
+import javafx.scene.control.Button;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextField;
+import javafx.stage.Window;
+
+@FxmlPath("/ui/contract/contract-invoice.fxml")
+@Component
+public class ContractInvoiceWindowController
+ extends AbstEntityController {
+
+ public static void show(ContractInvoiceViewModel viewModel, Window owner) {
+ show(ContractInvoiceWindowController.class, viewModel, owner);
+ }
+
+ public Tab baseInfoTab;
+ public TextField codeField;
+ public TextField nameField;
+ public TextField invoiceField;
+ public TextField amountField;
+ public TextArea remarkField;
+ public Button saveBtn;
+ public Button cancelBtn;
+
+ @Override
+ public ContractInvoiceService getViewModelService() {
+ return getBean(ContractInvoiceService.class);
+ }
+
+ @Override
+ protected void registerTabSkins() {
+ TabPane tabPane = baseInfoTab.getTabPane();
+ ObservableList tabs = tabPane.getTabs();
+ registerTabSkin(baseInfoTab, tab -> new ContractInvoiceTabSkinBase(this));
+ }
+
+}
\ No newline at end of file
diff --git a/client/src/main/java/com/ecep/contract/controller/contract/ContractTabSkinSaleOrders.java b/client/src/main/java/com/ecep/contract/controller/contract/ContractTabSkinSaleOrders.java
index 015e8e2..9980df2 100644
--- a/client/src/main/java/com/ecep/contract/controller/contract/ContractTabSkinSaleOrders.java
+++ b/client/src/main/java/com/ecep/contract/controller/contract/ContractTabSkinSaleOrders.java
@@ -3,24 +3,21 @@ package com.ecep.contract.controller.contract;
import java.time.LocalDate;
import com.ecep.contract.ContractPayWay;
+import com.ecep.contract.controller.contract.sale_order.SalesOrderWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.cell.CompanyTableCell;
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
-import com.ecep.contract.util.FxmlPath;
-import com.ecep.contract.controller.contract.sale_order.SalesOrderWindowController;
import com.ecep.contract.controller.table.cell.LocalDateFieldTableCell;
import com.ecep.contract.converter.EmployeeStringConverter;
import com.ecep.contract.service.CompanyService;
import com.ecep.contract.service.SaleOrdersService;
+import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.vm.SalesOrderViewModel;
-
import com.ecep.contract.vo.SalesOrderVo;
-import javafx.scene.control.Button;
+
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TableColumn;
-import javafx.scene.control.TextField;
-import javafx.scene.input.KeyCode;
import lombok.Setter;
/**
@@ -56,7 +53,6 @@ public class ContractTabSkinSaleOrders
public TableColumn verifierDateColumn;
public TableColumn refIdColumn;
public TableColumn taxRateColumn;
- public TableColumn customerColumn;
public TableColumn customerAddressColumn;
/**
* 修改人, Employee
@@ -70,8 +66,6 @@ public class ContractTabSkinSaleOrders
public TableColumn closerDateColumn;
public TableColumn descriptionColumn;
public MenuItem subContractTable_menu_refresh;
- public TextField contractSearchKeyField;
- public Button searchBtn;
private Tab tab;
public ContractTabSkinSaleOrders(ContractWindowController controller, Tab tab) {
@@ -98,13 +92,7 @@ public class ContractTabSkinSaleOrders
@Override
public void initializeTab() {
- contractSearchKeyField.setOnKeyReleased(event -> {
- if (event.getCode() == KeyCode.ENTER) {
- searchBtn.fire();
- }
- });
-
- searchBtn.setOnAction(this::onTableRefreshAction);
+ super.initializeTab();
subContractTable_menu_refresh.setOnAction(this::onTableRefreshAction);
idColumn.setCellValueFactory(param -> param.getValue().getId());
@@ -123,8 +111,6 @@ public class ContractTabSkinSaleOrders
// 设置新增字段的单元格值工厂和工厂类
refIdColumn.setCellValueFactory(param -> param.getValue().getRefId());
taxRateColumn.setCellValueFactory(param -> param.getValue().getTaxRate());
- customerColumn.setCellValueFactory(param -> param.getValue().getCustomer());
- customerColumn.setCellFactory(CompanyTableCell.forTableColumn(getCompanyService()));
customerAddressColumn.setCellValueFactory(param -> param.getValue().getCustomerAddress());
modifierColumn.setCellValueFactory(param -> param.getValue().getModifier());
modifierColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
@@ -136,7 +122,7 @@ public class ContractTabSkinSaleOrders
closerDateColumn.setCellFactory(LocalDateFieldTableCell.forTableColumn());
descriptionColumn.setCellValueFactory(param -> param.getValue().getDescription());
- super.initializeTab();
+
}
@Override
diff --git a/client/src/main/java/com/ecep/contract/controller/contract/ContractWindowController.java b/client/src/main/java/com/ecep/contract/controller/contract/ContractWindowController.java
index f38e16d..53e981d 100644
--- a/client/src/main/java/com/ecep/contract/controller/contract/ContractWindowController.java
+++ b/client/src/main/java/com/ecep/contract/controller/contract/ContractWindowController.java
@@ -13,6 +13,7 @@ import com.ecep.contract.controller.AbstEntityController;
import com.ecep.contract.controller.company.CompanyWindowController;
import com.ecep.contract.controller.tab.ContractTabSkinBase;
import com.ecep.contract.controller.tab.ContractTabSkinFiles;
+import com.ecep.contract.controller.tab.ContractTabSkinInvoices;
import com.ecep.contract.controller.tab.ContractTabSkinItemsV2;
import com.ecep.contract.controller.tab.ContractTabSkinPayPlan;
import com.ecep.contract.controller.tab.ContractTabSkinSubContract;
@@ -154,7 +155,7 @@ public class ContractWindowController
registerTabSkin(saleOrderTab, tab -> new ContractTabSkinSaleOrders(this, tab));
Tab invoiceTab = new Tab("发票");
tabs.add(invoiceTab);
- // registerTabSkin(invoiceTab, tab -> new ContractTabSkinInvoices(this, tab));
+ registerTabSkin(invoiceTab, tab -> new ContractTabSkinInvoices(this, tab));
tabs.add(new Tab("出库单"));
tabs.add(new Tab("发货单"));
tabs.add(new Tab("签收单"));
diff --git a/client/src/main/java/com/ecep/contract/controller/tab/ContractInvoiceTabSkinBase.java b/client/src/main/java/com/ecep/contract/controller/tab/ContractInvoiceTabSkinBase.java
new file mode 100644
index 0000000..ace0895
--- /dev/null
+++ b/client/src/main/java/com/ecep/contract/controller/tab/ContractInvoiceTabSkinBase.java
@@ -0,0 +1,31 @@
+package com.ecep.contract.controller.tab;
+
+import com.ecep.contract.controller.contract.ContractInvoiceWindowController;
+import com.ecep.contract.vm.ContractInvoiceViewModel;
+import com.ecep.contract.vo.ContractInvoiceVo;
+import javafx.scene.control.Tab;
+
+/**
+ * 合同发票基础信息标签页皮肤
+ */
+public class ContractInvoiceTabSkinBase
+ extends AbstEntityBasedTabSkin
+ implements TabSkin {
+
+ public ContractInvoiceTabSkinBase(ContractInvoiceWindowController controller) {
+ super(controller);
+ }
+
+ @Override
+ public Tab getTab() {
+ return controller.baseInfoTab;
+ }
+
+ @Override
+ public void initializeTab() {
+ // 可以在这里进行标签页的初始化工作
+ controller.codeField.textProperty().bindBidirectional(viewModel.getCode());
+ controller.nameField.textProperty().bindBidirectional(viewModel.getName());
+ controller.remarkField.textProperty().bindBidirectional(viewModel.getRemark());
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/com/ecep/contract/controller/tab/ContractTabSkinInvoices.java b/client/src/main/java/com/ecep/contract/controller/tab/ContractTabSkinInvoices.java
new file mode 100644
index 0000000..c0be320
--- /dev/null
+++ b/client/src/main/java/com/ecep/contract/controller/tab/ContractTabSkinInvoices.java
@@ -0,0 +1,124 @@
+package com.ecep.contract.controller.tab;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+import org.controlsfx.glyphfont.Glyph;
+import org.springframework.stereotype.Component;
+
+import com.ecep.contract.controller.contract.AbstContractTableTabSkin;
+import com.ecep.contract.controller.contract.ContractInvoiceWindowController;
+import com.ecep.contract.controller.contract.ContractWindowController;
+import com.ecep.contract.controller.table.EditableEntityTableTabSkin;
+import com.ecep.contract.controller.table.cell.ContractInvoiceTableCell;
+import com.ecep.contract.controller.table.cell.EmployeeTableCell;
+import com.ecep.contract.controller.table.cell.NumberTableCell;
+import com.ecep.contract.model.IdentityEntity;
+import com.ecep.contract.service.ContractInvoiceService;
+import com.ecep.contract.service.ContractService;
+import com.ecep.contract.service.InvoiceService;
+import com.ecep.contract.util.FxmlPath;
+import com.ecep.contract.util.UITools;
+import com.ecep.contract.vm.ContractInvoiceViewModel;
+import com.ecep.contract.vo.ContractInvoiceVo;
+
+import javafx.beans.binding.Bindings;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.event.ActionEvent;
+import javafx.scene.control.Label;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextField;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.control.cell.TextFieldTableCell;
+import javafx.scene.layout.Region;
+import javafx.util.StringConverter;
+import javafx.util.converter.CurrencyStringConverter;
+
+@FxmlPath("/ui/contract/contract-tab-invoices.fxml")
+public class ContractTabSkinInvoices
+ extends AbstContractTableTabSkin
+ implements TabSkin, EditableEntityTableTabSkin {
+
+ private Tab tab;
+
+
+ public TableColumn idColumn;
+ public TableColumn codeColumn;
+ public TableColumn nameColumn;
+ public TableColumn invoiceColumn;
+ public TableColumn amountColumn;
+ public TableColumn remarkColumn;
+ public TableColumn creatorColumn;
+ public TableColumn createDateColumn;
+ public TableColumn updaterColumn;
+ public TableColumn updateDateColumn;
+
+ public Label totalAmountLabel;
+
+ public ContractTabSkinInvoices(ContractWindowController controller, Tab tab) {
+ super(controller);
+ this.tab = tab;
+ }
+
+ @Override
+ public Tab getTab() {
+ return tab;
+ }
+
+ @Override
+ protected ContractInvoiceService getViewModelService() {
+ return getBean(ContractInvoiceService.class);
+ }
+
+ @Override
+ public void initializeTable() {
+ super.initializeTable();
+ getTableView().setEditable(true);
+
+ // 初始化列
+ idColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
+ idColumn.setPrefWidth(50);
+
+ codeColumn.setCellValueFactory(new PropertyValueFactory<>("code"));
+ codeColumn.setPrefWidth(120);
+ codeColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+
+ nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
+ nameColumn.setPrefWidth(200);
+ nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+
+ // 发票列
+ invoiceColumn.setCellValueFactory(new PropertyValueFactory<>("invoice"));
+ invoiceColumn.setPrefWidth(100);
+ invoiceColumn.setCellFactory(ContractInvoiceTableCell.forTableColumn(getCachedBean(InvoiceService.class)));
+
+ // 金额列
+ amountColumn.setCellValueFactory(new PropertyValueFactory<>("amount"));
+ amountColumn.setPrefWidth(100);
+ amountColumn.setCellFactory(NumberTableCell.forTableColumn(new CurrencyStringConverter(getLocale())));
+
+ remarkColumn.setCellValueFactory(new PropertyValueFactory<>("remark"));
+ remarkColumn.setPrefWidth(150);
+ remarkColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+
+ // 创建人列
+ creatorColumn.setCellValueFactory(new PropertyValueFactory<>("creator"));
+ creatorColumn.setPrefWidth(80);
+ creatorColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
+
+ createDateColumn.setCellValueFactory(new PropertyValueFactory<>("createDate"));
+ createDateColumn.setPrefWidth(120);
+
+ // 更新人列
+ updaterColumn.setCellValueFactory(new PropertyValueFactory<>("updater"));
+ updaterColumn.setPrefWidth(80);
+ updaterColumn.setCellFactory(EmployeeTableCell.forTableColumn(getEmployeeService()));
+
+ updateDateColumn.setCellValueFactory(new PropertyValueFactory<>("updateDate"));
+ updateDateColumn.setPrefWidth(120);
+ }
+
+}
\ No newline at end of file
diff --git a/client/src/main/java/com/ecep/contract/controller/tab/ContractTabSkinItemsV2.java b/client/src/main/java/com/ecep/contract/controller/tab/ContractTabSkinItemsV2.java
index 66a3765..4575d2f 100644
--- a/client/src/main/java/com/ecep/contract/controller/tab/ContractTabSkinItemsV2.java
+++ b/client/src/main/java/com/ecep/contract/controller/tab/ContractTabSkinItemsV2.java
@@ -4,6 +4,8 @@ import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import javax.naming.Binding;
+
import org.springframework.data.domain.Pageable;
import com.ecep.contract.ContractPayWay;
@@ -28,6 +30,7 @@ import com.ecep.contract.vo.InventoryVo;
import com.ecep.contract.vo.PurchaseOrderItemVo;
import javafx.application.Platform;
+import javafx.beans.binding.Bindings;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
@@ -196,6 +199,8 @@ public class ContractTabSkinItemsV2
}
showInOwner(InventoryWindowController.class, InventoryViewModel.from(inventory));
});
+ showInventory.visibleProperty().bind(
+ Bindings.select(getTableView().getSelectionModel().selectedItemProperty(), "inventory", "value").isNotNull());
contextMenu.getItems().addAll(showInventory);
}
diff --git a/client/src/main/java/com/ecep/contract/controller/table/cell/ContractInvoiceTableCell.java b/client/src/main/java/com/ecep/contract/controller/table/cell/ContractInvoiceTableCell.java
new file mode 100644
index 0000000..730db38
--- /dev/null
+++ b/client/src/main/java/com/ecep/contract/controller/table/cell/ContractInvoiceTableCell.java
@@ -0,0 +1,59 @@
+package com.ecep.contract.controller.table.cell;
+
+import static com.ecep.contract.SpringApp.getBean;
+
+import com.ecep.contract.service.InvoiceService;
+import com.ecep.contract.vo.InvoiceVo;
+
+import javafx.scene.control.TableCell;
+import javafx.scene.control.TableColumn;
+import javafx.util.Callback;
+import lombok.NoArgsConstructor;
+
+/**
+ * 合同发票表格单元格
+ * 用于在表格中显示合同发票信息
+ */
+@NoArgsConstructor
+public class ContractInvoiceTableCell extends AsyncUpdateTableCell {
+
+ /**
+ * 创建一个ContractInvoiceTableCell的TableCell工厂,自动获取InvoiceService实例
+ *
+ * @param 表格行类型
+ * @return TableCell工厂回调
+ */
+ public static Callback, TableCell> forTableColumn() {
+ return forTableColumn(getBean(InvoiceService.class));
+ }
+
+ /**
+ * 创建一个ContractInvoiceTableCell的TableCell工厂,使用提供的InvoiceService实例
+ *
+ * @param 表格行类型
+ * @param service InvoiceService实例
+ * @return TableCell工厂回调
+ */
+ public static Callback, TableCell> forTableColumn(InvoiceService service) {
+ return param -> new ContractInvoiceTableCell(service);
+ }
+
+ /**
+ * 使用指定的InvoiceService创建ContractInvoiceTableCell
+ *
+ * @param invoiceService InvoiceService实例
+ */
+ public ContractInvoiceTableCell(InvoiceService invoiceService) {
+ setService(invoiceService);
+ }
+
+ @Override
+ protected InvoiceService getServiceBean() {
+ return getBean(InvoiceService.class);
+ }
+
+ @Override
+ public String format(InvoiceVo entity) {
+ return entity.getCode();
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/com/ecep/contract/service/ContractInvoiceService.java b/client/src/main/java/com/ecep/contract/service/ContractInvoiceService.java
new file mode 100644
index 0000000..83bf4d2
--- /dev/null
+++ b/client/src/main/java/com/ecep/contract/service/ContractInvoiceService.java
@@ -0,0 +1,67 @@
+package com.ecep.contract.service;
+
+import org.springframework.cache.annotation.CacheConfig;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.Caching;
+import org.springframework.stereotype.Service;
+
+import com.ecep.contract.vm.ContractInvoiceViewModel;
+import com.ecep.contract.vo.ContractInvoiceVo;
+
+@Service
+@CacheConfig(cacheNames = "contract-invoice")
+public class ContractInvoiceService extends QueryService {
+
+ @Cacheable(key = "#p0")
+ @Override
+ public ContractInvoiceVo findById(Integer id) {
+ return super.findById(id);
+ }
+
+ @Caching(evict = {
+ @CacheEvict(key = "#p0.id"),
+ @CacheEvict(key = "'code-'+#p0.code")
+ })
+ public ContractInvoiceVo save(ContractInvoiceVo invoice) {
+ return super.save(invoice);
+ }
+
+ @Caching(evict = {
+ @CacheEvict(key = "#p0.id"),
+ @CacheEvict(key = "'code-'+#p0.code")
+ })
+ public void delete(ContractInvoiceVo invoice) {
+ super.delete(invoice);
+ }
+
+ @Cacheable(key = "'code-'+#p0")
+ public ContractInvoiceVo findByCode(String code) {
+ try {
+ return async("findByCode", code, String.class).handle((response, ex) -> {
+ if (ex != null) {
+ throw new RuntimeException("远程方法+findByCode+调用失败", ex);
+ }
+ if (response != null) {
+ return updateValue(createNewEntity(), response);
+ }
+ return null;
+ }).get();
+ } catch (Exception e) {
+ throw new RuntimeException("查找合同发票 " + code + " 时发生错误", e);
+ }
+ }
+
+ public ContractInvoiceVo findByContractId(Integer contractId) {
+ try {
+ return async("findByContractId", contractId, Integer.class).handle((response, ex) -> {
+ if (response != null) {
+ return updateValue(createNewEntity(), response);
+ }
+ return null;
+ }).get();
+ } catch (Exception e) {
+ throw new RuntimeException("查找合同ID为 " + contractId + " 的发票时发生错误", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/com/ecep/contract/service/EmployeeService.java b/client/src/main/java/com/ecep/contract/service/EmployeeService.java
index d4f5aa8..5ab33f7 100644
--- a/client/src/main/java/com/ecep/contract/service/EmployeeService.java
+++ b/client/src/main/java/com/ecep/contract/service/EmployeeService.java
@@ -10,7 +10,6 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
-import com.ecep.contract.model.EmployeeRole;
import com.ecep.contract.vm.EmployeeViewModel;
import com.ecep.contract.vo.EmployeeRoleVo;
import com.ecep.contract.vo.EmployeeVo;
diff --git a/client/src/main/java/com/ecep/contract/vm/ContractInvoiceViewModel.java b/client/src/main/java/com/ecep/contract/vm/ContractInvoiceViewModel.java
new file mode 100644
index 0000000..0f33a68
--- /dev/null
+++ b/client/src/main/java/com/ecep/contract/vm/ContractInvoiceViewModel.java
@@ -0,0 +1,119 @@
+package com.ecep.contract.vm;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+import com.ecep.contract.util.NumberUtils;
+import com.ecep.contract.vo.ContractInvoiceVo;
+
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class ContractInvoiceViewModel extends IdentityViewModel {
+ private final SimpleStringProperty code = new SimpleStringProperty();
+ private final SimpleStringProperty name = new SimpleStringProperty();
+ /**
+ * 关联的合同对象, Contract
+ */
+ private final SimpleObjectProperty contract = new SimpleObjectProperty<>();
+ /**
+ * 关联的发票对象, Invoice
+ */
+ private final SimpleObjectProperty invoice = new SimpleObjectProperty<>();
+ /**
+ * 发票金额
+ */
+ private final SimpleObjectProperty amount = new SimpleObjectProperty<>();
+ /**
+ * 备注
+ */
+ private final SimpleStringProperty remark = new SimpleStringProperty();
+ private final SimpleObjectProperty createDate = new SimpleObjectProperty<>();
+ private final SimpleObjectProperty updateDate = new SimpleObjectProperty<>();
+ private final SimpleObjectProperty creator = new SimpleObjectProperty<>();
+ private final SimpleObjectProperty updater = new SimpleObjectProperty<>();
+
+ @Override
+ protected void updateFrom(ContractInvoiceVo v) {
+ super.updateFrom(v);
+ code.set(v.getCode());
+ name.set(v.getName());
+ contract.set(v.getContractId());
+ invoice.set(v.getInvoiceId());
+ amount.set(v.getAmount() != null ? v.getAmount() : 0.0);
+ remark.set(v.getRemark());
+
+ updateDate.set(v.getUpdateDate());
+ creator.set(v.getSetupPersonId());
+ updater.set(v.getUpdatePersonId());
+ }
+
+ @Override
+ public boolean copyTo(ContractInvoiceVo v) {
+ boolean modified = super.copyTo(v);
+
+ if (!Objects.equals(code.get(), v.getCode())) {
+ v.setCode(code.get());
+ modified = true;
+ }
+
+ if (!Objects.equals(name.get(), v.getName())) {
+ v.setName(name.get());
+ modified = true;
+ }
+
+ if (!Objects.equals(contract.get(), v.getContractId())) {
+ v.setContractId(contract.get());
+ modified = true;
+ }
+
+ if (!Objects.equals(invoice.get(), v.getInvoiceId())) {
+ v.setInvoiceId(invoice.get());
+ modified = true;
+ }
+
+ if (!NumberUtils.equals(amount.get(), v.getAmount())) {
+ v.setAmount(amount.get());
+ modified = true;
+ }
+
+ if (!Objects.equals(remark.get(), v.getRemark())) {
+ v.setRemark(remark.get());
+ modified = true;
+ }
+ if (!Objects.equals(creator.get(), v.getSetupPersonId())) {
+ v.setSetupPersonId(creator.get());
+ modified = true;
+ }
+ if (!Objects.equals(updater.get(), v.getUpdatePersonId())) {
+ v.setUpdatePersonId(updater.get());
+ modified = true;
+ }
+ if (!Objects.equals(createDate.get(), v.getSetupDate())) {
+ v.setSetupDate(createDate.get());
+ modified = true;
+ }
+ if (!Objects.equals(updateDate.get(), v.getUpdateDate())) {
+ v.setUpdateDate(updateDate.get());
+ modified = true;
+ }
+
+ return modified;
+ }
+
+ /**
+ * 从VO对象创建ViewModel实例
+ *
+ * @param vo 发票VO对象
+ * @return 发票ViewModel实例
+ */
+ public static ContractInvoiceViewModel from(ContractInvoiceVo vo) {
+ ContractInvoiceViewModel viewModel = new ContractInvoiceViewModel();
+ viewModel.updateFrom(vo);
+ return viewModel;
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/resources/ui/contract/contract-invoice-manager.fxml b/client/src/main/resources/ui/contract/contract-invoice-manager.fxml
new file mode 100644
index 0000000..811b0a7
--- /dev/null
+++ b/client/src/main/resources/ui/contract/contract-invoice-manager.fxml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/src/main/resources/ui/contract/contract-invoice.fxml b/client/src/main/resources/ui/contract/contract-invoice.fxml
new file mode 100644
index 0000000..e218058
--- /dev/null
+++ b/client/src/main/resources/ui/contract/contract-invoice.fxml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/src/main/resources/ui/contract/contract-tab-invoices.fxml b/client/src/main/resources/ui/contract/contract-tab-invoices.fxml
new file mode 100644
index 0000000..3340475
--- /dev/null
+++ b/client/src/main/resources/ui/contract/contract-tab-invoices.fxml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/src/main/resources/ui/contract/contract-tab-sale-orders.fxml b/client/src/main/resources/ui/contract/contract-tab-sale-orders.fxml
index 54a0bf4..8a414f0 100644
--- a/client/src/main/resources/ui/contract/contract-tab-sale-orders.fxml
+++ b/client/src/main/resources/ui/contract/contract-tab-sale-orders.fxml
@@ -10,7 +10,7 @@
-
+
@@ -23,7 +23,6 @@
-
diff --git a/common/pom.xml b/common/pom.xml
index 59087d5..3adeaed 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -6,12 +6,12 @@
com.ecep.contract
Contract-Manager
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
com.ecep.contract
common
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
${java.version}
diff --git a/common/src/main/java/com/ecep/contract/vo/ContractInvoiceVo.java b/common/src/main/java/com/ecep/contract/vo/ContractInvoiceVo.java
new file mode 100644
index 0000000..77a20bc
--- /dev/null
+++ b/common/src/main/java/com/ecep/contract/vo/ContractInvoiceVo.java
@@ -0,0 +1,50 @@
+package com.ecep.contract.vo;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+
+import com.ecep.contract.model.IdentityEntity;
+import com.ecep.contract.model.NamedEntity;
+
+import lombok.Data;
+
+@Data
+public class ContractInvoiceVo implements IdentityEntity, NamedEntity, ContractBasedVo, Serializable {
+ private static final long serialVersionUID = 1L;
+ private Integer id;
+ private String code;
+ private String name;
+ /**
+ * 关联的合同对象, Contract
+ */
+ private Integer contractId;
+ /**
+ * 关联的发票对象, Invoice
+ */
+ private Integer invoiceId;
+ /**
+ * 发票金额
+ */
+ private Double amount;
+
+ /**
+ * 备注
+ */
+ private String remark;
+ /**
+ * 创建人
+ */
+ private Integer setupPersonId;
+ /**
+ * 提交日期
+ */
+ private LocalDate setupDate;
+ /**
+ * 更新人
+ */
+ private Integer updatePersonId;
+ /**
+ * 更新日期
+ */
+ private LocalDate updateDate;
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/ecep/contract/vo/SalesOrderVo.java b/common/src/main/java/com/ecep/contract/vo/SalesOrderVo.java
index 6b93e49..7ba3e01 100644
--- a/common/src/main/java/com/ecep/contract/vo/SalesOrderVo.java
+++ b/common/src/main/java/com/ecep/contract/vo/SalesOrderVo.java
@@ -25,6 +25,7 @@ public class SalesOrderVo implements IdentityEntity, ContractBasedVo, Serializab
private Integer refId;
private float taxRate;
private Integer customerId;
+ private Integer customerPersonId;
private String customerAddress;
private Integer modifierId;
private LocalDate modifierDate;
diff --git a/docs/db/CONTRACT_INVOICE.sql b/docs/db/CONTRACT_INVOICE.sql
new file mode 100644
index 0000000..91e7e47
--- /dev/null
+++ b/docs/db/CONTRACT_INVOICE.sql
@@ -0,0 +1,37 @@
+CREATE TABLE supplier_ms.CONTRACT_INVOICE (
+ ID INT NOT NULL AUTO_INCREMENT,
+ CODE VARCHAR(50) NULL,
+ NAME VARCHAR(200) NULL,
+ CONTRACT_ID INT NULL,
+ INVOICE_ID INT NULL,
+ AMOUNT DOUBLE NULL,
+ SETUP_PERSON_ID INT NULL,
+ SETUP_DATE DATE NULL,
+ UPDATE_PERSON_ID INT NULL,
+ UPDATE_DATE DATE NULL,
+ REMARK VARCHAR(500) NULL,
+ PRIMARY KEY (ID),
+ INDEX FK_CONTRACT_INVOICE_CONTRACT_idx (CONTRACT_ID ASC) VISIBLE,
+ INDEX FK_CONTRACT_INVOICE_INVOICE_idx (INVOICE_ID ASC) VISIBLE,
+ INDEX FK_CONTRACT_INVOICE_SETUP_PERSON_idx (SETUP_PERSON_ID ASC) VISIBLE,
+ INDEX FK_CONTRACT_INVOICE_UPDATE_PERSON_idx (UPDATE_PERSON_ID ASC) VISIBLE,
+ CONSTRAINT FK_CONTRACT_INVOICE_CONTRACT
+ FOREIGN KEY (CONTRACT_ID)
+ REFERENCES supplier_ms.CONTRACT (ID)
+ ON DELETE CASCADE
+ ON UPDATE NO ACTION,
+ CONSTRAINT FK_CONTRACT_INVOICE_INVOICE
+ FOREIGN KEY (INVOICE_ID)
+ REFERENCES supplier_ms.INVOICE (ID)
+ ON DELETE SET NULL
+ ON UPDATE NO ACTION,
+ CONSTRAINT FK_CONTRACT_INVOICE_SETUP_PERSON
+ FOREIGN KEY (SETUP_PERSON_ID)
+ REFERENCES supplier_ms.EMPLOYEE (ID)
+ ON DELETE SET NULL
+ ON UPDATE NO ACTION,
+ CONSTRAINT FK_CONTRACT_INVOICE_UPDATE_PERSON
+ FOREIGN KEY (UPDATE_PERSON_ID)
+ REFERENCES supplier_ms.EMPLOYEE (ID)
+ ON DELETE SET NULL
+ ON UPDATE NO ACTION);
\ No newline at end of file
diff --git a/docs/db/CONTRACT_SALES_ORDER.sql b/docs/db/CONTRACT_SALES_ORDER.sql
new file mode 100644
index 0000000..b2c5c56
--- /dev/null
+++ b/docs/db/CONTRACT_SALES_ORDER.sql
@@ -0,0 +1,37 @@
+CREATE TABLE supplier_ms.CONTRACT_SALES_ORDER (
+ ID INT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ CONTRACT_ID INT NOT NULL COMMENT '合同ID',
+ EMPLOYEE_ID INT COMMENT '业务员ID',
+ CODE VARCHAR(50) COMMENT '单据编号',
+ REF_ID INT COMMENT '引用ID',
+ TAX_RATE FLOAT COMMENT '税率',
+ CUSTOMER_ID INT COMMENT '客户ID',
+ CUSTOMER_PERSON_ID INT COMMENT '客户联系人ID',
+ CUSTOMER_ADDRESS VARCHAR(255) COMMENT '客户地址',
+ MAKER_ID INT COMMENT '制单人ID',
+ MAKER_DATE DATE COMMENT '制单日期',
+ VERIFIER_ID INT COMMENT '审核人ID',
+ VERIFIED_DATE DATE COMMENT '审核日期',
+ MODIFIER_ID INT COMMENT '修改人ID',
+ MODIFIED_DATE DATE COMMENT '修改日期',
+ CLOSER_ID INT COMMENT '关闭人ID',
+ CLOSED_DATE DATE COMMENT '关闭日期',
+ DESCRIPTION TEXT COMMENT '备注',
+ PRIMARY KEY (ID),
+ INDEX IDX_SALES_ORDER_CONTRACT (CONTRACT_ID),
+ INDEX IDX_SALES_ORDER_EMPLOYEE (EMPLOYEE_ID),
+ INDEX IDX_SALES_ORDER_CUSTOMER (CUSTOMER_ID),
+ INDEX IDX_SALES_ORDER_CUSTOMER_PERSON (CUSTOMER_PERSON_ID),
+ INDEX IDX_SALES_ORDER_MAKER (MAKER_ID),
+ INDEX IDX_SALES_ORDER_VERIFIER (VERIFIER_ID),
+ INDEX IDX_SALES_ORDER_MODIFIER (MODIFIER_ID),
+ INDEX IDX_SALES_ORDER_CLOSER (CLOSER_ID),
+ CONSTRAINT FK_SALES_ORDER_CONTRACT FOREIGN KEY (CONTRACT_ID) REFERENCES supplier_ms.CONTRACT (ID) ON DELETE CASCADE ON UPDATE NO ACTION,
+ CONSTRAINT FK_SALES_ORDER_EMPLOYEE FOREIGN KEY (EMPLOYEE_ID) REFERENCES supplier_ms.EMPLOYEE (ID) ON DELETE SET NULL ON UPDATE NO ACTION,
+ CONSTRAINT FK_SALES_ORDER_CUSTOMER FOREIGN KEY (CUSTOMER_ID) REFERENCES supplier_ms.COMPANY (ID) ON DELETE SET NULL ON UPDATE NO ACTION,
+ CONSTRAINT FK_SALES_ORDER_CUSTOMER_PERSON FOREIGN KEY (CUSTOMER_PERSON_ID) REFERENCES supplier_ms.EMPLOYEE (ID) ON DELETE SET NULL ON UPDATE NO ACTION,
+ CONSTRAINT FK_SALES_ORDER_MAKER FOREIGN KEY (MAKER_ID) REFERENCES supplier_ms.EMPLOYEE (ID) ON DELETE SET NULL ON UPDATE NO ACTION,
+ CONSTRAINT FK_SALES_ORDER_VERIFIER FOREIGN KEY (VERIFIER_ID) REFERENCES supplier_ms.EMPLOYEE (ID) ON DELETE SET NULL ON UPDATE NO ACTION,
+ CONSTRAINT FK_SALES_ORDER_MODIFIER FOREIGN KEY (MODIFIER_ID) REFERENCES supplier_ms.EMPLOYEE (ID) ON DELETE SET NULL ON UPDATE NO ACTION,
+ CONSTRAINT FK_SALES_ORDER_CLOSER FOREIGN KEY (CLOSER_ID) REFERENCES supplier_ms.EMPLOYEE (ID) ON DELETE SET NULL ON UPDATE NO ACTION
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='销售订单表';
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b2fd326..8d17172 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
com.ecep.contract
Contract-Manager
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
pom
server
diff --git a/server/pom.xml b/server/pom.xml
index 53b445e..59b2c61 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -6,12 +6,12 @@
com.ecep.contract
Contract-Manager
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
com.ecep.contract
server
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
${java.version}
@@ -22,7 +22,7 @@
com.ecep.contract
common
- 0.0.102-SNAPSHOT
+ 0.0.122-SNAPSHOT
org.springframework.boot
diff --git a/server/src/main/java/com/ecep/contract/ds/contract/model/ContractInvoice.java b/server/src/main/java/com/ecep/contract/ds/contract/model/ContractInvoice.java
new file mode 100644
index 0000000..42e0bae
--- /dev/null
+++ b/server/src/main/java/com/ecep/contract/ds/contract/model/ContractInvoice.java
@@ -0,0 +1,139 @@
+package com.ecep.contract.ds.contract.model;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+import com.ecep.contract.ds.other.model.Invoice;
+import com.ecep.contract.model.Employee;
+import com.ecep.contract.model.IdentityEntity;
+import com.ecep.contract.model.NamedEntity;
+import com.ecep.contract.model.Voable;
+import com.ecep.contract.vo.ContractInvoiceVo;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import org.hibernate.annotations.OnDelete;
+import org.hibernate.annotations.OnDeleteAction;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 合同发票实体类
+ */
+@Getter
+@Setter
+@Entity
+@Table(name = "CONTRACT_INVOICE", schema = "supplier_ms")
+@ToString
+public class ContractInvoice implements IdentityEntity, NamedEntity, Voable {
+
+ @Id
+ @Column(name = "ID", nullable = false)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Integer id;
+
+ @Column(name = "CODE", length = 50)
+ private String code;
+
+ @Column(name = "NAME", length = 200)
+ private String name;
+
+ /**
+ * 关联的合同对象
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "CONTRACT_ID", foreignKey = @jakarta.persistence.ForeignKey(name = "FK_CONTRACT_INVOICE_CONTRACT"))
+ @OnDelete(action = OnDeleteAction.CASCADE)
+ @ToString.Exclude
+ private Contract contract;
+
+ /**
+ * 关联的发票对象
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "INVOICE_ID", foreignKey = @jakarta.persistence.ForeignKey(name = "FK_CONTRACT_INVOICE_INVOICE"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
+ @ToString.Exclude
+ private Invoice invoice;
+
+ /**
+ * 发票金额
+ */
+ @Column(name = "AMOUNT")
+ private Double amount;
+
+ /**
+ * 创建人
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "SETUP_PERSON_ID", foreignKey = @jakarta.persistence.ForeignKey(name = "FK_CONTRACT_INVOICE_SETUP_PERSON"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
+ @ToString.Exclude
+ private Employee setupPerson;
+ /**
+ * 提交日期
+ */
+ @Column(name = "SETUP_DATE")
+ private LocalDate setupDate;
+
+ /**
+ * 更新人
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "UPDATE_PERSON_ID", foreignKey = @jakarta.persistence.ForeignKey(name = "FK_CONTRACT_INVOICE_UPDATE_PERSON"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
+ @ToString.Exclude
+ private Employee updatePerson;
+ /**
+ * 更新日期
+ */
+ @Column(name = "UPDATE_DATE")
+ private LocalDate updateDate;
+
+ /**
+ * 备注
+ */
+ @Column(name = "REMARK", length = 500)
+ private String remark;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ ContractInvoice that = (ContractInvoice) o;
+ return Objects.equals(id, that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ @Override
+ public ContractInvoiceVo toVo() {
+ ContractInvoiceVo vo = new ContractInvoiceVo();
+ vo.setId(id);
+ vo.setCode(code);
+ vo.setName(name);
+ vo.setContractId(contract != null ? contract.getId() : null);
+ vo.setInvoiceId(invoice != null ? invoice.getId() : null);
+ vo.setAmount(amount);
+ vo.setRemark(remark);
+ vo.setSetupPersonId(setupPerson != null ? setupPerson.getId() : null);
+ vo.setUpdatePersonId(updatePerson != null ? updatePerson.getId() : null);
+ vo.setSetupDate(setupDate);
+ vo.setUpdateDate(updateDate);
+
+ return vo;
+ }
+}
\ No newline at end of file
diff --git a/server/src/main/java/com/ecep/contract/ds/contract/repository/ContractInvoiceRepository.java b/server/src/main/java/com/ecep/contract/ds/contract/repository/ContractInvoiceRepository.java
new file mode 100644
index 0000000..b7faa3e
--- /dev/null
+++ b/server/src/main/java/com/ecep/contract/ds/contract/repository/ContractInvoiceRepository.java
@@ -0,0 +1,35 @@
+package com.ecep.contract.ds.contract.repository;
+
+import java.util.List;
+import java.util.Optional;
+import org.springframework.stereotype.Repository;
+import com.ecep.contract.ds.MyRepository;
+import com.ecep.contract.ds.contract.model.ContractInvoice;
+
+@Repository
+public interface ContractInvoiceRepository extends MyRepository {
+
+ /**
+ * 根据GUID查找发票的方法
+ *
+ * @param code 发票代码
+ * @return 返回一个Optional对象,可能包含找到的发票,也可能为空
+ */
+ Optional findByCode(String code);
+
+ /**
+ * 根据合同ID查找发票列表
+ *
+ * @param contractId 合同ID
+ * @return 发票列表
+ */
+ List findByContractId(Integer contractId);
+
+ /**
+ * 根据发票ID查找合同发票
+ *
+ * @param invoiceId 发票ID
+ * @return 合同发票实体
+ */
+ Optional findByInvoiceId(Integer invoiceId);
+}
\ No newline at end of file
diff --git a/server/src/main/java/com/ecep/contract/ds/contract/service/ContractInvoiceService.java b/server/src/main/java/com/ecep/contract/ds/contract/service/ContractInvoiceService.java
new file mode 100644
index 0000000..158923d
--- /dev/null
+++ b/server/src/main/java/com/ecep/contract/ds/contract/service/ContractInvoiceService.java
@@ -0,0 +1,200 @@
+package com.ecep.contract.ds.contract.service;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheConfig;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+
+import com.ecep.contract.IEntityService;
+import com.ecep.contract.QueryService;
+import com.ecep.contract.SpringApp;
+import com.ecep.contract.ds.company.service.InvoiceService;
+import com.ecep.contract.ds.contract.model.ContractInvoice;
+import com.ecep.contract.ds.contract.repository.ContractInvoiceRepository;
+import com.ecep.contract.ds.other.service.EmployeeService;
+import com.ecep.contract.service.VoableService;
+import com.ecep.contract.util.SpecificationUtils;
+import com.ecep.contract.vo.ContractInvoiceVo;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+@CacheConfig(cacheNames = "contract-invoice")
+public class ContractInvoiceService implements IEntityService, QueryService,
+ VoableService {
+
+ @Autowired
+ private ContractInvoiceRepository repository;
+
+ @Cacheable(key = "#p0")
+ public ContractInvoiceVo findById(Integer id) {
+ return repository.findById(id).map(ContractInvoice::toVo).orElse(null);
+ }
+
+ @Cacheable(key = "'code-'+#p0")
+ public ContractInvoiceVo findByCode(String code) {
+ return repository.findByCode(code).map(ContractInvoice::toVo).orElse(null);
+ }
+
+ /**
+ * 根据合同ID查找发票列表
+ *
+ * @param contractId 合同ID
+ * @return 发票VO列表
+ */
+ public List findByContractId(Integer contractId) {
+ return repository.findByContractId(contractId)
+ .stream()
+ .map(ContractInvoice::toVo)
+ .toList();
+ }
+
+ /**
+ * 根据发票ID查找合同发票
+ *
+ * @param invoiceId 发票ID
+ * @return 合同发票VO
+ */
+ public ContractInvoiceVo findByInvoiceId(Integer invoiceId) {
+ return repository.findByInvoiceId(invoiceId)
+ .map(ContractInvoice::toVo)
+ .orElse(null);
+ }
+
+ /**
+ * 保存发票
+ *
+ * @param invoice 发票实体
+ * @return 保存后的发票VO
+ */
+ @Override
+ public ContractInvoice save(ContractInvoice invoice) {
+ try {
+ ContractInvoice saved = repository.save(invoice);
+ return saved;
+ } catch (Exception e) {
+ log.error("保存发票失败", e);
+ throw new RuntimeException("保存发票失败", e);
+ }
+ }
+
+ @Override
+ public void updateByVo(ContractInvoice entity, ContractInvoiceVo vo) {
+ if (entity == null || vo == null) {
+ return;
+ }
+
+ entity.setCode(vo.getCode());
+ entity.setName(vo.getName());
+ entity.setAmount(vo.getAmount());
+ entity.setRemark(vo.getRemark());
+
+ // 处理关联实体 - 合同
+ if (vo.getContractId() == null) {
+ entity.setContract(null);
+ } else {
+ ContractService contractService = SpringApp.getBean(ContractService.class);
+ if (entity.getContract() == null || !entity.getContract().getId().equals(vo.getContractId())) {
+ entity.setContract(contractService.getById(vo.getContractId()));
+ }
+ }
+
+ // 处理关联实体 - 发票
+ if (vo.getInvoiceId() == null) {
+ entity.setInvoice(null);
+ } else {
+ InvoiceService invoiceService = SpringApp.getBean(InvoiceService.class);
+ if (entity.getInvoice() == null || !entity.getInvoice().getId().equals(vo.getInvoiceId())) {
+ entity.setInvoice(invoiceService.getById(vo.getInvoiceId()));
+ }
+ }
+
+ // 处理关联实体 - 发票
+ EmployeeService personService = SpringApp.getBean(EmployeeService.class);
+ if (vo.getSetupPersonId() == null) {
+ entity.setSetupPerson(null);
+ } else {
+ if (entity.getSetupPerson() == null || !entity.getSetupPerson().getId().equals(vo.getSetupPersonId())) {
+ entity.setSetupPerson(personService.getById(vo.getSetupPersonId()));
+ }
+ }
+ if (vo.getUpdatePersonId() == null) {
+ entity.setUpdatePerson(null);
+ } else {
+ if (entity.getUpdatePerson() == null || !entity.getUpdatePerson().getId().equals(vo.getUpdatePersonId())) {
+ entity.setUpdatePerson(personService.getById(vo.getUpdatePersonId()));
+ }
+ }
+
+ }
+
+ @Override
+ public Page findAll(JsonNode paramsNode, Pageable pageable) {
+ Specification spec = buildParameterSpecification(paramsNode);
+ Page page = repository.findAll(spec, pageable);
+ return page.map(ContractInvoice::toVo);
+ }
+
+ /**
+ * 构建参数规范
+ *
+ * @param paramsNode 参数节点
+ * @return 规范对象
+ */
+ protected Specification buildParameterSpecification(JsonNode paramsNode) {
+ Specification spec = null;
+
+ // 处理基本字段参数
+ spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "name", "code");
+
+ // 处理关联实体参数
+ spec = SpecificationUtils.andParam(spec, paramsNode, "contract", "invoice", "setupPerson", "updatePerson");
+
+ // 处理搜索文本
+ if (paramsNode.has("searchText")) {
+ String searchText = paramsNode.get("searchText").asText();
+ if (searchText != null && !searchText.isEmpty()) {
+ spec = SpecificationUtils.and(spec, getSpecification(searchText));
+ }
+ }
+
+ return spec;
+ }
+
+ @Override
+ public ContractInvoice getById(Integer id) {
+ return repository.findById(id).orElse(null);
+ }
+
+ @Override
+ public Page findAll(Specification spec, Pageable pageable) {
+ return repository.findAll(spec, pageable);
+ }
+
+ @Override
+ public Specification getSpecification(String searchText) {
+ return (root, query, builder) -> {
+ return builder.or(
+ builder.like(root.get("name"), "%" + searchText + "%"),
+ builder.like(root.get("code"), "%" + searchText + "%"),
+ builder.like(root.get("remark"), "%" + searchText + "%"));
+ };
+ }
+
+ @Override
+ public void delete(ContractInvoice entity) {
+ try {
+ repository.delete(entity);
+ } catch (Exception e) {
+ log.error("删除合同发票失败", e);
+ throw new RuntimeException("删除合同发票失败", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/server/src/main/java/com/ecep/contract/ds/customer/model/SalesOrder.java b/server/src/main/java/com/ecep/contract/ds/customer/model/SalesOrder.java
index 4890905..43816ca 100644
--- a/server/src/main/java/com/ecep/contract/ds/customer/model/SalesOrder.java
+++ b/server/src/main/java/com/ecep/contract/ds/customer/model/SalesOrder.java
@@ -3,6 +3,9 @@ package com.ecep.contract.ds.customer.model;
import java.time.LocalDate;
import java.util.Objects;
+import org.hibernate.annotations.OnDelete;
+import org.hibernate.annotations.OnDeleteAction;
+
import com.ecep.contract.ds.company.model.Company;
import com.ecep.contract.ds.contract.model.Contract;
import com.ecep.contract.ds.contract.model.ContractBasedEntity;
@@ -22,6 +25,7 @@ import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
+import jakarta.persistence.ForeignKey;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@@ -42,7 +46,8 @@ public class SalesOrder implements IdentityEntity, BasedEntity, ContractBasedEnt
private Integer id;
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "CONTRACT_ID")
+ @JoinColumn(name = "CONTRACT_ID", foreignKey = @ForeignKey(name = "FK_SALES_ORDER_CONTRACT"))
+ @OnDelete(action = OnDeleteAction.CASCADE)
@ToString.Exclude
private Contract contract;
@@ -50,7 +55,8 @@ public class SalesOrder implements IdentityEntity, BasedEntity, ContractBasedEnt
* 业务员
*/
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "EMPLOYEE_ID")
+ @JoinColumn(name = "EMPLOYEE_ID", foreignKey = @ForeignKey(name = "FK_SALES_ORDER_EMPLOYEE"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
@ToString.Exclude
private Employee employee;
@@ -73,10 +79,20 @@ public class SalesOrder implements IdentityEntity, BasedEntity, ContractBasedEnt
* 客户
*/
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "CUSTOMER_ID")
+ @JoinColumn(name = "CUSTOMER_ID", foreignKey = @ForeignKey(name = "FK_SALES_ORDER_CUSTOMER"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
@ToString.Exclude
private Company customer;
+ /**
+ * 客户联系人
+ */
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "CUSTOMER_PERSON_ID", foreignKey = @ForeignKey(name = "FK_SALES_ORDER_CUSTOMER_PERSON"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
+ @ToString.Exclude
+ private Employee customerPerson;
+
/**
* 客户地址
*/
@@ -87,7 +103,8 @@ public class SalesOrder implements IdentityEntity, BasedEntity, ContractBasedEnt
* 制单人
*/
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "MAKER_ID")
+ @JoinColumn(name = "MAKER_ID", foreignKey = @ForeignKey(name = "FK_SALES_ORDER_MAKER"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
@ToString.Exclude
private Employee maker;
@@ -100,7 +117,8 @@ public class SalesOrder implements IdentityEntity, BasedEntity, ContractBasedEnt
* 审核人
*/
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "VERIFIER_ID")
+ @JoinColumn(name = "VERIFIER_ID", foreignKey = @ForeignKey(name = "FK_SALES_ORDER_VERIFIER"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
@ToString.Exclude
private Employee verifier;
/**
@@ -109,8 +127,12 @@ public class SalesOrder implements IdentityEntity, BasedEntity, ContractBasedEnt
@Column(name = "VERIFIED_DATE")
private LocalDate verifierDate;
+ /**
+ * 修改人
+ */
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "MODIFIER_ID")
+ @JoinColumn(name = "MODIFIER_ID", foreignKey = @ForeignKey(name = "FK_SALES_ORDER_MODIFIER"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
@ToString.Exclude
private Employee modifier;
@@ -121,7 +143,8 @@ public class SalesOrder implements IdentityEntity, BasedEntity, ContractBasedEnt
* 关闭人
*/
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "CLOSER_ID")
+ @JoinColumn(name = "CLOSER_ID", foreignKey = @ForeignKey(name = "FK_SALES_ORDER_CLOSER"))
+ @OnDelete(action = OnDeleteAction.SET_NULL)
@ToString.Exclude
private Employee closer;
@@ -170,7 +193,7 @@ public class SalesOrder implements IdentityEntity, BasedEntity, ContractBasedEnt
vo.setModifierDate(modifierDate);
vo.setCloserDate(closerDate);
vo.setDescription(description);
-
+
// 关联对象ID映射
if (contract != null) {
vo.setContractId(contract.getId());