001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.item.server.graphql;
018
019import com.echothree.model.data.inventory.common.InventoryCostingPoolConstants;
020import com.echothree.model.control.inventory.server.graphql.InventoryCostingPoolObject;
021import com.echothree.model.control.inventory.server.control.InventoryCostingPoolControl;
022import com.echothree.model.control.accounting.server.graphql.AccountingSecurityUtils;
023import com.echothree.model.control.accounting.server.graphql.ItemAccountingCategoryObject;
024import com.echothree.model.control.cancellationpolicy.server.graphql.CancellationPolicyObject;
025import com.echothree.model.control.cancellationpolicy.server.graphql.CancellationPolicySecurityUtils;
026import com.echothree.model.control.graphql.server.graphql.BaseEntityInstanceObject;
027import com.echothree.model.control.graphql.server.graphql.TimeObject;
028import com.echothree.model.control.graphql.server.graphql.count.Connections;
029import com.echothree.model.control.graphql.server.graphql.count.CountedObjects;
030import com.echothree.model.control.graphql.server.graphql.count.CountingDataConnectionFetcher;
031import com.echothree.model.control.graphql.server.graphql.count.CountingPaginatedData;
032import com.echothree.model.control.graphql.server.util.BaseGraphQl;
033import com.echothree.model.control.graphql.server.util.count.ObjectLimiter;
034import com.echothree.model.control.inventory.server.control.InventoryLocationControl;
035import com.echothree.model.control.inventory.server.graphql.InventoryLocationObject;
036import com.echothree.model.control.inventory.server.graphql.InventorySecurityUtils;
037import com.echothree.model.control.item.common.ItemDescriptionTypes;
038import com.echothree.model.control.item.common.workflow.ItemStatusConstants;
039import com.echothree.model.control.item.server.control.ItemControl;
040import com.echothree.model.control.offer.server.control.OfferItemControl;
041import com.echothree.model.control.offer.server.graphql.OfferItemObject;
042import com.echothree.model.control.offer.server.graphql.OfferSecurityUtils;
043import com.echothree.model.control.party.server.graphql.CompanyObject;
044import com.echothree.model.control.party.server.graphql.PartySecurityUtils;
045import com.echothree.model.control.returnpolicy.server.graphql.ReturnPolicyObject;
046import com.echothree.model.control.returnpolicy.server.graphql.ReturnPolicySecurityUtils;
047import com.echothree.model.control.sequence.server.graphql.SequenceObject;
048import com.echothree.model.control.sequence.server.graphql.SequenceSecurityUtils;
049import com.echothree.model.control.uom.server.graphql.UnitOfMeasureKindObject;
050import com.echothree.model.control.uom.server.graphql.UomSecurityUtils;
051import com.echothree.model.control.vendor.server.control.VendorControl;
052import com.echothree.model.control.vendor.server.graphql.ItemPurchasingCategoryObject;
053import com.echothree.model.control.vendor.server.graphql.VendorItemObject;
054import com.echothree.model.control.vendor.server.graphql.VendorSecurityUtils;
055import com.echothree.model.control.workflow.server.graphql.WorkflowEntityStatusObject;
056import com.echothree.model.data.inventory.common.InventoryLocationConstants;
057import com.echothree.model.data.item.common.ItemAliasConstants;
058import com.echothree.model.data.item.common.ItemPriceConstants;
059import com.echothree.model.data.item.common.ItemUnitOfMeasureTypeConstants;
060import com.echothree.model.data.item.common.RelatedItemTypeConstants;
061import com.echothree.model.data.item.server.entity.Item;
062import com.echothree.model.data.item.server.entity.ItemDetail;
063import com.echothree.model.data.item.server.entity.RelatedItemType;
064import com.echothree.model.data.offer.common.OfferItemConstants;
065import com.echothree.model.data.vendor.common.VendorItemConstants;
066import com.echothree.util.server.persistence.Session;
067import graphql.annotations.annotationTypes.GraphQLDescription;
068import graphql.annotations.annotationTypes.GraphQLField;
069import graphql.annotations.annotationTypes.GraphQLName;
070import graphql.annotations.annotationTypes.GraphQLNonNull;
071import graphql.annotations.connection.GraphQLConnection;
072import graphql.schema.DataFetchingEnvironment;
073import java.util.ArrayList;
074import java.util.stream.Collectors;
075
076@GraphQLDescription("item object")
077@GraphQLName("Item")
078public class ItemObject
079        extends BaseEntityInstanceObject {
080    
081    private final Item item; // Always Present
082    
083    public ItemObject(Item item) {
084        super(item.getPrimaryKey());
085
086        this.item = item;
087    }
088
089    private ItemDetail itemDetail; // Optional, use getItemDetail()
090    
091    private ItemDetail getItemDetail() {
092        if(itemDetail == null) {
093            itemDetail = item.getLastDetail();
094        }
095        
096        return itemDetail;
097    }
098    
099    @GraphQLField
100    @GraphQLDescription("item name")
101    @GraphQLNonNull
102    public String getItemName() {
103        return getItemDetail().getItemName();
104    }
105
106    @GraphQLField
107    @GraphQLDescription("item type")
108    public ItemTypeObject getItemType(final DataFetchingEnvironment env) {
109        return ItemSecurityUtils.getHasItemTypeAccess(env) ? new ItemTypeObject(getItemDetail().getItemType()) : null;
110    }
111
112    @GraphQLField
113    @GraphQLDescription("item use type")
114    public ItemUseTypeObject getItemUseType(final DataFetchingEnvironment env) {
115        return ItemSecurityUtils.getHasItemUseTypeAccess(env) ? new ItemUseTypeObject(getItemDetail().getItemUseType()) : null;
116    }
117
118    @GraphQLField
119    @GraphQLDescription("item category")
120    public ItemCategoryObject getItemCategory(final DataFetchingEnvironment env) {
121        return ItemSecurityUtils.getHasItemCategoryAccess(env) ? new ItemCategoryObject(getItemDetail().getItemCategory()) : null;
122    }
123
124    @GraphQLField
125    @GraphQLDescription("item accounting category")
126    public ItemAccountingCategoryObject getItemAccountingCategory(final DataFetchingEnvironment env) {
127        return AccountingSecurityUtils.getHasItemAccountingCategoryAccess(env) ? new ItemAccountingCategoryObject(getItemDetail().getItemAccountingCategory()) : null;
128    }
129
130    @GraphQLField
131    @GraphQLDescription("item purchasing category")
132    public ItemPurchasingCategoryObject getItemPurchasingCategory(final DataFetchingEnvironment env) {
133        return VendorSecurityUtils.getHasItemPurchasingCategoryAccess(env) ? new ItemPurchasingCategoryObject(getItemDetail().getItemPurchasingCategory()) : null;
134    }
135
136    @GraphQLField
137    @GraphQLDescription("company")
138    public CompanyObject getCompany(final DataFetchingEnvironment env) {
139        var companyParty = getItemDetail().getCompanyParty();
140
141        return PartySecurityUtils.getHasPartyAccess(env, companyParty) ? new CompanyObject(companyParty) : null;
142    }
143
144    @GraphQLField
145    @GraphQLDescription("item delivery type")
146    public ItemDeliveryTypeObject getItemDeliveryType(final DataFetchingEnvironment env) {
147        return ItemSecurityUtils.getHasItemDeliveryTypeAccess(env) ? new ItemDeliveryTypeObject(getItemDetail().getItemDeliveryType()) : null;
148    }
149
150    @GraphQLField
151    @GraphQLDescription("item inventory type")
152    public ItemInventoryTypeObject getItemInventoryType(final DataFetchingEnvironment env) {
153        return ItemSecurityUtils.getHasItemInventoryTypeAccess(env) ? new ItemInventoryTypeObject(getItemDetail().getItemInventoryType()) : null;
154    }
155
156    @GraphQLField
157    @GraphQLDescription("inventory serialized")
158    public Boolean getInventorySerialized() {
159        return getItemDetail().getInventorySerialized();
160    }
161
162    @GraphQLField
163    @GraphQLDescription("serial number sequence")
164    public SequenceObject getSalesOrderSequence(final DataFetchingEnvironment env) {
165        var salesOrderSequence = getItemDetail().getSerialNumberSequence();
166
167        return salesOrderSequence == null ? null : (SequenceSecurityUtils.getHasSequenceAccess(env) ? new SequenceObject(salesOrderSequence) : null);
168    }
169
170    @GraphQLField
171    @GraphQLDescription("shipping charge exempt")
172    @GraphQLNonNull
173    public Boolean getShippingChargeExempt() {
174        return getItemDetail().getShippingChargeExempt();
175    }
176
177    @GraphQLField
178    @GraphQLDescription("shipping start time")
179    @GraphQLNonNull
180    public TimeObject getShippingStartTime(final DataFetchingEnvironment env) {
181        return new TimeObject(getItemDetail().getShippingStartTime());
182    }
183    
184    @GraphQLField
185    @GraphQLDescription("shipping end time")
186    public TimeObject getShippingEndTime(final DataFetchingEnvironment env) {
187        var shippingEndTime = getItemDetail().getShippingEndTime();
188
189        return shippingEndTime == null ? null : new TimeObject(shippingEndTime);
190    }
191    
192    @GraphQLField
193    @GraphQLDescription("sales order start time")
194    @GraphQLNonNull
195    public TimeObject getSalesOrderStartTime(final DataFetchingEnvironment env) {
196        return new TimeObject(getItemDetail().getSalesOrderStartTime());
197    }
198
199    @GraphQLField
200    @GraphQLDescription("sales order end time")
201    public TimeObject getSalesOrderEndTime(final DataFetchingEnvironment env) {
202        var salesOrderEndTime = getItemDetail().getSalesOrderEndTime();
203
204        return salesOrderEndTime == null ? null : new TimeObject(salesOrderEndTime);
205    }
206
207    @GraphQLField
208    @GraphQLDescription("purchase order start time")
209    public TimeObject getPurchaseOrderStartTime(final DataFetchingEnvironment env) {
210        var purchaseOrderStartTime = getItemDetail().getPurchaseOrderStartTime();
211
212        return purchaseOrderStartTime == null ? null : new TimeObject(purchaseOrderStartTime);
213    }
214    
215    @GraphQLField
216    @GraphQLDescription("purchase order end time")
217    public TimeObject getPurchaseOrderEndTime(final DataFetchingEnvironment env) {
218        var purchaseOrderEndTime = getItemDetail().getPurchaseOrderEndTime();
219
220        return purchaseOrderEndTime == null ? null : new TimeObject(purchaseOrderEndTime);
221    }
222
223    @GraphQLField
224    @GraphQLDescription("allow club discounts")
225    @GraphQLNonNull
226    public Boolean getAllowClubDiscounts() {
227        return getItemDetail().getAllowClubDiscounts();
228    }
229
230    @GraphQLField
231    @GraphQLDescription("allow coupon discounts")
232    @GraphQLNonNull
233    public Boolean getAllowCouponDiscounts() {
234        return getItemDetail().getAllowCouponDiscounts();
235    }
236
237    @GraphQLField
238    @GraphQLDescription("allow associate payments")
239    @GraphQLNonNull
240    public Boolean getAllowAssociatePayments() {
241        return getItemDetail().getAllowAssociatePayments();
242    }
243
244    @GraphQLField
245    @GraphQLDescription("unit of measure kind")
246    public UnitOfMeasureKindObject getUnitOfMeasureKind(final DataFetchingEnvironment env) {
247        return UomSecurityUtils.getHasUnitOfMeasureKindAccess(env) ? new UnitOfMeasureKindObject(getItemDetail().getUnitOfMeasureKind()) : null;
248    }
249
250    @GraphQLField
251    @GraphQLDescription("item price type")
252    public ItemPriceTypeObject getItemPriceType(final DataFetchingEnvironment env) {
253        return ItemSecurityUtils.getHasItemPriceTypeAccess(env) ? new ItemPriceTypeObject(getItemDetail().getItemPriceType()) : null;
254    }
255
256    @GraphQLField
257    @GraphQLDescription("cancellation policy")
258    public CancellationPolicyObject getCancellationPolicy(final DataFetchingEnvironment env) {
259        var cancellationPolicy = getItemDetail().getCancellationPolicy();
260
261        return cancellationPolicy == null ? null : (CancellationPolicySecurityUtils.getHasCancellationPolicyAccess(env) ? new CancellationPolicyObject(cancellationPolicy) : null);
262    }
263
264    @GraphQLField
265    @GraphQLDescription("return policy")
266    public ReturnPolicyObject getReturnPolicy(final DataFetchingEnvironment env) {
267        var returnPolicy = getItemDetail().getReturnPolicy();
268
269        return returnPolicy == null ? null : (ReturnPolicySecurityUtils.getHasReturnPolicyAccess(env) ? new ReturnPolicyObject(returnPolicy) : null);
270    }
271
272    //| itm_stylpth_stylepathid             | bigint      | YES  |     | NULL    |       |
273
274    @GraphQLField
275    @GraphQLDescription("item prices")
276    @GraphQLNonNull
277    @GraphQLConnection(connectionFetcher = CountingDataConnectionFetcher.class)
278    public CountingPaginatedData<ItemPriceObject> getItemPrices(final DataFetchingEnvironment env) {
279        if(ItemSecurityUtils.getHasItemPricesAccess(env)) {
280            var itemControl = Session.getModelController(ItemControl.class);
281            var totalCount = itemControl.countItemPricesByItem(item);
282
283            try(var objectLimiter = new ObjectLimiter(env, ItemPriceConstants.COMPONENT_VENDOR_NAME, ItemPriceConstants.ENTITY_TYPE_NAME, totalCount)) {
284                var entities = itemControl.getItemPricesByItem(item);
285                var itemPrices = entities.stream().map(ItemPriceObject::new).collect(Collectors.toCollection(() -> new ArrayList<>(entities.size())));
286
287                return new CountedObjects<>(objectLimiter, itemPrices);
288            }
289        } else {
290            return Connections.emptyConnection();
291        }
292    }
293
294    @GraphQLField
295    @GraphQLDescription("item unit of measure types")
296    @GraphQLNonNull
297    @GraphQLConnection(connectionFetcher = CountingDataConnectionFetcher.class)
298    public CountingPaginatedData<ItemUnitOfMeasureTypeObject> getItemUnitOfMeasureTypes(final DataFetchingEnvironment env) {
299        if(ItemSecurityUtils.getHasItemUnitOfMeasureTypesAccess(env)) {
300            var itemControl = Session.getModelController(ItemControl.class);
301            var totalCount = itemControl.countItemUnitOfMeasureTypesByItem(item);
302
303            try(var objectLimiter = new ObjectLimiter(env, ItemUnitOfMeasureTypeConstants.COMPONENT_VENDOR_NAME, ItemUnitOfMeasureTypeConstants.ENTITY_TYPE_NAME, totalCount)) {
304                var entities = itemControl.getItemUnitOfMeasureTypesByItem(item);
305                var itemAliass = entities.stream().map(ItemUnitOfMeasureTypeObject::new).collect(Collectors.toCollection(() -> new ArrayList<>(entities.size())));
306
307                return new CountedObjects<>(objectLimiter, itemAliass);
308            }
309        } else {
310            return Connections.emptyConnection();
311        }
312    }
313
314    @GraphQLField
315    @GraphQLDescription("item aliases")
316    @GraphQLNonNull
317    @GraphQLConnection(connectionFetcher = CountingDataConnectionFetcher.class)
318    public CountingPaginatedData<ItemAliasObject> getItemAliases(final DataFetchingEnvironment env) {
319        if(ItemSecurityUtils.getHasItemAliasesAccess(env)) {
320            var itemControl = Session.getModelController(ItemControl.class);
321            var totalCount = itemControl.countItemAliasesByItem(item);
322
323            try(var objectLimiter = new ObjectLimiter(env, ItemAliasConstants.COMPONENT_VENDOR_NAME, ItemAliasConstants.ENTITY_TYPE_NAME, totalCount)) {
324                var entities = itemControl.getItemAliasesByItem(item);
325                var itemAliases = entities.stream().map(ItemAliasObject::new).collect(Collectors.toCollection(() -> new ArrayList<>(entities.size())));
326
327                return new CountedObjects<>(objectLimiter, itemAliases);
328            }
329        } else {
330            return Connections.emptyConnection();
331        }
332    }
333
334    @GraphQLField
335    @GraphQLDescription("description")
336    public String getDescription(final DataFetchingEnvironment env) {
337        var itemControl = Session.getModelController(ItemControl.class);
338        var itemDescriptionType = itemControl.getItemDescriptionTypeByName(ItemDescriptionTypes.DEFAULT_DESCRIPTION.name());
339
340        return itemDescriptionType == null ? null : itemControl.getBestItemStringDescription(itemDescriptionType, item, BaseGraphQl.getLanguageEntity(env));
341    }
342
343    @GraphQLField
344    @GraphQLDescription("item status")
345    public WorkflowEntityStatusObject getItemStatus(final DataFetchingEnvironment env) {
346        return getWorkflowEntityStatusObject(env, ItemStatusConstants.Workflow_ITEM_STATUS);
347    }
348
349    @GraphQLField
350    @GraphQLDescription("offer items")
351    @GraphQLNonNull
352    @GraphQLConnection(connectionFetcher = CountingDataConnectionFetcher.class)
353    public CountingPaginatedData<OfferItemObject> getOfferItems(final DataFetchingEnvironment env) {
354        if(OfferSecurityUtils.getHasOfferItemsAccess(env)) {
355            var offerItemControl = Session.getModelController(OfferItemControl.class);
356            var totalCount = offerItemControl.countOfferItemsByItem(item);
357
358            try(var objectLimiter = new ObjectLimiter(env, OfferItemConstants.COMPONENT_VENDOR_NAME, OfferItemConstants.ENTITY_TYPE_NAME, totalCount)) {
359                var entities = offerItemControl.getOfferItemsByItem(item);
360                var offerItems = entities.stream().map(OfferItemObject::new).collect(Collectors.toCollection(() -> new ArrayList<>(entities.size())));
361
362                return new CountedObjects<>(objectLimiter, offerItems);
363            }
364        } else {
365            return Connections.emptyConnection();
366        }
367    }
368
369    @GraphQLField
370    @GraphQLDescription("related item types")
371    @GraphQLNonNull
372    @GraphQLConnection(connectionFetcher = CountingDataConnectionFetcher.class)
373    public CountingPaginatedData<RelatedItemTypeObject> getRelatedItemTypes(final DataFetchingEnvironment env) {
374        if(ItemSecurityUtils.getHasRelatedItemTypesAccess(env)) {
375            var itemControl = Session.getModelController(ItemControl.class);
376            var totalCount = itemControl.countRelatedItemTypes();
377
378            try(var objectLimiter = new ObjectLimiter(env, RelatedItemTypeConstants.COMPONENT_VENDOR_NAME, RelatedItemTypeConstants.ENTITY_TYPE_NAME, totalCount)) {
379                var entities = itemControl.getRelatedItemTypes();
380                var relatedItemTypes = entities.stream()
381                        .map((RelatedItemType relatedItemType) -> new RelatedItemTypeObject(relatedItemType, item))
382                        .collect(Collectors.toCollection(() -> new ArrayList<>(entities.size())));
383
384                return new CountedObjects<>(objectLimiter, relatedItemTypes);
385            }
386        } else {
387            return Connections.emptyConnection();
388        }
389    }
390
391    @GraphQLField
392    @GraphQLDescription("vendor items")
393    @GraphQLNonNull
394    @GraphQLConnection(connectionFetcher = CountingDataConnectionFetcher.class)
395    public CountingPaginatedData<VendorItemObject> getVendorItems(final DataFetchingEnvironment env) {
396        if(VendorSecurityUtils.getHasVendorItemsAccess(env)) {
397            var itemControl = Session.getModelController(VendorControl.class);
398            var totalCount = itemControl.countVendorItemsByItem(item);
399
400            try(var objectLimiter = new ObjectLimiter(env, VendorItemConstants.COMPONENT_VENDOR_NAME, VendorItemConstants.ENTITY_TYPE_NAME, totalCount)) {
401                var entities = itemControl.getVendorItemsByItem(item);
402                var items = entities.stream().map(VendorItemObject::new).collect(Collectors.toCollection(() -> new ArrayList<>(entities.size())));
403
404                return new CountedObjects<>(objectLimiter, items);
405            }
406        } else {
407            return Connections.emptyConnection();
408        }
409    }
410
411    @GraphQLField
412    @GraphQLDescription("inventory locations")
413    @GraphQLNonNull
414    @GraphQLConnection(connectionFetcher = CountingDataConnectionFetcher.class)
415    public CountingPaginatedData<InventoryLocationObject> getInventoryLocations(final DataFetchingEnvironment env) {
416        if(InventorySecurityUtils.getHasInventoryLocationsAccess(env)) {
417            var inventoryLocationControl = Session.getModelController(InventoryLocationControl.class);
418            var totalCount = inventoryLocationControl.countInventoryLocationsByItem(item);
419
420            try(var objectLimiter = new ObjectLimiter(env, InventoryLocationConstants.COMPONENT_VENDOR_NAME,
421                    InventoryLocationConstants.ENTITY_TYPE_NAME, totalCount)) {
422                var entities = inventoryLocationControl.getInventoryLocationsByItem(item);
423                var inventoryLocations = entities.stream()
424                        .map(InventoryLocationObject::new)
425                        .collect(Collectors.toCollection(() -> new ArrayList<>(entities.size())));
426
427                return new CountedObjects<>(objectLimiter, inventoryLocations);
428            }
429        } else {
430            return Connections.emptyConnection();
431        }
432    }
433
434    @GraphQLField
435    @GraphQLDescription("inventory costing pools")
436    @GraphQLNonNull
437    @GraphQLConnection(connectionFetcher = CountingDataConnectionFetcher.class)
438    public CountingPaginatedData<InventoryCostingPoolObject> getInventoryCostingPools(final DataFetchingEnvironment env) {
439        if(InventorySecurityUtils.getHasInventoryCostingPoolsAccess(env)) {
440            var inventoryCostingPoolControl = Session.getModelController(InventoryCostingPoolControl.class);
441            var totalCount = inventoryCostingPoolControl.countInventoryCostingPoolsByItem(item);
442
443            try(var objectLimiter = new ObjectLimiter(env, InventoryCostingPoolConstants.COMPONENT_VENDOR_NAME, InventoryCostingPoolConstants.ENTITY_TYPE_NAME, totalCount)) {
444                var entities = inventoryCostingPoolControl.getInventoryCostingPoolsByItem(item);
445                var inventoryCostingPools = entities.stream()
446                        .map(InventoryCostingPoolObject::new)
447                        .collect(Collectors.toCollection(() -> new ArrayList<>(entities.size())));
448
449                return new CountedObjects<>(objectLimiter, inventoryCostingPools);
450            }
451        } else {
452            return Connections.emptyConnection();
453        }
454    }
455
456}