# HkInventoryPurchaseOrderServiceImpl.java

    package cloud.wconnect.workperformance.service.hkinventory.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationNumberDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.InternationalizationUtils;
    import cloud.wconnect.commonlib.util.UpdateLogUtil;
    import cloud.wconnect.workperformance.entity.hkinventory.HkInventory;
    import cloud.wconnect.workperformance.entity.hkinventory.HkInventoryItem;
    import cloud.wconnect.workperformance.entity.hkinventory.HkInventoryPurchaseOrder;
    import cloud.wconnect.workperformance.mapper.hkinventory.HkInventoryPurchaseOrderMapper;
    import cloud.wconnect.workperformance.pojo.dto.hkinventory.HkInventoryPurchaseOrderDTO;
    import cloud.wconnect.workperformance.pojo.dto.hkinventory.HkInventoryTaxRateDTO;
    import cloud.wconnect.workperformance.pojo.query.hkinventory.HkInventoryPurchaseOrderQuery;
    import cloud.wconnect.workperformance.pojo.vo.hkinventory.HkInventoryPurchaseOrderVO;
    import cloud.wconnect.workperformance.service.hkinventory.IHkInventoryPurchaseOrderService;
    import cloud.wconnect.workperformance.service.hkinventory.IHkInventoryService;
    import cloud.wconnect.workperformance.service.hkinventory.IHkInventoryTaxRateService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Sets;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;

    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.text.MessageFormat;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 客房库存管理-物品采购订单表 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2025-04-19
     */
    @Service
    @DS("work_performance")
    public class HkInventoryPurchaseOrderServiceImpl extends ServiceImpl<HkInventoryPurchaseOrderMapper, HkInventoryPurchaseOrder> implements IHkInventoryPurchaseOrderService {

        @Autowired
        @Lazy
        private IHkInventoryService hkInventoryService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private UpdateLogUtil updateLogUtil;
        @Autowired
        private IHkInventoryTaxRateService taxRateService;

        /**
         * 指定分类指定房型下的item最新订单
         *
         * @param items item集合
         * @return java.util.List<cloud.wconnect.workperformance.entity.hkinventory.HkInventoryPurchaseOrder>
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public List<HkInventoryPurchaseOrder> listItemsLatestPurchaseOrder(Integer propertyId, List<HkInventoryItem> items) {
            if (CollectionUtils.isEmpty(items)) return Collections.emptyList();
            //指定分类指定房型下的item最新订单
            List<HkInventoryPurchaseOrder> latestPurchaseOrders = baseMapper.listItemsLatestPurchaseOrder(propertyId, items);
            if (CollectionUtils.isEmpty(latestPurchaseOrders)) return Collections.emptyList();
            //根据分类id、房型id、item id、采购时间查询采购单
            return baseMapper.listByCategoryIdAndRoomTypeIdAndItemIdAndPurchaseTime(propertyId, latestPurchaseOrders);
        }

        /**
         * 更新分类名称
         *
         * @param propertyId   资产id
         * @param categoryId   分类id
         * @param categoryName 分类名称
         * @return void
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public void updateCategoryNameByCategoryId(Integer propertyId, Integer categoryId, String categoryName) {
            update(new LambdaUpdateWrapper<HkInventoryPurchaseOrder>()
                    .eq(HkInventoryPurchaseOrder::getPropertyId, propertyId)
                    .eq(HkInventoryPurchaseOrder::getCategoryId, categoryId)
                    .set(HkInventoryPurchaseOrder::getCategoryName, categoryName));
        }

        /**
         * 更新item名称
         *
         * @param propertyId 资产id
         * @param categoryId 分类id
         * @param roomTypeId 房型id
         * @param itemId     item id
         * @param itemName   item名称
         * @return void
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public void updateItemNameByCategoryIdAndRoomTypeIdAndItemId(Integer propertyId, Integer categoryId, Integer roomTypeId, Integer itemId, String itemName) {
            update(new LambdaUpdateWrapper<HkInventoryPurchaseOrder>()
                    .eq(HkInventoryPurchaseOrder::getPropertyId, propertyId)
                    .eq(HkInventoryPurchaseOrder::getCategoryId, categoryId)
                    .eq(HkInventoryPurchaseOrder::getRoomTypeId, roomTypeId)
                    .eq(HkInventoryPurchaseOrder::getItemId, itemId)
                    .set(HkInventoryPurchaseOrder::getItemName, itemName));
        }

        /**
         * 修改房型名称
         *
         * @param propertyId   资产id
         * @param roomTypeId   房型id
         * @param roomTypeName 房型名称
         * @return void
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public void updateRoomTypeNameByRoomTypeId(Integer propertyId, Integer roomTypeId, String roomTypeName) {
            update(new LambdaUpdateWrapper<HkInventoryPurchaseOrder>()
                    .eq(HkInventoryPurchaseOrder::getPropertyId, propertyId)
                    .eq(HkInventoryPurchaseOrder::getRoomTypeId, roomTypeId)
                    .set(HkInventoryPurchaseOrder::getRoomTypeName, roomTypeName));
        }

        /**
         * 分页查询采购单
         *
         * @param propertyDTO 资产DTO
         * @param query       查询条件
         * @return cloud.wconnect.commonlib.pojo.dto.ListPagedDTO<cloud.wconnect.workperformance.pojo.vo.hkinventory.HkInventoryPurchaseOrderVO>
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public ListPagedDTO<HkInventoryPurchaseOrderVO> pageList(PropertyDTO propertyDTO, HkInventoryPurchaseOrderQuery query) {
            //构建查询条件
            LambdaQueryWrapper<HkInventoryPurchaseOrder> queryWrapper = new LambdaQueryWrapper<HkInventoryPurchaseOrder>()
                    .eq(HkInventoryPurchaseOrder::getPropertyId, propertyDTO.getId())
                    .between(Objects.nonNull(query.getStartPurchaseTime()), HkInventoryPurchaseOrder::getPurchaseTime,
                            query.getStartPurchaseTime(), query.getEndPurchaseTime())
                    .like(StringUtils.isNotBlank(query.getCategoryName()), HkInventoryPurchaseOrder::getCategoryName, query.getCategoryName())
                    .like(StringUtils.isNotBlank(query.getRoomTypeName()), HkInventoryPurchaseOrder::getRoomTypeName, query.getRoomTypeName())
                    .like(StringUtils.isNotBlank(query.getItemName()), HkInventoryPurchaseOrder::getItemName, query.getItemName())
                    .orderByDesc(HkInventoryPurchaseOrder::getId);

            List<HkInventoryPurchaseOrder> records;
            long total;
            if (Objects.nonNull(query.getPage()) && Objects.nonNull(query.getSize())) {
                Page<HkInventoryPurchaseOrder> page = page(new Page<>(query.getPage(), query.getSize()), queryWrapper);
                records = page.getRecords();
                total = page.getTotal();
            } else {
                records = list(queryWrapper);
                total = records.size();
            }
            if (total == 0) return new ListPagedDTO<>();

            //汇总年份和月份集合
            //此处会多查询出来数据，比如：2024-1,2025-2 会多查出来 2024-2,2025-1 的数据。
            //指定资产一年12条数据，数据量层面无影响。
            //后续使用时只使用对应月份的数据
            HashSet<Integer> years = Sets.newHashSetWithExpectedSize(records.size());
            HashSet<Integer> monthValues = Sets.newHashSetWithExpectedSize(records.size());
            for (HkInventoryPurchaseOrder record : records) {
                LocalDate localDate = DateTimeUtils.longToLocalDate(record.getPurchaseTime(), propertyDTO.getTimezone());
                years.add(localDate.getYear());
                monthValues.add(localDate.getMonthValue());
            }
            //查询年份和月份下的Housekeeping Inventory
            List<HkInventory> hkInventoryList = hkInventoryService.listByYearsAndMonths(propertyDTO.getId(), years, monthValues);
            Map<String, HkInventory> hkInventoryId2HkInventory = hkInventoryList.stream().collect(
                    Collectors.toMap(item -> {
                        LocalDate localDate = LocalDate.of(item.getYear(), item.getMonth(), 1);
                        return DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYY_MM);
                    }, Function.identity()));

            List<HkInventoryPurchaseOrderVO> hkInventoryPurchaseOrderVOS = beanMapper.mapList(records, HkInventoryPurchaseOrderVO.class, (entity, dto) -> {
                LocalDate localDate = DateTimeUtils.longToLocalDate(entity.getPurchaseTime(), propertyDTO.getTimezone());
                String yearMonth = DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYY_MM);
                HkInventory hkInventoryByYearMonth = hkInventoryId2HkInventory.get(yearMonth);
                if (Objects.nonNull(hkInventoryByYearMonth)) {
                    dto.setYearMonth(yearMonth);
                    dto.setStatus(hkInventoryByYearMonth.getStatus());
                }
            });
            return new ListPagedDTO<>(total, hkInventoryPurchaseOrderVOS);
        }

        /**
         * 校验采购单是否存在
         *
         * @param purchaseOrderId 采购单id
         * @return cloud.wconnect.workperformance.entity.hkinventory.HkInventoryPurchaseOrder
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public HkInventoryPurchaseOrder checkExist(Integer purchaseOrderId) {
            HkInventoryPurchaseOrder hkInventoryPurchaseOrder = getById(purchaseOrderId);
            if (Objects.isNull(hkInventoryPurchaseOrder)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Housekeeping Inventory purchase order not found,id=" + purchaseOrderId);
            }
            return hkInventoryPurchaseOrder;
        }

        /**
         * 校验采购单是否归属于当前资产
         *
         * @param hkInventoryPurchaseOrder 采购单
         * @param propertyId               资产id
         * @return void
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public void checkBelongProperty(HkInventoryPurchaseOrder hkInventoryPurchaseOrder, Integer propertyId) {
            if (!hkInventoryPurchaseOrder.getPropertyId().equals(propertyId)) {
                throw new BadRequestException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(), BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("The hkInventoryPurchaseOrder not belong currentPropertyId, hkInventoryPurchaseOrderId:{0}, belongPropertyId:{1}, currentPropertyId:{2}",
                                hkInventoryPurchaseOrder.getId(), hkInventoryPurchaseOrder.getPropertyId(), propertyId));
            }
        }

        /**
         * 新增采购单
         *
         * @param propertyDTO      资产DTO
         * @param purchaseOrderDTO 采购单DTO
         * @param loginUserId      登录用户id
         * @return java.lang.Integer
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public Integer insert(PropertyDTO propertyDTO, HkInventoryPurchaseOrderDTO purchaseOrderDTO, int loginUserId) {
            Integer propertyId = propertyDTO.getId();
            InternationalizationNumberDTO number = propertyDTO.getInternationalization().getNumber();
            String calculationRule = number.getCalculationRule();
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(calculationRule);

            //获取税率
            HkInventoryTaxRateDTO hkInventoryTaxRateDTO = taxRateService.getByPropertyId(propertyId);

            HkInventoryPurchaseOrder hkInventoryPurchaseOrder = beanMapper.map(purchaseOrderDTO, HkInventoryPurchaseOrder.class);
            hkInventoryPurchaseOrder.setTenantId(propertyDTO.getTenantId());
            hkInventoryPurchaseOrder.setPropertyId(propertyId);

            BigDecimal priceCs = purchaseOrderDTO.getPriceCs();
            Integer unitPerCs = purchaseOrderDTO.getUnitPerCs();
            //计算单价
            BigDecimal unitCost = priceCs.divide(new BigDecimal(unitPerCs), BaseConstant.DATA_PRECISION, roundingMode);
            //计算税后单价
            BigDecimal afterTaxUnitCost = unitCost.multiply(new BigDecimal(1).add(hkInventoryTaxRateDTO.getTaxRate()));
            hkInventoryPurchaseOrder.setUnitCost(afterTaxUnitCost);
            //购买的数量
            int purchaseCount = purchaseOrderDTO.getQty() * unitPerCs;
            hkInventoryPurchaseOrder.setPurchaseCount(purchaseCount);
            hkInventoryPurchaseOrder.setUpdateLog(updateLogUtil.get(loginUserId));
            //新增采购单
            save(hkInventoryPurchaseOrder);

            return hkInventoryPurchaseOrder.getId();
        }

        /**
         * 修改采购单
         *
         * @param propertyId       资产id
         * @param oriPurchaseOrder 修改前的采购单
         * @param purchaseOrderDTO 修改后的采购单DTO
         * @param loginUserId      登录用户id
         * @return void
         * @author lichen
         * @date 2025/4/23
         */
        @Override
        public void update(int propertyId, HkInventoryPurchaseOrder oriPurchaseOrder, HkInventoryPurchaseOrderDTO purchaseOrderDTO, Integer loginUserId) {
            //国际化配置相关参数
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            String calculationRule = propertyDTO.getInternationalization().getNumber().getCalculationRule();
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(calculationRule);

            HkInventoryTaxRateDTO hkInventoryTaxRateDTO = taxRateService.getByPropertyId(propertyId);

            oriPurchaseOrder.setQty(purchaseOrderDTO.getQty());
            oriPurchaseOrder.setUnitPerCs(purchaseOrderDTO.getUnitPerCs());
            oriPurchaseOrder.setPriceCs(purchaseOrderDTO.getPriceCs());
            oriPurchaseOrder.setPurchaseTime(purchaseOrderDTO.getPurchaseTime());
            oriPurchaseOrder.setUpdateLog(updateLogUtil.append(oriPurchaseOrder.getUpdateLog(), loginUserId));

            //计算单价
            BigDecimal unitCost = purchaseOrderDTO.getPriceCs().divide(new BigDecimal(purchaseOrderDTO.getUnitPerCs()),
                    BaseConstant.DATA_PRECISION, roundingMode);
            //计算税后单价
            BigDecimal afterTaxUnitCost = unitCost.multiply(new BigDecimal(1).add(hkInventoryTaxRateDTO.getTaxRate()));
            oriPurchaseOrder.setUnitCost(afterTaxUnitCost);

            //购买的数量
            int purchaseCount = purchaseOrderDTO.getQty() * purchaseOrderDTO.getUnitPerCs();
            oriPurchaseOrder.setPurchaseCount(purchaseCount);

            //修改采购单
            updateById(oriPurchaseOrder);
        }

        /**
         * 指定客房库存管理下的所有订单
         *
         * @param propertyId  资产id
         * @param hkInventory 客房库存管理
         * @return java.util.List<cloud.wconnect.workperformance.entity.hkinventory.HkInventoryPurchaseOrder>
         * @author lichen
         * @date 2025/4/25
         */
        @Override
        public List<HkInventoryPurchaseOrder> listByHkInventoryId(Integer propertyId, HkInventory hkInventory) {
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            //指定月份第一天0点
            LocalDateTime localDateTime = LocalDateTime.of(hkInventory.getYear(), hkInventory.getMonth(),
                    1, 0, 0, 0);
            long startTime = DateTimeUtils.localDateTimeToTimestamp(localDateTime, propertyDTO.getTimezone());
            //下个月第一天0点
            long endTime = DateTimeUtils.localDateTimeToTimestamp(localDateTime.plusMonths(1), propertyDTO.getTimezone());
            return list(new LambdaQueryWrapper<HkInventoryPurchaseOrder>().eq(HkInventoryPurchaseOrder::getPropertyId, propertyId)
                    .ge(HkInventoryPurchaseOrder::getPurchaseTime, startTime)
                    .lt(HkInventoryPurchaseOrder::getPurchaseTime, endTime));
        }
    }