2 Revize 2d29579018 ... d41cffa42a

Autor SHA1 Zpráva Datum
  admin d41cffa42a fix: 宜搭实例可推送包材核价,正式回写改走正式区 před 4 dny
  admin 17338c8d8f fix: 杂收发改为 aint302,并修复花名册银行字段合并重复 před 4 dny

+ 2 - 2
README.md

@@ -329,7 +329,7 @@ Content-Type: application/json
 | `FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K` | `MlWorkOrderCraftPriceFormPushHandler` | ML-工单工艺工价审定 | `FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K` | `TPROC--WG966BA1VWF8Q1K4OGF8H9KW47RR3ZREH0YSM2` |
 | `FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K` | `MlWorkOrderCraftPriceFormPushHandler` | ML-工单工艺工价审定 | `FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K` | `TPROC--WG966BA1VWF8Q1K4OGF8H9KW47RR3ZREH0YSM2` |
 | `axmt500` | `ApproveOrderFormPushHandler` | 客户订单审批 | `FORM-8454FA17732746058A2D90B1C559CC19G5SR` | `TPROC--9VD66QA18AJ8317KK3NXM54W1HIT30C7N28TM7` |
 | `axmt500` | `ApproveOrderFormPushHandler` | 客户订单审批 | `FORM-8454FA17732746058A2D90B1C559CC19G5SR` | `TPROC--9VD66QA18AJ8317KK3NXM54W1HIT30C7N28TM7` |
 | `axmt140` | `CustomerReleaseFormPushHandler` | 客户放行审批 | `FORM-3BA9FE18494F4FDD85FE6ABF7DE741BAE30C` | `TPROC--6TG66381N8G8Y7L9GBEEY7QVXTUY2LBBGZ7TMU` |
 | `axmt140` | `CustomerReleaseFormPushHandler` | 客户放行审批 | `FORM-3BA9FE18494F4FDD85FE6ABF7DE741BAE30C` | `TPROC--6TG66381N8G8Y7L9GBEEY7QVXTUY2LBBGZ7TMU` |
-| `cint301` | `MiscReceiptIssueFormPushHandler` | 杂收杂发审批 | `FORM-4B08ADABCDAA45788C043729A855E03ED76G` | `TPROC--MQD66371C1N84FPSJZO4W79IN4BW2AE7I08TM0` |
+| `aint302` | `MiscReceiptIssueFormPushHandler` | 杂收杂发审批 | `FORM-4B08ADABCDAA45788C043729A855E03ED76G` | `TPROC--MQD66371C1N84FPSJZO4W79IN4BW2AE7I08TM0` |
 
 
 ### 5.2 `mainData` 规则
 ### 5.2 `mainData` 规则
 
 
@@ -362,7 +362,7 @@ Content-Type: application/json
 | `FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K` | `detailData` |
 | `FORM-4403B45A7F184CB79088FCDAFD2FD925LZ3K` | `detailData` |
 | `axmt500` | `detailData1`、`detailData2`,兼容历史 `detailData` |
 | `axmt500` | `detailData1`、`detailData2`,兼容历史 `detailData` |
 | `axmt140` | 优先 `detailData1`,缺失时回退到 `detailData` |
 | `axmt140` | 优先 `detailData1`,缺失时回退到 `detailData` |
-| `cint301` | `detailData1` |
+| `aint302` | `detailData1` |
 
 
 明细对象中可以直接传入宜搭 `fieldId`。服务端会保留包含 `Field_` 的字段值,用于补充默认字段映射。
 明细对象中可以直接传入宜搭 `fieldId`。服务端会保留包含 `Field_` 的字段值,用于补充默认字段映射。
 
 

+ 55 - 0
pom.xml

@@ -78,6 +78,61 @@
             <artifactId>bcprov-jdk15on</artifactId>
             <artifactId>bcprov-jdk15on</artifactId>
             <version>1.70</version>
             <version>1.70</version>
         </dependency>
         </dependency>
