Pārlūkot izejas kodu

feat: 核价料号改读新字段,并按订单号查询数量和型号

客户订单项次同时返回 xmda004。

Co-authored-by: Cursor <cursoragent@cursor.com>
admin 2 dienas atpakaļ
vecāks
revīzija
f50770e532

+ 24 - 0
DATA-DICTIONARY-API.md

@@ -44,6 +44,8 @@
 | `erp-item-view` | ERP 料件视图 | `ent` | `keyword` |
 | `erp-sales-order` | 销售订单开窗 | `t100id` | `keyword` |
 | `erp-sales-order-line` | 客户订单项次 | 无 | `keyword` |
+| `erp-order-qty` | 订单号查订单数量 | `ddh` | 无 |
+| `erp-order-product-model` | 订单号查产品型号 | `ddh` | 无 |
 | `erp-production-order` | 生产令开窗 | `t100id`、`userId` | 无 |
 | `erp-supplier-disabled` | ERP 供应商禁用视图 | `t100id`、`ent` | `keyword` |
 | `erp-active-item` | T100 有效料号 | 无 | `keyword` |
@@ -127,6 +129,28 @@ GET /api/dictionary/erp-sales-order-line?keyword=SO-001-10
 - SQL 使用 `T100客户订单项次.sql`,公司 `86`、据点 `ML` 及原有筛选条件保持不变。
 - `keyword`:可选,普通文本,不是数组。对返回列 `docseq` 模糊匹配。例如 `keyword=SO-001-10`。
 
