Skip to content
Developerhome
X3

Page extension - Customization on View stock by product site

  Less than to read

In the example displayed in this page, the MobileViewStockByProductSiteProductDetails page is altered to match the following requirements:

  • Remove the Owner fields and value on mobile-view-stock-by-product-site-product-details.ts.
  • Add the new details list SPE01List and display the item SPE01 on mobile-view-stock-by-product-site-product-details.ts.
  • Add an action button on mobile-view-stock-by-product-site-product-details.ts to jump to the Stock change transaction.
  • Open the Stock change details page (MobileInternalStockChangeDetails) while keeping the same stock line data.

Start with creating a new SPE package: new SPE package

Run the following command in the root of your project:

cd x3-services && pnpm run generate && cd && pnpm run build:x3-services

The result of this command is stored in the folder x3-spe. stored in folder x3-spe

In the page-extensions folder located inside the lib folder, a file is created matching the page to be customized. The file name is mobile-view-stock-by-product-site-product-details-extension.

import * as ui from '@sage/xtrem-ui';
import { GraphApi } from "@sage/x3-stock-api";
import { MobileViewStockByProductSiteProductDetails } from "@sage/x3-stock/build/lib/pages/mobile-view-stock-by-product-site-product-details";
 
@ui.decorators.pageExtension<MobileViewStockByProductSiteProductDetailsExtension>({
    extends: '@sage/x3-stock/MobileViewStockByProductSiteProductDetails',
})
 
export class MobileViewStockByProductSiteProductDetailsExtension extends ui.PageExtension<MobileViewStockByProductSiteProductDetails,GraphApi> {
 
}
 
declare module '@sage/x3-stock/build/lib/pages/mobile-view-stock-by-product-site-product-details' {
    interface MobileViewStockByProductSiteProductDetails extends MobileViewStockByProductSiteProductDetailsExtension {}
}

Customization step-by-step

For example, begin with this page:
Starting Page

Then try the following customizations.

Remove the Owner fields and value on mobile-view-stock-by-product-site-product-details.ts

In the mobile-view-stock-by-product-site-product-details.ts, the detailListField component is named stockDetailLines. To customize it, override it as follows:

@ui.decorators.detailListFieldOverride<MobileViewStockByProductSiteProductDetailsExtension>({ 
    fieldOverrides: [ 
        ui.nestedFieldOverrides.text({ 
            bind: 'owner', 
            isHidden: true, 
        }), 
    ], 
}) 
stockDetailLines: ui.fields.DetailList; 

The resulting page based on this action is:
Resulting Page

Add the new details list SPE01List and display the item SPE01 on mobile-view-stock-by-product-site-product-details.ts

   @ui.decorators.pageExtension<MobileViewStockByProductSiteProductDetailsExtension>({ 
   extends: '@sage/x3-stock/MobileViewStockByProductSiteProductDetails', 
   async onLoad() { 
       await this._init(); 
       this.spe01Line.value = [ 
           { 
               _spacerColumn: '', 
               spe01: 'SPE01', 
           }, 
       ] as Partial<any>[]; 
   }, 
   })  
   @ui.decorators.detailListField<MobileViewStockByProductSiteProductDetailsExtension>({ 
       parent() { 
           return this.mainBlock; 
       }, 
       fields: [ 
           ui.nestedFields.text({ 
               bind: '_spacerColumn', 
               isReadOnly: true, 
               isTransient: true, 
           }), 
           ui.nestedFields.text({ 
               bind: 'spe01', 
               title: 'Specific field', 
               isReadOnly: true, 
               isTransient: true, 
           }), 
       ], 
   }) 
   spe01Line: ui.fields.DetailList; 

The resulting page based on this action is:
Resulting Page

Add an action button on mobile-view-stock-by-product-site-product-details.ts to jump to the Stock change transaction

