From 4e6ce16a431e40c044693b01aa5c314a577f887d Mon Sep 17 00:00:00 2001 From: Kasper Lind Date: Fri, 28 Aug 2026 08:21:15 +0200 Subject: [PATCH 1/4] basic functionality added --- .../.gitignore | 7 + .../.vscode/settings.json | 9 + .../ProductConfig.permissionset.al | 10 + .../app.json | 44 +++ .../Extensions/PTEProductCardExt.PageExt.al | 97 +++++ .../PTEProductMaterialsExt.TableExt.al | 12 + .../ProductSetup/PTEProductJobItem.Table.al | 362 ++++++++++++++++++ .../PTEProductJobItemColors.Page.al | 136 +++++++ .../ProductSetup/PTEProductJobItemSub.Page.al | 241 ++++++++++++ .../PTEProductSetupState.Codeunit.al | 125 ++++++ .../PTEProductTemplateMgt.Codeunit.al | 318 +++++++++++++++ 11 files changed, 1361 insertions(+) create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/.gitignore create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/.vscode/settings.json create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/ProductConfig.permissionset.al create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/app.json create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductCardExt.PageExt.al create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductMaterialsExt.TableExt.al create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItem.Table.al create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemColors.Page.al create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemSub.Page.al create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductSetupState.Codeunit.al create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/.gitignore b/Product Configuration Enhancement/Product Configuration Enhancement/.gitignore new file mode 100644 index 0000000..aeefae1 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/.gitignore @@ -0,0 +1,7 @@ +#This file lists which files should be ignored by git +*.app +rad.json +.snapshots/ +.alcache/ +.altestrunner/ +.vscode/launch.json diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/.vscode/settings.json b/Product Configuration Enhancement/Product Configuration Enhancement/.vscode/settings.json new file mode 100644 index 0000000..c023ecd --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "al.enableCodeAnalysis": true, + "al.codeAnalyzers": [ + "${CodeCop}", + "${UICop}", + "${PerTenantExtensionCop}" + ], + "al.packageCachePath": ".alpackages" +} \ No newline at end of file diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/ProductConfig.permissionset.al b/Product Configuration Enhancement/Product Configuration Enhancement/ProductConfig.permissionset.al new file mode 100644 index 0000000..9e9cf00 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/ProductConfig.permissionset.al @@ -0,0 +1,10 @@ +permissionset 75000 ProductConfig +{ + Assignable = true; + Permissions = tabledata "PTE Product Job Item" = RIMD, + table "PTE Product Job Item" = X, + codeunit "PTE Product Setup State" = X, + codeunit "PTE Product Template Mgt" = X, + page "PTE Product Job Item Colors" = X, + page "PTE Product Job Item Sub" = X; +} \ No newline at end of file diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/app.json b/Product Configuration Enhancement/Product Configuration Enhancement/app.json new file mode 100644 index 0000000..0be1a44 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/app.json @@ -0,0 +1,44 @@ +{ + "id": "8d206bcf-bc23-4041-9c54-ccfeef79998d", + "name": "Product Configuration Enhancement", + "publisher": "PrintVis A/S", + "version": "1.0.0.0", + "brief": "Product Job Item templates and user field setup for PrintVis products.", + "description": "Adds Product Job Item template lines to the PrintVis Product Card and syncs them to Job Items when a product is applied to a case. Also surfaces the PrintVis Job user fields on the Product Card.", + "privacyStatement": "http://www.printvis.com", + "EULA": "http://www.printvis.com", + "help": "https://learn.printvis.com/", + "url": "http://www.printvis.com", + "dependencies": [ + { + "id": "5452f323-059e-499a-9753-5d2c07eef904", + "name": "PrintVis", + "publisher": "PrintVis A/S", + "version": "28.0.0.0" + } + ], + "screenshots": [], + "idRanges": [ + { + "from": 75000, + "to": 75100 + } + ], + "application": "28.0.0.0", + "platform": "28.0.0.0", + + "contextSensitiveHelpUrl": "https://learn.printvis.com/", + "resourceExposurePolicy": { + "allowDebugging": true, + "allowDownloadingSource": false, + "includeSourceInSymbolFile": true + }, + "runtime": "15.0", + "target": "Cloud", + "features": [ + "TranslationFile" + ], + "supportedLocales": [ + "en-US" + ] +} diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductCardExt.PageExt.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductCardExt.PageExt.al new file mode 100644 index 0000000..8f9b239 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductCardExt.PageExt.al @@ -0,0 +1,97 @@ +pageextension 75006 "PTE Product Card Ext" extends "PVS Product Card" +{ + layout + { + // Hide the base "Inks and Consumables" product colors part; the Product Job Item + // sub page below replaces it for job-item based colors/materials configuration. + modify(InksAndConsumables) + { + Visible = false; + } + + addafter(InksAndConsumables) + { + part(PTE_ProductJobItemSub; "PTE Product Job Item Sub") + { + ApplicationArea = All; + Caption = 'Product Job Items'; + SubPageLink = "Product Code" = field("Code"); + } + } + } + + actions + { + addafter(AdditionalInfo) + { + group("PTE_UserFields") + { + Caption = 'User Fields'; + action("PTE_PUSH_USERFIELDS_1") + { + ApplicationArea = All; + Caption = 'User Fields 1'; + Image = PeriodStatus; + ShortCutKey = 'Shift+Ctrl+F1'; + ToolTip = 'User Fields (1) JOB'; + Visible = PUSH_USERFIELDS_1Visible; + + trigger OnAction() + var + ProductSetupState: Codeunit "PTE Product Setup State"; + begin + ProductSetupState.Form_Userfield_Edit_Product(0, Rec.Code); + end; + } + action("PTE_PUSH_USERFIELDS_2") + { + ApplicationArea = All; + Caption = 'User Fields 2'; + Image = ReopenPeriod; + ShortCutKey = 'Shift+Ctrl+F2'; + ToolTip = 'User Fields (2) JOB'; + Visible = PUSH_USERFIELDS_2Visible; + + trigger OnAction() + var + ProductSetupState: Codeunit "PTE Product Setup State"; + begin + ProductSetupState.Form_Userfield_Edit_Product(1, Rec.Code); + end; + } + action("PTE_PUSH_USERFIELDS_3") + { + ApplicationArea = All; + Caption = 'User Fields 3'; + Image = ClosePeriod; + ShortCutKey = 'Shift+Ctrl+F3'; + ToolTip = 'User Fields (3) JOB'; + Visible = PUSH_USERFIELDS_3Visible; + + trigger OnAction() + var + ProductSetupState: Codeunit "PTE Product Setup State"; + begin + ProductSetupState.Form_Userfield_Edit_Product(2, Rec.Code); + end; + } + } + } + } + + trigger OnOpenPage() + var + SetupRec: Record "PVS General Setup"; + begin + SetupRec.Get(); + + PUSH_USERFIELDS_1Visible := SetupRec."Enable User Fields Job 1"; + PUSH_USERFIELDS_2Visible := SetupRec."Enable User Fields Job 2"; + PUSH_USERFIELDS_3Visible := SetupRec."Enable User Fields Job 3"; + end; + + var + PUSH_USERFIELDS_1Visible: Boolean; + PUSH_USERFIELDS_2Visible: Boolean; + PUSH_USERFIELDS_3Visible: Boolean; +} diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductMaterialsExt.TableExt.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductMaterialsExt.TableExt.al new file mode 100644 index 0000000..325ff36 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductMaterialsExt.TableExt.al @@ -0,0 +1,12 @@ +tableextension 75005 "PTE Product Materials Ext" extends "PVS Product Materials" +{ + fields + { + field(75010; "Job Item No."; Integer) + { + Caption = 'Job Item No.'; + DataClassification = SystemMetadata; + TableRelation = "PTE Product Job Item"."Job Item No." where("Product Code" = field("Product Code")); + } + } +} diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItem.Table.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItem.Table.al new file mode 100644 index 0000000..ebf911c --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItem.Table.al @@ -0,0 +1,362 @@ +table 75000 "PTE Product Job Item" +{ + DataClassification = ToBeClassified; + + fields + { + field(1; "Product Code"; Code[20]) + { + Caption = 'Product Code'; + DataClassification = SystemMetadata; + TableRelation = "PVS Product".Code; + ToolTip = 'Specifies the product code.'; + } + field(2; "Job Item No."; Integer) + { + Caption = 'Job Item No.'; + DataClassification = SystemMetadata; + ToolTip = 'Specifies the job item number.'; + } + field(6010116; "Format Code"; Code[20]) + { + DataClassification = CustomerContent; + Caption = 'Format Code'; + ToolTip = 'Rather than using Format1 and Format2, a Format Code may be entered to give the size of the Paper/Substrate or Plate.'; + TableRelation = "PVS Format Code" where(Type = filter(Miscellaneous | "Final format")); + ValidateTableRelation = false; + + trigger OnValidate() + var + GeneralSetup: Record "PVS General Setup"; + FormatCode: Record "PVS Format Code"; + begin + if not FormatCode.Get(Rec."Format Code") then + exit; + + if GeneralSetup.Get() then; + + if GeneralSetup."Manual Grain Direction" then begin + Rec.Validate(Width, FormatCode."Width Centimeter"); + Rec.Validate(Length, FormatCode."Length Centimeter"); + end else + if GeneralSetup."Format Entry" = GeneralSetup."format entry"::"Depth x Width" then begin + Rec.Validate("Format1", FormatCode."Length Centimeter"); + Rec.Validate("Format2", FormatCode."Width Centimeter"); + end else begin + Rec.Validate("Format1", FormatCode."Width Centimeter"); + Rec.Validate("Format2", FormatCode."Length Centimeter"); + end; + end; + } + field(6010051; "Format1"; Decimal) + { + DataClassification = CustomerContent; + AutoFormatType = 0; + DecimalPlaces = 2 : 6; + BlankZero = true; + Caption = 'Format1'; + ToolTip = 'Size of the current product (width or length depending on setup).'; + } + field(6010052; "Format2"; Decimal) //widrg + { + DataClassification = CustomerContent; + AutoFormatType = 0; + DecimalPlaces = 2 : 6; + BlankZero = true; + Caption = 'Format2'; + ToolTip = 'Size of the current product (width or length depending on setup).'; + } + //Refactored to Length and Width fields only used for the format code validation, the Format1 and Format2 fields are used for the actual format code values. + field(6010130; Length; Decimal) + { + DataClassification = CustomerContent; + AutoFormatType = 0; + DecimalPlaces = 2 : 6; + Caption = 'Length'; + ToolTip = 'Specifies the length used for format code validation.'; + Description = 'PRINTVIS'; + } + //Refactored to Length and Width fields only used for the format code validation, the Format1 and Format2 fields are used for the actual format code values. + + field(6010131; Width; Decimal) + { + DataClassification = CustomerContent; + AutoFormatType = 0; + DecimalPlaces = 2 : 6; + Caption = 'Width'; + ToolTip = 'Specifies the width used for format code validation.'; + } + + field(6010054; "Weight"; Decimal) + { + DataClassification = CustomerContent; + AutoFormatType = 0; + DecimalPlaces = 2 : 6; + BlankZero = true; + Caption = 'Weight'; + ToolTip = 'Displays the weight of the Paper.'; + } + field(6010073; "Imposition Code"; Code[20]) + { + DataClassification = CustomerContent; + Caption = 'Imposition Code'; + TableRelation = "PVS Imposition Code"; + ToolTip = 'Use to link a specific Imposition Type to the Item that will be transferred to the new case if: 1.Field is empty in template, AND 2. Number of items in the length and width on imposition code must match length and width [leaves] of the template case, or the "Manual Pages On The Sheet" is set to True, by entering manual pages on sheet or impose 1/2.'; + + trigger OnValidate() + var + ImpositionRec: Record "PVS Imposition Code"; + begin + if "Imposition Code" <> '' then begin + ImpositionRec.Get("Imposition Code"); + Rec."Format1" := ImpositionRec."Format 1"; + Rec."Format2" := ImpositionRec."Format 2"; + end; + end; + } + field(6010105; "Paper No."; Code[20]) + { + DataClassification = CustomerContent; + Caption = 'Paper No.'; + ToolTip = 'Specifies the paper/substrate item number.'; + TableRelation = Item; + + trigger OnValidate() + var + ItemRec: Record Item; + begin + // Mirror PVS Job Sheet.Change_Paper: pull the paper weight from the item. + // Only overwrite when the item has a weight defined, to avoid clearing a + // manually entered value when the paper item has no weight. + if ItemRec.Get("Paper No.") then + if ItemRec."PVS Weight" <> 0 then + Validate(Weight, ItemRec."PVS Weight"); + end; + } + field(6010115; "Paper Description"; Text[100]) + { + CalcFormula = lookup(Item.Description where("No." = field("Paper No."))); + Caption = 'Paper Description'; + ToolTip = 'Specifies the description of the paper/substrate item.'; + Editable = false; + FieldClass = FlowField; + } + field(6010106; "Finishing"; Code[20]) + { + DataClassification = CustomerContent; + Caption = 'Finishing'; + ToolTip = 'If a certain Finishing Type code is to be added to estimations for this Job Item, enter it here.'; + TableRelation = "PVS Finishing Types"; + } + field(6010330; "Colors Front"; Integer) + { + DataClassification = CustomerContent; + Caption = 'Colors Front'; + ToolTip = 'Displays number of colors on the front side.'; + } + field(6010331; "Colors Back"; Integer) + { + DataClassification = CustomerContent; + Caption = 'Colors Back'; + ToolTip = 'Displays number of colors on the back side.'; + } + field(6010401; "Tool 1"; Code[20]) + { + DataClassification = CustomerContent; + captionclass = 'PVS,FIELD6010388-1'; + Caption = 'Tool 1'; + ToolTip = 'Displays the first Tool for the current PrintVis Product, to properly load the required tools when doing re-runs of the product.'; + TableRelation = "PVS Tool"; + } + field(6010402; "Tool 2"; Code[20]) + { + DataClassification = CustomerContent; + captionclass = 'PVS,FIELD6010388-2'; + Caption = 'Tool 2'; + ToolTip = 'Displays the second Tool for the current PrintVis Product, to properly load the required tools when doing re-runs of the product.'; + TableRelation = "PVS Tool"; + } + field(6010403; "Tool 3"; Code[20]) + { + DataClassification = CustomerContent; + captionclass = 'PVS,FIELD6010388-3'; + Caption = 'Tool 3'; + ToolTip = 'Displays the third Tool for the current PrintVis Product, to properly load the required tools when doing re-runs of the product.'; + TableRelation = "PVS Tool"; + } + field(6010404; "Tool 4"; Code[20]) + { + DataClassification = CustomerContent; + captionclass = 'PVS,FIELD6010388-4'; + Caption = 'Tool 4'; + ToolTip = 'Displays the fourth Tool for the current PrintVis Product, to properly load the required tools when doing re-runs of the product.'; + TableRelation = "PVS Tool"; + } + field(6010410; "Media Type"; Option) + { + DataClassification = CustomerContent; + Caption = 'Media Type'; + ToolTip = 'Specifies the media type (e.g. Envelope).'; + OptionCaption = ' ,Envelope,Envelope Plain,Envelope Window'; + OptionMembers = " ",Envelope,"Envelope Plain","Envelope Window"; + } + field(6010420; "List Of Units"; Code[20]) + { + DataClassification = CustomerContent; + Caption = 'List Of Units'; + ToolTip = 'Specifies the list of units code.'; + TableRelation = "PVS Calculation Unit Setup".Code where("List Of Units" = const(true)); + } + field(6010411; "Envelope Window Shape Type"; Option) + { + DataClassification = CustomerContent; + Caption = 'Envelope Window Shape Type'; + ToolTip = 'Specifies the shape type of the envelope window.'; + OptionCaption = ' ,Rectangular,Round,Path'; + OptionMembers = " ",Rectangular,Round,Path; + } + field(6010412; "Envelope Window Size X"; Decimal) + { + DataClassification = CustomerContent; + AutoFormatType = 0; + DecimalPlaces = 2 : 6; + BlankZero = true; + Caption = 'Envelope Window Size X'; + ToolTip = 'Specifies the horizontal size of the envelope window.'; + } + field(6010413; "Envelope Window Size Y"; Decimal) + { + DataClassification = CustomerContent; + AutoFormatType = 0; + DecimalPlaces = 2 : 6; + BlankZero = true; + Caption = 'Envelope Window Size Y'; + ToolTip = 'Specifies the vertical size of the envelope window.'; + } + field(6010414; "Opacity"; Option) + { + DataClassification = CustomerContent; + Caption = 'Opacity'; + ToolTip = 'Specifies the opacity type of the product.'; + OptionCaption = ' ,Opaque,Translucent,Transparent'; + OptionMembers = " ",Opaque,Translucent,Transparent; + } + field(6010415; "Opacity Level"; Decimal) + { + DataClassification = CustomerContent; + AutoFormatType = 0; + BlankZero = true; + Caption = 'Opacity Level'; + ToolTip = 'Specifies the opacity level of the product.'; + DecimalPlaces = 0 : 0; + } + field(70; "Component Type"; Code[20]) + { + DataClassification = CustomerContent; + Caption = 'Component Type'; + ToolTip = 'Specifies the component type for this job item, defining how it is treated in the production calculation.'; + TableRelation = "PVS Component Types"; + } + field(71; "Component Type Description"; Text[50]) + { + CalcFormula = lookup("PVS Component Types".Description where(Code = field("Component Type"))); + Caption = 'Component Type Description'; + ToolTip = 'Specifies the description of the component type.'; + Editable = false; + FieldClass = FlowField; + } + field(23; "No. Of Pages"; Integer) + { + DataClassification = CustomerContent; + BlankZero = true; + Caption = 'No. of Pages'; + ToolTip = 'Specifies the total number of pages for this job item. Must be an even number.'; + + trigger OnValidate() + var + unevenErr: Label 'No. of pages cannot be uneven'; + begin + if ("No. Of Pages" mod 2) <> 0 then + Error(unevenErr); + end; + } + field(25; "Pages With Print"; Decimal) + { + DataClassification = CustomerContent; + BlankZero = true; + Caption = 'Pages with Print'; + ToolTip = 'Specifies the number of pages that carry print for this job item.'; + DecimalPlaces = 0 : 4; + } + } + + keys + { + key(Key1; "Product Code", "Job Item No.") + { + Clustered = true; + } + } + + fieldgroups + { + // Add changes to field groups here + } + + trigger OnInsert() + var + ProductJobItem: Record "PTE Product Job Item"; + ProductRec: Record "PVS Product"; + begin + if "Job Item No." = 0 then begin + ProductJobItem.SetRange("Product Code", "Product Code"); + if ProductJobItem.FindLast() then + "Job Item No." := ProductJobItem."Job Item No." + 1 + else + "Job Item No." := 1; + end; + ProductRec.Get("Product Code"); + if ProductRec."Format Code" <> '' then + Validate("Format Code", ProductRec."Format Code") + else begin + + "Format1" := ProductRec."Format1"; + "Format2" := ProductRec."Format2"; + Length := ProductRec.Length; + Width := ProductRec.Width; + end; + Weight := ProductRec.Weight; + "Imposition Code" := ProductRec."Imposition Code"; + "Paper No." := ProductRec."Paper No."; + Finishing := ProductRec.Finishing; + "Colors Front" := ProductRec."Colors Front"; + "Colors Back" := ProductRec."Colors Back"; + "Tool 1" := ProductRec."Tool 1"; + "Tool 2" := ProductRec."Tool 2"; + "Tool 3" := ProductRec."Tool 3"; + "Tool 4" := ProductRec."Tool 4"; + "Media Type" := ProductRec."Media Type"; + "Envelope Window Shape Type" := ProductRec."Envelope Window Shape Type"; + "Envelope Window Size X" := ProductRec."Envelope Window Size X"; + "Envelope Window Size Y" := ProductRec."Envelope Window Size Y"; + Opacity := ProductRec.Opacity; + "Opacity Level" := ProductRec."Opacity Level"; + + end; + + trigger OnModify() + begin + + end; + + trigger OnDelete() + begin + + end; + + trigger OnRename() + begin + + end; + +} diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemColors.Page.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemColors.Page.al new file mode 100644 index 0000000..4b3be60 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemColors.Page.al @@ -0,0 +1,136 @@ +/// +/// Popup list page showing PVS Product Materials (inks / consumables) for a specific +/// Product Job Item. Opened from the "PTE Product Job Item Sub" page via the +/// "Colors / Materials" action or by drilling down on the Colors Front / Colors Back fields. +/// +page 75002 "PTE Product Job Item Colors" +{ + PageType = List; + SourceTable = "PVS Product Materials"; + Caption = 'Job Item Colors / Materials'; + ApplicationArea = All; + InsertAllowed = true; + DeleteAllowed = true; + + layout + { + area(Content) + { + repeater(Lines) + { + field("Item Quality Code"; Rec."Item Quality Code") + { + ApplicationArea = All; + ToolTip = 'Specifies the quality of the ink or consumable item.'; + } + field("Item No."; Rec."Item No.") + { + ApplicationArea = All; + ToolTip = 'Specifies the ink or consumable item number.'; + + trigger OnLookup(var Text: Text): Boolean + var + Item: Record Item; + begin + // When an Item Quality Code is specified on the line, restrict the + // Item drilldown/lookup to items that share the same quality code. + if Rec."Item Quality Code" <> '' then + Item.SetRange("PVS Item Quality Code", Rec."Item Quality Code"); + + if Page.RunModal(0, Item) = Action::LookupOK then begin + Text := Item."No."; + exit(true); + end; + exit(false); + end; + } + field(Text; Rec.Text) + { + ApplicationArea = All; + ToolTip = 'Description transferred from the selected item, may be overwritten.'; + } + field("Text 2"; Rec."Text 2") + { + ApplicationArea = All; + ToolTip = 'Secondary description transferred from the selected item, may be overwritten.'; + } + field("F/B"; Rec."F/B") + { + ApplicationArea = All; + ToolTip = 'Specifies whether the color is printed on the Front, Back, or Both sides.'; + } + field(Type; Rec.Type) + { + ApplicationArea = All; + ToolTip = 'Specifies the color type: Base Color, Spot Color, or Other.'; + } + field("Coverage Pct."; Rec."Coverage Pct.") + { + ApplicationArea = All; + ToolTip = 'Specifies the percentage of the sheet area covered with this ink at 100% coverage.'; + } + field("Area Per Weight"; Rec."Area Per Weight") + { + ApplicationArea = All; + ToolTip = 'Specifies how much area is covered with one unit of ink at 100% coverage.'; + } + field("No. Of Washups"; Rec."No. Of Washups") + { + ApplicationArea = All; + ToolTip = 'Specifies the number of color changes and washups to count for this sheet.'; + } + field("Cliche No."; Rec."Cliche No.") + { + ApplicationArea = All; + ToolTip = 'Specifies the cliche item number for flexo production.'; + Visible = false; + } + field("Plate No."; Rec."Plate No.") + { + ApplicationArea = All; + ToolTip = 'Specifies the printing plate item number.'; + Visible = false; + } + field("Job Item No."; Rec."Job Item No.") + { + ApplicationArea = All; + ToolTip = 'Specifies which Product Job Item this material line belongs to.'; + } + field("Product Code"; Rec."Product Code") + { + ApplicationArea = All; + ToolTip = 'Specifies the product code.'; + Visible = false; + } + field("Entry No."; Rec."Entry No.") + { + ApplicationArea = All; + ToolTip = 'Specifies the entry number.'; + Visible = false; + } + } + } + } + + trigger OnNewRecord(BelowxRec: Boolean) + var + ProductMaterials: Record "PVS Product Materials"; + FilterProductCode: Code[20]; + FilterJobItemNo: Integer; + begin + // Inherit the filtered Product Code and Job Item No. so every new line + // is automatically linked to the correct product and job item. + FilterProductCode := CopyStr(Rec.GetFilter("Product Code"), 1, MaxStrLen(Rec."Product Code")); + Evaluate(FilterJobItemNo, Rec.GetFilter("Job Item No.")); + + Rec."Product Code" := FilterProductCode; + Rec."Job Item No." := FilterJobItemNo; + + // Auto-increment Entry No. within this product. + ProductMaterials.SetRange("Product Code", FilterProductCode); + if ProductMaterials.FindLast() then + Rec."Entry No." := ProductMaterials."Entry No." + 1 + else + Rec."Entry No." := 1; + end; +} diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemSub.Page.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemSub.Page.al new file mode 100644 index 0000000..a10e053 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemSub.Page.al @@ -0,0 +1,241 @@ +page 75001 "PTE Product Job Item Sub" +{ + PageType = ListPart; + SourceTable = "PTE Product Job Item"; + Caption = 'Product Job Items'; + InsertAllowed = false; + DeleteAllowed = true; + DelayedInsert = true; + AutoSplitKey = true; + SourceTableView = sorting("Product Code", "Job Item No."); + ApplicationArea = All; + + layout + { + area(Content) + { + repeater(General) + { + field("Product Code"; Rec."Product Code") + { + + Visible = false; + } + field("Job Item No."; Rec."Job Item No.") + { + + } + field("Component Type"; Rec."Component Type") + { + + } + field("Component Type Description"; Rec."Component Type Description") + { + + } + field("No. Of Pages"; Rec."No. Of Pages") + { + + } + field("Pages With Print"; Rec."Pages With Print") + { + + } + field("Format Code"; Rec."Format Code") + { + + } + field("Format1"; Rec."Format1") + { + + } + field("Format2"; Rec."Format2") + { + + } + field(Weight; Rec.Weight) + { + + } + field("Imposition Code"; Rec."Imposition Code") + { + + } + field("Paper No."; Rec."Paper No.") + { + + } + field("Paper Description"; Rec."Paper Description") + { + + } + field(Finishing; Rec.Finishing) + { + + } + field("Colors Front"; Rec."Colors Front") + { + + trigger OnDrillDown() + begin + OpenColorsPage(); + end; + } + field("Colors Back"; Rec."Colors Back") + { + + trigger OnDrillDown() + begin + OpenColorsPage(); + end; + } + field("Media Type"; Rec."Media Type") + { + + } + field("List Of Units"; Rec."List Of Units") + { + + + trigger OnLookup(var Text: Text): Boolean + var + ProductRec: Record "PVS Product"; + Temp_CalcUnitSetup: Record "PVS Calculation Unit Setup" temporary; + PageMgt: Codeunit "PVS Page Management"; + ProductGroupCode: Code[20]; + begin + if ProductRec.Get(Rec."Product Code") then + ProductGroupCode := ProductRec."Product Group Code"; + + PageMgt.Get_ListOfUnitTMP(ProductGroupCode, false, Temp_CalcUnitSetup); + + Commit(); + if Page.RunModal(Page::"PVS Calculation List of Units", Temp_CalcUnitSetup) = Action::LookupOK then begin + Rec.Validate("List Of Units", Temp_CalcUnitSetup.Code); + Text := Temp_CalcUnitSetup.Code; + exit(true); + end; + end; + } + field("Envelope Window Shape Type"; Rec."Envelope Window Shape Type") + { + + } + field("Envelope Window Size X"; Rec."Envelope Window Size X") + { + + } + field("Envelope Window Size Y"; Rec."Envelope Window Size Y") + { + + } + field(Opacity; Rec.Opacity) + { + + } + field("Opacity Level"; Rec."Opacity Level") + { + + } + field("Tool1"; Rec."Tool 1") + { + + } + field("Tool2"; Rec."Tool 2") + { + + } + field("Tool3"; Rec."Tool 3") + { + + } + field("Tool4"; Rec."Tool 4") + { + + } + } + } + + } + + actions + { + area(Processing) + { + action(NewJobItem) + { + + Caption = 'New Job Item'; + ToolTip = 'Creates a new product job item line for this product.'; + Image = NewRow; + + trigger OnAction() + var + ProductJobItem: Record "PTE Product Job Item"; + begin + ProductJobItem.Init(); + ProductJobItem."Product Code" := Rec."Product Code"; + ProductJobItem.Insert(true); + CurrPage.Update(false); + end; + } + action(CopyJobItem) + { + + Caption = 'Copy Job Item'; + ToolTip = 'Creates a copy of the current product job item line.'; + Image = Copy; + Enabled = HasJobItemLines; + trigger OnAction() + var + NewProductJobItem: Record "PTE Product Job Item"; + begin + Rec.TestField("Product Code"); + NewProductJobItem := Rec; + NewProductJobItem."Job Item No." := 0; + NewProductJobItem.Insert(true); + CurrPage.Update(false); + end; + } + action(ViewColors) + { + ApplicationArea = All; + Caption = 'Colors / Materials'; + ToolTip = 'Opens the colors and materials lines for the selected job item.'; + Image = ItemLedger; + trigger OnAction() + begin + OpenColorsPage(); + end; + } + } + + } + + trigger OnAfterGetCurrRecord() + var + ProductJobItem: Record "PTE Product Job Item"; + begin + ProductJobItem.SetRange("Product Code", Rec."Product Code"); + HasJobItemLines := not ProductJobItem.IsEmpty(); + end; + + var + HasJobItemLines: Boolean; + + procedure GetCurrentJobItemNo(): Integer + begin + exit(Rec."Job Item No."); + end; + + local procedure OpenColorsPage() + var + ProductMaterials: Record "PVS Product Materials"; + begin + Rec.TestField("Product Code"); + ProductMaterials.SetRange("Product Code", Rec."Product Code"); + if Rec."Job Item No." <> 0 then + ProductMaterials.SetRange("Job Item No.", Rec."Job Item No."); + Page.RunModal(Page::"PTE Product Job Item Colors", ProductMaterials); + end; +} diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductSetupState.Codeunit.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductSetupState.Codeunit.al new file mode 100644 index 0000000..d1bc119 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductSetupState.Codeunit.al @@ -0,0 +1,125 @@ +/// +/// SingleInstance codeunit that holds transient UI state for the PVS Product Setup pages. +/// Used to communicate the currently selected Product Job Item No. from the job item +/// subpage to sibling subpages (e.g. the Item Colors Part) without requiring a round-trip +/// through the parent page record. +/// +codeunit 75003 "PTE Product Setup State" +{ + SingleInstance = true; + + var + CurrentJobItemNo: Integer; + CurrentProductCode: Code[20]; + + procedure SetCurrentJobItem(ProductCode: Code[20]; JobItemNo: Integer) + begin + CurrentProductCode := ProductCode; + CurrentJobItemNo := JobItemNo; + end; + + procedure GetCurrentJobItemNo(): Integer + begin + exit(CurrentJobItemNo); + end; + + procedure GetCurrentProductCode(): Code[20] + begin + exit(CurrentProductCode); + end; + + /// + /// Opens the combined User Fields page for a Product. + /// Field/option definitions are read from the Job User Field setup, while values are + /// stored under the Product table keyed by the Product Code. + /// + procedure Form_Userfield_Edit_Product(in_Subtype: Integer; in_ProductCode: Code[50]) + var + TempBufferRec: Record "PVS Userfield Field Buffer" temporary; + UserFieldMgt: Codeunit "PVS Userfield Management"; + DataCode: Code[20]; + begin + DataCode := CopyStr(in_ProductCode, 1, MaxStrLen(DataCode)); + if DataCode = '' then + exit; + + UserFieldMgt.Populate_Initial_Values(Database::"PVS Product", in_Subtype, DataCode, '', 0, 0, 0, 0, 0); + Commit(); + + TempBufferRec.SetRange("Table ID", Database::"PVS Product"); + TempBufferRec.SetRange("Table Subtype", in_Subtype); + TempBufferRec.SetRange("Table Code", DataCode); + TempBufferRec.SetRange(ID1, 0); + TempBufferRec.SetRange(ID2, 0); + TempBufferRec.SetRange(ID3, 0); + TempBufferRec.SetRange(ID4, 0); + TempBufferRec.SetRange(ID5, 0); + UserFieldMgt.Populate_Fields_Tmp_Buffer(TempBufferRec, true, 0, 0, 0, 0); + + Page.Run(Page::"PVS Userfields Combined", TempBufferRec); + end; + + /// + /// Copies the Product User Field values onto the Job User Fields when a Product is applied to a Job. + /// + procedure Transfer_Product_Userfields_To_Job(in_ProductCode: Code[50]; in_JobID: Integer; in_JobNo: Integer; in_Version: Integer) + var + FieldRec: Record "PVS Userfield Field"; + ProductValueRec: Record "PVS Userfield Field Value"; + JobValueRec: Record "PVS Userfield Field Value"; + ProductCode: Code[20]; + Subtype: Integer; + begin + ProductCode := CopyStr(in_ProductCode, 1, MaxStrLen(ProductCode)); + if ProductCode = '' then + exit; + if in_JobID = 0 then + exit; + + for Subtype := 0 to 2 do begin + FieldRec.Reset(); + FieldRec.SetRange("Table ID", Database::"PVS Job"); + FieldRec.SetRange("Table Subtype", Subtype); + FieldRec.SetRange("Table Code", ''); + if FieldRec.FindSet() then + repeat + ProductValueRec.Reset(); + ProductValueRec.SetRange("Table ID", Database::"PVS Product"); + ProductValueRec.SetRange("Table Subtype", Subtype); + ProductValueRec.SetRange("Table Code", ProductCode); + ProductValueRec.SetRange("Field No.", FieldRec."Field No."); + ProductValueRec.SetRange(ID1, 0); + ProductValueRec.SetRange(ID2, 0); + ProductValueRec.SetRange(ID3, 0); + ProductValueRec.SetRange(ID4, 0); + ProductValueRec.SetRange(ID5, 0); + ProductValueRec.SetRange(Code1, ''); + if ProductValueRec.FindFirst() then + if ProductValueRec.Text <> '' then + if not JobValueRec.Get(Database::"PVS Job", Subtype, '', FieldRec."Field No.", + in_JobID, in_JobNo, in_Version, 0, 0, '', 10000) + then begin + JobValueRec.Init(); + JobValueRec."Table ID" := Database::"PVS Job"; + JobValueRec."Table Subtype" := Subtype; + JobValueRec."Table Code" := ''; + JobValueRec."Field No." := FieldRec."Field No."; + JobValueRec.ID1 := in_JobID; + JobValueRec.ID2 := in_JobNo; + JobValueRec.ID3 := in_Version; + JobValueRec.ID4 := 0; + JobValueRec.ID5 := 0; + JobValueRec.Code1 := ''; + JobValueRec."Entry No." := 10000; + JobValueRec."Option Value Entry No." := ProductValueRec."Option Value Entry No."; + JobValueRec.Text := ProductValueRec.Text; + JobValueRec.Insert(true); + end else begin + JobValueRec."Option Value Entry No." := ProductValueRec."Option Value Entry No."; + JobValueRec.Text := ProductValueRec.Text; + JobValueRec.Modify(true); + end; + until FieldRec.Next() = 0; + end; + end; +} diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al new file mode 100644 index 0000000..f743b79 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al @@ -0,0 +1,318 @@ +codeunit 75004 "PTE Product Template Mgt" +{ + trigger OnRun() + begin + end; + + /// + /// When a product is validated on a PVS Job, apply the product header fields + /// (Format Code / Colors), transfer the product user fields to the job, and sync + /// all matching PVS Job Item records with the field values from the corresponding + /// PTE Product Job Item template lines. + /// Matching is by Job Item No. Only primary lines (Entry No. = 1) are targeted. + /// Extra existing PVS Job Items without a template match are left untouched. + /// + [EventSubscriber(ObjectType::Codeunit, Codeunit::"PVS Product Management", OnAfterValidateProductPVSJob, '', false, false)] + local procedure OnAfterValidateProductPVSJob(var Job: Record "PVS Job"; var xJob: Record "PVS Job") + var + ProductSetupState: Codeunit "PTE Product Setup State"; + begin + // 1. Copy product header fields (Format Code / Colors) to the job header. + ApplyProductHeaderFields(Job); + + // 2. Transfer the product user field values to the job. + ProductSetupState.Transfer_Product_Userfields_To_Job(Job."Product Code", Job.ID, Job.Job, Job.Version); + + // 3. Sync template lines to the job items. + SyncTemplateLinesToJobItems(Job); + end; + + /// + /// Copies the product header fields (Format Code, Colors Front, Colors Back) to the + /// PVS Job when a product is applied. Formerly executed inside + /// PVS Product Management.ValidateProductPVSJob; moved here so the base app remains + /// unchanged. Persisted with Modify(false) so the values survive the subsequent + /// Job.Get refresh performed by SyncTemplateLinesToJobItems. + /// + local procedure ApplyProductHeaderFields(var Job: Record "PVS Job") + var + Product: Record "PVS Product"; + Changed: Boolean; + begin + if Job."Product Code" = '' then + exit; + if not Product.Get(Job."Product Code") then + exit; + + if (Product."Format Code" <> '') and (Job."Format Code" <> Product."Format Code") then begin + Job.Validate("Format Code", Product."Format Code"); + Changed := true; + end; + + if (Product."Colors Front" <> 0) or (Product."Colors Back" <> 0) then begin + if Job."Colors Front" <> Product."Colors Front" then begin + Job.Validate("Colors Front", Product."Colors Front"); + Changed := true; + end; + if Job."Colors Back" <> Product."Colors Back" then begin + Job.Validate("Colors Back", Product."Colors Back"); + Changed := true; + end; + end; + + if Changed then + Job.Modify(false); + + OnAfterApplyProductHeaderFields(Job); + end; + + local procedure SyncTemplateLinesToJobItems(var Job: Record "PVS Job") + var + TmplLine: Record "PTE Product Job Item"; + JobItem: Record "PVS Job Item"; + JobSheet: Record "PVS Job Sheet"; + PageMgt: Codeunit "PVS Page Management"; + SheetMgt: Codeunit "PVS Sheet Management"; + UnitCode: Code[20]; + Changed: Boolean; + SheetChanged: Boolean; + begin + if Job."Product Code" = '' then + exit; + + TmplLine.SetRange("Product Code", Job."Product Code"); + TmplLine.SetCurrentKey("Product Code", "Job Item No."); + if not TmplLine.FindSet() then + exit; + + repeat + // Locate primary PVS Job Item for this job + template line number + JobItem.SetRange(ID, Job.ID); + JobItem.SetRange(Job, Job.Job); + JobItem.SetRange(Version, Job.Version); + JobItem.SetRange("Job Item No.", TmplLine."Job Item No."); + JobItem.SetRange("Entry No.", 1); + if not JobItem.FindFirst() then begin + // Job Item does not exist: create a new Sheet + Job Item via PVS Sheet Management. + // Event_Create_Sheet takes var in_Rec and calls FindLast() before returning, + // so JobItem is already positioned on the newly created record afterwards. + JobItem.Reset(); + JobItem.ID := Job.ID; + JobItem.Job := Job.Job; + JobItem.Version := Job.Version; + SheetMgt.Event_Create_Sheet(JobItem, false); + + // If the record was not repositioned (creation failed), skip this line. + if JobItem."Entry No." = 0 then + continue; + end; + + Clear(Changed); + + // --- 1. Component Type --- + // Apply first: sets description only, no size or calc cascades. + if (TmplLine."Component Type" <> '') and (JobItem."Component Type" <> TmplLine."Component Type") then begin + JobItem.Validate("Component Type", TmplLine."Component Type"); + Changed := true; + end; + + // --- 2. No. Of Pages --- + // Standalone even-number validation. Must precede Pages With Print. + if JobItem."No. Of Pages" <> TmplLine."No. Of Pages" then + if (TmplLine."No. Of Pages" <> 0) and ((TmplLine."No. Of Pages" mod 2) = 0) then begin + JobItem.Validate("No. Of Pages", TmplLine."No. Of Pages"); + Changed := true; + end; + + // --- 3. Pages With Print --- + // Logically depends on No. Of Pages; validate after. + if (TmplLine."Pages With Print" <> 0) and (JobItem."Pages With Print" <> TmplLine."Pages With Print") then begin + JobItem.Validate("Pages With Print", TmplLine."Pages With Print"); + Changed := true; + end; + + // --- 4-5. Size: Length and Width --- + // Resolved from template using priority: Imposition wins > Format Code > direct Length/Width. + // Length validated before Width to trigger imposition recalc only once. + ApplyTemplateSizeToJobItem(TmplLine, JobItem, Changed); + + // --- 6-7. Colors --- + // Standalone; no cascades between front and back. + if (TmplLine."Colors Front" <> 0) and (JobItem."Colors Front" <> TmplLine."Colors Front") then begin + JobItem.Validate("Colors Front", TmplLine."Colors Front"); + Changed := true; + end; + if (TmplLine."Colors Back" <> 0) and (JobItem."Colors Back" <> TmplLine."Colors Back") then begin + JobItem.Validate("Colors Back", TmplLine."Colors Back"); + Changed := true; + end; + + // --- 8. Paper --- + // The Job Item's "Paper Item No." is a non-editable FlowField into the Sheet, + // so Paper (like Weight) is applied at sheet level below - see step 19. + + // --- 9-12. Tools --- + // All standalone; no cascades between tool slots. + if (TmplLine."Tool 1" <> '') and (JobItem.Tool <> TmplLine."Tool 1") then begin + JobItem.Validate(Tool, TmplLine."Tool 1"); + Changed := true; + end; + if (TmplLine."Tool 2" <> '') and (JobItem."Tool 2" <> TmplLine."Tool 2") then begin + JobItem.Validate("Tool 2", TmplLine."Tool 2"); + Changed := true; + end; + if (TmplLine."Tool 3" <> '') and (JobItem."Tool 3" <> TmplLine."Tool 3") then begin + JobItem.Validate("Tool 3", TmplLine."Tool 3"); + Changed := true; + end; + if (TmplLine."Tool 4" <> '') and (JobItem."Tool 4" <> TmplLine."Tool 4") then begin + JobItem.Validate("Tool 4", TmplLine."Tool 4"); + Changed := true; + end; + + // --- 13-18. Media / Envelope / Opacity --- + // All standalone; grouped by functional area to minimize record traffic. + if (TmplLine."Media Type" <> TmplLine."Media Type"::" ") and (JobItem."Media Type" <> TmplLine."Media Type") then begin + JobItem.Validate("Media Type", TmplLine."Media Type"); + Changed := true; + end; + if (TmplLine."Envelope Window Shape Type" <> TmplLine."Envelope Window Shape Type"::" ") and (JobItem."Envelope Window Shape Type" <> TmplLine."Envelope Window Shape Type") then begin + JobItem.Validate("Envelope Window Shape Type", TmplLine."Envelope Window Shape Type"); + Changed := true; + end; + if (TmplLine."Envelope Window Size X" <> 0) and (JobItem."Envelope Window Size X" <> TmplLine."Envelope Window Size X") then begin + JobItem.Validate("Envelope Window Size X", TmplLine."Envelope Window Size X"); + Changed := true; + end; + if (TmplLine."Envelope Window Size Y" <> 0) and (JobItem."Envelope Window Size Y" <> TmplLine."Envelope Window Size Y") then begin + JobItem.Validate("Envelope Window Size Y", TmplLine."Envelope Window Size Y"); + Changed := true; + end; + if (TmplLine.Opacity <> TmplLine.Opacity::" ") and (JobItem.Opacity <> TmplLine.Opacity) then begin + JobItem.Validate(Opacity, TmplLine.Opacity); + Changed := true; + end; + if (TmplLine."Opacity Level" <> 0) and (JobItem."Opacity Level" <> TmplLine."Opacity Level") then begin + JobItem.Validate("Opacity Level", TmplLine."Opacity Level"); + Changed := true; + end; + + // Persist all field changes in a single Modify before List Of Units. + // Use Modify(false) to avoid triggering PVS Job Item's OnModify cascade, + // which would call Modify() on the parent PVS Job record and cause an + // optimistic concurrency conflict when the page tries to save Product Code. + // Field-level triggers have already run via Validate() above. + if Changed then + JobItem.Modify(false); + + // --- 19. Paper / Weight --- + // Both are stored on the PVS Job Sheet (the Job Item's "Paper Item No." and + // "Paper Weight" are FlowField lookups into it). Applied at sheet level so their + // OnValidate cascades run. Paper is applied before Weight because changing paper + // (Change_Paper) can reset the default weight, which we then override. + // Skipped silently when no Sheet exists yet (Sheet ID = 0). + if JobItem."Sheet ID" <> 0 then + if JobSheet.Get(JobItem."Sheet ID") then begin + Clear(SheetChanged); + if (TmplLine."Paper No." <> '') and (JobSheet."Paper Item No." <> TmplLine."Paper No.") then begin + JobSheet.Validate("Paper Item No.", TmplLine."Paper No."); + SheetChanged := true; + end; + if (TmplLine.Weight <> 0) and (JobSheet.Weight <> TmplLine.Weight) then begin + JobSheet.Validate(Weight, TmplLine.Weight); + SheetChanged := true; + end; + if SheetChanged then + JobSheet.Modify(true); + end; + + // --- 20. List Of Units --- + // Always validated when set; uses PVS sheet-level unit assignment logic. + // Must be after Modify so Job_Item_Input_Unit reads the persisted state. + // Skipped silently when no Sheet exists yet (Sheet ID = 0). + if TmplLine."List Of Units" <> '' then + if JobItem."Sheet ID" <> 0 then begin + UnitCode := TmplLine."List Of Units"; + PageMgt.Job_Item_Input_Unit(JobItem, UnitCode); + end; + + until TmplLine.Next() = 0; + + // Refresh the Job record from the database. SheetMgt.Event_Create_Sheet and + // field Validate cascades may have internally called Modify() on the PVS Job + // record, bumping its DB timestamp. Because Job is passed as var, re-reading it + // here updates the caller's (PVS Product Management) copy so its subsequent + // Modify() uses the current timestamp and avoids the optimistic concurrency + // conflict on the case card page. + if Job.Get(Job.ID, Job.Job, Job.Version) then; + end; + + /// + /// Resolves the final Length and Width for a PVS Job Item from the template line. + /// Priority: Imposition Code (wins) > Format Code > direct Length/Width values. + /// Validates Length before Width to trigger imposition recalculation only once. + /// + local procedure ApplyTemplateSizeToJobItem(TmplLine: Record "PTE Product Job Item"; var JobItem: Record "PVS Job Item"; var Changed: Boolean) + var + GeneralSetup: Record "PVS General Setup"; + ImpositionRec: Record "PVS Imposition Code"; + FormatCode: Record "PVS Format Code"; + FinalLength: Decimal; + FinalWidth: Decimal; + begin + if not GeneralSetup.Get() then + GeneralSetup.Init(); + + FinalLength := 0; + FinalWidth := 0; + + // Imposition Code wins: read Format 1 / Format 2 from imposition record + if TmplLine."Imposition Code" <> '' then begin + if ImpositionRec.Get(TmplLine."Imposition Code") then + if GeneralSetup."Format Entry" = GeneralSetup."format entry"::"Depth x Width" then begin + // Depth x Width: Format 1 = depth/length, Format 2 = width + FinalLength := ImpositionRec."Format 1"; + FinalWidth := ImpositionRec."Format 2"; + end else begin + // Width x Length (default): Format 1 = width, Format 2 = length + FinalWidth := ImpositionRec."Format 1"; + FinalLength := ImpositionRec."Format 2"; + end; + end else + if TmplLine."Format Code" <> '' then begin + // Format Code: resolve centimeters via setup + if FormatCode.Get(TmplLine."Format Code") then + if GeneralSetup."Manual Grain Direction" then begin + FinalWidth := FormatCode."Width Centimeter"; + FinalLength := FormatCode."Length Centimeter"; + end else + if GeneralSetup."Format Entry" = GeneralSetup."format entry"::"Depth x Width" then begin + FinalLength := FormatCode."Length Centimeter"; + FinalWidth := FormatCode."Width Centimeter"; + end else begin + FinalWidth := FormatCode."Width Centimeter"; + FinalLength := FormatCode."Length Centimeter"; + end; + end else begin + // No format driver: use template Length/Width directly + FinalLength := TmplLine.Length; + FinalWidth := TmplLine.Width; + end; + + // Apply Length first (triggers imposition recalculation internally once), + // then Width; skip zero values to avoid clearing existing data. + if (FinalLength <> 0) and (JobItem.Length <> FinalLength) then begin + JobItem.Validate(Length, FinalLength); + Changed := true; + end; + if (FinalWidth <> 0) and (JobItem.Width <> FinalWidth) then begin + JobItem.Validate(Width, FinalWidth); + Changed := true; + end; + end; + + [IntegrationEvent(false, false)] + local procedure OnAfterApplyProductHeaderFields(var Job: Record "PVS Job") + begin + end; +} From be129565dcd034e8071ecacf91e8470c8fcc7297 Mon Sep 17 00:00:00 2001 From: Kasper Lind Date: Fri, 28 Aug 2026 09:39:08 +0200 Subject: [PATCH 2/4] fix list of unit pop up --- .../ProductSetup/PTEProductJobItem.Table.al | 139 ++++++++++++++++++ .../ProductSetup/PTEProductJobItemSub.Page.al | 34 +++++ .../PTEProductTemplateMgt.Codeunit.al | 51 +++++-- 3 files changed, 210 insertions(+), 14 deletions(-) diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItem.Table.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItem.Table.al index ebf911c..2bc008d 100644 --- a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItem.Table.al +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItem.Table.al @@ -206,6 +206,17 @@ table 75000 "PTE Product Job Item" Caption = 'List Of Units'; ToolTip = 'Specifies the list of units code.'; TableRelation = "PVS Calculation Unit Setup".Code where("List Of Units" = const(true)); + + trigger OnValidate() + begin + ResolveConfiguration(); + end; + } + field(6010421; "Configuration"; Code[20]) + { + DataClassification = CustomerContent; + Caption = 'Configuration'; + ToolTip = 'Specifies the Cost Center Configuration to use for the List Of Units. Resolved automatically when unambiguous, or selected manually when the machine/cost center has more than one matching configuration (mirrors the "PVS Calculation Configurations" selection shown on the PVS Job Item).'; } field(6010411; "Envelope Window Shape Type"; Option) { @@ -359,4 +370,132 @@ table 75000 "PTE Product Job Item" end; + /// + /// Resolves the Cost Center Configuration for the current "List Of Units" selection, + /// mirroring the base app's ambiguity handling on the real PVS Job Item (page + /// "PVS Calculation Configurations" / 6010357): candidate configurations are filtered + /// by this line's current Format1/Format2/Weight against each configuration's + /// Min/Max Printing Format and Weight ranges. + /// - 0 matches: "Configuration" is cleared (left for manual/runtime resolution). + /// - 1 match: assigned silently. + /// - 2+ matches: the user is prompted via "PVS Calculation Configurations" to pick one. + /// + local procedure ResolveConfiguration() + var + TempMatchingUnitSetup: Record "PVS Calculation Unit Setup" temporary; + Page_Configurations: Page "PVS Calculation Configurations"; + Config_ARR: array[100] of Code[20]; + Config_Max: Integer; + MatchCount: Integer; + begin + Rec.Configuration := ''; + + if "List Of Units" = '' then + exit; + + MatchCount := GetMatchingConfigurations(TempMatchingUnitSetup); + case MatchCount of + 0: + exit; + 1: + begin + TempMatchingUnitSetup.FindFirst(); + Rec.Configuration := TempMatchingUnitSetup.Configuration; + end; + else begin + Commit(); + Clear(Page_Configurations); + Page_Configurations.Set_Filter_Units(TempMatchingUnitSetup); + if Page_Configurations.RunModal() = Action::OK then begin + Page_Configurations.Find_Marked(Config_Max, Config_ARR); + if Config_Max > 0 then + if TempMatchingUnitSetup.Get(TempMatchingUnitSetup.Type::"Price Unit", Config_ARR[1]) then + Rec.Configuration := TempMatchingUnitSetup.Configuration; + end; + end; + end; + end; + + /// + /// Builds the set of Cost Center Configurations that are compatible with this line's + /// current Format1/Format2/Weight, under the Cost Center resolved from the "List Of + /// Units" calculation unit. Each match is represented by its concrete "PVS Calculation + /// Unit Setup" record, because the "PVS Calculation Configurations" picker page + /// (6010357) actually has SourceTable = "PVS Calculation Unit Setup" (confirmed from + /// the base app symbols) - not "PVS Cost Center Configuration". Also used by the sub + /// page to let the user manually reopen the picker and override the automatically + /// resolved Configuration. + /// + /// Filled with one Calculation Unit Setup record per matching Configuration. + /// The number of matching configurations found. + procedure GetMatchingConfigurations(var TempMatchingUnitSetup: Record "PVS Calculation Unit Setup" temporary): Integer + var + UnitSetup: Record "PVS Calculation Unit Setup"; + CostCenterConfig: Record "PVS Cost Center Configuration"; + CandidateUnitSetup: Record "PVS Calculation Unit Setup"; + begin + TempMatchingUnitSetup.Reset(); + TempMatchingUnitSetup.DeleteAll(); + + if "List Of Units" = '' then + exit(0); + + if not UnitSetup.Get(UnitSetup.Type::"Price Unit", "List Of Units") then + exit(0); + + if UnitSetup."Cost Center Code" = '' then + exit(0); + + CostCenterConfig.SetRange("Cost Center Code", UnitSetup."Cost Center Code"); + if CostCenterConfig.FindSet() then + repeat + if IsConfigurationMatch(CostCenterConfig) then begin + CandidateUnitSetup.Reset(); + CandidateUnitSetup.SetRange(Type, CandidateUnitSetup.Type::"Price Unit"); + CandidateUnitSetup.SetRange("Cost Center Code", CostCenterConfig."Cost Center Code"); + CandidateUnitSetup.SetRange(Configuration, CostCenterConfig.Configuration); + CandidateUnitSetup.SetRange("List Of Units", false); + if CandidateUnitSetup.FindFirst() then + if not TempMatchingUnitSetup.Get(CandidateUnitSetup.Type, CandidateUnitSetup.Code) then begin + TempMatchingUnitSetup := CandidateUnitSetup; + TempMatchingUnitSetup.Insert(); + end; + end; + until CostCenterConfig.Next() = 0; + + exit(TempMatchingUnitSetup.Count()); + end; + + /// + /// Checks whether the given Cost Center Configuration's Min/Max Printing Format and + /// Weight ranges accommodate this line's current Format1/Format2/Weight values. A + /// blank/zero limit on the configuration, or a blank/zero value on this line, is + /// treated as "no constraint" for that dimension. + /// + local procedure IsConfigurationMatch(CostCenterConfig: Record "PVS Cost Center Configuration"): Boolean + begin + if (Format1 <> 0) then begin + if (CostCenterConfig."Max Printing Format Length" <> 0) and (Format1 > CostCenterConfig."Max Printing Format Length") then + exit(false); + if (CostCenterConfig."Min Print Format Length" <> 0) and (Format1 < CostCenterConfig."Min Print Format Length") then + exit(false); + end; + + if (Format2 <> 0) then begin + if (CostCenterConfig."Max Printing Format Width" <> 0) and (Format2 > CostCenterConfig."Max Printing Format Width") then + exit(false); + if (CostCenterConfig."Min Print Format Width" <> 0) and (Format2 < CostCenterConfig."Min Print Format Width") then + exit(false); + end; + + if (Weight <> 0) then begin + if (CostCenterConfig."Max Weight" <> 0) and (Weight > CostCenterConfig."Max Weight") then + exit(false); + if (CostCenterConfig."Min Weight" <> 0) and (Weight < CostCenterConfig."Min Weight") then + exit(false); + end; + + exit(true); + end; + } diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemSub.Page.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemSub.Page.al index a10e053..48a22d1 100644 --- a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemSub.Page.al +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductJobItemSub.Page.al @@ -117,6 +117,40 @@ page 75001 "PTE Product Job Item Sub" end; end; } + field("Configuration"; Rec."Configuration") + { + ToolTip = 'Specifies the Cost Center Configuration resolved for the List Of Units. Automatically resolved when unambiguous; drill down to pick manually when the machine has more than one matching configuration.'; + + trigger OnLookup(var Text: Text): Boolean + var + TempMatchingUnitSetup: Record "PVS Calculation Unit Setup" temporary; + Page_Configurations: Page "PVS Calculation Configurations"; + Config_ARR: array[100] of Code[20]; + Config_Max: Integer; + begin + if Rec."List Of Units" = '' then + exit(false); + + if Rec.GetMatchingConfigurations(TempMatchingUnitSetup) = 0 then + exit(false); + + Commit(); + Clear(Page_Configurations); + Page_Configurations.Set_Filter_Units(TempMatchingUnitSetup); + if Page_Configurations.RunModal() = Action::OK then begin + Page_Configurations.Find_Marked(Config_Max, Config_ARR); + if Config_Max = 0 then + exit(false); + + if not TempMatchingUnitSetup.Get(TempMatchingUnitSetup.Type::"Price Unit", Config_ARR[1]) then + exit(false); + + Rec.Validate("Configuration", TempMatchingUnitSetup.Configuration); + Text := TempMatchingUnitSetup.Configuration; + exit(true); + end; + end; + } field("Envelope Window Shape Type"; Rec."Envelope Window Shape Type") { diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al index f743b79..e45e334 100644 --- a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al @@ -73,9 +73,11 @@ codeunit 75004 "PTE Product Template Mgt" JobSheet: Record "PVS Job Sheet"; PageMgt: Codeunit "PVS Page Management"; SheetMgt: Codeunit "PVS Sheet Management"; + SingleInstance: Codeunit "PVS SingleInstance"; UnitCode: Code[20]; Changed: Boolean; SheetChanged: Boolean; + PrevGUINotAllowed: Boolean; begin if Job."Product Code" = '' then exit; @@ -85,6 +87,13 @@ codeunit 75004 "PTE Product Template Mgt" if not TmplLine.FindSet() then exit; + // Suppress any base-app popups (e.g. "PVS Calculation Configurations" ambiguity + // prompt, or List Of Units surcharge prompts) while template lines are applied + // automatically. The base app is left to auto-resolve a default silently; there + // is no writable Configuration field on "PVS Job Sheet" to force a specific value. + PrevGUINotAllowed := SingleInstance.Get_GUINOTALLOWED(); + SingleInstance.Set_GUINOTALLOWED(true); + repeat // Locate primary PVS Job Item for this job + template line number JobItem.SetRange(ID, Job.ID); @@ -205,15 +214,37 @@ codeunit 75004 "PTE Product Template Mgt" if Changed then JobItem.Modify(false); - // --- 19. Paper / Weight --- - // Both are stored on the PVS Job Sheet (the Job Item's "Paper Item No." and - // "Paper Weight" are FlowField lookups into it). Applied at sheet level so their - // OnValidate cascades run. Paper is applied before Weight because changing paper + // --- 19. List Of Units --- + // Assigns the Controlling (Sheet) Unit and, through it, resolves the Cost + // Center Configuration for the sheet. Must run BEFORE Finishing/Paper/Weight: + // the base app's "PVS Calculation Configurations" selection page is prompted + // when Finishing/Paper/Weight are validated on a sheet whose Configuration has + // not been resolved yet. Manually picking List Of Units first (as done on the + // Product Job Item sub page / base Job Items list) avoids that prompt, so the + // same order is used here. Must be after Modify so Job_Item_Input_Unit reads + // the persisted state. Skipped silently when no Sheet exists yet (Sheet ID = 0). + if TmplLine."List Of Units" <> '' then + if JobItem."Sheet ID" <> 0 then begin + UnitCode := TmplLine."List Of Units"; + PageMgt.Job_Item_Input_Unit(JobItem, UnitCode); + end; + + // --- 20. Finishing / Paper / Weight --- + // All three are stored on the PVS Job Sheet (the Job Item's "Paper Item No." and + // "Paper Weight" are FlowField lookups into it). Applied at sheet level, after + // List Of Units above, so the sheet's Configuration is already resolved and the + // "PVS Calculation Configurations" selection page does not get triggered. Finishing + // is applied before Paper/Weight since it is the more likely input to that + // configuration resolution. Paper is applied before Weight because changing paper // (Change_Paper) can reset the default weight, which we then override. // Skipped silently when no Sheet exists yet (Sheet ID = 0). if JobItem."Sheet ID" <> 0 then if JobSheet.Get(JobItem."Sheet ID") then begin Clear(SheetChanged); + if (TmplLine.Finishing <> '') and (JobSheet.Finishing <> TmplLine.Finishing) then begin + JobSheet.Validate(Finishing, TmplLine.Finishing); + SheetChanged := true; + end; if (TmplLine."Paper No." <> '') and (JobSheet."Paper Item No." <> TmplLine."Paper No.") then begin JobSheet.Validate("Paper Item No.", TmplLine."Paper No."); SheetChanged := true; @@ -226,18 +257,10 @@ codeunit 75004 "PTE Product Template Mgt" JobSheet.Modify(true); end; - // --- 20. List Of Units --- - // Always validated when set; uses PVS sheet-level unit assignment logic. - // Must be after Modify so Job_Item_Input_Unit reads the persisted state. - // Skipped silently when no Sheet exists yet (Sheet ID = 0). - if TmplLine."List Of Units" <> '' then - if JobItem."Sheet ID" <> 0 then begin - UnitCode := TmplLine."List Of Units"; - PageMgt.Job_Item_Input_Unit(JobItem, UnitCode); - end; - until TmplLine.Next() = 0; + SingleInstance.Set_GUINOTALLOWED(PrevGUINotAllowed); + // Refresh the Job record from the database. SheetMgt.Event_Create_Sheet and // field Validate cascades may have internally called Modify() on the PVS Job // record, bumping its DB timestamp. Because Job is passed as var, re-reading it From 8f3a358fa8ae80be97bc2afdf69206013de04082 Mon Sep 17 00:00:00 2001 From: Kasper Lind Date: Thu, 3 Sep 2026 08:40:39 +0200 Subject: [PATCH 3/4] end user doc --- .../Documentation.md | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/Documentation.md diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/Documentation.md b/Product Configuration Enhancement/Product Configuration Enhancement/Documentation.md new file mode 100644 index 0000000..f221b45 --- /dev/null +++ b/Product Configuration Enhancement/Product Configuration Enhancement/Documentation.md @@ -0,0 +1,136 @@ +# Product Configuration Enhancement – End-User Documentation + +## Overview + +**Product Configuration Enhancement** is an add-on for PrintVis that lets you define +detailed, reusable **Job Item templates** directly on the Product Card. When a product is +applied to a Job, these templates are automatically copied onto the Job's Job Items, +saving time on repetitive manual setup for multi-component products (for example, a book +with a cover and inner pages, or an envelope with several sub-parts). + +It also exposes the PrintVis **Job User Fields** setup on the Product Card, so User Field +values can be prepared once on the product and automatically carried over to every Job +that uses it. + +## Where to find it + +Everything is added to the standard **PrintVis Product Card** page: + +- A new **Product Job Items** section (below the "Inks and Consumables" area). +- A **User Fields** action group (next to the "Additional Info" actions). + +No new menu items or role center entries are required — open any Product Card as usual. + +## Product Job Items + +The **Product Job Items** part on the Product Card lets you build one or more template +lines describing the components of the product (e.g. "Cover", "Text pages", "Insert"). +Each line is a full template for one Job Item, including size, paper, colors, tools, +finishing and more. + +### Fields on a Product Job Item line + +| Field | Description | +|---|---| +| Job Item No. | Sequential number identifying the line. Assigned automatically for new lines. | +| Component Type | Classifies how this job item is treated in production calculation (e.g. Cover, Text). | +| No. of Pages | Total number of pages for this component. Must be an even number. | +| Pages with Print | Number of pages that carry print. | +| Format Code | Optional standard format (paper/substrate/plate size) to apply. When set, it fills in Format1/Format2 automatically according to the general Format Entry setup. | +| Format1 / Format2 | Manual size values, used when no Format Code is selected. | +| Weight | Paper/substrate weight. | +| Imposition Code | Links a specific imposition type; also used to derive Format1/Format2 automatically for pages-on-sheet layouts. | +| Paper No. | The paper/substrate item. Selecting an item automatically pulls its weight (if defined). | +| Paper Description | Read-only description of the selected paper item. | +| Finishing | Finishing type to add to estimations for this component. | +| Colors Front / Colors Back | Number of print colors on the front and back side. | +| Tool 1–4 | Tools required when re-running this component. | +| Media Type | Optional media classification (e.g. Envelope, Envelope Plain, Envelope Window). | +| List of Units | The calculation unit (cost center/machine) this component is produced on. | +| Configuration | The Cost Center Configuration to use for the List of Units. Resolved automatically when only one configuration matches the component's size/weight; if several match, you are prompted to choose. | +| Envelope Window Shape Type / Size X / Size Y | Envelope window details, relevant only for envelope media types. | +| Opacity / Opacity Level | Opacity classification and level of the substrate. | + +### Working with Product Job Item lines + +- **New Job Item** – adds a blank template line to the product. Header values (format, + paper, colors, tools, etc.) already set on the Product itself are copied in as a + starting point. +- **Copy Job Item** – duplicates the currently selected line as a new line, useful when + several components are very similar. +- **Colors / Materials** – opens the ink/consumable lines (colors, coverage, washups, + plates, etc.) for the selected job item. You can also drill down directly from the + **Colors Front** or **Colors Back** fields to open the same list. +- Deleting a line removes that component's template; it does not affect Job Items already + created on existing Jobs. + +### Colors / Materials + +Each Product Job Item can have its own set of ink/consumable lines, matching the standard +PrintVis "Inks and Consumables" concept but scoped per component: + +| Field | Description | +|---|---| +| Item Quality Code | Quality of the ink/consumable. | +| Item No. | The ink/consumable item. Lookup is restricted to items matching the selected Item Quality Code, if any. | +| Text / Text 2 | Description(s), pulled from the item and editable. | +| F/B | Whether the color prints on the Front, Back, or Both sides. | +| Type | Base Color, Spot Color, or Other. | +| Coverage Pct. | Percentage of the sheet covered at 100% coverage. | +| Area Per Weight | Area covered per unit of ink at 100% coverage. | +| No. of Washups | Number of color changes/washups to count. | + +New lines automatically inherit the Product Code and Job Item No. of the component you +opened them from. + +## User Fields on the Product Card + +Three new actions, **User Fields 1**, **User Fields 2**, and **User Fields 3**, are added +next to the standard Additional Info actions. Each opens the combined user field editor +for the product, using the same field/option definitions configured for **Job User +Fields** in PrintVis General Setup. + +- Each action is only visible if the corresponding Job User Fields group (1, 2, or 3) is + enabled in **General Setup**. +- Shortcut keys: Shift+Ctrl+F1, Shift+Ctrl+F2, Shift+Ctrl+F3. +- Values entered here are stored against the product and are automatically copied to the + Job's user fields the next time this product is applied to a Job (see below). + +## What happens when a product is applied to a Job + +When you set/change the **Product Code** on a PVS Job, the following happens +automatically, without any extra steps: + +1. **Header fields** – if the product has a Format Code and/or Colors Front/Back set, + these are copied onto the Job header. +2. **User Fields** – any values entered via User Fields 1/2/3 on the product are copied + onto the Job's user fields. +3. **Job Items** – every Product Job Item line on the product is matched to a Job Item on + the Job (by Job Item No.): + - If a matching Job Item does not yet exist, a new sheet and Job Item are created. + - If it exists, its fields are updated wherever the template line has a value set + (component type, page counts, size, paper, colors, tools, media/envelope/opacity + settings, list of units/configuration). + - Fields left blank on the template are not overwritten, so manual adjustments already + made on the Job are preserved where the template doesn't specify a value. + - Any extra Job Items on the Job that don't correspond to a template line are left + untouched. + +This means the recommended workflow is: + +1. Build out the product's components once, on the Product Card, using **Product Job + Items**. +2. Set up User Fields on the product if applicable. +3. Apply the product to any Job as usual — sizes, paper, colors, tools and user fields for + every component are filled in automatically. + +## Tips + +- Set up **Format Code** or **Imposition Code** on a template line instead of manual + Format1/Format2 where possible, so sizes stay consistent if the underlying master data + changes. +- Use **Copy Job Item** to quickly create near-identical components (e.g. multiple insert + types) and then adjust only the differing fields. +- If **Configuration** shows blank after choosing a **List of Units**, either no + configuration matches the current size/weight, or more than one does — in the latter + case, drill down into **Configuration** to pick the correct one manually. From 6cbfe37227852948c625a2b2703d57abf663b70a Mon Sep 17 00:00:00 2001 From: Brandon Steele Date: Thu, 3 Sep 2026 13:14:41 -0400 Subject: [PATCH 4/4] UpdateBasedonUFTesting --- .../ProductConfig.permissionset.al | 1 - .../Extensions/PTEProductCardExt.PageExt.al | 24 +++- .../PTEProductSetupState.Codeunit.al | 125 ------------------ .../PTEProductTemplateMgt.Codeunit.al | 25 +++- 4 files changed, 40 insertions(+), 135 deletions(-) delete mode 100644 Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductSetupState.Codeunit.al diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/ProductConfig.permissionset.al b/Product Configuration Enhancement/Product Configuration Enhancement/ProductConfig.permissionset.al index 9e9cf00..cc693ab 100644 --- a/Product Configuration Enhancement/Product Configuration Enhancement/ProductConfig.permissionset.al +++ b/Product Configuration Enhancement/Product Configuration Enhancement/ProductConfig.permissionset.al @@ -3,7 +3,6 @@ permissionset 75000 ProductConfig Assignable = true; Permissions = tabledata "PTE Product Job Item" = RIMD, table "PTE Product Job Item" = X, - codeunit "PTE Product Setup State" = X, codeunit "PTE Product Template Mgt" = X, page "PTE Product Job Item Colors" = X, page "PTE Product Job Item Sub" = X; diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductCardExt.PageExt.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductCardExt.PageExt.al index 8f9b239..61dbca7 100644 --- a/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductCardExt.PageExt.al +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/Extensions/PTEProductCardExt.PageExt.al @@ -32,15 +32,19 @@ pageextension 75006 "PTE Product Card Ext" extends "PVS Product Card" ApplicationArea = All; Caption = 'User Fields 1'; Image = PeriodStatus; + Promoted = true; + PromotedCategory = Process; ShortCutKey = 'Shift+Ctrl+F1'; ToolTip = 'User Fields (1) JOB'; Visible = PUSH_USERFIELDS_1Visible; trigger OnAction() var - ProductSetupState: Codeunit "PTE Product Setup State"; + UserFieldMgt: Codeunit "PVS Userfield Management"; + Code1: Code[20]; begin - ProductSetupState.Form_Userfield_Edit_Product(0, Rec.Code); + Code1 := CopyStr(Rec.Code, 1, MaxStrLen(Code1)); + UserFieldMgt.Form_Userfield_Edit(6010313, 0, '', Code1, 0, 0, 0, 0, 0); end; } action("PTE_PUSH_USERFIELDS_2") @@ -48,15 +52,19 @@ pageextension 75006 "PTE Product Card Ext" extends "PVS Product Card" ApplicationArea = All; Caption = 'User Fields 2'; Image = ReopenPeriod; + Promoted = true; + PromotedCategory = Process; ShortCutKey = 'Shift+Ctrl+F2'; ToolTip = 'User Fields (2) JOB'; Visible = PUSH_USERFIELDS_2Visible; trigger OnAction() var - ProductSetupState: Codeunit "PTE Product Setup State"; + UserFieldMgt: Codeunit "PVS Userfield Management"; + Code1: Code[20]; begin - ProductSetupState.Form_Userfield_Edit_Product(1, Rec.Code); + Code1 := CopyStr(Rec.Code, 1, MaxStrLen(Code1)); + UserFieldMgt.Form_Userfield_Edit(6010313, 1, '', Code1, 0, 0, 0, 0, 0); end; } action("PTE_PUSH_USERFIELDS_3") @@ -64,15 +72,19 @@ pageextension 75006 "PTE Product Card Ext" extends "PVS Product Card" ApplicationArea = All; Caption = 'User Fields 3'; Image = ClosePeriod; + Promoted = true; + PromotedCategory = Process; ShortCutKey = 'Shift+Ctrl+F3'; ToolTip = 'User Fields (3) JOB'; Visible = PUSH_USERFIELDS_3Visible; trigger OnAction() var - ProductSetupState: Codeunit "PTE Product Setup State"; + UserFieldMgt: Codeunit "PVS Userfield Management"; + Code1: Code[20]; begin - ProductSetupState.Form_Userfield_Edit_Product(2, Rec.Code); + Code1 := CopyStr(Rec.Code, 1, MaxStrLen(Code1)); + UserFieldMgt.Form_Userfield_Edit(6010313, 2, '', Code1, 0, 0, 0, 0, 0); end; } } diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductSetupState.Codeunit.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductSetupState.Codeunit.al deleted file mode 100644 index d1bc119..0000000 --- a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductSetupState.Codeunit.al +++ /dev/null @@ -1,125 +0,0 @@ -/// -/// SingleInstance codeunit that holds transient UI state for the PVS Product Setup pages. -/// Used to communicate the currently selected Product Job Item No. from the job item -/// subpage to sibling subpages (e.g. the Item Colors Part) without requiring a round-trip -/// through the parent page record. -/// -codeunit 75003 "PTE Product Setup State" -{ - SingleInstance = true; - - var - CurrentJobItemNo: Integer; - CurrentProductCode: Code[20]; - - procedure SetCurrentJobItem(ProductCode: Code[20]; JobItemNo: Integer) - begin - CurrentProductCode := ProductCode; - CurrentJobItemNo := JobItemNo; - end; - - procedure GetCurrentJobItemNo(): Integer - begin - exit(CurrentJobItemNo); - end; - - procedure GetCurrentProductCode(): Code[20] - begin - exit(CurrentProductCode); - end; - - /// - /// Opens the combined User Fields page for a Product. - /// Field/option definitions are read from the Job User Field setup, while values are - /// stored under the Product table keyed by the Product Code. - /// - procedure Form_Userfield_Edit_Product(in_Subtype: Integer; in_ProductCode: Code[50]) - var - TempBufferRec: Record "PVS Userfield Field Buffer" temporary; - UserFieldMgt: Codeunit "PVS Userfield Management"; - DataCode: Code[20]; - begin - DataCode := CopyStr(in_ProductCode, 1, MaxStrLen(DataCode)); - if DataCode = '' then - exit; - - UserFieldMgt.Populate_Initial_Values(Database::"PVS Product", in_Subtype, DataCode, '', 0, 0, 0, 0, 0); - Commit(); - - TempBufferRec.SetRange("Table ID", Database::"PVS Product"); - TempBufferRec.SetRange("Table Subtype", in_Subtype); - TempBufferRec.SetRange("Table Code", DataCode); - TempBufferRec.SetRange(ID1, 0); - TempBufferRec.SetRange(ID2, 0); - TempBufferRec.SetRange(ID3, 0); - TempBufferRec.SetRange(ID4, 0); - TempBufferRec.SetRange(ID5, 0); - UserFieldMgt.Populate_Fields_Tmp_Buffer(TempBufferRec, true, 0, 0, 0, 0); - - Page.Run(Page::"PVS Userfields Combined", TempBufferRec); - end; - - /// - /// Copies the Product User Field values onto the Job User Fields when a Product is applied to a Job. - /// - procedure Transfer_Product_Userfields_To_Job(in_ProductCode: Code[50]; in_JobID: Integer; in_JobNo: Integer; in_Version: Integer) - var - FieldRec: Record "PVS Userfield Field"; - ProductValueRec: Record "PVS Userfield Field Value"; - JobValueRec: Record "PVS Userfield Field Value"; - ProductCode: Code[20]; - Subtype: Integer; - begin - ProductCode := CopyStr(in_ProductCode, 1, MaxStrLen(ProductCode)); - if ProductCode = '' then - exit; - if in_JobID = 0 then - exit; - - for Subtype := 0 to 2 do begin - FieldRec.Reset(); - FieldRec.SetRange("Table ID", Database::"PVS Job"); - FieldRec.SetRange("Table Subtype", Subtype); - FieldRec.SetRange("Table Code", ''); - if FieldRec.FindSet() then - repeat - ProductValueRec.Reset(); - ProductValueRec.SetRange("Table ID", Database::"PVS Product"); - ProductValueRec.SetRange("Table Subtype", Subtype); - ProductValueRec.SetRange("Table Code", ProductCode); - ProductValueRec.SetRange("Field No.", FieldRec."Field No."); - ProductValueRec.SetRange(ID1, 0); - ProductValueRec.SetRange(ID2, 0); - ProductValueRec.SetRange(ID3, 0); - ProductValueRec.SetRange(ID4, 0); - ProductValueRec.SetRange(ID5, 0); - ProductValueRec.SetRange(Code1, ''); - if ProductValueRec.FindFirst() then - if ProductValueRec.Text <> '' then - if not JobValueRec.Get(Database::"PVS Job", Subtype, '', FieldRec."Field No.", - in_JobID, in_JobNo, in_Version, 0, 0, '', 10000) - then begin - JobValueRec.Init(); - JobValueRec."Table ID" := Database::"PVS Job"; - JobValueRec."Table Subtype" := Subtype; - JobValueRec."Table Code" := ''; - JobValueRec."Field No." := FieldRec."Field No."; - JobValueRec.ID1 := in_JobID; - JobValueRec.ID2 := in_JobNo; - JobValueRec.ID3 := in_Version; - JobValueRec.ID4 := 0; - JobValueRec.ID5 := 0; - JobValueRec.Code1 := ''; - JobValueRec."Entry No." := 10000; - JobValueRec."Option Value Entry No." := ProductValueRec."Option Value Entry No."; - JobValueRec.Text := ProductValueRec.Text; - JobValueRec.Insert(true); - end else begin - JobValueRec."Option Value Entry No." := ProductValueRec."Option Value Entry No."; - JobValueRec.Text := ProductValueRec.Text; - JobValueRec.Modify(true); - end; - until FieldRec.Next() = 0; - end; - end; -} diff --git a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al index e45e334..62ac876 100644 --- a/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al +++ b/Product Configuration Enhancement/Product Configuration Enhancement/src/ProductSetup/PTEProductTemplateMgt.Codeunit.al @@ -14,19 +14,38 @@ codeunit 75004 "PTE Product Template Mgt" /// [EventSubscriber(ObjectType::Codeunit, Codeunit::"PVS Product Management", OnAfterValidateProductPVSJob, '', false, false)] local procedure OnAfterValidateProductPVSJob(var Job: Record "PVS Job"; var xJob: Record "PVS Job") - var - ProductSetupState: Codeunit "PTE Product Setup State"; begin // 1. Copy product header fields (Format Code / Colors) to the job header. ApplyProductHeaderFields(Job); // 2. Transfer the product user field values to the job. - ProductSetupState.Transfer_Product_Userfields_To_Job(Job."Product Code", Job.ID, Job.Job, Job.Version); + ApplyProductUserFields(Job); // 3. Sync template lines to the job items. SyncTemplateLinesToJobItems(Job); end; + /// + /// Copies the userfield values assigned to the product into a job + /// + local procedure ApplyProductUserFields(var Job: Record "PVS Job") + var + UserfieldValueRec: Record "PVS Userfield Field Value"; + UserfieldMgt: Codeunit "PVS Userfield Management"; + UniqueTables: Dictionary of [Integer, Boolean]; + ProductCode: Code[20]; + begin + ProductCode := CopyStr(Job."Product Code", 1, MaxStrLen(ProductCode)); + UserfieldValueRec.SetRange(Code1, ProductCode); + if UserfieldValueRec.Findset() then + repeat + if not UniqueTables.ContainsKey(UserfieldValueRec."Table ID") then begin + UniqueTables.Add(UserfieldValueRec."Table ID", true); + UserfieldMgt.Copy_Record_UserFields(UserfieldValueRec."Table ID", '', ProductCode, 0, 0, 0, 0, 0, '', Job.ID, Job.Job, Job.Version, 0, 0, false); + end; + until UserfieldValueRec.Next() = 0; + end; + /// /// Copies the product header fields (Format Code, Colors Front, Colors Back) to the /// PVS Job when a product is applied. Formerly executed inside