+        <!--
+          Local maven repo only has a stub java-mcli parent POM, so mjava:0.0.3
+          brings almost no transitive deps. Explicitly restore the libs present in
+          the production fat jar built by teammates (~60MB).
+        -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.6.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>2.2.7</version>
+        </dependency>
+        <dependency>
+            <groupId>com.querydsl</groupId>
+            <artifactId>querydsl-jpa</artifactId>
+            <version>5.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib</artifactId>
+            <version>3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>jaxb-runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-ui</artifactId>
+            <version>1.6.15</version>
+        </dependency>
         <dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <artifactId>spring-boot-starter-test</artifactId>

+ 56 - 5
src/main/java/com/malk/minglei/controller/MingleiPortController.java

@@ -1,7 +1,11 @@
 package com.malk.minglei.controller;
 package com.malk.minglei.controller;
 
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.malk.minglei.dto.MingleiPortRequest;
 import com.malk.minglei.dto.MingleiPortRequest;
+import com.malk.minglei.dto.SupplierPriceApprovalProcessRequest;
 import com.malk.minglei.service.port.MingleiPortService;
 import com.malk.minglei.service.port.MingleiPortService;
+import com.malk.minglei.service.port.SupplierPriceApprovalProcessService;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.HttpStatus;
@@ -15,10 +19,13 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.client.RestClientException;
 import org.springframework.web.client.RestClientException;
 
 
-import javax.validation.Valid;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 /**
 /**
  * 提供调用明磊外部单据接口的本地触发入口。
  * 提供调用明磊外部单据接口的本地触发入口。
@@ -30,27 +37,60 @@ public class MingleiPortController {
     private static final Logger LOGGER = LoggerFactory.getLogger(MingleiPortController.class);
     private static final Logger LOGGER = LoggerFactory.getLogger(MingleiPortController.class);
 
 
     private final MingleiPortService mingleiPortService;
     private final MingleiPortService mingleiPortService;
+    private final SupplierPriceApprovalProcessService supplierPriceApprovalProcessService;
+    private final ObjectMapper objectMapper;
+    private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
 
 
     /**
     /**
      * 创建明磊外部单据接口控制器。
      * 创建明磊外部单据接口控制器。
      *
      *
      * @param mingleiPortService 明磊外部单据接口服务
      * @param mingleiPortService 明磊外部单据接口服务
+     * @param supplierPriceApprovalProcessService 宜搭实例转 T100 单据服务
+     * @param objectMapper JSON 解析器
      */
      */
-    public MingleiPortController(MingleiPortService mingleiPortService) {
+    public MingleiPortController(MingleiPortService mingleiPortService,
+                                 SupplierPriceApprovalProcessService supplierPriceApprovalProcessService,
+                                 ObjectMapper objectMapper) {
         this.mingleiPortService = mingleiPortService;
         this.mingleiPortService = mingleiPortService;
+        this.supplierPriceApprovalProcessService = supplierPriceApprovalProcessService;
+        this.objectMapper = objectMapper;
     }
     }
 
 
     /**
     /**
-     * 接收本系统的供应商价格审批单请求,并调用外部 T100 接口。
+     * 接收两类请求。带 formId 时按宜搭实例读取表单并推送 T100;否则按已组装好的 head、detail 转发。
      *
      *
-     * @param request 仅包含 head 和 detail 的供应商价格审批单请求体
+     * @param body 宜搭实例标识,或 T100 单据 head、detail
      * @return 外部 T100 接口响应内容
      * @return 外部 T100 接口响应内容
      */
      */
     @PostMapping("/supplier-price-approvals")
     @PostMapping("/supplier-price-approvals")
