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