diff --git a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/application/service/contractManage/ContractManageApplicationService.java b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/application/service/contractManage/ContractManageApplicationService.java index 14db5245a..e6cc462bf 100644 --- a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/application/service/contractManage/ContractManageApplicationService.java +++ b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/application/service/contractManage/ContractManageApplicationService.java @@ -337,6 +337,99 @@ public class ContractManageApplicationService { return result; } + /** 单据类型「仓库进出仓单」编码(与 BMS / WMS 推送一致) */ + public static final String DOCUMENT_TYPE_CODE_WAREHOUSE_IN_OUT_ORDER = "WMS_ckjccd"; + + /** + * 按结算主体取仓储合同结算设置中,单据类型包含指定类型的费用科目编码(按明细行顺序,可重复,调用方去重)。 + * 科目编码优先取二级 {@code second_subject_code},为空则取一级 {@code first_subject_code}。 + * settlementCustomersCode 为空时与同模块合同逻辑一致:使用通用仓储合同(commonContractFlag=1)。 + */ + public List listSecondSubjectCodesBySettlementAndDocumentType(String settlementCustomersCode, + Long organizationId, + Long topOrganizationId, + String documentTypeCode) { + if (StringUtils.isEmpty(documentTypeCode)) { + return Collections.emptyList(); + } + ContractManagePO contractManagePO = getInfoByCustomerAndOrganizationId(settlementCustomersCode, 1, topOrganizationId, organizationId); + if (contractManagePO == null || contractManagePO.getContractManageId() == null) { + return Collections.emptyList(); + } + List detailPOS = contractManageDetailDomainService.queryListByManageId(contractManagePO.getContractManageId()); + if (detailPOS == null || detailPOS.isEmpty()) { + return Collections.emptyList(); + } + List codes = new ArrayList<>(); + for (ContractManageDetailPO detail : detailPOS) { + if (detail == null) { + continue; + } + String subjectCode = resolveContractDetailExpenseSubjectCode(detail); + if (StringUtils.isEmpty(subjectCode)) { + continue; + } + if (!matchesContractDetailWarehouseInOut(detail, documentTypeCode)) { + continue; + } + codes.add(subjectCode); + } + return codes; + } + + /** + * 合同结算行的费用科目编码:有二级科目用二级,否则用一级。 + */ + private static String resolveContractDetailExpenseSubjectCode(ContractManageDetailPO detail) { + if (detail == null) { + return null; + } + if (!StringUtils.isEmpty(detail.getSecondSubjectCode())) { + return detail.getSecondSubjectCode().trim(); + } + if (!StringUtils.isEmpty(detail.getFirstSubjectCode())) { + return detail.getFirstSubjectCode().trim(); + } + return null; + } + + /** + * 是否属于「仓库进出仓单」类结算行:优先按单据类型编码精确匹配(含逗号分隔多编码), + * 避免因 BMS 主数据编码与 WMS 计费推送编码(如 WMS_ckjccd)不一致导致筛掉全部行; + * 兼容按单据类型名称包含「仓库进出仓」判定(合同保存时会拼接中文名称)。 + */ + private static boolean matchesContractDetailWarehouseInOut(ContractManageDetailPO detail, String documentTypeCode) { + if (detail == null || StringUtils.isEmpty(documentTypeCode)) { + return false; + } + if (contractDetailContainsDocumentType(detail.getDocumentTypeCode(), documentTypeCode)) { + return true; + } + String typeNames = detail.getDocumentType(); + if (StringUtils.isEmpty(typeNames)) { + return false; + } + return Stream.of(typeNames.split(",")) + .map(String::trim) + .filter(s -> !StringUtils.isEmpty(s)) + .anyMatch(n -> n.contains("仓库进出仓")); + } + + private static boolean contractDetailContainsDocumentType(String detailDocumentTypeCodes, String documentTypeCode) { + if (StringUtils.isEmpty(detailDocumentTypeCodes)) { + return false; + } + return Stream.of(detailDocumentTypeCodes.split(",")) + .map(String::trim) + .filter(s -> !StringUtils.isEmpty(s)) + .anyMatch(code -> code.equalsIgnoreCase(documentTypeCode)); + } + + /** + * 按单据类型取合同结算行上的计费单价(计费策略展开的 isFee 项)。 + *

多条结算明细共用同一计费策略(accounting_strategy_id 相同)时,只展开一次策略参数, + * 避免前端出现重复的单价列。

+ */ public List getPrice(String documentTypeCode, String settlementCustomersCode,Long organizationId,Long topOrganizationId) { List result = new ArrayList<>(); ContractManagePO contractManagePO = getInfoByCustomerAndOrganizationId(settlementCustomersCode, 1, topOrganizationId, organizationId); @@ -346,11 +439,15 @@ public class ContractManageApplicationService { List contractManageDetailPOList = contractManagePO.getContractManageDetailPOList(); if (contractManageDetailPOList != null && contractManageDetailPOList.size() > 0) { List contractManageDetailPOs = contractManageDetailPOList.stream() - .filter(item -> item.getDocumentTypeCode().equals(documentTypeCode)) + .filter(item -> contractDetailContainsDocumentType(item.getDocumentTypeCode(), documentTypeCode)) .collect(Collectors.toList()); if (contractManageDetailPOs != null && contractManageDetailPOs.size() > 0) { + Set expandedAccountingStrategyIds = new HashSet<>(); for (ContractManageDetailPO contractManageDetailPO : contractManageDetailPOs) { Long accountingStrategyId = contractManageDetailPO.getAccountingStrategyId(); + if (accountingStrategyId == null || !expandedAccountingStrategyIds.add(accountingStrategyId)) { + continue; + } List billingParams = contractManageDetailMapper.getBillingParams(accountingStrategyId); if (billingParams != null && billingParams.size() > 0) { //if (billingParams != null && billingParams.size() > 0) { diff --git a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/application/service/expenseAccount/ExpenseAccountApplicationService.java b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/application/service/expenseAccount/ExpenseAccountApplicationService.java index 6b9c350ac..e50c442e8 100644 --- a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/application/service/expenseAccount/ExpenseAccountApplicationService.java +++ b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/application/service/expenseAccount/ExpenseAccountApplicationService.java @@ -9,6 +9,7 @@ import com.mhd.basic.domain.expenseAccount.repository.po.QueryExpenseAccountPO; import com.mhd.basic.domain.expenseAccount.repository.todo.ExpenseAccountDO; import com.mhd.basic.domain.expenseAccount.repository.todo.ExpenseAccountDoMap; import com.mhd.basic.domain.expenseAccount.repository.todo.SearchExpenseAccountDO; +import com.mhd.basic.application.service.contractManage.ContractManageApplicationService; import com.mhd.basic.domain.expenseAccount.service.ExpenseAccountDomainService; import com.mhd.basic.infrastructure.feign.vo.SysDictDataVo; import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountDTO; @@ -27,6 +28,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; @@ -45,8 +47,42 @@ public class ExpenseAccountApplicationService { @Autowired private ExpenseAccountDomainService expenseAccountDomainService; @Autowired + private ContractManageApplicationService contractManageApplicationService; + @Autowired private SystemServiceFeign systemServiceFeign; + /** + * 入库/出库费用登记等场景:按仓储合同「仓库进出仓单」结算行取科目(二级编码优先,否则一级),再关联费用科目主数据。 + *

科目范围以合同结算设置为准,不按费用科目主数据的 in_order_display/out_order_display 再筛,避免合同有 7 条主数据未勾「入库显示」却只剩几条的情况。

+ * settlementCustomersCode 不传或为空时使用通用仓储合同。 + */ + public List queryListFromContractWarehouseInOutSettlement(ExpenseAccountDO expenseAccountDO, String settlementCustomersCode) { + Long topOrgIdForExpense = expenseAccountDO.getTopOrganizationId(); + if (topOrgIdForExpense == null) { + LoginUser lu = SecurityUtils.getLoginUser(); + if (lu != null && lu.getUserPo() != null && lu.getUserPo().getTopOrganizationId() != null) { + topOrgIdForExpense = lu.getUserPo().getTopOrganizationId(); + } + } + List orderedCodes = contractManageApplicationService.listSecondSubjectCodesBySettlementAndDocumentType( + settlementCustomersCode, + expenseAccountDO.getOrganizationId(), + topOrgIdForExpense, + ContractManageApplicationService.DOCUMENT_TYPE_CODE_WAREHOUSE_IN_OUT_ORDER); + log.info("按合同取费用科目编码:topOrgIdForExpense={}, 命中结算行科目数={}", topOrgIdForExpense, + orderedCodes != null ? orderedCodes.size() : 0); + List result = new ArrayList<>(); + for (String code : orderedCodes) { + if (StringUtils.isEmpty(code)) { + continue; + } + ExpenseAccountPO po = expenseAccountDomainService.getInfoByCode2(code.trim(), topOrgIdForExpense); + if (po != null && po.getExpenseAccountId() != null) { + result.add(po); + } + } + return result; + } /** * 分页查询费用科目列表 diff --git a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/domain/expenseAccount/repository/todo/ExpenseAccountDO.java b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/domain/expenseAccount/repository/todo/ExpenseAccountDO.java index cb47009f1..90ab9e727 100644 --- a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/domain/expenseAccount/repository/todo/ExpenseAccountDO.java +++ b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/domain/expenseAccount/repository/todo/ExpenseAccountDO.java @@ -175,4 +175,10 @@ public class ExpenseAccountDO extends BaseVOEntity{ @ApiModelProperty("nc科目编码") private String ncSubjectCode; + + /** 查询参数:是否按仓储合同结算设置拉取科目 */ + private Boolean contractSettlementQuery; + + /** 查询参数:结算主体编码(货主编码等) */ + private String settlementCustomersCode; } \ No newline at end of file diff --git a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/interfaces/dto/expenseAccount/ExpenseAccountDTO.java b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/interfaces/dto/expenseAccount/ExpenseAccountDTO.java index 5bc54f46a..d0f4045fe 100644 --- a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/interfaces/dto/expenseAccount/ExpenseAccountDTO.java +++ b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/interfaces/dto/expenseAccount/ExpenseAccountDTO.java @@ -160,4 +160,13 @@ public class ExpenseAccountDTO extends BaseVOEntity{ @ApiModelProperty("nc科目编码") private String ncSubjectCode; + + /** + * 为 true 时从仓储合同「结算设置」取费用科目(入库/出库费用登记等) + */ + @ApiModelProperty("是否按合同结算设置查询费用科目(入库/出库费用登记,出库建议配合 outOrderDisplay=1)") + private Boolean contractSettlementQuery; + + @ApiModelProperty("结算主体编码(通常为货主编码);不传则按通用仓储合同的结算设置取科目") + private String settlementCustomersCode; } \ No newline at end of file diff --git a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/interfaces/facade/expenseAccount/ExpenseAccountApi.java b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/interfaces/facade/expenseAccount/ExpenseAccountApi.java index 5c39ccc49..e35106693 100644 --- a/mhd-modules/mhd-system/src/main/java/com/mhd/basic/interfaces/facade/expenseAccount/ExpenseAccountApi.java +++ b/mhd-modules/mhd-system/src/main/java/com/mhd/basic/interfaces/facade/expenseAccount/ExpenseAccountApi.java @@ -50,6 +50,12 @@ public class ExpenseAccountApi extends BaseController{ /** * 分页查询费用科目列表 + * + *

入库/出库费用登记等场景传 {@code contractSettlementQuery=true}(与单据类型「仓库进出仓单」合同结算行一致), + * 科目来源、结算主体为空走通用合同等逻辑入库与出库相同。 + * 该路径仅以合同结算行为准拉取费用科目主数据,不会再按 {@code inOrderDisplay}/{@code outOrderDisplay} 二次过滤, + * 以免合同配置了多条但主数据未勾选「入库/出库显示」导致条数被裁掉; + * 若在其它场景需要按展示位收窄列表,请勿传 {@code contractSettlementQuery=true},走默认 {@code queryList} 分页查询即可。

*/ @ApiOperation("查询费用科目列表") @GetMapping("/list") @@ -62,6 +68,16 @@ public class ExpenseAccountApi extends BaseController{ expenseAccountDTO = new ExpenseAccountDTO(); logger.info("expenseAccountDTO 为 null,创建新对象"); } + if (Boolean.TRUE.equals(expenseAccountDTO.getContractSettlementQuery())) { + ExpenseAccountDO contractQueryDO = new ExpenseAccountDO(); + BeanUtils.copyProperties(expenseAccountDTO, contractQueryDO); + logger.info("按合同结算设置查询费用科目,settlementCustomersCode={}", expenseAccountDTO.getSettlementCustomersCode()); + List contractList = + expenseAccountApplicationService.queryListFromContractWarehouseInOutSettlement( + contractQueryDO, expenseAccountDTO.getSettlementCustomersCode()); + logger.info("按合同结算设置查询完成,条数:{}", contractList != null ? contractList.size() : 0); + return getDataTable(contractList != null ? contractList : new ArrayList<>()); + } ExpenseAccountDO expenseAccountDO = new ExpenseAccountDO(); BeanUtils.copyProperties(expenseAccountDTO, expenseAccountDO); logger.info("转换后的 ExpenseAccountDO:{}", expenseAccountDO);