+### 4.5.2 订单号查订单数量
+
+```http
+GET /api/dictionary/erp-order-qty?ddh=SO-001
+```
+
+- SQL 使用 `订单号查订单数量.sql`。表名加 `DSDATA`,公司 `86`、据点 `ML` 及原有筛选条件保持不变。
+- `ddh`:订单号,必填,精确匹配 `xmdcdocno`。
+- 只汇总 `xmdc019 = '1'` 且料号以 `1` 开头的明细。
+- 返回字段:`xmdc007`。没有符合条件的明细时仍返回一行,数量为 `0`。
+
+### 4.5.3 订单号查产品型号
+
+```http
+GET /api/dictionary/erp-order-product-model?ddh=SO-001
+```
+
+- SQL 使用 `订单号查产品型号.sql`。表名加 `DSDATA`,公司 `86`、据点 `ML` 及原有筛选条件保持不变。
+- `ddh`:订单号,必填,精确匹配 `xmdcdocno`。
+- 只取 `xmdc019 = '1'` 且料号以 `1` 开头的明细,返回这些明细规格 `imaal004` 的最大值。
+- 返回字段:`imaal004`。没有符合条件的明细时仍返回一行,型号为空。
+
 ### 4.6 生产令开窗
 
 ```http

+ 64 - 1
src/main/java/com/malk/minglei/service/impl/dictionary/DictionaryQueryServiceImpl.java

@@ -111,7 +111,7 @@ public class DictionaryQueryServiceImpl implements DictionaryQueryService {
                     "xmdc001, " +
                     "(select imaal003 from DSDATA.imaal_t where imaalent = xmdcent and imaal001 = xmdc001 and imaal002 = 'zh_CN') xmdc001_desc, " +
                     "(select imaal004 from DSDATA.imaal_t where imaalent = xmdcent and imaal001 = xmdc001 and imaal002 = 'zh_CN') xmdc001_spec, " +
-                    "xmdc002, xmdc007, xmdd006, xmdd014, xmdd015, ooff013 " +
+                    "xmdc002, xmdc007, xmdd006, xmdd014, xmdd015, ooff013, xmda004 " +
                     "from DSDATA.xmda_t " +
                     "left join DSDATA.xmdc_t on xmdaent = xmdcent and xmdasite = xmdcsite and xmdadocno = xmdcdocno " +
                     "left join DSDATA.xmdd_t on xmdcent = xmddent and xmdcsite = xmddsite and xmdcdocno = xmdddocno and xmdcseq = xmddseq " +
@@ -122,6 +122,36 @@ public class DictionaryQueryServiceImpl implements DictionaryQueryService {
     private static final String ERP_SALES_ORDER_LINE_COUNT_SQL =
             "select count(1) from (" + ERP_SALES_ORDER_LINE_DATA_SQL + ")";
 
+    // 订单号查订单数量.sql。表名加 DSDATA,供字典库账号访问。ddh 精确匹配订单号。
+    private static final String ERP_ORDER_QTY_DICTIONARY_CODE = "erp-order-qty";
+    private static final String ERP_ORDER_QTY_DATA_SQL =
+            "select nvl(sum(xmdc007), 0) as xmdc007 " +
+                    "from DSDATA.xmdc_t " +
+                    "where xmdcdocno = :ddh " +
+                    "and xmdcent = '86' " +
+                    "and xmdcsite = 'ML' " +
+                    "and xmdc019 = '1' " +
+                    "and substr(xmdc001, 1, 1) = '1'";
+    private static final String ERP_ORDER_QTY_COUNT_SQL =
+            "select count(1) from (" + ERP_ORDER_QTY_DATA_SQL + ")";
+
+    // 订单号查产品型号.sql。表名加 DSDATA。ddh 精确匹配订单号。
+    private static final String ERP_ORDER_PRODUCT_MODEL_DICTIONARY_CODE = "erp-order-product-model";
+    private static final String ERP_ORDER_PRODUCT_MODEL_DATA_SQL =
+            "select max(a.imaal004) as imaal004 " +
+                    "from DSDATA.xmdc_t b " +
+                    "left join DSDATA.imaal_t a " +
+                    "on a.imaal001 = b.xmdc001 " +
+                    "and a.imaalent = b.xmdcent " +
+                    "and a.imaal002 = 'zh_CN' " +
+                    "where b.xmdcdocno = :ddh " +
+                    "and b.xmdcent = '86' " +
+                    "and b.xmdcsite = 'ML' " +
+                    "and b.xmdc019 = '1' " +
+                    "and substr(b.xmdc001, 1, 1) = '1'";
+    private static final String ERP_ORDER_PRODUCT_MODEL_COUNT_SQL =
+            "select count(1) from (" + ERP_ORDER_PRODUCT_MODEL_DATA_SQL + ")";
+
     // 生产令开窗沿用销售订单主明细信息,但按用户所属部门筛选,且不限制未交数量。
     private static final String ERP_PRODUCTION_ORDER_DICTIONARY_CODE = "erp-production-order";
     private static final String ERP_PRODUCTION_ORDER_DATA_SQL =
@@ -381,6 +411,12 @@ public class DictionaryQueryServiceImpl implements DictionaryQueryService {
         if (ERP_SALES_ORDER_LINE_DICTIONARY_CODE.equals(dictionaryCode)) {
             return queryErpSalesOrderLine(safeFilters);
         }
+        if (ERP_ORDER_QTY_DICTIONARY_CODE.equals(dictionaryCode)) {
+            return queryErpOrderQty(safeFilters);
+        }
+        if (ERP_ORDER_PRODUCT_MODEL_DICTIONARY_CODE.equals(dictionaryCode)) {
+            return queryErpOrderProductModel(safeFilters);
+        }
         if (ERP_PRODUCTION_ORDER_DICTIONARY_CODE.equals(dictionaryCode)) {
             return queryErpProductionOrder(safeFilters);
         }
@@ -557,6 +593,33 @@ public class DictionaryQueryServiceImpl implements DictionaryQueryService {
         return queryPage(dataSql, countSql, parameters, createPageRequest());
     }
 
+    /**
+     * 按订单号汇总符合条件的订单数量。
+     *
+     * @param filters 调用方传入的筛选条件
+     * @return 订单数量,字段 xmdc007
+     */
+    private Map<String, Object> queryErpOrderQty(Map<String, String> filters) {
+        validateFilters(filters, ERP_ORDER_QTY_DICTIONARY_CODE, "ddh");
+        MapSqlParameterSource parameters = new MapSqlParameterSource();
+        parameters.addValue("ddh", requireTextFilter(filters, "ddh"));
+        return queryPage(ERP_ORDER_QTY_DATA_SQL, ERP_ORDER_QTY_COUNT_SQL, parameters, createPageRequest());
+    }
+
+    /**
+     * 按订单号取符合条件明细的产品型号。
+     *
+     * @param filters 调用方传入的筛选条件
+     * @return 产品型号,字段 imaal004
+     */
+    private Map<String, Object> queryErpOrderProductModel(Map<String, String> filters) {
+        validateFilters(filters, ERP_ORDER_PRODUCT_MODEL_DICTIONARY_CODE, "ddh");
+        MapSqlParameterSource parameters = new MapSqlParameterSource();
+        parameters.addValue("ddh", requireTextFilter(filters, "ddh"));
+        return queryPage(ERP_ORDER_PRODUCT_MODEL_DATA_SQL, ERP_ORDER_PRODUCT_MODEL_COUNT_SQL,
+                parameters, createPageRequest());
+    }
+
     /**
      * 查询指定据点及指定用户所属部门范围内的生产令开窗数据。
      * 调用方传入钉钉 userId,服务先转换为 ERP 使用的员工工号后再执行 Oracle 查询。

+ 1 - 1
src/main/java/com/malk/minglei/service/impl/port/SupplierPriceApprovalProcessServiceImpl.java

@@ -85,7 +85,7 @@ public class SupplierPriceApprovalProcessServiceImpl implements SupplierPriceApp
                 "employeeField_msh186rr", "selectField_mszqctpc", "textField_mvhhjsy8",
                 "dateField_mszqctpf", "textField_mvius864", "dateField_mszqctpx",
                 "dateField_mszqctpy", "selectField_mszqctq2", "serialNumberField_msmwc0yx",
-                "tableField_mszqctqd", "selectField_mvhrhghk", "numberField_mszqctqh",
+                "tableField_mszqctqd", "selectField_mw00yr4j", "numberField_mszqctqh",
                 "textField_mszqctql"));
         //供应商价格审批单(暂估)
         mappings.put(ESTIMATED_SUPPLIER_PRICE_APPROVAL_FORM_ID, new SupplierPriceApprovalFormMapping(

+ 76 - 0
src/test/java/com/malk/minglei/service/impl/dictionary/DictionaryQueryServiceImplTest.java

@@ -324,6 +324,7 @@ class DictionaryQueryServiceImplTest {
         ArgumentCaptor<SqlParameterSource> countParameters = ArgumentCaptor.forClass(SqlParameterSource.class);
         verify(jdbcTemplate).queryForObject(countSql.capture(), countParameters.capture(), eq(Long.class));
         assertEquals(true, countSql.getValue().contains("xmdadocno||'-'||xmdcseq docseq"));
+        assertEquals(true, countSql.getValue().contains("ooff013, xmda004"));
         assertEquals(true, countSql.getValue().contains("where xmdaent =86 and xmdasite = 'ML'"));
         assertEquals(true, countSql.getValue().contains("where docseq like :keyword"));
         assertEquals("%SO-001-10%", countParameters.getValue().getValue("keyword"));
@@ -337,6 +338,81 @@ class DictionaryQueryServiceImplTest {
         assertEquals(Long.valueOf(3L), result.get("total"));
     }
 
+    /**
+     * 验证订单号查订单数量按订单号精确汇总,并固定返回前 100 条记录。
+     */
+    @Test
+    void queryErpOrderQtyRequiresOrderNumber() {
+        ObjectProvider<NamedParameterJdbcTemplate> jdbcTemplateProvider = mock(ObjectProvider.class);
+        NamedParameterJdbcTemplate jdbcTemplate = mock(NamedParameterJdbcTemplate.class);
+        given(jdbcTemplateProvider.getIfAvailable()).willReturn(jdbcTemplate);
+        given(jdbcTemplate.queryForObject(anyString(), any(SqlParameterSource.class), eq(Long.class)))
+                .willReturn(Long.valueOf(1L));
+        given(jdbcTemplate.queryForList(anyString(), any(SqlParameterSource.class)))
+                .willReturn(Collections.<Map<String, Object>>emptyList());
+
+        DictionaryQueryServiceImpl service = new DictionaryQueryServiceImpl(jdbcTemplateProvider);
+        Map<String, String> filters = new LinkedHashMap<String, String>();
+        filters.put("ddh", "SO-001");
+
+        Map<String, Object> result = service.query("erp-order-qty", filters);
+
+        ArgumentCaptor<String> countSql = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<SqlParameterSource> countParameters = ArgumentCaptor.forClass(SqlParameterSource.class);
+        verify(jdbcTemplate).queryForObject(countSql.capture(), countParameters.capture(), eq(Long.class));
+        assertEquals(true, countSql.getValue().contains("nvl(sum(xmdc007), 0) as xmdc007"));
+        assertEquals(true, countSql.getValue().contains("from DSDATA.xmdc_t"));
+        assertEquals(true, countSql.getValue().contains("xmdcdocno = :ddh"));
+        assertEquals(true, countSql.getValue().contains("xmdcent = '86'"));
+        assertEquals(true, countSql.getValue().contains("xmdcsite = 'ML'"));
+        assertEquals(true, countSql.getValue().contains("xmdc019 = '1'"));
+        assertEquals(true, countSql.getValue().contains("substr(xmdc001, 1, 1) = '1'"));
+        assertEquals("SO-001", countParameters.getValue().getValue("ddh"));
+        assertEquals(1, result.get("page"));
+        assertEquals(100, result.get("pageSize"));
+        assertEquals(Long.valueOf(1L), result.get("total"));
+
+        Map<String, String> missingOrderNumber = new LinkedHashMap<String, String>();
+        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+                () -> service.query("erp-order-qty", missingOrderNumber));
+        assertEquals("ddh must not be blank", exception.getMessage());
+    }
+
+    /**
+     * 验证订单号查产品型号按订单号精确查询,并固定返回前 100 条记录。
+     */
+    @Test
+    void queryErpOrderProductModelRequiresOrderNumber() {
+        ObjectProvider<NamedParameterJdbcTemplate> jdbcTemplateProvider = mock(ObjectProvider.class);
+        NamedParameterJdbcTemplate jdbcTemplate = mock(NamedParameterJdbcTemplate.class);
+        given(jdbcTemplateProvider.getIfAvailable()).willReturn(jdbcTemplate);
+        given(jdbcTemplate.queryForObject(anyString(), any(SqlParameterSource.class), eq(Long.class)))
+                .willReturn(Long.valueOf(1L));
+        given(jdbcTemplate.queryForList(anyString(), any(SqlParameterSource.class)))
+                .willReturn(Collections.<Map<String, Object>>emptyList());
+
+        DictionaryQueryServiceImpl service = new DictionaryQueryServiceImpl(jdbcTemplateProvider);
+        Map<String, String> filters = new LinkedHashMap<String, String>();
+        filters.put("ddh", "SO-001");
+
+        Map<String, Object> result = service.query("erp-order-product-model", filters);
+
+        ArgumentCaptor<String> countSql = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<SqlParameterSource> countParameters = ArgumentCaptor.forClass(SqlParameterSource.class);
+        verify(jdbcTemplate).queryForObject(countSql.capture(), countParameters.capture(), eq(Long.class));
+        assertEquals(true, countSql.getValue().contains("max(a.imaal004) as imaal004"));
+        assertEquals(true, countSql.getValue().contains("from DSDATA.xmdc_t b"));
+        assertEquals(true, countSql.getValue().contains("left join DSDATA.imaal_t a"));
+        assertEquals(true, countSql.getValue().contains("a.imaal002 = 'zh_CN'"));
+        assertEquals(true, countSql.getValue().contains("b.xmdcdocno = :ddh"));
+        assertEquals(true, countSql.getValue().contains("b.xmdcent = '86'"));
+        assertEquals(true, countSql.getValue().contains("substr(b.xmdc001, 1, 1) = '1'"));
+        assertEquals("SO-001", countParameters.getValue().getValue("ddh"));
+        assertEquals(1, result.get("page"));
+        assertEquals(100, result.get("pageSize"));
+        assertEquals(Long.valueOf(1L), result.get("total"));
+    }
+
     /**
      * 验证生产令开窗按据点和用户所属部门查询,并固定返回前 100 条记录。
      */

+ 7 - 1
src/test/java/com/malk/minglei/service/impl/port/SupplierPriceApprovalProcessServiceImplTest.java

@@ -3,6 +3,7 @@ package com.malk.minglei.service.impl.port;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.malk.minglei.config.YidaApplicationsProperties;
 import com.malk.minglei.dto.EmployeeRosterInfo;
+import com.malk.minglei.dto.MingleiPortRequest;
 import com.malk.minglei.service.employee.EmployeeRosterService;
 import com.malk.minglei.service.port.MingleiPortService;
 import com.malk.minglei.service.yida.YidaFormInstanceClient;
@@ -151,6 +152,11 @@ class SupplierPriceApprovalProcessServiceImplTest {
         service.submitByInstanceIds(SUPPLIER_PRICE_APPROVAL_FORM_ID,
                 "form-instance-001", "process-instance-001");
 
+        ArgumentCaptor<MingleiPortRequest> requestCaptor = ArgumentCaptor.forClass(MingleiPortRequest.class);
+        verify(mingleiPortService).submitSupplierPriceApproval(requestCaptor.capture());
+        Map<?, ?> payload = objectMapper.convertValue(requestCaptor.getValue(), Map.class);
+        List<?> detail = (List<?>) payload.get("detail");
+        assertEquals("ITEM-001", ((Map<?, ?>) detail.get(0)).get("pmdj002"));
         verify(yidaFormInstanceClient).getFormInstance(SUPPLIER_PRICE_APPROVAL_FORM_ID, "form-instance-001");
         ArgumentCaptor<Map<String, Object>> updateBodyCaptor = ArgumentCaptor.forClass(Map.class);
         verify(yidaProcessOpenApiClient).updateProcessInstance(updateBodyCaptor.capture());
@@ -295,7 +301,7 @@ class SupplierPriceApprovalProcessServiceImplTest {
 
     private Map<String, Object> supplierPriceProcessInstance(String formId) {
         Map<String, Object> pricingRow = new LinkedHashMap<String, Object>();
-        pricingRow.put("selectField_mvhrhghk", "ITEM-001");
+        pricingRow.put("selectField_mw00yr4j", "ITEM-001");
         pricingRow.put("selectField_mvanfd8d", "ITEM-001");
         pricingRow.put("selectField_mvejv4mz", "ITEM-001");
         pricingRow.put("selectField_mvf0cizk", "ITEM-001");