table 50100 "Customer Feedback" { Caption = 'Customer Feedback'; DataClassification = ToBeClassified; // Fields fields { // 1. Auto-incrementing primary key field field(1; "Feedback ID"; Integer) { DataClassification = SystemMetadata; AutoIncrement = true; } // 2. Customer No. – lookup on Customer table field(2; "Customer No."; Code[20]) { DataClassification = CustomerContent; Caption = 'Customer No.'; } // 3. Order No. – lookup on Sales Header table field(3; "Order No."; Code[20]) { DataClassification = CustomerContent; Caption = 'Order No.'; } // 4. Rating – allowed values 1 to 5; validated in OnValidate field(4; "Rating"; Integer) { DataClassification = CustomerContent; Caption = 'Rating'; trigger OnValidate() begin if ("Rating" < 1) or ("Rating" > 5) then Error('Rating must be between 1 and 5.'); end; } // 5. Comments field(5; "Comments"; Text[250]) { DataClassification = ToBeClassified; Caption = 'Comments'; } // 6. Feedback Date with default of Today field(6; "Feedback Date"; Date) { DataClassification = ToBeClassified; Caption = 'Feedback Date'; DefaultExpression = Today(); } } // Primary key on Feedback ID keys { key(PK; "Feedback ID") { Clustered = true; } } } page 50100 "Customer Feedback List" { Caption = 'Customer Feedback List'; PageType = List; SourceTable = "Customer Feedback"; ApplicationArea = All; layout { area(content) { repeater(Group) { field("Feedback ID"; "Feedback ID") { ApplicationArea = All; } field("Customer No."; "Customer No.") { ApplicationArea = All; } field("Order No."; "Order No.") { ApplicationArea = All; } field("Rating"; "Rating") { ApplicationArea = All; } field("Comments"; "Comments") { ApplicationArea = All; } field("Feedback Date"; "Feedback Date") { ApplicationArea = All; } } } } } tableextension 50101 "Sales Header Extension" extends "Sales Header" { fields { field(50100; "Has Feedback"; Boolean) { DataClassification = ToBeClassified; Caption = 'Has Feedback'; // The CalcFormula sets the field to true if any "Customer Feedback" record exists with the current order number. CalcFormula = Exists("Customer Feedback" WHERE ("Order No." = field("No."))); } } } pageextension 50102 "Sales Order Extension" extends "Sales Order" { layout { modify("Work Description") { addafter("Work Description") { field("Has Feedback"; "Has Feedback") { ApplicationArea = All; Editable = false; ToolTip = 'Indicates if this order has customer feedback.'; } } } } } codeunit 50103 "Feedback Notification Handler" { Caption = 'Feedback Notification Handler'; [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", 'OnAfterPostSalesDoc', '', false, false)] local procedure OnAfterPostSalesDocHandler(var SalesHeader: Record "Sales Header"; DocType: Option Invoice; var TempSalesInvoiceHeader: Record "Sales Header") var CustFeedback: Record "Customer Feedback"; begin // Filter for feedback matching the current Sales Order number CustFeedback.SetRange("Order No.", SalesHeader."No."); if CustFeedback.IsEmpty() then Message('The invoiced order %1 has no associated customer feedback. Please request feedback from the customer.', SalesHeader."No."); end; } page 50100 "Customer Feedback API" { Caption = 'Customer Feedback API'; PageType = API; SourceTable = "Customer Feedback"; ApiGroup = 'mycompany'; // Customize your API group EntityName = 'CustomerFeedback'; // Singular name EntitySetName = 'CustomerFeedbacks'; // Plural name Editable = true; // Allow POST (create) operations ApplicationArea = All; layout { area(content) { group("Customer Feedback") { field("Feedback ID"; "Feedback ID") { } field("Customer No."; "Customer No.") { } field("Order No."; "Order No.") { } field("Rating"; "Rating") { } field("Comments"; "Comments") { } field("Feedback Date"; "Feedback Date") { } } } } } report 50100 "Customer Feedback Summary" { Caption = 'Customer Feedback Summary'; UsageCategory = ReportsAndAnalysis; RequestFilterFields = "Feedback Date"; dataset { // Primary data item: Customer dataitem(Customer; Customer) { DataItemTableView = sorting("No."); column("Customer No."; Customer."No.") { } column("Customer Name"; Customer.Name) { } // These columns are calculated in the report triggers. column("Total Feedback Count"; TotalFeedbackCount) { DataType = Integer; } column("Average Rating"; AverageRating) { DataType = Decimal; } } } var CustFeedback: Record "Customer Feedback"; TotalFeedbackCount: Integer; SumRating: Decimal; FeedbackCount: Integer; AverageRating: Decimal; FromDate: Date; ToDate: Date; requestpage { layout { area(content) { group("Date Filter") { field(FromDate; FromDate) { ApplicationArea = All; Caption = 'From Date'; } field(ToDate; ToDate) { ApplicationArea = All; Caption = 'To Date'; } } } } } trigger OnPreReport() begin // Set default date filters if none are provided (e.g. last 30 days) if FromDate = 0D then FromDate := Today() - 30; if ToDate = 0D then ToDate := Today(); end; trigger OnAfterGetRecord() begin // For each customer record, calculate feedback totals over the specified date range. TotalFeedbackCount := 0; SumRating := 0; FeedbackCount := 0; CustFeedback.Reset(); CustFeedback.SetRange("Customer No.", Customer."No."); CustFeedback.SetRange("Feedback Date", FromDate, ToDate); if CustFeedback.FindSet() then repeat FeedbackCount += 1; SumRating += CustFeedback."Rating"; until CustFeedback.Next() = 0; TotalFeedbackCount := FeedbackCount; if FeedbackCount > 0 then AverageRating := SumRating / FeedbackCount else AverageRating := 0; end; } codeunit 50104 "Customer Feedback Reminder" { Caption = 'Customer Feedback Reminder'; // This codeunit might be scheduled or run on-demand trigger OnRun() var SalesHeader: Record "Sales Header"; Customer: Record Customer; EmailSubject: Text; EmailBody: Text; SMTPMail: Codeunit "SMTP Mail"; // Standard codeunit for sending SMTP email messages begin // Loop through Sales Orders – in this sample we scan all orders. SalesHeader.Reset(); if SalesHeader.FindSet() then repeat // Check if the order does not have feedback. if not SalesHeader."Has Feedback" then begin // Get the customer from the Sales Header (using the Bill-to Customer No.) if Customer.Get(SalesHeader."Bill-to Customer No.") then begin // Only send email if the customer has an email address if Customer."E-Mail" <> '' then begin EmailSubject := 'Feedback Request for Order ' + SalesHeader."No."; EmailBody := 'Dear ' + Customer.Name + ',' + NewLine + NewLine + 'We noticed that we have not received your feedback for your recent order ' + SalesHeader."No." + '.' + NewLine + 'We would appreciate it if you could take a moment to share your experience with us.' + NewLine + NewLine + 'Thank you!'; SMTPMail.CreateMessage(Customer."E-Mail", EmailSubject, EmailBody); SMTPMail.Send(); end; end; end; until SalesHeader.Next() = 0; end; }