@ui.decorators.buttonField<MobileViewStockByProductSiteProductDetailsExtension>({ 
     parent() { 
         return this.secondBlock; 
     }, 
     map() { 
         return 'Go to Stock Change'; 
     }, 
     isFullWidth: true, 
     async onClick() { 
         this.$.router.goTo('@sage/x3-stock/MobileInternalStockChangeDetails', { 
             _id: `${this._productSite?._id}`, 
             mobileSettings: JSON.stringify({ ...this._mobileSettings }), 
             stockSite: JSON.stringify(this._productSite?.stockSite), 
             selectedProduct: JSON.stringify(this._productSite), 
             globalTradeItemNumber: this._productSite.product?.globalTradeItemNumber ?? '', 
         }); 
     }, 
 }) 
 goToStockChangeButton: ui.fields.Button; 

The resulting page based on this action is:
Resulting Page

Open the Stock change details page MobileInternalStockChangeDetails while keeping the same stock line data

To go directly from the customized MobileViewStockByProductSiteProductDetailsExtension page to the MobileInternalStockChangeDetails page, the necessary context must be initialized. This means passing the parameters stock line ID, stock site, product, mobileSettings, and the global trade item number.

To make a stock change, an input transaction must be defined. To define an input transaction, the transaction field must be added:

import { ProductSite } from '@sage/x3-master-data-api'; 
import { ProductInput } from '@sage/x3-master-data-api-partial'; 
import { StockChangeLineInput, StockEntryTransaction } from '@sage/x3-stock-api'; 
import { GraphApi, MobileSettings, Stock } from '@sage/x3-stock-data-api'; 
import { MobileViewStockByProductSiteProductDetails } from '@sage/x3-stock/build/lib/pages/mobile-view-stock-by-product-site-product-details'; 
import { inputsStockChange } from '@sage/x3-stock/lib/pages/mobile-internal-stock-change'; 
import { ExtractEdgesPartial, extractEdges } from '@sage/xtrem-client'; 
import * as ui from '@sage/xtrem-ui'; 

type DeepPartial<T> = T extends Object ? { [K in keyof T]?: DeepPartial<T[K]> } : T; 
type PartialStockEntryTransaction = DeepPartial<StockEntryTransaction>; 

@ui.decorators.pageExtension<MobileViewStockByProductSiteProductDetailsExtension>({ 
    extends: '@sage/x3-stock/MobileViewStockByProductSiteProductDetails', 
    async onLoad() { 
        await this._init(); 
        this.spe01.value = 'SPE01'; 
    }, 
}) 

export class MobileViewStockByProductSiteProductDetailsExtension extends ui.PageExtension< 
    MobileViewStockByProductSiteProductDetails, 
    GraphApi 