-    public ResponseEntity<Map<String, Object>> submitSupplierPriceApproval(@Valid @RequestBody MingleiPortRequest request) {
+    public ResponseEntity<Map<String, Object>> submitSupplierPriceApproval(@RequestBody JsonNode body) {
+        if (body != null && body.has("formId")) {
+            return ResponseEntity.ok(submitYidaInstance(body));
+        }
+        MingleiPortRequest request = objectMapper.convertValue(body, MingleiPortRequest.class);
+        Set<ConstraintViolation<MingleiPortRequest>> violations = validator.validate(request);
+        if (!violations.isEmpty()) {
+            return ResponseEntity.badRequest().body(Collections.<String, Object>singletonMap("message", "Request validation failed"));
+        }
         return ResponseEntity.ok(mingleiPortService.submitSupplierPriceApproval(request));
         return ResponseEntity.ok(mingleiPortService.submitSupplierPriceApproval(request));
     }
     }
 
 
+    private Map<String, Object> submitYidaInstance(JsonNode body) {
+        SupplierPriceApprovalProcessRequest request = objectMapper.convertValue(body, SupplierPriceApprovalProcessRequest.class);
+        if (isBlank(request.getFormId()) || isBlank(request.getFormInstanceId())) {
+            throw new IllegalArgumentException("formId and formInstanceId must not be blank");
+        }
+        if (request.requiresProcessInstanceId() && isBlank(request.getProcessInstanceId())) {
+            throw new IllegalArgumentException("processInstanceId must not be blank");
+        }
+        return supplierPriceApprovalProcessService.submitByInstanceIds(
+                request.getFormId(), request.getFormInstanceId(), request.getProcessInstanceId());
+    }
+
+    private static boolean isBlank(String value) {
+        return value == null || value.trim().isEmpty();
+    }
+
     /**
     /**
      * 返回本地请求体校验或 JSON 解析失败的信息,不发起外部调用。
      * 返回本地请求体校验或 JSON 解析失败的信息,不发起外部调用。
      *
      *
@@ -62,6 +102,17 @@ public class MingleiPortController {
         return ResponseEntity.badRequest().body(Collections.singletonMap("message", "Request validation failed"));
         return ResponseEntity.badRequest().body(Collections.singletonMap("message", "Request validation failed"));
     }
     }
 
 
+    /**
+     * 返回宜搭实例参数不完整或表单不受支持的错误信息。
+     *
+     * @param exception 参数异常
+     * @return 参数错误响应
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    public ResponseEntity<Map<String, String>> handleInvalidArgument(IllegalArgumentException exception) {
+        return ResponseEntity.badRequest().body(Collections.singletonMap("message", exception.getMessage()));
+    }
+
     /**
     /**
      * 将外部服务的连接、超时或响应异常转换为本地网关错误。
      * 将外部服务的连接、超时或响应异常转换为本地网关错误。
      *
      *

+ 12 - 26
src/main/java/com/malk/minglei/dto/EmployeeRosterInfo.java

@@ -11,17 +11,15 @@ public class EmployeeRosterInfo {
     private String dingtalkId;
     private String dingtalkId;
     private String employeeNo;
     private String employeeNo;
     private String username;
     private String username;
-    @JsonProperty("bank_card_no")
-    private String bankCardNo;
-    @JsonProperty("bank_name")
-    private String bankName;
     private String jobTitle;
     private String jobTitle;
     private String jobTitleId;
     private String jobTitleId;
     private String jobGrade;
     private String jobGrade;
     private String jobGradeId;
     private String jobGradeId;
     private String department;
     private String department;
     private String firstDepartment;
     private String firstDepartment;
+    @JsonProperty("bank_card_no")
     private String bankCardNo;
     private String bankCardNo;
+    @JsonProperty("bank_name")
     private String bankName;
     private String bankName;
     private String bankBranchCode;
     private String bankBranchCode;
 
 
@@ -57,22 +55,6 @@ public class EmployeeRosterInfo {
         this.username = username;
         this.username = username;
     }
     }
 
 
-    public String getBankCardNo() {
-        return bankCardNo;
-    }
-
-    public void setBankCardNo(String bankCardNo) {
-        this.bankCardNo = bankCardNo;
-    }
-
-    public String getBankName() {
-        return bankName;
-    }
-
-    public void setBankName(String bankName) {
-        this.bankName = bankName;
-    }
-
     public String getJobTitle() {
     public String getJobTitle() {
         return jobTitle;
         return jobTitle;
     }
     }
@@ -123,15 +105,16 @@ public class EmployeeRosterInfo {
 
 
     /**
     /**
      * 获取银行卡号。
      * 获取银行卡号。
+     *
      * @return 银行卡号
      * @return 银行卡号
      */
      */
