InventoryApiService.java

/*
 * Copyright 2026 Global Crop Diversity Trust
 * Licensed under the Apache License, Version 2.0
 * See LICENSE file in project root folder or http://www.apache.org/licenses/LICENSE-2.0
 */

package org.gringlobal.api.v2.facade;

import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.genesys.blocks.model.filters.NumberFilter;

import org.gringlobal.api.FilteredPage;
import org.gringlobal.api.model.AccessionInfo;
import org.gringlobal.api.model.AuditLogDTO;
import org.gringlobal.api.model.CooperatorInfo;
import org.gringlobal.api.model.GeographyInfo;
import org.gringlobal.api.model.InventoryActionDTO;
import org.gringlobal.api.model.InventoryDTO;
import org.gringlobal.api.model.InventoryDetailsDTO;
import org.gringlobal.api.model.InventoryInfo;
import org.gringlobal.api.model.InventoryMaintenancePolicyInfo;
import org.gringlobal.api.model.OrderRequestDTO;
import org.gringlobal.api.model.SiteInfo;
import org.gringlobal.custom.validation.javax.CodeValueField;
import org.gringlobal.custom.validation.javax.SimpleString;
import org.gringlobal.model.DateVersionEntityId;
import org.gringlobal.model.Inventory;
import org.gringlobal.model.community.CommunityCodeValues;
import org.gringlobal.service.InventoryService;
import org.gringlobal.service.filter.AccessionFilter;
import org.gringlobal.service.filter.InventoryActionFilter;
import org.gringlobal.service.filter.InventoryFilter;

import lombok.Data;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.fasterxml.jackson.annotation.JsonUnwrapped;

public interface InventoryApiService extends APIFilteredServiceFacade<InventoryDTO, Inventory, InventoryFilter>, LabelGenerator {

	void shareAttachment(Long attachId, List<Long> inventoryIds);

	InventoryDetailsDTO getInventoryDetails(String barcode);

	InventoryDetailsDTO getInventoryDetails(long inventoryId);

	void discardMaterial(Map<Long, Integer> discardQuantities);

	InventoryDTO setInventoryQuantity(InventoryQuantityRequestDTO inventoryQuantity);

	@Data
	class InventoryQuantityRequestDTO {
		@NotNull public long id; // inventory id
		@Min(value = 0) public double quantityOnHand; // Required new quantity
		@CodeValueField("UNIT_OF_QUANTITY")
		public String quantityOnHandUnitCode;
		@Min(value = 0) public Double hundredSeedWeight;
		public String note;
	}

	List<InventoryDTO> assignLocation(@Valid AssignLocationRequestDTO assignLocationRequest);

	List<InventoryDTO> assignLocations(@Valid @Size(min = 1) List<AssignLocationRequestDTO> assignLocationRequests);

	@Data
	class AssignLocationRequestDTO {
		@NotNull public Location location;
		@NotNull @Size(min = 1) public Set<DateVersionEntityId.EntityIdAndModifiedDate> inventories;

		public static class Location {
			@NotNull public Long siteId;
			public String storageLocationPart1;
			public String storageLocationPart2;
			public String storageLocationPart3;
			public String storageLocationPart4;
		}
	}

	Page<ComparedSitesResponseDTO> compareSites(CompareSitesRequestDTO requestDTO, Pageable toPageRequest);

	@Data
	class CompareSitesRequestDTO {
		public AccessionFilter filter;
		@NotNull @Size(min = 2) public Map<Long, NumberFilter<Long>> sites;
	}

	@Data
	class ComparedSitesResponseDTO {
		private Long accessionId;
		private String accessionNumber;
		private Map<String, Long> siteInventories;
	}

	String assignBarcode(Long inventoryId);

	Page<AuditLogDTO> listAuditLogs(Long inventoryId, Pageable toPageRequest);

	List<InventoryDTO> splitInventory(SplitInventoryRequestDTO request);

	@Data
	class SplitInventoryRequestDTO {
		public SourceSplitInventory sourceSplitInventory;
		public List<SplitInventory> splits;