> { 
    _mobileSettings: MobileSettings; 
    _productSite: ExtractEdgesPartial<ProductSite>; 
    _savedObject: inputsStockChange; 
    _transactions: PartialStockEntryTransaction[]; 
 
    @ui.decorators.detailListFieldOverride<MobileViewStockByProductSiteProductDetailsExtension>({ 
        fieldOverrides: [ 
            ui.nestedFieldOverrides.text({ 
                bind: 'owner', 
                isHidden: true, 
            }), 
        ], 
    }) 
    stockDetailLines: ui.fields.DetailList; 
    @ui.decorators.block<MobileViewStockByProductSiteProductDetailsExtension>({ 
        parent() { 
            return this.mainSection; 
        }, 
    }) 
    secondBlock: ui.containers.Block; 
    @ui.decorators.textField<MobileViewStockByProductSiteProductDetailsExtension>({ 
        parent() { 
            return this.mainBlock; 
        }, 
        isTransient: true, 
        isReadOnly: true, 
    }) 
    spe01: ui.fields.Text; 
    @ui.decorators.dropdownListField<MobileViewStockByProductSiteProductDetailsExtension>({ 
        parent() { 
            return this.mainBlock; 
        }, 
        title: 'Transaction', 
        isTransient: true, 
    }) 
    transaction: ui.fields.DropdownList; 
    @ui.decorators.buttonField<MobileViewStockByProductSiteProductDetailsExtension>({ 
        parent() { 
            return this.secondBlock; 
        }, 
        map() { 
            return 'Go to Stock Change'; 
        }, 
        isFullWidth: true, 
        async onClick() { 
            this.$.router.goTo('@sage/x3-stock/MobileInternalStockChangeDetails', { 
                _id: `${this._productSite?._id}`, 
                mobileSettings: JSON.stringify({ ...this._mobileSettings }), 
                stockSite: JSON.stringify(this._productSite?.stockSite), 
                selectedProduct: JSON.stringify(this._productSite), 
                globalTradeItemNumber: this._productSite.product?.globalTradeItemNumber ?? '', 
            }); 
        }, 
    }) 
    goToStockChangeButton: ui.fields.Button; 
    private async _init(): Promise<void> { 
        this._mobileSettings = JSON.parse(this.$.storage.get('mobile-settings-stock-change') as string); 
        await this._readTransaction(); 
        await this._readProductSite(); 
        this._savedObject = { 
            stockChange: { 
                id: '', 
                stockChangeLines: new Array<StockChangeLineInput>(), 
            }, 
            currentLine: 0, 
            username: this.$.username ?? null, 
            started: false, 
            selectedTransaction: this._transactions[0] ?? undefined, 
            selectedProduct: this._productSite.product as ProductInput ?? undefined, 
        }; 
        this.$.storage.set('mobile-stockChange', JSON.stringify({ ...this._savedObject })); 
    } 
    private async _readProductSite(): Promise<void> { 
        const _result = await this.$.graph 
            .node('@sage/x3-master-data/ProductSite') 
            .read( 
                { 
                    _id: true, 
                    isLocationManaged: true, 
                    isLicensePlateNumberManaged: true, 
                    distinctCountOfLocations: true, 
                    countOfStockRecords: true, 
                    distinctCountOfStockQuantity: true, 
                    distinctCountOfLots: true, 
                    distinctCountOfSublots: true, 
                    stockUnitCode: true, 
                    product: { 
                        code: true, 
                        localizedDescription1: true, 
                        upc: true, 
                        lotManagementMode: true, 
                        globalTradeItemNumber: true, 
                    }, 
                    stockSite: { 
                        code: true, 
                    }, 
                }, 
                `${(this as unknown as MobileViewStockByProductSiteProductDetails).product.value}|${(this as unknown as MobileViewStockByProductSiteProductDetails).site.value}`, 
            ) 
            .execute(); 
        this._productSite = _result; 
    } 
    private async _readTransaction(): Promise<void | never> { 
        try { 
            this._transactions = extractEdges( 
                await this.$.graph 
                    .node('@sage/x3-stock/StockEntryTransaction') 
                    .query( 
                        ui.queryUtils.edgesSelector( 
                            { 
                                code: true, 
                                isActive: true, 
                                stockAutomaticJournal: { 
                                    code: true, 
                                }, 
                                localizedDescription: true, 
                                transactionType: true, 
                                identifier1Destination: true, 
                                identifier2Destination: true, 
                                identifier1Detail: true, 
                                identifier2Detail: true, 
                                printingMode: true, 
                                defaultStockMovementGroup: { 
                                    code: true, 
                                }, 
                                stockMovementCode: { 
                                    code: true, 
                                }, 
                                companyOrSiteGroup: { 
                                    group: true, 
                                }, 
                            }, 
                            { 
                                filter: { 
                                    transactionType: 'stockChange', 
                                    isActive: true, 
                                    stockChangeDestination: 'internal', 
                                    stockChangeAccessMode: { _ne: 'containerNumber' }, 
                                }, 
                            }, 
                        ), 
                    ) 
                    .execute(), 
            ); 
        } catch (err) { 
            ui.console.error(err); 
        } 
        if (this._transactions) this.transaction.options = this._transactions.map(trs => trs.code); 
        this.transaction.value = this._transactions[0].code ?? ''; 
    } 
} 
declare module '@sage/x3-stock/build/lib/pages/mobile-view-stock-by-product-site-product-details' { 
    interface MobileViewStockByProductSiteProductDetails extends MobileViewStockByProductSiteProductDetailsExtension {} 
} 

This is the result of these changes applied to the mobile automation pages: Resulting Mobile Page1 Resulting Mobile Page2 Resulting Mobile Page3