InventoryService.java

/*
 * Copyright 2020 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gringlobal.service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.Valid;
import javax.validation.constraints.Max;
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.custom.validation.javax.CodeValueField;
import org.gringlobal.custom.validation.javax.SimpleString;
import org.gringlobal.model.Accession;
import org.gringlobal.model.AccessionInvAttach;
import org.gringlobal.model.AccessionInvGroup;
import org.gringlobal.model.AccessionInvName;
import org.gringlobal.model.Cooperator;
import org.gringlobal.model.DateVersionEntityId.EntityIdAndModifiedDate;
import org.gringlobal.model.Inventory;
import org.gringlobal.model.InventoryAction;
import org.gringlobal.model.InventoryQualityStatus;
import org.gringlobal.model.InventoryViability;
import org.gringlobal.model.OrderRequest;
import org.gringlobal.model.SeedInventoryExtra;
import org.gringlobal.model.Site;
import org.gringlobal.model.community.CommunityCodeValues;
import org.gringlobal.persistence.InventoryRepositoryCustom;
import org.gringlobal.service.filter.AccessionFilter;
import org.gringlobal.service.filter.InventoryFilter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.fasterxml.jackson.annotation.JsonUnwrapped;

import lombok.Data;

/**
 * The Interface InventoryService.
 */
public interface InventoryService extends FilteredCRUDService2<Inventory, InventoryFilter> {

	Inventory assureSystemInventory(Accession accession);

	int ensureSystemInventories();

	InventoryDetails getInventoryDetails(Inventory inventory);

	/**
	 * Set inventory quantity on hand
	 * 
	 * @param inventoryQuantity Inventory quantity
	 * @return the updated inventory
	 */
	Inventory setInventoryQuantity(@Valid InventoryQuantityRequest inventoryQuantity);

	/**
	 * Generate barcode for Inventory based on a template
	 *
	 * @param inventory the inventory
	 * @return the barcode
	 */
	String assignBarcode(Inventory inventory);

	/**
	 * Discard the specified quantities for inventories.
	 *
	 * @param discardQuantities A map of `inventoryId` and `quantity` to discard.
	 */
	void discardMaterial(@NotNull Map<Long, Integer> discardQuantities);

	/**
	 * Get the aggregated `quantityOnHand` for specified inventories.
	 *
	 * @param filter inventory filters
	 * @param page pagination and sort options
	 * 
	 * @return a {@link Page} of {@link InventoryRepositoryCustom.AggregatedInventoryQuantity}.
	 */
	Page<InventoryRepositoryCustom.AggregatedInventoryQuantity> aggregateQuantity(@NotNull InventoryFilter filter, Pageable page);

	List<Inventory> splitInventory(@Valid SplitInventoryRequest splitInventoryRequest);

	List<Inventory> assignLocation(@Valid AssignLocationRequest assignLocationRequest);

	List<Inventory> assignLocations(@Valid @Size(min = 1) List<AssignLocationRequest> assignLocationRequests);

	/**
	 * Get inventory by barcode value
	 *
	 * @param barcode the barcode
	 * @return the inventory
	 */
	Inventory getByBarcode(String barcode);

	class AssignLocationRequest {
		@NotNull
		public Location location;
		@NotNull
		@Size(min = 1)
		public Set<EntityIdAndModifiedDate> inventories;

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

	class InventoryDetails {
		@JsonUnwrapped
		public Inventory inventory;

		public List<AccessionInvName> names;
		public List<AccessionInvAttach> attachments;
		public List<AccessionInvGroup> groups;

		public List<InventoryAction> actions;
		public List<InventoryViability> viability;
		public List<InventoryQualityStatus> qualityStatus;
	}

	class InventoryQuantityRequest {
		@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;
	}

	
	static class SplitInventoryRequest {
		public SourceSplitInventory sourceSplitInventory;
		public List<SplitInventory> splits;

		public static class SourceSplitInventory extends 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
			}
		}
	}

	/**
	 * Inventory overview.
	 *
	 * @param groupBy Group by property of Inventory
	 * @param filter the inventory filters
	 * @return the map with statistics
	 */
	Map<?, ?> inventoryOverview(String groupBy, InventoryFilter filter);

	/**
	 * Recalculate all inventory_number fields in the DB for existing records.
	 */
	void recalculateAllInventoryNumbers();

	/**
	 * Calculate {@link Inventory#inventoryNumber} if null
	 */
	void assignMissingInventoryNumbers();

	/**
	 * Create multiplication OrderRequest for inventories.
	 */
	OrderRequest multiplicationOrder(Site site, Set<Long> inventoryIds, Cooperator multiplicationCooperator, String orderType, String intendedUseCode);

	/**
	 * Share attachment for inventories.
	 */
	void shareAttachment(Long attachId, List<Long> inventoryIds);

	/**
	 * Update moisture content for SeedInventoryExtra.
	 */
	SeedInventoryExtra updateMoistureContent(SeedInventoryExtra extra, MoistureContentRequest request);
	
	class MoistureContentRequest {

		@Min(0) @Max(100)
		public Float moistureContent;

		public Date moistureContentDate;

		@CodeValueField(CommunityCodeValues.DATE_FORMAT)
		public String moistureContentDateCode;
	}

	/**
	 * Compare the number of inventories across sites
	 */
	Page<ComparedSitesResponse> compareSites(AccessionFilter filter, Map<Long, NumberFilter<Long>> sites, Pageable page);

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

}