Untitled

 avatar
unknown
ruby
10 months ago
39 kB
10
Indexable
require_relative '../../../app/LA/entities/aggregator_value_vo'
require_relative '../../lib/outbound_request_type_utility'
require_relative '../factories/external_client_factory'


class TransferListWsnLocationAggregation < TransferListWidLocationAggregation

  TRANSACTION_NAMESPACE_LIST = [StorageNamespaceEnum::SHIP_GROUPS, StorageNamespaceEnum::RESERVATIONS,
                                StorageNamespaceEnum::INVENTORY_ITEMS]

  RESERVE_RESOURCE_LOCK_TTL = 300
  RESERVE_RESOURCE_LOCK_WAIT_TIME = 1000
  RESERVE_RESOURCE_LOCK_RETRY_COUNT = 3
  RESERVE_RESHUFFLE_LIMIT = 500

  INVENTORY_ITEM = "InventoryItem"

  def initialize(warehouse_id)
    @warehouse_id = warehouse_id
  end

  # request1 = {"wsn_to_pick" => 'wsn_entered',  "partial_pick" => true,
  #                      'source_loaction_id' => 'source_location'}
  def resolve_unpicked_items(pickable_items, transfer_list, picked_inventories = [], flow = nil)
    transfer_list_items = []
    pickable_items = pickable_items.is_a?(Array) ? pickable_items : pickable_items.nil? ? [] : [pickable_items]
    tote_label_id_map = get_tote_label_id_map(pickable_items)
    movement_request_vo = movement_request_service.get_vos_by_ids([transfer_list.movement_request_id]).first
    outbound_request_type = (WorkFlowEntityType::OUTBOUND_REQUEST == movement_request_vo.group_type)
    transfer_list_items << pickable_items.collect do |pickable_item|
      list_items = []
      wsn = pickable_item[:wsn_to_pick]
      if wsn
      wh_serial_number = wsn_service.validate_and_fetch_wsn(wsn)
      begin
        inventory_item = InventoryModuleDataService.
            get_inventory_item_by_wh_serial_number_id_and_quantity!(wh_serial_number.id, 1)
      rescue ActiveRecord::RecordNotFound => e
        inventory_item = nil
      end
      list_items << if inventory_item.present?
                      validate_scanned_wsn_warehouse(inventory_item, transfer_list, wsn)
                      validate_duplicate_wsn(inventory_item, transfer_list, wsn)
                      item = transfer_list_service.
                                   get_transfer_list_item_by_transfer_list_id_and_inventory_item_id(transfer_list.id,
                                                                                                    inventory_item.id)
                      picked_inventories << item[:inventory_item_id] if item.present?
                      item.present? ? item : validate_picked_item_and_swap(pickable_item, transfer_list, wsn,
                                                                           inventory_item, outbound_request_type,
                                                                           picked_inventories, flow)
                    else
                      # change incoming payload
                      swap_wsns(wsn, pickable_item[:source_loaction_id], transfer_list, picked_inventories, flow)
                    end
      else
        wid = pickable_item[:wid]
        product_detail_vo = get_product_details_by_wid(wid)
        product_detail_id = product_detail_vo[0][:id]
        imei_array = pickable_item[:imei]
        quantity = pickable_item[:quantity].to_i
        tote = pickable_item[:tote]
        shelf = pickable_item[:source_loaction_id]
        logger.debug("Processing WID: #{wid},Product Detail ID: #{product_detail_id} , Quantity: #{quantity},TOTE: #{tote}, Shelf : #{shelf} IMEI_Array, :#{imei_array}")
        wsn_vos = []
        if imei_array.present?
          imei_array.each do |imei|
            next if imei.nil?
            wsn_vo = wh_serial_number_data_service.get_wsn_vos_by_attribute_name_and_attribute_values_and_product(WhSerialNumberAttributeNameEnum::SERIAL_NUMBER, imei, product_detail_id)
            unless wsn_vo.empty?
              wsn_vos += wsn_vo
            end
          end
        end

        missing_quantity = quantity - wsn_vos.size
        new_wsns = []
        if missing_quantity > 0
          # Creating missing WSNs in a single API call
          idempotence_key = "#{product_detail_id}_#{tote}_#{shelf}_#{transfer_list.id}_#{missing_quantity}"
          payload = {
            product_detail_id: product_detail_id,
            idempotence_key: idempotence_key,
            quantity: missing_quantity
          }
          begin
            new_wsn_vos = b2b_warehouse_client.create_wsn_v1(payload)
            if new_wsn_vos.is_a?(Array) && new_wsn_vos.any?
              new_wsns = new_wsn_vos.map { |vo| vo['display_id'] }
            else
              logger.error("Unexpected wsn_vo response structure or empty response: #{new_wsn_vos.inspect}")
              raise "WSN creation failed due to invalid response structure"
            end
          rescue StandardError => e
            logger.error("Failed to create WSN VOs for payload: #{payload}. Error: #{e.message}")
            raise "WSN creation failed : #{e.message}"
          end
        end

        all_wsns = (wsn_vos.map { |vo| vo.display_id } + new_wsns).take(quantity)

        all_wsns.each_with_index do |wsn, i|
          wh_serial_number = wsn_service.validate_and_fetch_wsn(wsn)
          begin
            inventory_item = InventoryModuleDataService.
              get_inventory_item_by_wh_serial_number_id_and_quantity!(wh_serial_number.id, 1)
          rescue ActiveRecord::RecordNotFound => e
            inventory_item = nil
          end
          list_items << if inventory_item.present?
                          validate_scanned_wsn_warehouse(inventory_item, transfer_list, wsn)
                          validate_duplicate_wsn(inventory_item, transfer_list, wsn)
                          item = transfer_list_service.
                            get_transfer_list_item_by_transfer_list_id_and_inventory_item_id(transfer_list.id,
                                                                                             inventory_item.id)
                          picked_inventories << item[:inventory_item_id] if item.present?
                          item.present? ? item : validate_picked_item_and_swap(pickable_item, transfer_list, wsn,
                                                                               inventory_item, outbound_request_type,
                                                                               picked_inventories, flow)
                        else
                          # change incoming payload
                          swap_wsns(wsn, pickable_item[:source_loaction_id], transfer_list, picked_inventories, flow)
                        end
        end
      end
      list_items.flatten!
      if pickable_item[:imei].present?
        list_items.each_with_index do |item_taken, i|
          item_taken[:imei] = i < pickable_item[:imei].length ? pickable_item[:imei][i] : nil
        end
      end
      list_items.each do |item|
        item[:tote_id] = tote_label_id_map[pickable_item[:tote]] unless pickable_item[:tote].nil?
      end
      list_items
    end
    transfer_list_items.flatten
  end

  # request1 = {"wsn_to_put" => 'wsn_entered',  "partial_pick" => true,
  #                      'destination_location_id' => 'destination_location'}
  def resolve_putable_items(putable_items, transfer_list, puted_inventories=[])
    transfer_list_items = []
    putable_items = putable_items.is_a?(Array) ? putable_items : putable_items.nil? ? [] : [putable_items]
    tote_label_id_map = get_tote_label_id_map(putable_items)
    transfer_list_items << putable_items.collect do |putable_item|
      list_items = []
      wsn = putable_item[:wsn_to_put]
      tote_label = putable_item[:tote] unless tote_label_id_map.nil?
      wh_serial_number = wsn_service.validate_and_fetch_wsn(wsn)
      inventory_item = InventoryModuleDataService.
          get_inventory_item_by_warehouse_id_and_wh_serial_number_id(transfer_list.warehouse_id, wh_serial_number.id)
      raise SupplyChainError.new(:message => "Inventory item with wsn: #{wsn} is not found") if inventory_item.blank?
      item = transfer_list_service.get_transfer_list_item_by_transfer_list_id_and_inventory_item_id(transfer_list.id,
                                                                                                    inventory_item.id)
      raise SupplyChainError.new(:message => "Wsn #{wsn} does not belong to this TL") if item.blank?
      if (!tote_label.nil? && item[:tote_id] != tote_label_id_map[tote_label])
          raise SupplyChainError.new(:message => "Wsn #{wsn} does not belong to tote #{tote_label}")
      end
      item[:destination_location_id] =
          get_and_validate_destination_location(putable_item[:destination_location_id], transfer_list.destination_area,
                                                @warehouse_id).id
      list_items << item
      puted_inventories << item[:inventory_item_id]
      list_items.flatten
    end
    transfer_list_items.flatten
  end


  # return inventory item ids corresponding to wsn
  def validate_and_resolve_inventory_item_to_pack(transfer_list, input, input_type)
    wsns = input.collect { |items| items[input_type] }.flatten
    wsn_id_map = Hash[wsns.map { |wsn| [wsn_service.get_wsn_vo_by_display_id(wsn).id, wsn] }]
    inventory_item_id_maps = Hash[wsn_id_map.keys.map { |wh_serial_number_id| [inventory_service.get_vo_by_wsn_id(wh_serial_number_id).id, wh_serial_number_id] }]
    inventory_items_in_tl = transfer_list_service.get_inventory_items_by_transfer_list_id(transfer_list.id).collect { |item| item[:inventory_item_id] }
    inventory_items_not_in_tl = inventory_item_id_maps.keys - inventory_items_in_tl
    if inventory_items_not_in_tl.present?
      wsns_not_in_tl = inventory_items_not_in_tl.collect do |inventory_item_id|
        wsn_id_map[inventory_item_id_maps[inventory_item_id]]
      end
      raise WsnNotPartOfPicklistError.new(wsns_not_in_tl)
    end
    movement_request = movement_request_service.get_vos_by_ids([transfer_list.movement_request_id]).first
    outbound_request_items_for_tl = outbound_request_service.get_item_vos_by_outbound_request_id_inventory_item_ids(movement_request.group_id, inventory_items_in_tl).select { |item| item.status == OutboundRequestItemStatusEnum::IN_CONSIGNMENT }
    inventory_item_ids = outbound_request_items_for_tl.collect(&:inventory_item_id)
    unpacked_inventory_item_ids = inventory_item_ids - inventory_item_id_maps.keys
    [inventory_item_id_maps.keys, unpacked_inventory_item_ids]
  end

  def item_aggregator_fields
    ['aggregator_value_id', 'status', 'source_location_id', 'destination_location_id', 'tote_id']
  end

  def putable_items_aggregator_fields
    ['tote_id', 'aggregator_value_id', 'status']
  end

  def is_serialized
    false
  end

  def is_inv_swap_enabled_for_flow(outbound_request_type)
    outbound_request_type = OutboundRequestTypeUtility.get_outbound_request_type(outbound_request_type)
    config_hash = WarehouseService.settings.inventory_swapping_allowed_flows
    if config_hash['ALL'].present? && (config_hash['ALL'].include?('ALL') || config_hash['ALL'].include?(outbound_request_type))
      true
    else
      config_hash[@warehouse_id].present? && (config_hash[@warehouse_id].include?('ALL') || config_hash[@warehouse_id].include?(outbound_request_type))
    end
  end

  def is_inv_swap_enabled_for_area(source_area)
    storage_areas = WarehouseService.settings.inventory_swapping_restricted_areas
    return storage_areas.none?{ |area| area == source_area }
  end

  private

  def get_ready_to_pick_and_relabel_needed_inv_items(transfer_list_id)
    ready_to_pick_tl_items =
        transfer_list_service.get_inventory_item_id_and_transfer_list_item_by_transfer_list_id_and_item_status(
            transfer_list_id, TransferListStatusEnum::READY_TO_PICK)
    raise TransferListItemNotFoundError.new('No pickable items in this TL') if ready_to_pick_tl_items.empty?
    inventory_service
        .get_relabelling_required_map_for_inventory_item_ids(ready_to_pick_tl_items.collect{ |tli| tli[:inventory_item_id] })
        .select{ |_k, v| v }.keys
  end

  def swap_inventories(wsn, storage_location, transfer_list, picked_inventories, picked_inv_item_vo, flow = nil)
    # method to swap inventories for a outbound request
    wid = wsn_service.get_wid_for_wsn(wsn)
    is_relabeled = false
    # get reserved inventory
    reserved_inv_item_vo, transfer_list_items_map =
        if storage_location.present?
          begin
            get_inventory_item_vo_using_aggregator(wsn, storage_location.first, transfer_list, wid, picked_inventories, is_relabeled)
          rescue TransferListItemNotFoundError
            seller_wid = wid_seller_mapping_data_service.get_fki_to_seller_wids_hash([wid])[wid]
            raise TransferListItemNotFoundError.new("WSN : #{wsn} , Storage_location: #{storage_location} not available to pick") if seller_wid.nil?
            is_relabeled = true
            get_inventory_item_vo_using_aggregator(wsn, storage_location.first, transfer_list, seller_wid, picked_inventories, is_relabeled)
          end
        else
          get_inventory_item_vo(wsn, transfer_list, wid, picked_inventories)
        end

    fetch_and_swap_inventories(transfer_list, picked_inv_item_vo, reserved_inv_item_vo, transfer_list_items_map, flow)
    [transfer_list_items_map]
  end

  def fetch_and_swap_inventories(transfer_list, picked_inv_item_vo, reserved_inv_item_vo, transfer_list_items_map, flow = nil)
    # method to fetch and swap inventory items
    logger.info("Starting inventory swap - inventory id #{reserved_inv_item_vo.id} with #{picked_inv_item_vo.id}")
    ResourceLock.hold!(ResourceLockNamespaceEnum::RESHUFFLE_RESOURCE_LOCK_NAME, {:warehouse_id => @warehouse_id}, nil,
                       RESERVE_RESOURCE_LOCK_TTL, RESERVE_RESOURCE_LOCK_WAIT_TIME, RESERVE_RESOURCE_LOCK_RETRY_COUNT) do
      shard_manager.using_transaction(TRANSACTION_NAMESPACE_LIST) do
        shard_manager.using_shard_for_warehouse_id(@warehouse_id) do
          # picked and reserved inventory are in different storage location
        if reserved_inv_item_vo.storage_location_id != picked_inv_item_vo.storage_location_id
          update_aggregator_value(picked_inv_item_vo, reserved_inv_item_vo, transfer_list)
        end
        if picked_inv_item_vo.atp == 1
          swap_if_inventory_is_promisable(picked_inv_item_vo, reserved_inv_item_vo, flow)
        else
          swap_if_inventory_is_not_promisable(picked_inv_item_vo, reserved_inv_item_vo)
        end
        # update in_transit info for inventory items
        inventory_service.update_in_transit(reserved_inv_item_vo.id, 0, flow)
        inventory_service.update_in_transit(picked_inv_item_vo.id, 1, flow)
        # update inventory in return object
        transfer_list_items_map[:inventory_item_id] = picked_inv_item_vo.id
        end
      end
    end
    logger.info("Completed inventory swap - inventory id #{reserved_inv_item_vo.id} with #{picked_inv_item_vo.id}")
    transfer_list_items_map
  end

  def swap_if_inventory_is_promisable(picked_inv_item_vo, reserved_inv_item_vo, flow = nil)
    # swapping inventory if picked inventory atp = 1
    # update atp for inventory item
    inventory_service.change_atp(picked_inv_item_vo.id, -1, flow)
    inventory_service.change_atp(reserved_inv_item_vo.id, 1, flow)
    # update outbound request item, movement request item and transfer list item for reserved inventory
    update_inventory_in_reservations(picked_inv_item_vo, reserved_inv_item_vo)
  end

  def swap_if_inventory_is_not_promisable(picked_inv_item_vo, reserved_inv_item_vo)
    # get outbound request item for picked inventory
    picked_inv_outbound_request_item_vo = outbound_request_service.get_by_inventory_item_id(picked_inv_item_vo.id).
        find { |item| ![OutboundRequestItemStatusEnum::CANCELLED, OutboundRequestItemStatusEnum::COMPLETED, OutboundRequestItemStatusEnum::LOST].include? item.status}
    if picked_inv_outbound_request_item_vo.present?
      # update outbound request item for picked inventory
      update_inventory_in_outbound_request_item(picked_inv_outbound_request_item_vo, reserved_inv_item_vo)
      # update movement request / task for picked inventory
      update_picked_inventory_reservation(picked_inv_item_vo, reserved_inv_item_vo, picked_inv_outbound_request_item_vo.outbound_request_id)
    else
      # get transfer request item for picked inventory
      picked_inv_transfer_request_item_vo = transfer_request_service.get_item_vo_by_inventory_item_id(picked_inv_item_vo.id).
          find { |item| ![TransferRequestItemStatusEnum::CANCELLED, TransferRequestItemStatusEnum::COMPLETED, TransferRequestItemStatusEnum::LOST].include? item.status}
      if picked_inv_transfer_request_item_vo.present?
        # update transfer request item for picked inventory
        update_inventory_in_transfer_request_item(picked_inv_transfer_request_item_vo, reserved_inv_item_vo)
        # update movement request / task for picked inventory
        update_picked_inventory_reservation(picked_inv_item_vo, reserved_inv_item_vo, picked_inv_transfer_request_item_vo.transfer_request_id)
      else
        raise InvalidInputError.new("Picked inventory #{picked_inv_item_vo.id} is reserved for flow other than Outbound/Transfer")
      end
    end
    # update outbound and movement request item for reserved inventory
    update_inventory_in_reservations(picked_inv_item_vo, reserved_inv_item_vo)
  end

  def update_inventory_in_reservations(picked_inv_item_vo, reserved_inv_item_vo)
    # method to update picked inv in outbound request, movement request and transfer list tables
    reserved_inv_outbound_request_item_vo = outbound_request_service.get_by_inventory_item_id(reserved_inv_item_vo.id).
        find { |item| ![OutboundRequestItemStatusEnum::CANCELLED, OutboundRequestItemStatusEnum::COMPLETED, OutboundRequestItemStatusEnum::LOST].include? item.status}
    reserved_inv_movement_request_item_vo = movement_request_service.get_movement_request_item_vos_by_inventory_item_ids([reserved_inv_item_vo.id]).
        find { |item| ![MovementRequestItemStatusEnum::CANCELLED, MovementRequestItemStatusEnum::COMPLETED, MovementRequestItemStatusEnum::LOST].include? item.status}
    reserved_inv_transfer_list_item_vo = transfer_list_service.get_transfer_list_items_by_movement_request_item_ids([reserved_inv_movement_request_item_vo.id]).first

    update_inventory_in_outbound_request_item(reserved_inv_outbound_request_item_vo, picked_inv_item_vo)
    update_inventory_in_movement_request_item(reserved_inv_movement_request_item_vo, picked_inv_item_vo)
    update_details_in_transfer_list_item(reserved_inv_transfer_list_item_vo, picked_inv_item_vo)
  end

  def update_aggregator_value(picked_inv_item_vo, reserved_inv_item_vo, transfer_list)
    # update aggregator_value in transfer list
    reserved_inv_transfer_list_item_hash = transfer_list_service.get_transfer_list_item_by_transfer_list_id_and_inventory_item_id(transfer_list.id, reserved_inv_item_vo.id)
    aggregator_value_hash = add_new_aggregator_value(transfer_list.id, picked_inv_item_vo.wid, picked_inv_item_vo.storage_location_id)
    update_hash = {:aggregator_value_id => aggregator_value_hash[:id]}
    transfer_list_service.update_transfer_list_item(reserved_inv_transfer_list_item_hash[:id], update_hash)
  end

  def add_new_aggregator_value(transfer_list_id, wid, storage_location_id)
    # add new aggregator_value for picked inventory
    storage_location_vo = storage_location_service.get_vos_by_ids([storage_location_id]).first
    value = "#{storage_location_vo.label}|#{wid}"
    insert_hash = {
        :transfer_list_id => transfer_list_id,
        :value => value
    }
    aggregator_value_dao.bulk_insert([insert_hash])
    aggregator_value_dao.get_record_by_transfer_list_id_and_value(transfer_list_id, value)
  end

  def update_picked_inventory_reservation(picked_inv_item_vo, reserved_inv_item_vo, reservation_id)
    # method to update movement request item / task for picked inventory item
    # get movement request item for picked inv
    picked_inv_movement_request_item_vo = movement_request_service.get_movement_request_item_vos_by_inventory_item_ids([picked_inv_item_vo.id]).
        find { |item| ![MovementRequestItemStatusEnum::CANCELLED, MovementRequestItemStatusEnum::COMPLETED, MovementRequestItemStatusEnum::LOST].include? item.status}
    if picked_inv_movement_request_item_vo.present?
      # update movement request item for reserved inv
      update_inventory_in_movement_request_item(picked_inv_movement_request_item_vo, reserved_inv_item_vo)
      begin
        # check if transfer list present for picked inventory. (If not present do nothing)
        picked_inv_transfer_list_item_vo = transfer_list_service.get_transfer_list_items_by_movement_request_item_ids([picked_inv_movement_request_item_vo.id]).first
        if picked_inv_transfer_list_item_vo.present?
          update_details_in_transfer_list_item(picked_inv_transfer_list_item_vo, reserved_inv_item_vo)
        end
      rescue
        nil
      end
    else
      # get task item for picked inventory
      picked_inv_task_item_vo = TaskModuleDataService::Task.where(:group_id => reservation_id,
                                                                  :work_item_id => picked_inv_item_vo.id,
                                                                  :work_item_type => INVENTORY_ITEM)
      if picked_inv_task_item_vo.present?
        # update task item for picked inventory
        update_inventory_in_tasks(picked_inv_task_item_vo, reserved_inv_item_vo)
      end
    end
  end

  def update_inventory_in_outbound_request_item(outbound_request_item_vo, inventory_item_vo)
    # update inventory in outbound request item
    update_item_hash = {:inventory_item_id => inventory_item_vo.id, :source_storage_location_id => inventory_item_vo.storage_location_id}
    outbound_request_service.update_outbound_request_item(outbound_request_item_vo.id, update_item_hash)
  end

  def update_inventory_in_transfer_request_item(transfer_request_item_vo, inventory_item_vo)
    # update inventory in transfer request item
    update_item_hash = {:inventory_item_id => inventory_item_vo.id, :from_storage_location_id => inventory_item_vo.storage_location_id}
    transfer_request_service.update_items_by_ids(transfer_request_item_vo.id, update_item_hash)
  end

  def update_inventory_in_movement_request_item(movement_request_item_vo, inventory_item_vo)
    # update inventory in movement request item
    update_item_hash = {:inventory_item_id => inventory_item_vo.id, :source_storage_location_id => inventory_item_vo.storage_location_id}
    movement_request_service.update_items_by_ids(movement_request_item_vo.id, update_item_hash)
  end

  def update_details_in_transfer_list_item(transfer_list_item_vo, inventory_item_vo)
    # update inventory in transfer list item
    update_item_hash = {:source_location_id => inventory_item_vo.storage_location_id}
    transfer_list_service.update_transfer_list_item(transfer_list_item_vo.id, update_item_hash)
  end

  def update_inventory_in_tasks(tasks_vos, inventory_item_vo)
    # update inventory in tasks
    update_item_hash = {:work_item_id => inventory_item_vo.id}
    for tasks_vo in tasks_vos
        TaskModuleDataService::Task.where(:id => tasks_vo.id).first.update_attributes!(update_item_hash)
        transfer_task_data_vo = TaskModuleDataService::TransferTaskData.where(:task_id => tasks_vo.id).first
        if transfer_task_data_vo.present?
          if transfer_task_data_vo.source_location_id.present?
            update_hash = {:source_location_id => inventory_item_vo.storage_location_id}
            TaskModuleDataService::TransferTaskData.where(:id => transfer_task_data_vo.id).first.update_attributes!(update_hash)
          end
        end
    end
  end

  def swap_wsns(wsn, storage_location, transfer_list, picked_inventories, flow = nil)
    wid = wsn_service.get_wid_for_wsn(wsn)
    is_relabeled = false
    begin
      relabelling_inventory_item_ids = get_ready_to_pick_and_relabel_needed_inv_items(transfer_list.id)
      relabelling_inventory_item_vos = relabelling_inventory_item_ids.empty? ? [] : inventory_service.get_vos_by_ids(relabelling_inventory_item_ids)
      fki_to_alpha_seller_map = inventory_service.get_fki_to_alpha_wid_map(relabelling_inventory_item_vos.collect(&:wid).uniq)
    rescue Warehouse::RecordNotFoundError => e
      fki_to_alpha_seller_map = {}
    end

    inventory_item_vo, transfer_list_items_map =
        if storage_location.present?
          begin
            get_inventory_item_vo_using_aggregator(wsn, storage_location.first, transfer_list, wid, picked_inventories, is_relabeled)
          rescue TransferListItemNotFoundError
            #seller_wid = wid_seller_mapping_data_service.get_fki_to_seller_wids_hash([wid])[wid]
            seller_wid = fki_to_alpha_seller_map[wid]
            raise TransferListItemNotFoundError.new("WSN : #{wsn} , Storage_location: #{storage_location} not available to pick") if seller_wid.nil?
            is_relabeled = true
            get_inventory_item_vo_using_aggregator(wsn, storage_location.first, transfer_list, seller_wid, picked_inventories, is_relabeled)
          end
        else
          get_inventory_item_vo(wsn, transfer_list, wid, picked_inventories)
        end
    UtilityFactory.inventory_utility.fetch_and_swap_wsn(inventory_item_vo, wsn, is_relabeled, flow)
    [transfer_list_items_map]
  end

  def get_inventory_item_vo_using_aggregator(wsn, storage_location, transfer_list, wid, picked_inventories, expected_relabel_status = false)
    aggregation_value = storage_location + '|' + wid
    begin
      aggregator_value_vo = transfer_list_service.get_aggregator_value_vo_by_transfer_list_id_and_value(
          transfer_list.id, aggregation_value)
    rescue Warehouse::RecordNotFoundError => e
      raise TransferListItemNotFoundError.new("WSN : #{wsn} , Storage_location: #{storage_location} not available to pick")
    end
    transfer_list_items_maps = transfer_list_service.
        get_inventory_item_id_and_transfer_list_item_by_transfer_list_id_and_item_status_and_aggregator_value_id(transfer_list.id,
                                                                                                                 TransferListItemStatusEnum::READY_TO_PICK,
                                                                                                                 aggregator_value_vo.id)
    relabel_status_map = inventory_service.get_relabelling_required_map_for_inventory_item_ids(
                                                    transfer_list_items_maps.collect{ |map| map[:inventory_item_id] })
    transfer_list_items_map = transfer_list_items_maps.detect do |map|
      (picked_inventories.exclude? map[:inventory_item_id]) &&
        (relabel_status_map[map[:inventory_item_id]] == expected_relabel_status)
    end
    if transfer_list_items_map.present?
      picked_inventories << transfer_list_items_map[:inventory_item_id]
    else
      raise TransferListItemNotFoundError.new("WSN : #{wsn} , Storage_location: #{storage_location} not available to pick")
    end
    raise TransferListItemNotFoundError.new("WSN : #{wsn} , Storage_location: #{storage_location} not available to pick") unless transfer_list_items_map.present?
    [inventory_service.get_vos_by_ids([transfer_list_items_map[:inventory_item_id]]).first, transfer_list_items_map]
  end

  def get_inventory_item_vo(wsn, transfer_list, wid, picked_inventories)
    transfer_list_items_map_list =
        transfer_list_service.get_inventory_item_id_and_transfer_list_item_by_transfer_list_id_and_item_status(
        transfer_list.id, TransferListItemStatusEnum::READY_TO_PICK)
    inventory_item_vo = if transfer_list_items_map_list.present?
                          inventory_item_vos = inventory_service.get_vos_by_ids(transfer_list_items_map_list.collect { |item| item[:inventory_item_id] })
                          inventory_item_vos.detect { |item_vo| (item_vo.wid == wid && (picked_inventories.exclude? item_vo.id))}
                        else
                          nil
                        end
    if inventory_item_vo.present?
      picked_inventories << inventory_item_vo.id
    else
      raise TransferListItemNotFoundError.new("WSN : #{wsn} not available to pick")
    end
    [inventory_item_vo, transfer_list_items_map_list.detect{ |map| map[:inventory_item_id] == inventory_item_vo.id }]
  end

  def is_inventory_reserved_or_in_transit_out_of_current_TL(inventory_item, current_transfer_list)
    is_inv_locked_outside_of_TL = ((inventory_item.atp == 1 && inventory_item.in_transit == false) ? false  : true)

    if is_inv_locked_outside_of_TL
      inventory_TL = transfer_list_service.get_trasfer_list_for_inventory_item(inventory_item)
      is_inv_locked_outside_of_TL = false if inventory_TL.present? && inventory_TL.id == current_transfer_list.id
    end

    return is_inv_locked_outside_of_TL
  end

  def validate_wsn_reserved(inventory_item, wsn, transfer_list, outbound_request_type = true)
    unless inventory_item.area == transfer_list.source_area
      raise WarehouseService::InvalidInputError.new("WSN:#{wsn} is in location #{inventory_item.area}, cannot pick from location #{transfer_list.source_area}")
    end
    return true if inventory_item.atp == 1 && inventory_item.in_transit == false
    if inventory_item.in_transit == true and !allow_in_transit_swapping(inventory_item, wsn, transfer_list, outbound_request_type)
      raise WarehouseService::InvalidInputError.new("WSN:#{wsn} is already reserved")
    end
    outbound_request_items = outbound_request_service.get_item_vos_by_inventory_item_id(inventory_item.id)
    if outbound_request_type
      outbound_request = movement_request_service.get_outbound_request_for_movement_request_id(transfer_list.movement_request_id)
      if outbound_request_items.present?
        outbound_request_items.each do |item|
          next if item.outbound_request_id == outbound_request.id
          outbound_request_item_reserved_against_wsn_flow?(item, wsn)
        end
      end
    else
      outbound_request_items.each{|item| outbound_request_item_reserved_against_wsn_flow?(item, wsn)}
      WsnService.validate_picked_item_against_blocking_entities(inventory_item)
    end
  end

  def allow_in_transit_swapping(inventory_item, wsn, transfer_list, outbound_request_type)
    # method to check if in_transit swapping of inventory is allowed
    if outbound_request_type
      outbound_request_vo = movement_request_service
                                .get_outbound_request_for_movement_request_id(transfer_list.movement_request_id)
      # validate warehouse, flow and area
      unless is_in_transit_swap_enabled_for_flow(outbound_request_vo.outbound_request_type) and \
                                                 is_in_transit_swap_enabled_for_area(transfer_list.source_area)
        return false
      end

      # check if scanned inventory in same location as reserved inventory in TL
      begin
        storage_location_vo = storage_location_service.get_vos_by_ids([inventory_item.storage_location_id]).first
        aggregator_value = "#{storage_location_vo.label}|#{inventory_item.wid}"
        aggregator_value_vo = transfer_list_service
                                  .get_aggregator_value_vo_by_transfer_list_id_and_value(transfer_list.id, aggregator_value)
      rescue Warehouse::RecordNotFoundError
        raise WarehouseService::InvalidInputError
                  .new("Scanned WSN:#{wsn} is in different location than TL - #{transfer_list.display_id}")
      end
      # check if TL item in ready to pick state for scanned wid
      begin
        transfer_list_items_vos = transfer_list_service
                                      .get_transfer_list_items_by_transfer_list_id_and_gregator_value_id_and_status(
                                          transfer_list.id,
                                          aggregator_value_vo.id,
                                          TransferListStatusEnum::READY_TO_PICK)
        unless transfer_list_items_vos.present?
          return false
        end
      rescue Warehouse::RecordNotFoundError
        raise WarehouseService::InvalidInputError
                  .new("WID:#{inventory_item.wid} in location #{storage_location_vo.label} already picked for TL - #{transfer_list.display_id}")
      end
      # check if scanned inventory not yet picked
      movement_request_item_vo = movement_request_service
                                     .get_movement_request_item_vos_by_inventory_item_ids(inventory_item.id)
                                     .map{ |item| item if [MovementRequestItemStatusEnum::CREATED,
                                                           MovementRequestItemStatusEnum::PROCESSING].include? item.status}
                                     .compact.first
      unless movement_request_item_vo.present?
        return false
      end
      transfer_list_item_vo = transfer_list_service
                                  .get_transfer_list_items_by_movement_request_item_ids(movement_request_item_vo.id).first
      allowed_transfer_list_item_status = [TransferListItemStatusEnum::CREATED,
                                           TransferListItemStatusEnum::READY_TO_PICK,
                                           TransferListItemStatusEnum::CANCELLED]
      unless allowed_transfer_list_item_status.include? transfer_list_item_vo.status
        return false
      end
      log.info("In transit swapping of WSN - #{wsn} and Inventory - #{inventory_item.id} and TL - #{transfer_list.display_id}")
      return true
    end
    return false
  end

  def is_in_transit_swap_enabled_for_flow(outbound_request_type)
    config_hash = WarehouseService.settings.in_transit_inventory_swap_allowed_flows
    config_hash[outbound_request_type].present? &&
        (config_hash[outbound_request_type].include?('ALL') || config_hash[outbound_request_type].include?(@warehouse_id))
  end

  def is_in_transit_swap_enabled_for_area(source_area)
    storage_areas = WarehouseService.settings.in_transit_inventory_swap_allowed_areas
    storage_areas.include? source_area
  end

  def get_fki_to_seller_wids_hash(fki_wids)
    # catching exception here to keep the usage cleaner
    begin
      wid_seller_mapping_data_service.get_fki_to_seller_wids_hash(fki_wids)
    rescue Warehouse::RecordNotFoundError
      nil
    end
  end

  def outbound_request_item_reserved_against_wsn_flow?(item, wsn)
    if (([OutboundRequestStatusEnum::CANCELLED, OutboundRequestStatusEnum::COMPLETED].exclude? item.status) &&
        picked_item_reserved_against_wsn_flow?(item.outbound_request_id))
      raise WarehouseService::InvalidInputError.new("WSN:#{wsn} is already reserved for outbound request #{item.outbound_request_id}")
    end
  end

  def picked_item_reserved_against_wsn_flow?(outbound_request_id)
    outbound_request = outbound_request_service.get_vos_by_ids([outbound_request_id]).first
    OutboundRequestEnums.get_serialized_pick_flows.include?(outbound_request.outbound_request_type.to_s)
  end

  def validate_duplicate_wsn(inventory_item, transfer_list, wsn)
    transfer_list_items = transfer_list_service.find_transfer_list_items_by_inventory_item_id(inventory_item.id)
    transfer_list_items.each do |transfer_list_item|
      if transfer_list_item.status == TransferListItemStatusEnum::PICKED
        transfer_list_for_item = transfer_list_item.transfer_list_id == transfer_list.id ? transfer_list : transfer_list_service.get_vo_by_id(transfer_list_item.transfer_list_id)
        raise WarehouseService::InvalidInputError.new("WSN:#{wsn} is already picked for TL#{transfer_list_item.transfer_list_id}") if
            transfer_list.movement_request_id == transfer_list_for_item.movement_request_id
      end
    end
  end

  def validate_scanned_wsn_warehouse(inventory_item, transfer_list, wsn)
    unless inventory_item.warehouse_id == transfer_list.warehouse_id
      raise InvalidInputError.new("WSN #{wsn} belongs to warehouse #{inventory_item.warehouse_id}")
    end
  end

  def validate_picked_item_and_swap(pickable_item, transfer_list, wsn, inventory_item, outbound_request_type, picked_inventories, flow = nil)
    if WarehouseService.settings.kalash_warehouses.include?(@warehouse_id)
      block_swap_for_kalash = is_inventory_reserved_or_in_transit_out_of_current_TL(inventory_item, transfer_list)
      err_msg = "Cannot pick #{wsn} for TL id #{transfer_list.id}. Choose a different WSN to pick. Scanned wsn has zero atp or it is in-transit for another process." if block_swap_for_kalash
      raise WarehouseService::InvalidInputError.new(err_msg) if err_msg.present?
    end

    validate_wsn_reserved(inventory_item, wsn, transfer_list, outbound_request_type)
    WsnService.validate_picked_item_not_reserved_against_wsn_flow(inventory_item)
    if outbound_request_type
      outbound_request_vo = movement_request_service.get_outbound_request_for_movement_request_id(transfer_list.movement_request_id)
      if is_inv_swap_enabled_for_flow(outbound_request_vo.outbound_request_type) && is_inv_swap_enabled_for_area(transfer_list.source_area)
        swap_inventories(wsn, pickable_item[:source_loaction_id], transfer_list, picked_inventories, inventory_item, flow)
      else
        swap_wsns(wsn, pickable_item[:source_loaction_id], transfer_list, picked_inventories, flow)
      end
    else
      swap_wsns(wsn, pickable_item[:source_loaction_id], transfer_list, picked_inventories, flow)
    end
  end

  def get_product_details_by_wid(wid)
    product_details_dao.get_by_wid(wid)
  end
  def wsn_service
    BusinessServiceFactory.get_wsn_service
  end

  def storage_zone_service
    BusinessServiceFactory.storage_location_service(@warehouse_id)
  end

  def transfer_list_service
    BusinessServiceFactory.get_transfer_list_service(@warehouse_id)
  end

  def inventory_service
    BusinessServiceFactory.inventory_service(@warehouse_id)
  end

  def outbound_request_service
    BusinessServiceFactory.get_outbound_request_service(@warehouse_id)
  end

  def movement_request_service
    BusinessServiceFactory.get_movement_request_service(@warehouse_id)
  end

  def wid_seller_mapping_data_service
    DataServiceFactory.get_wid_seller_mapping_data_service
  end

  def relabel_status_service
    BusinessServiceFactory.get_inventory_relabel_status_service(@warehouse_id)
  end

  def shard_manager
    ShardManager.new
  end

  def transfer_request_service
    BusinessServiceFactory.get_transfer_request_service(@warehouse_id)
  end

  def storage_location_service
    BusinessServiceFactory.storage_location_service(@warehouse_id)
  end

  def product_details_dao
    DAOFactory.get_dao(StorageNamespaceEnum::PRODUCT_DETAIL, [@warehouse_id])[@warehouse_id]
  end
  def b2b_warehouse_client
    ExternalClientFactory.get_b2b_warehouse_client
  end
  def wh_serial_number_data_service
    BusinessServiceFactory.get_wh_serial_number_data_service
  end
  def aggregator_value_dao
    DAOFactory.get_dao("aggregator_values", [@warehouse_id])[@warehouse_id]
  end
end

Editor is loading...
Leave a Comment