-    @JsonProperty("bank_card_no")
     public String getBankCardNo() {
     public String getBankCardNo() {
         return bankCardNo;
         return bankCardNo;
     }
     }
 
 
     /**
     /**
      * 设置银行卡号。
      * 设置银行卡号。
+     *
      * @param bankCardNo 银行卡号
      * @param bankCardNo 银行卡号
      */
      */
     public void setBankCardNo(String bankCardNo) {
     public void setBankCardNo(String bankCardNo) {
@@ -139,17 +122,18 @@ public class EmployeeRosterInfo {
     }
     }
 
 
     /**
     /**
-     * 获取开户行。
-     * @return 开户行
+     * 获取开户行名称。
+     *
+     * @return 开户行名称
      */
      */
-    @JsonProperty("bank_name")
     public String getBankName() {
     public String getBankName() {
         return bankName;
         return bankName;
     }
     }
 
 
     /**
     /**
-     * 设置开户行。
-     * @param bankName 开户行
+     * 设置开户行名称。
+     *
+     * @param bankName 开户行名称
      */
      */
     public void setBankName(String bankName) {
     public void setBankName(String bankName) {
         this.bankName = bankName;
         this.bankName = bankName;
@@ -157,6 +141,7 @@ public class EmployeeRosterInfo {
 
 
     /**
     /**
      * 获取支行联行号。
      * 获取支行联行号。
+     *
      * @return 支行联行号
      * @return 支行联行号
      */
      */
     @JsonProperty("bank_branch_code")
     @JsonProperty("bank_branch_code")
@@ -166,6 +151,7 @@ public class EmployeeRosterInfo {
 
 
     /**
     /**
      * 设置支行联行号。
      * 设置支行联行号。
+     *
      * @param bankBranchCode 支行联行号
      * @param bankBranchCode 支行联行号
      */
      */
     public void setBankBranchCode(String bankBranchCode) {
     public void setBankBranchCode(String bankBranchCode) {

+ 1 - 2
src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterRepository.java

@@ -154,14 +154,13 @@ public class EmployeeRosterRepository {
         info.setDingtalkId(resultSet.getString("dingtalk_id"));
         info.setDingtalkId(resultSet.getString("dingtalk_id"));
         info.setEmployeeNo(resultSet.getString("employee_no"));
         info.setEmployeeNo(resultSet.getString("employee_no"));
         info.setUsername(resultSet.getString("username"));
         info.setUsername(resultSet.getString("username"));
-        info.setBankCardNo(resultSet.getString("bank_card_no"));
-        info.setBankName(resultSet.getString("bank_name"));
         info.setJobTitle(resultSet.getString("job_title"));
         info.setJobTitle(resultSet.getString("job_title"));
         info.setJobTitleId(resultSet.getString("job_title_id"));
         info.setJobTitleId(resultSet.getString("job_title_id"));
         info.setJobGrade(resultSet.getString("job_grade"));
         info.setJobGrade(resultSet.getString("job_grade"));
         info.setJobGradeId(resultSet.getString("job_grade_id"));
         info.setJobGradeId(resultSet.getString("job_grade_id"));
         info.setDepartment(resultSet.getString("department"));
         info.setDepartment(resultSet.getString("department"));
         info.setFirstDepartment(resultSet.getString("first_department"));
         info.setFirstDepartment(resultSet.getString("first_department"));
+        // 银行信息统一在末尾映射,避免与同事合并后重复赋值
         info.setBankCardNo(resultSet.getString("bank_card_no"));
         info.setBankCardNo(resultSet.getString("bank_card_no"));
         info.setBankName(resultSet.getString("bank_name"));
         info.setBankName(resultSet.getString("bank_name"));
         info.setBankBranchCode(resultSet.getString("bank_branch_code"));
         info.setBankBranchCode(resultSet.getString("bank_branch_code"));

+ 3 - 1
src/main/java/com/malk/minglei/service/impl/yida/MiscReceiptIssueFormPushHandler.java

@@ -30,7 +30,7 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
 
 
     private static final Logger LOGGER = LoggerFactory.getLogger(MiscReceiptIssueFormPushHandler.class);
     private static final Logger LOGGER = LoggerFactory.getLogger(MiscReceiptIssueFormPushHandler.class);
     private static final String START_PROCESS_ENDPOINT = "/v1.0/yida/processes/instances/start";
     private static final String START_PROCESS_ENDPOINT = "/v1.0/yida/processes/instances/start";
-    public static final String TYPE = "cint301";
+    public static final String TYPE = "aint302";
     private static final String FORM_NAME = "miscReceiptIssue";
     private static final String FORM_NAME = "miscReceiptIssue";
     private static final String FORM_UUID = "FORM-4B08ADABCDAA45788C043729A855E03ED76G";
     private static final String FORM_UUID = "FORM-4B08ADABCDAA45788C043729A855E03ED76G";
     private static final String PROCESS_CODE = "TPROC--MQD66371C1N84FPSJZO4W79IN4BW2AE7I08TM0";
     private static final String PROCESS_CODE = "TPROC--MQD66371C1N84FPSJZO4W79IN4BW2AE7I08TM0";
@@ -48,6 +48,7 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
     private static final String REASON_CODE_FIELD_ID = "textField_mt80nq6t";
     private static final String REASON_CODE_FIELD_ID = "textField_mt80nq6t";
     private static final String REASON_DESC_FIELD_ID = "textField_mt80nq6v";
     private static final String REASON_DESC_FIELD_ID = "textField_mt80nq6v";
     private static final String REMARK_FIELD_ID = "textField_mt7zi4fz";
     private static final String REMARK_FIELD_ID = "textField_mt7zi4fz";
+    private static final String DOC_NO_FIELD_ID = "textField_mvc41oom";
 
 
     private static final String DETAIL_TABLE_FIELD_ID = "tableField_msh186sv";
     private static final String DETAIL_TABLE_FIELD_ID = "tableField_msh186sv";
     private static final String DETAIL_SEQ_FIELD_ID = "textField_mt7zi4g1";
     private static final String DETAIL_SEQ_FIELD_ID = "textField_mt7zi4g1";
@@ -163,6 +164,7 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
         putIfNotBlank(formData, REASON_CODE_FIELD_ID, value(main, "inba007"));
         putIfNotBlank(formData, REASON_CODE_FIELD_ID, value(main, "inba007"));
         putIfNotBlank(formData, REASON_DESC_FIELD_ID, value(main, "oocql004"));
         putIfNotBlank(formData, REASON_DESC_FIELD_ID, value(main, "oocql004"));
         putIfNotBlank(formData, REMARK_FIELD_ID, value(main, "inba008"));
         putIfNotBlank(formData, REMARK_FIELD_ID, value(main, "inba008"));
+        putIfNotBlank(formData, DOC_NO_FIELD_ID, docNo);
 
 
         List<Map<String, Object>> rows = toDetailRows(detailRows);
         List<Map<String, Object>> rows = toDetailRows(detailRows);
         if (!rows.isEmpty()) {
         if (!rows.isEmpty()) {

+ 2 - 0
src/main/resources/application-prod.yml

@@ -1,6 +1,8 @@
 minglei:
 minglei:
   port:
   port:
     supplier-price-approval-url: ${MINGLEI_SUPPLIER_PRICE_APPROVAL_URL:http://10.0.2.60/wstopprd/ws/r/awsp920}
     supplier-price-approval-url: ${MINGLEI_SUPPLIER_PRICE_APPROVAL_URL:http://10.0.2.60/wstopprd/ws/r/awsp920}
+  reply-erp:
+    url: ${MINGLEI_REPLY_ERP_URL:http://10.0.2.60/wstopprd/ws/r/awsp920}
   erp-t100:
   erp-t100:
     request:
     request:
       service-id: topprd
       service-id: topprd

+ 2 - 0
src/main/resources/application-test.yml

@@ -1,6 +1,8 @@
 minglei:
 minglei:
   port:
   port:
     supplier-price-approval-url: ${MINGLEI_SUPPLIER_PRICE_APPROVAL_URL:http://10.0.2.60/wtoptst/ws/r/awsp920}
     supplier-price-approval-url: ${MINGLEI_SUPPLIER_PRICE_APPROVAL_URL:http://10.0.2.60/wtoptst/ws/r/awsp920}
+  reply-erp:
+    url: ${MINGLEI_REPLY_ERP_URL:http://10.0.2.60/wtoptst/ws/r/awsp920}
   erp-t100:
   erp-t100:
     request:
     request:
       service-id: toptst
       service-id: toptst

+ 2 - 0
src/main/resources/application.yml

@@ -44,6 +44,8 @@ minglei:
     connect-timeout-millis: ${MINGLEI_BANKING_CONNECT_TIMEOUT_MILLIS:10000}
     connect-timeout-millis: ${MINGLEI_BANKING_CONNECT_TIMEOUT_MILLIS:10000}
     read-timeout-millis: ${MINGLEI_BANKING_READ_TIMEOUT_MILLIS:30000}
     read-timeout-millis: ${MINGLEI_BANKING_READ_TIMEOUT_MILLIS:30000}
     supplier-price-approval-key: ${MINGLEI_SUPPLIER_PRICE_APPROVAL_KEY:f5458f5c0f9022db743a7c0710145903}
     supplier-price-approval-key: ${MINGLEI_SUPPLIER_PRICE_APPROVAL_KEY:f5458f5c0f9022db743a7c0710145903}
+  reply-erp:
+    key: ${MINGLEI_REPLY_ERP_KEY:f5458f5c0f9022db743a7c0710145903}
   erp-t100:
   erp-t100:
     request:
     request:
       service-id: toptst
       service-id: toptst

+ 28 - 2
src/test/java/com/malk/minglei/controller/MingleiPortControllerTest.java

@@ -1,7 +1,9 @@
 package com.malk.minglei.controller;
 package com.malk.minglei.controller;
 
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.malk.minglei.dto.MingleiPortRequest;
 import com.malk.minglei.dto.MingleiPortRequest;
 import com.malk.minglei.service.port.MingleiPortService;
 import com.malk.minglei.service.port.MingleiPortService;
+import com.malk.minglei.service.port.SupplierPriceApprovalProcessService;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Test;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MockMvc;
@@ -10,6 +12,8 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import java.util.Collections;
 import java.util.Collections;
 
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verify;
@@ -27,7 +31,7 @@ class MingleiPortControllerTest {
         MingleiPortService service = mock(MingleiPortService.class);
         MingleiPortService service = mock(MingleiPortService.class);
         given(service.submitSupplierPriceApproval(any(MingleiPortRequest.class)))
         given(service.submitSupplierPriceApproval(any(MingleiPortRequest.class)))
                 .willReturn(Collections.<String, Object>singletonMap("srvcode", "000"));
                 .willReturn(Collections.<String, Object>singletonMap("srvcode", "000"));
-        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new MingleiPortController(service)).build();
+        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller(service, mock(SupplierPriceApprovalProcessService.class))).build();
 
 
         mockMvc.perform(post("/api/minglei/ports/supplier-price-approvals")
         mockMvc.perform(post("/api/minglei/ports/supplier-price-approvals")
                         .contentType(MediaType.APPLICATION_JSON)
                         .contentType(MediaType.APPLICATION_JSON)
@@ -41,7 +45,7 @@ class MingleiPortControllerTest {
     @Test
     @Test
     void rejectsRequestWithoutRequiredHeadField() throws Exception {
     void rejectsRequestWithoutRequiredHeadField() throws Exception {
         MingleiPortService service = mock(MingleiPortService.class);
         MingleiPortService service = mock(MingleiPortService.class);
-        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new MingleiPortController(service)).build();
+        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller(service, mock(SupplierPriceApprovalProcessService.class))).build();
 
 
         mockMvc.perform(post("/api/minglei/ports/supplier-price-approvals")
         mockMvc.perform(post("/api/minglei/ports/supplier-price-approvals")
                         .contentType(MediaType.APPLICATION_JSON)
                         .contentType(MediaType.APPLICATION_JSON)
@@ -50,6 +54,28 @@ class MingleiPortControllerTest {
                 .andExpect(jsonPath("$.message").value("Request validation failed"));
                 .andExpect(jsonPath("$.message").value("Request validation failed"));
     }
     }
 
 
+    @Test
+    void submitsPackagingMaterialPriceApprovalFromYidaInstance() throws Exception {
+        MingleiPortService portService = mock(MingleiPortService.class);
+        SupplierPriceApprovalProcessService processService = mock(SupplierPriceApprovalProcessService.class);
+        given(processService.submitByInstanceIds(eq("FORM-69A12600F87349D3B653BF86E419224DLIX9"),
+                eq("form-instance-1"), isNull())).willReturn(Collections.<String, Object>singletonMap("srvcode", "000"));
+        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller(portService, processService)).build();
+
+        mockMvc.perform(post("/api/minglei/ports/supplier-price-approvals")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content("{\"formId\":\"FORM-69A12600F87349D3B653BF86E419224DLIX9\",\"formInstanceId\":\"form-instance-1\"}"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.srvcode").value("000"));
+
+        verify(processService).submitByInstanceIds("FORM-69A12600F87349D3B653BF86E419224DLIX9", "form-instance-1", null);
+    }
+
+    private MingleiPortController controller(MingleiPortService portService,
+                                             SupplierPriceApprovalProcessService processService) {
+        return new MingleiPortController(portService, processService, new ObjectMapper());
+    }
+
     private String validRequestJson() {
     private String validRequestJson() {
         return "{\"head\":{\"pmdidocno\":\"CH3\",\"pmdi001\":\"N\",\"pmdi034\":\"605188\","
         return "{\"head\":{\"pmdidocno\":\"CH3\",\"pmdi001\":\"N\",\"pmdi034\":\"605188\","
                 + "\"pmdidocdt\":\"2026-08-06\",\"pmdi004\":\"517022\",\"pmdi015\":\"2026-08-06\","
                 + "\"pmdidocdt\":\"2026-08-06\",\"pmdi004\":\"517022\",\"pmdi015\":\"2026-08-06\","

+ 1 - 0
src/test/java/com/malk/minglei/service/impl/yida/MiscReceiptIssueFormPushHandlerTest.java

@@ -110,6 +110,7 @@ class MiscReceiptIssueFormPushHandlerTest {
         assertEquals("RSN-001", formData.getString("textField_mt80nq6t"));
         assertEquals("RSN-001", formData.getString("textField_mt80nq6t"));
         assertEquals("reason-desc-main", formData.getString("textField_mt80nq6v"));
         assertEquals("reason-desc-main", formData.getString("textField_mt80nq6v"));
         assertEquals("remark-001", formData.getString("textField_mt7zi4fz"));
         assertEquals("remark-001", formData.getString("textField_mt7zi4fz"));
+        assertEquals("MS-001", formData.getString("textField_mvc41oom"));
 
 
         JSONArray detailRows = formData.getJSONArray("tableField_msh186sv");
         JSONArray detailRows = formData.getJSONArray("tableField_msh186sv");
         assertEquals(1, detailRows.size());
         assertEquals(1, detailRows.size());