		public static class SourceSplitInventory extends DateVersionEntityId.EntityIdAndModifiedDate {
			public String note; // note to add to split action
			@Min(value = 0) public Double quantityOnHand; // optionally update quantity
			@CodeValueField("UNIT_OF_QUANTITY")
			public String quantityOnHandUnitCode;
			@CodeValueField("CONTAINER_TYPE")
			public String containerTypeCode;
		}

		public static class SplitInventory {
			public InventoryMaintenancePolicyId inventoryMaintenancePolicy;
			@NotNull @SimpleString
			public String inventoryNumberPart1;
			@Min(-1) public Long inventoryNumberPart2;
			public String inventoryNumberPart3;

			@Min(value = 0) public Double quantityOnHand; // optional quantity of the new inventory
			@CodeValueField("UNIT_OF_QUANTITY")
			public String quantityOnHandUnitCode; // optional unit code
			@CodeValueField("CONTAINER_TYPE")
			public String containerTypeCode; // optional container type
			public String note;

			public static class InventoryMaintenancePolicyId {
				@NotNull public Long id; // inventory maintenance policy id
			}
		}
	}

	Map<?, ?> inventoryOverview(String groupBy, InventoryFilter filter);


	Page<InventoryApiService.AggregatedInventoryQuantityDTO> aggregateQuantity(InventoryFilter filter, Pageable pageable);

	@Data
	class AggregatedInventoryQuantityDTO {
		public AccessionInfo accession;
		public String formTypeCode;
		public String availabilityStatusCode;
		public String quantityOnHandUnitCode;
		public long inventoryMaintenancePolicyCount;
		public long parentInventoryCount;
		public long inventoryCount;
		public double quantityOnHand;
	}

	OrderRequestDTO multiplicationOrder(MultiplicationOrderRequestDTO request);

	@Data
	class MultiplicationOrderRequestDTO {
		public SiteInfo site;
		@NotNull public CooperatorInfo cooperator;
		@NotNull @Size(min = 1) public Set<Long> inventoryIds;
		@NotNull public String orderTypeCode;
		@NotNull public String intendedUseCode;
	}

	FilteredPage<InventoryHarvestDTO, InventoryActionFilter> listInventoryHarvest(InventoryActionFilter filter, Pageable page);

	/**
	 * Data on the planted inventory, the associated HARVEST action, and the resulting harvested inventory.
	 */
	@Data
	class InventoryHarvestDTO {

		/** The planted inventory */
		@JsonUnwrapped
		private InventoryDTO plantedInventory;

		/** The resulting harvested inventory */
		private InventoryDTO harvestedInventory;

		/** The associated harvest action */
		private InventoryActionDTO harvestAction;

	}

	InventoryHarvestDTO createHarvestedInventory(InventoryHarvestRequest request);

	/**
	 * Data on the planted inventory, the associated HARVEST action, and the resulting harvested inventory.
	 */
	@Data
	class InventoryHarvestRequest {
		/** The source, planted inventory */
		@NotNull private InventoryInfo plantedInventory;

		/** The target site for harvested inventory */
		@NotNull private SiteInfo site;
		/** Inventory maintenance policy of the resulting harvested inventory */
		@NotNull private InventoryMaintenancePolicyInfo inventoryMantenancePolicy;

		/** The prefix for the new inventory */
		private String inventoryNumberPart1;
		/** The suffix for the new inventory */
		private String inventoryNumberPart3;

		/** Quantity on hand of harvested material */
		@Min(0) private Double quantityOnHand;

		/** Unit of measure of quantity of harvested material */
		@CodeValueField(CommunityCodeValues.UNIT_OF_QUANTITY)
		private String quantityOnHandUnitCode;

		/** Container type */
		@CodeValueField(CommunityCodeValues.CONTAINER_TYPE)
		private String containerTypeCode;

		/** Production location */
		private GeographyInfo productionGeography;

		// Storage location
		private String storageLocationPart1;
		private String storageLocationPart2;
		private String storageLocationPart3;
		private String storageLocationPart4;

		/** Is this the final batch of harvest for this inventory? */
		private boolean finalBatch = false;
	}

	List<String> autocompleteStorageLocation(int part, InventoryService.InventoryStorageLocationAutocompleteRequest request);
}