Untitled
package replenishment_inbound_order import ( v2 "code.byted.org/oec/pbgen/go/oec/supply_chain/merchant/v2" "code.byted.org/oec/rpcv2_oec_supply_chain_business_order/kitex_gen/oec/supply_chain/business_order_base" "code.byted.org/oec/rpcv2_oec_supply_chain_planning_calc/kitex_gen/oec/supply_chain/planning_calc" "code.byted.org/oec/rpcv2_oec_supply_chain_warehouse_meta/kitex_gen/oec/supply_chain/warehouse_meta" "code.byted.org/oec/sc_merchant_gateway/consts" "code.byted.org/oec/sc_merchant_gateway/internal/cerr" "code.byted.org/oec/sc_merchant_gateway/util" "context" "sort" "time" ) func (i *ReplenishmentInboundOrderLogicImpl) GetReplenishmentAvailableInboundMethod(ctx context.Context, req *v2.GetReplenishmentAvailableInboundMethodRequest) (*v2.GetReplenishmentAvailableInboundMethodResponse, error) { inboundOrderId := req.GetOrderId() inboundOrderList, err := i.BusinessOrderClient.GetReplenishmentInboundOrder(ctx, []int64{inboundOrderId}) if err != nil { return nil, err } if len(inboundOrderList) == 0 { return nil, cerr.ErrNotExists{Key: "order"} } inboundOrder := inboundOrderList[0] ipeReq := planning_calc.GetInboundMethodRequest{ MerchantId: inboundOrder.MerchantId, EtaRangeList: buildTimeRangeForIpe(req.GetExpectedArrivalTimeRange().GetStart()), GoodsUnits: int64(inboundOrder.GetExpectedCount()), CartonCnt: int64(inboundOrder.GetCartonsQuantity()), NeedInboundMethodList: []planning_calc.InboundMethod{ planning_calc.InboundMethod_D2FC, planning_calc.InboundMethod_SingleHub, planning_calc.InboundMethod_MultiHub, }, } timeRangeMap := i.GatewayConfig.GetInboundMethodTimeRangeConfig(ctx) methodList, err := i.ReplPlanningCalcClient.GetInboundMethod(ctx, ipeReq) if err != nil { return nil, err } // get the warehouse data warehouseIdList := make([]int64, 0) sort.Slice(methodList.InboundMethodResultList, func(i, j int) bool { return methodList.InboundMethodResultList[i].EtaRange.EtaStartTime < methodList.InboundMethodResultList[i].EtaRange.EtaStartTime }) //the first element we need to get the warehouse list for _, method := range methodList.InboundMethodResultList[0].InboundMethodDetailList { if len(method.WarehouseIds) > 0 { warehouseIdList = append(warehouseIdList, method.WarehouseIds...) } } //get the earliest available week by other elements nextAvailableWeekMap := buildNextAvailableWeek(methodList.GetInboundMethodResultList()) request := &warehouse_meta.GetWarehouseByIdsRequest{ WarehouseIds: warehouseIdList, } warehouseList, err := i.WarehouseClient.GetWarehouseMetaByIds(ctx, request) if err != nil { return nil, err } warehouseInfoMap := make(map[int64]*v2.WarehouseSimpleInfo) for _, warehouseDetail := range warehouseList { warehouseInfoMap[warehouseDetail.WarehouseBasicInfo.WarehouseId] = util.ConvertWarehouseInfo2WarehouseSimpleInfo(warehouseDetail) } availableMethodList := make([]*v2.AvailableInboundMethod, 0) for _, method := range methodList.InboundMethodResultList[0].InboundMethodDetailList { inboundMethod := v2.InboundMethod(method.InboundMethod) earliestAppointmentDate, err := i.getAvailableAppointment(ctx, *method, inboundOrder) if err != nil { return nil, err } availableWarehouseList := make([]*v2.WarehouseSimpleInfo, 0) for _, id := range method.GetWarehouseIds() { availableWarehouseList = append(availableWarehouseList, warehouseInfoMap[id]) } availableMethod := &v2.AvailableInboundMethod{ InboundMethod: inboundMethod, Sla: &v2.TimeRange{ Start: timeRangeMap[inboundMethod].Min, End: timeRangeMap[inboundMethod].Max, }, ValidMethod: true, EarliestAppointmentDate: earliestAppointmentDate, OptionalWarehouseList: availableWarehouseList, NextAvailableWeek: nextAvailableWeekMap[inboundMethod], } if len(availableWarehouseList) == 0 { availableMethod.ValidMethod = false } availableMethodList = append(availableMethodList, availableMethod) } resp := &v2.GetReplenishmentAvailableInboundMethodResponse{ AvailableInboundMethodList: availableMethodList, } if err != nil { return nil, err } return resp, nil } func (i *ReplenishmentInboundOrderLogicImpl) getAvailableAppointment(ctx context.Context, method planning_calc.InboundMethodDetail, inboundOrder *business_order_base.InboundOrder) (int64, error) { appointmentTimeList := make([][]int64, 0) for _, warehouseId := range method.WarehouseIds { tempList := make([]int64, 0) calendar, err := i.CapacityPlanClient.GetInboundCapacityCalendar(ctx, &v2.GetBookableTimeslotRequest{ InboundOrderId: inboundOrder.GetOrderId(), WarehouseId: warehouseId, ShippingMethod: v2.AppointmentShippingMethodType_APPOINTMENT_SHIPPING_METHOD_TYPE_PALLETIZED, TimeRange: &v2.TimeRange{ Start: time.Now().UnixMilli(), End: time.Now().UnixMilli() + consts.FOUR_WEEK_MILLI, }, PalletCount: 1, CartonCount: inboundOrder.GetCartonsQuantity(), }) if err != nil { return 0, nil } if calendar == nil || len(calendar.CapacityList) <= 0 || len(calendar.CapacityList[0].DaysCapacity) <= 0 { return consts.EARLIEST_APPOINTMENT_DATE, nil } targetDayCapacity := calendar.CapacityList[0].DaysCapacity[0].WaveList for _, item := range targetDayCapacity { if item.AvailableCapacity > 0 { tempList = append(tempList, item.StartTime) } } if len(tempList) == 0 { return consts.EARLIEST_APPOINTMENT_DATE, cerr.ErrValidation{Message: "no appointment is available for next four weeks"} } appointmentTimeList = append(appointmentTimeList, tempList) } appointmentTime, err := findLatestAppointment(appointmentTimeList) if err != nil { return 0, err } return appointmentTime, nil } func findLatestAppointment(allTimes [][]int64) (int64, error) { var maxTime int64 = 0 for _, times := range allTimes { for _, t := range times { if t > maxTime { maxTime = t } } } return daysUntil(maxTime), nil } func daysUntil(maxTime int64) int64 { latestDate := time.UnixMilli(maxTime).UTC() today := time.Now().UTC() todayDateOnly := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, time.UTC) latestDateOnly := time.Date(latestDate.Year(), latestDate.Month(), latestDate.Day(), 0, 0, 0, 0, time.UTC) days := latestDateOnly.Sub(todayDateOnly).Hours() / 24 return int64(days) } func buildTimeRangeForIpe(start int64) []*planning_calc.EtaRange { var ranges []*planning_calc.EtaRange // Start time in seconds (assuming input is in milliseconds) startTime := time.Unix(start/1000, 0) // Adjust start time to the beginning of the next Sunday unless it's already a Sunday if startTime.Weekday() != time.Sunday { startTime = startTime.AddDate(0, 0, int(time.Sunday-startTime.Weekday())) } // Weekly duration in seconds oneWeek := int64(time.Hour * 24 * 7 / time.Second) // Generate weekly ranges for eight weeks for week := 0; week < 8; week++ { currentStart := startTime.Add(time.Duration(week) * time.Duration(oneWeek)).Unix() currentEnd := currentStart + oneWeek - 1 ranges = append(ranges, &planning_calc.EtaRange{ EtaStartTime: currentStart, EtaEndTime: currentEnd, }) } return ranges } func buildNextAvailableWeek(inboundMethodResult []*planning_calc.InboundMethodResult_) map[v2.InboundMethod]int64 { respMap := make(map[v2.InboundMethod]int64) for week, list := range inboundMethodResult { for _, result := range list.InboundMethodDetailList { if result.WarehouseIds != nil && len(result.GetWarehouseIds()) > 0 && respMap[v2.InboundMethod(result.GetInboundMethod())] != 0 { respMap[v2.InboundMethod(result.GetInboundMethod())] = int64(week) } } } return respMap }
Leave a Comment