### Apex Controller for Project Creation Source: https://developer.certinia.com/create-project-from-template-api-global-action-button This Apex class serves as the controller for creating projects from templates. It handles input fields for the project template lookup and start date, and calls the CreateProjectFromTemplateService. Ensure the Case object has a lookup field 'Project_Template__c' to Project and a 'Subject' field for the project name. ```apex public class SimpleCPFTController { //Used to get input fields for Project Lookup and Name public Case myCase{get;set;} //Used to get input fields for date public pse__Proj__c inputProj{ get{ if(inputProj == null) inputProj = new pse__Proj__c(); return inputProj; }set;} public SimpleCPFTController(ApexPages.StandardController stdController) { myCase = (Case)stdController.getRecord(); } public PageReference createProject(){ //Set up input fields for template service Id templateProjectId = myCase.Project_Template__c; Date startDate = inputProj.pse__Start_Date__c; //Instantiate the request pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest projRequest = new pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest(templateProjectId, startDate); //Set new project name to specified name projRequest.ProjectName = myCase.Subject; //Call the service and create project List projResponse = pse.CreateProjectFromTemplateService.createProjectsFromTemplates( new List{projRequest}); //If project was created successfully then redirect if(projResponse[0].isSuccess()){ //Obtain new project id and redirect to project page Id ProjectId = projResponse[0].NewProjectId; PageReference nextPage = new PageReference('/' + ProjectId); nextPage.setRedirect(true); return nextPage; //If unsuccessful output error messages }else{ for(pse.CreateProjectFromTemplateService.CreateProjectError err : projResponse[0].Errors){ ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR, err.Message)); } return null; } } } ``` -------------------------------- ### Visualforce Page for Project Creation Form Source: https://developer.certinia.com/create-project-from-template-api-global-action-button This Visualforce page provides the user interface for creating a project from a template. It extends the Apex controller 'SimpleCPFTController' and includes input fields for the project name, template selection, and start date, along with a button to initiate the creation process. Ensure the page is named 'CreateProjectForCase'. ```visualforce ``` -------------------------------- ### Pay Invoices Based on Outstanding Amount Source: https://developer.certinia.com/accounting-payments-plus-api This example demonstrates paying multiple vendors by selecting available transaction line items (TLIs) for payable invoices with an outstanding amount up to a specified value. It includes adding TLIs to a proposal, creating media data, assigning check information, and finally posting and matching the payment. ```apex // Build the list of codaTransactionLineItem__c that match our criteria, and add them to the proposal Set transLineIDs = new Map([Select Id from c2g__codaTransactionLineItem__c where c2g__LineType__c = 'Account' and c2g__transaction__r.c2g__TransactionType__c = 'Invoice' and c2g__DocumentOutstandingValue__c >= -100 and c2g__DocumentOutstandingValue__c < 0 and c2g__MatchingStatus__c = 'Available']).keySet(); List transLineIDList = new List (transLineIDs); c2g.PaymentsPlusService.addToProposal(pay.Id, transLineIDList); ``` ```apex // Once we have some transactions added to the payment proposal, we can add more transactions up to 10.000 // If the proposal is ready, the next step is create media data: c2g.PaymentsPlusService.createMediaData(pay.Id); ``` ```apex // Because we chose a payment media based in checks, we need to add the checks information to the payment. // We must to assign a check for each account to be paid. So if our payment have just one account to paid (accountId) // and the first available check number in the active check range is 17: List checks = new List(); c2g.PaymentsPlusService.Check check = new c2g.PaymentsPlusService.Check(); Id accountId; check.AccountId = accountId; check.CheckNumber = 17; check.Status = c2g.PaymentsPlusService.CheckStatus.StatusValid; checks.add(check); ``` ```apex // Once we've created the checks, we set them to the payment using: c2g.PaymentsPlusService.updateCheckNumbers(pay.Id, checks); ``` ```apex // Finally we can post and match the payment. c2g.PaymentsPlusService.postAndMatchAsync(pay.Id); ``` -------------------------------- ### Example Company Name Filter for Financial Transactions Source: https://developer.certinia.com/using-the-financial-report-builder-message-channel This example demonstrates a filter for the 'CompanyName' field within the 'FinancialTransactions' dataset. It uses a 'dimension' field type and an 'in' operator. ```json { fieldApiName: "CompanyName", // field name datasetApiName: "FinancialTransactions", // dataset api name in analytics fieldType: "dimension", // field type (dimension, measure, date) version: 1, constraint: { operator: "in", // filter type (see schema) value: ["Merlin Technologies Ltd."] // filter value (type varies by filter type, see schema) } } ``` -------------------------------- ### Create Project from Template with Checklist Items Source: https://developer.certinia.com/create-project-from-template-api-custom-objects Uses pse.SObjectCloneMapper to define field mappings for checklist items and invokes the CreateProjectFromTemplateService. ```Apex public with sharing class CPFTIncludingCheckListItems { public static void callCPFTIncludingCheckListItems(Id templateProjectId, Date startDate, String projectName) { //Clone mapper fields for checklist item //Lookup field required in order for related objects to be copied over pse.SObjectCloneMapper.Field itemMapperField1 = new pse.SObjectCloneMapper.Field(Schema.Checklist_Item__c.Project_Phase__c); pse.SObjectCloneMapper.Field itemMapperField2 = new pse.SObjectCloneMapper.Field(Schema.Checklist_Item__c.Name); pse.SObjectCloneMapper.Field itemMapperField3 = new pse.SObjectCloneMapper.Field(Schema.Checklist_Item__c.Done__c); //Checklist items are set to unchecked as default itemMapperField3.DefaultValue = false; //Group together the pse.SObjectCloneMapper.Field instantiated above for each object and prepare a set. Set setItemMapperFields = new Set{itemMapperField1, itemMapperField2, itemMapperField3}; //Instantiate the SObjectCloneMapper using the prepared set of SObjectCloneMapper.Field for each object. pse.SObjectCloneMapper itemCloneMap = new pse.SObjectCloneMapper(Checklist_Item__c.SObjectType, setItemMapperFields); //Instantiate the request pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest projRequest = new pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest(templateProjectId, startDate); projRequest.ProjectName = projectName; projRequest.Mappers = new List { itemCloneMap }; //Call the service to create the projects using the request list and store a list of responses. List projResponses = pse.CreateProjectFromTemplateService.createProjectsFromTemplates( new List{projRequest}); } } ``` -------------------------------- ### Implement Queueable Interface for Project Creation Source: https://developer.certinia.com/create-project-from-template-api-opportunity-product A wrapper class implementing the Queueable interface to allow asynchronous execution of the project creation logic. ```Apex public with sharing class QueueCPFT implements Queueable{ private Date startDate; private Id templateId; private Id oppId; private String projectName; public QueueCPFT(Date startDate, Id templateProjectId, Id oppId, String projectName){ this.templateId = templateProjectId; this.startDate = startDate; this.oppId = oppId; this.projectName = projectName; } public void execute(QueueableContext context){ CPFT.createProject(startDate, templateId, oppId, projectName); } } ``` -------------------------------- ### Implement Schedulable Versioning Class Source: https://developer.certinia.com/version-service-api-scheduled-versioning Create a global class implementing the Schedulable interface to query projects and invoke the Version Service API asynchronously. ```Apex public class ScheduleVersioning implements Schedulable{ public void execute(SchedulableContext ctx){ //Call our versioning method callVersioning(); } private static void callVersioning() { //Query for Projects that need versioning List p = [SELECT Name, Id FROM pse__Proj__c WHERE Versioning__c = true]; //Create list of versions List versions = new List(); //Create versions for each project for(pse__Proj__c proj : p){ Id projectId = proj.Id; //Version setup pse.VersionService.Version dto = new pse.VersionService.Version(); dto.ProjectId = projectId; dto.VersionName = 'Monthly Version ' + Date.Today(); dto.Notes = 'Demo notes.'; dto.Baseline = false; versions.add(dto); } //Call API List versionIds = pse.VersionService.createAsync(versions); } } ``` -------------------------------- ### Create Project from Template Service Class Source: https://developer.certinia.com/create-project-from-template-api-opportunity-product This class handles the invocation of the Create Project from Template service and includes error handling via email notifications. ```Apex public class CPFT { public static void createProject(Date startDate, Id templateProjectId, Id opportunityId, String projectName){ // Instantiate the request. pse.CreateProjectFromTemplateService.CreateProjectFromTemplateAndOpportunityRequest oppProjReq = new pse.CreateProjectFromTemplateService.CreateProjectFromTemplateAndOpportunityRequest(opportunityId, templateProjectId, startDate); oppProjReq.ProjectName = projectName; // Call the service to create the projects using the request list and store a list of responses. List oppProjResponse = pse.CreateProjectFromTemplateService.createProjectsFromTemplates( new List {oppProjReq}); //Email error message if project not created successfully if(!oppProjResponse[0].isSuccess()){ String eSubject = 'Error in creating ' + projectName; String eBody = 'Request for this project: ' + oppProjResponse[0].Request + 'nn' + 'Errors related to the unsuccessful request: ' + oppProjResponse[0].Errors; sendEmail(eSubject, eBody); } } //Private method to send email private static void sendEmail(String subject, String body){ Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage(); String address = UserInfo.getUserEmail(); message.toAddresses = new String[] { address }; message.subject = subject; message.plainTextBody = body; Messaging.SingleEmailMessage[] messages = new List {message}; Messaging.SendEmailResult[] results = Messaging.sendEmail(messages); } } ``` -------------------------------- ### Implement CPFT with Checklist Items and Materials Source: https://developer.certinia.com/create-project-from-template-api-custom-objects Uses pse.SObjectCloneMapper to define field mappings for related objects during project creation from a template. ```Apex public with sharing class CPFTIncludingChecklistItemsAndMaterials { public static void callCPFTIncludingChecklistItemsAndMaterials(Id templateProjectId, Date startDate, String projectName) { //Clone mapper fields for checklist item //Lookup field required in order for related objects to be copied over pse.SObjectCloneMapper.Field itemMapperField1 = new pse.SObjectCloneMapper.Field(Schema.Checklist_Item__c.Project_Phase__c); pse.SObjectCloneMapper.Field itemMapperField2 = new pse.SObjectCloneMapper.Field(Schema.Checklist_Item__c.Name); pse.SObjectCloneMapper.Field itemMapperField3 = new pse.SObjectCloneMapper.Field(Schema.Checklist_Item__c.Done__c); //Checklist items are set to unchecked as default itemMapperField3.DefaultValue = false; //Clone mapper fields for Material //Lookup field required in order for related objects to be copied over pse.SObjectCloneMapper.Field materialMapperField1 = new pse.SObjectCloneMapper.Field(Schema.Material__c.Name); pse.SObjectCloneMapper.Field materialMapperField2 = new pse.SObjectCloneMapper.Field(Schema.Material__c.Project__c); pse.SObjectCloneMapper.Field materialMapperField3 = new pse.SObjectCloneMapper.Field(Schema.Material__c.Quantity__c); pse.SObjectCloneMapper.Field materialMapperField4 = new pse.SObjectCloneMapper.Field(Schema.Material__c.Unit_of_Measurement__c); pse.SObjectCloneMapper.Field materialMapperField5 = new pse.SObjectCloneMapper.Field(Schema.Material__c.Company__c); pse.SObjectCloneMapper.Field materialMapperField6 = new pse.SObjectCloneMapper.Field(Schema.Material__c.Material_Group__c); //Clone mapper fields for Material Group pse.SObjectCloneMapper.Field groupMapperfield1 = new pse.SObjectCloneMapper.Field(Schema.Material_Group__c.Name); //Group together the pse.SObjectCloneMapper.Field instantiated above for each object and prepare a set. Set setItemMapperFields = new Set{itemMapperField1, itemMapperField2, itemMapperField3}; Set setMaterialMapperFields = new Set{materialMapperField1, materialMapperField2, materialMapperField3, materialMapperField4, materialMapperField5, materialMapperField6}; Set setGroupMapperFields = new Set{groupMapperfield1}; //Instantiate the SObjectCloneMapper using the prepared set of SObjectCloneMapper.Field for each object. pse.SObjectCloneMapper itemCloneMap = new pse.SObjectCloneMapper(Checklist_Item__c.SObjectType, setItemMapperFields); pse.SObjectCloneMapper materialCloneMap = new pse.SObjectCloneMapper(Material__c.SObjectType, setMaterialMapperFields); pse.SObjectCloneMapper groupCloneMap = new pse.SObjectCloneMapper(Material_Group__c.SObjectType, setGroupMapperFields); //Instantiate the request pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest projRequest = new pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest(templateProjectId, startDate); projRequest.ProjectName = projectName; projRequest.Mappers = new List { itemCloneMap, materialCloneMap, groupCloneMap }; //Call the service to create the projects using the request list and store a list of responses. List projResponses = pse.CreateProjectFromTemplateService.createProjectsFromTemplates( new List{projRequest}); } } ``` -------------------------------- ### Implement Project Creation from Case in Apex Controller Source: https://developer.certinia.com/create-project-from-template-api-global-action-button This Apex controller class is used to create a project from a Case record using the Certinia CPFT API. It handles input fields for project lookup and name, and manages the project creation process. ```apex public class SimpleCPFTController { //Used to get input fields for Project Lookup and Name public Case myCase{get;set;} //Used to get input fields for date public pse__Proj__c inputProj{ get{ if(inputProj == null) inputProj = new pse__Proj__c(); return inputProj; }set;} public SimpleCPFTController(ApexPages.StandardController stdController) { myCase = (Case)stdController.getRecord(); } public PageReference createProject(){ //Instantiate the request pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest projRequest = createRequest(); //Call the service and create project List projResponse = pse.CreateProjectFromTemplateService.createProjectsFromTemplates( new List{projRequest}); //If project was created successfully then redirect if(projResponse[0].isSuccess()){ //Obtain new project id and redirect to project page Id ProjectId = projResponse[0].NewProjectId; PageReference nextPage = new PageReference('/' + ProjectId); nextPage.setRedirect(true); return nextPage; //If unsuccessful output error messages }else{ System.debug(LoggingLevel.ERROR, projResponse); for(pse.CreateProjectFromTemplateService.CreateProjectError err : projResponse[0].Errors){ System.debug(LoggingLevel.ERROR, 'Adding Error: ' + err.Message); ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR, err.Message)); } return null; } } private pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest createRequest() { //Set up input fields for template service Id templateProjectId = myCase.Project_Template__c; Date startDate = inputProj.pse__Start_Date__c; //Instantiate the request pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest projRequest = new pse.CreateProjectFromTemplateService.CreateProjectFromTemplateRequest(templateProjectId, startDate); //Set new project name to specified name projRequest.ProjectName = myCase.Subject; //Make a fieldMapper for the Case field on Project. Set the default value to the case we are using. pse.SObjectCloneMapper.Field caseMapper = new pse.SObjectCloneMapper.Field(Schema.pse__Proj__c.Case__c); caseMapper.DefaultValue = myCase.Id; Set setOfFields = new Set{caseMapper}; //Make a mapper for the Project object pse.SObjectCloneMapper itemCloneMap = new pse.SObjectCloneMapper(pse__Proj__c.SObjectType, setOfFields); projRequest.Mappers = new List { itemCloneMap }; return projRequest; } } ``` -------------------------------- ### Unit Test for Project Creation Service Source: https://developer.certinia.com/create-project-from-template-api-custom-objects Verifies the successful creation of a project and its associated checklist items using the service. ```Apex @isTest private class TestCPFTIncludingCheckListItems { @isTest static void testChecklistItems() { pse__Proj__c p = new pse__Proj__c(); p.pse__Start_Date__c = Date.Today(); p.Name = 'Test'; p.pse__Is_Template__c = true; insert p; pse__Project_Phase__c phase = new pse__Project_Phase__c(); phase.Name = 'Phase Test'; phase.pse__Project__c = p.Id; insert phase; Checklist_Item__c ci = new Checklist_Item__c(); ci.Name = 'Checklist Item Test'; ci.Done__c = true; ci.Project_Phase__c = phase.Id; insert ci; Date d = Date.Today(); String s = 'Test Case Project'; Test.StartTest(); CPFTIncludingCheckListItems.callCPFTIncludingCheckListItems(p.Id, d, s); Test.StopTest(); Checklist_Item__c checkItem = [SELECT Name, Done__c, Project_Phase__r.Name, Project_Phase__r.pse__Project__r.Name, Project_phase__r.pse__Project__r.pse__Start_Date__c FROM Checklist_Item__c WHERE Project_Phase__r.pse__Project__r.Name ='Test Case Project' LIMIT 1]; //Check Checklist Item fields and successfully created Project and related fields System.assertEquals(false, checkItem.Done__c); System.assertEquals(‘Checklist Item Test’, checkItem.Name); System.assertEquals('Phase Test',checkItem.Project_Phase__r.Name); System.assertEquals(s, checkItem.Project_Phase__r.pse__Project__r.Name); System.assertEquals(d, checkItem.Project_phase__r.pse__Project__r.pse__Start_Date__c); } } ``` -------------------------------- ### Create Certinia Project and Related Records in Apex Source: https://developer.certinia.com/create-project-from-template-api-custom-objects This Apex code demonstrates the creation of a Certinia project, a project phase, a checklist item, a company, a material group, and a material. It then calls a utility method to process these records and performs assertions to verify the creation and data integrity. Use this for setting up test data or automating project creation. ```apex p.pse__Start_Date__c = Date.Today(); p.Name = 'Test'; p.pse__Is_Template__c = true; insert p; pse__Project_Phase__c phase = new pse__Project_Phase__c(); phase.Name = 'Phase Test'; phase.pse__Project__c = p.Id; insert phase; Checklist_Item__c ci = new Checklist_Item__c(); ci.Name = 'Checklist Item Test'; ci.Done__c = true; ci.Project_Phase__c = phase.Id; insert ci; fferpcore__Company__c c = new fferpcore__Company__c(); c.Name = 'Test Company'; insert c; Material_Group__c mg = new Material_Group__c(); mg.Name = 'Test Group'; insert mg; Material__c m = new Material__c(); m.Name = 'Test Material'; m.Project__c = p.Id; m.Quantity__c = 10; m.Unit_of_Measurement__c = 'ft'; m.Company__c = c.Id; m.Material_Group__c = mg.Id; insert m; Date d = Date.Today(); String s = 'Test Case Project'; Test.StartTest(); CPFTIncludingChecklistItemsAndMaterials.callCPFTIncludingChecklistItemsAndMaterials(p.Id, d, s); Test.StopTest(); Checklist_Item__c checkItem = [SELECT Name, Done__c, Project_Phase__r.Name, Project_Phase__r.pse__Project__r.Name, Project_phase__r.pse__Project__r.pse__Start_Date__c FROM Checklist_Item__c WHERE Project_Phase__r.pse__Project__r.Name ='Test Case Project' LIMIT 1]; //Check Checklist Item fields and successfully created Project and related fields System.assertEquals(false, checkItem.Done__c); System.assertEquals('Checklist Item Test', checkItem.Name); System.assertEquals('Phase Test',checkItem.Project_Phase__r.Name); System.assertEquals(s, checkItem.Project_Phase__r.pse__Project__r.Name); System.assertEquals(d, checkItem.Project_phase__r.pse__Project__r.pse__Start_Date__c); Material__c checkMaterial = [SELECT Name, Company__r.Name, Material_Group__r.Name, Quantity__c, Unit_of_Measurement__c, Project__r.Name FROM Material__c WHERE Project__r.Name = 'Test Case Project' LIMIT 1]; //Check Material fields System.assertEquals('Test Company', checkMaterial.Company__r.Name); System.assertEquals('Test Group', checkMaterial.Material_Group__r.Name); System.assertEquals('Test Material', checkMaterial.Name); System.assertEquals(10, checkMaterial.Quantity__c); System.assertEquals('ft', checkMaterial.Unit_of_Measurement__c); System.assertEquals(s, checkMaterial.Project__r.Name); ``` -------------------------------- ### Apex Unit Test for Project Version Creation Source: https://developer.certinia.com/version-service-api-milestone-status This Apex test class verifies the functionality of the `InvocableVersioning.createProjectVersion` method. It requires setting up project, permission, and milestone records before executing the method and asserting the creation of a project version. ```apex @isTest private class TestInvocableVersioning { @isTest static void TestUpdate() { //Set up test data pse__Proj__c proj = new pse__Proj__c(); proj.pse__Start_Date__c = Date.Today(); proj.Name = 'Test Project'; proj.pse__Is_Active__c = true; insert proj; //Set up permission controls to allow versioning pse__Permission_Control__c permissions = new pse__Permission_Control__c(); permissions.pse__Compare_Project_Version__c = true; permissions.pse__Create_Project_Version__c = true; permissions.pse__Delete_Project_Version__c = true; permissions.pse__User__c = UserInfo.getUserId(); permissions.pse__Project__c = proj.Id; insert permissions; //More test data pse__Milestone__c m = new pse__Milestone__c(); m.Name = 'Test Milestone'; m.pse__Target_Date__c = Date.Today(); m.pse__Project__c = proj.Id; m.pse__Milestone_Amount__c = 4; m.pse__Status__c = 'Planned'; insert m; List milestones = new List (); milestones.add(m); Test.startTest(); //Call invocable method InvocableVersioning.createProjectVersion(milestones); Test.stopTest(); //Query for created version List ver = [SELECT Name, pse__Notes__c FROM pse__Version__c WHERE pse__Project__c = :proj.Id]; //See if version was created System.assertEquals(1, ver.size(), 'Version was not created'); String versionName = 'Approved Test Milestone Version ' + Date.Today(); //See if name matches System.assertEquals(versionName, ver[0].Name); System.assertEquals('Version automatically created on approved Milestone: Test Milestone', ver[0].pse__Notes__c); } } ``` -------------------------------- ### Create Media Tables (Asynchronous) Source: https://developer.certinia.com/accounting-payments-plus-api Use this asynchronous service to create media tables for external tools after a payment proposal is finalized. It returns the ID of the related Payment Media Control object. ```javascript c2g.PaymentsPlusService.createMediaDataAsync(pay.Id); ``` -------------------------------- ### Create Media Tables (Synchronous) Source: https://developer.certinia.com/accounting-payments-plus-api Use this synchronous service to create media tables for external tools after a payment proposal is finalized. It returns the ID of the related Payment Media Control object. ```javascript c2g.PaymentsPlusService.createMediaData(pay.Id); ``` -------------------------------- ### POST /createMediaData Source: https://developer.certinia.com/accounting-payments-plus-api Generates media tables for a payment proposal, available in synchronous and asynchronous versions. ```APIDOC ## POST /createMediaData ### Description Generates media tables for a payment proposal. Available as synchronous (createMediaData) or asynchronous (createMediaDataAsync). ### Parameters #### Request Body - **pay.Id** (String) - Required - The ID of the payment (c2g__codaPayment__c). ### Response #### Success Response (200) - **Payment Media Control ID** (String) - The ID of the Payment Media Control object related to the payment. ``` -------------------------------- ### Unit test for SimpleCPFTController Source: https://developer.certinia.com/create-project-from-template-api-global-action-button Verifies that the controller correctly sets the Case field and redirects to the appropriate project page. Requires an existing Case and Project Template record. ```Apex @isTest static void testButton() { Case c = new Case(); c.Subject = 'Test Case Project'; c.Status = 'Escalated'; c.Origin = 'Phone'; pse__Proj__c p = new pse__Proj__c(); p.pse__Start_Date__c = Date.Today(); p.Name = 'Test'; p.pse__Is_Template__c = true; insert p; c.Project_Template__c = p.Id; insert c; Test.StartTest(); ApexPages.StandardController sc = new ApexPages.StandardController(c); SimpleCPFTController scc = new SimpleCPFTController(sc); scc.inputProj = p; PageReference returnPage = scc.createProject(); Test.StopTest(); //Check input fields from Visualforce page match System.assertEquals(p.Id, scc.myCase.Project_Template__c, 'Project template does not match'); System.assertEquals('Test Case Project', scc.myCase.Subject, 'Inputted Project name does not match'); System.assertEquals(Date.Today(), scc.inputProj.pse__Start_Date__c, 'inputted start date does not match'); pse__Proj__c proj = [SELECT Id, Name, Case__c FROM pse__Proj__c WHERE Name='Test Case Project' LIMIT 1]; System.assertEquals(c.Id, proj.Case__c, 'Case field on project does not match'); PageReference testPage = new PageReference('/' + proj.Id); //Check we are redirected correctly System.assertEquals(testPage.getURL(), returnPage.getURL(), 'Not redirected to correct page'); } ``` -------------------------------- ### Unit Test for ScheduleVersioning Source: https://developer.certinia.com/version-service-api-scheduled-versioning Tests the scheduling and versioning functionality by creating a project, setting permissions, and verifying version creation after the scheduled job runs. ```Apex @isTest private class TestScheduleVersioning { @isTest static void testScheduleAndVersion() { //Create test data pse__Proj__c proj = new pse__Proj__c(); proj.pse__Start_Date__c = Date.Today(); proj.Name = 'Test Project'; proj.pse__Is_Active__c = true; proj.Versioning__c = true; insert proj; //Permission controls to allow versioning pse__Permission_Control__c permissions = new pse__Permission_Control__c(); permissions.pse__Compare_Project_Version__c = true; permissions.pse__Create_Project_Version__c = true; permissions.pse__Delete_Project_Version__c = true; permissions.pse__User__c = UserInfo.getUserId(); permissions.pse__Project__c = proj.Id; insert permissions; String cronExpr = '0 0 0 1 3 ? 2022'; Test.StartTest(); //Schedule the test job ScheduleVersioning sv = new ScheduleVersioning(); System.schedule('test', cronExpr, sv); //Verify job has not run yet List ver = [SELECT Name, Id FROM pse__Version__c WHERE pse__Project__c = :proj.Id]; System.assertEquals(0, ver.size(), 'Version created before job has run'); //Job will run synchronously when stopping test Test.StopTest(); //Verify version was created ver = [SELECT Name, Id FROM pse__Version__c WHERE pse__Project__c = :proj.Id]; System.assertEquals(1, ver.size(), 'Version was not created'); } } ``` -------------------------------- ### Create Project Version via Invocable Method Source: https://developer.certinia.com/version-service-api-milestone-status An Apex class containing an invocable method that initializes a project version DTO and calls the VersionService API asynchronously. Ensure the executing user has appropriate permissions to perform versioning operations. ```apex global class InvocableVersioning { @InvocableMethod(label='Create Project Version ' description='Create Project Version') global static void createProjectVersion (List milestone) { List versions = new List(); //Version setup. pse.VersionService.Version dto = new pse.VersionService.Version(); dto.ProjectId = milestone[0].pse__Project__c; dto.VersionName = 'Approved ' + milestone[0].Name + ' Version ' + Date.Today(); dto.Notes = 'Version automatically created on approved Milestone: ' + milestone[0].Name; dto.Baseline = false; versions.add(dto); //Call API List versionIds = pse.VersionService.createAsync(versions); } } ``` -------------------------------- ### Create a Trigger Plugin Constructor Source: https://developer.certinia.com/pluggable-triggers Implement the fferpcore.PluggableTriggerApi.PluginConstructor interface to instantiate the trigger plugin. This class and its methods must be declared as global. ```apex global class TriggerNameConstructor implements fferpcore.PluggableTriggerApi.PluginConstructor { global SObjectType sObjectType() { // The SObjectType for the SObject you want to add the trigger to, // e.g. Billing Document. return fferpcore__BillingDocument__c.SObjectType; } global fferpcore.PluggableTriggerApi.Plugin construct( List records, fferpcore.PluggableTriggerApi.Context context // Currently unused ) { return new TriggerName(records); } } ``` -------------------------------- ### Create a new payment record Source: https://developer.certinia.com/accounting-payments-plus-api Initializes and inserts a new c2g__codaPayment__c record. Ensure all mandatory fields like bank account, currency, and period are populated before insertion. ```Apex c2g__codaPayment__c pay = new c2g__codaPayment__c(); // Payment details pay.c2g__BankAccount__c = bankAccountId; pay.c2g__PaymentCurrency__c = paymentCurrencyId; pay.c2g__PaymentDate__c = system.today(); pay.c2g__Period__c = periodId; pay.c2g__DiscountDate__c = system.today(); pay.c2g__PaymentMediaTypes__c = 'Check'; // Posting information pay.c2g__SettlementDiscountReceived__c = settlementDiscountReceivedId; pay.c2g__SDRDimension1__c = null; pay.c2g__SDRDimension2__c = null; pay.c2g__SDRDimension3__c = null; pay.c2g__SDRDimension4__c = null; pay.c2g__CurrencyWriteOff__c = currencyWriteOffId; pay.c2g__CWODimension1__c = null; pay.c2g__CWODimension2__c = null; pay.c2g__CWODimension3__c = null; pay.c2g__CWODimension4__c = null; pay.c2g__CreatedByPaymentsPlus__c = true; insert pay; ``` -------------------------------- ### Post and Match Payment Asynchronously Source: https://developer.certinia.com/accounting-payments-plus-api Invoke this asynchronous service to post and match a payment to the ledgers after all changes are finalized. It returns the batch process record ID or null if running off-platform. ```javascript c2g.PaymentsPlusService.postAndMatchAsync(pay.Id); ``` -------------------------------- ### POST /postAndMatchAsync Source: https://developer.certinia.com/accounting-payments-plus-api Posts and matches a payment to the ledgers. ```APIDOC ## POST /postAndMatchAsync ### Description Posts and matches a payment to the ledgers. ### Parameters #### Request Body - **pay.Id** (String) - Required - The ID of the payment (c2g__codaPayment__c). ### Response #### Success Response (200) - **Batch Process ID** (String) - The ID of the batch process record for the job, or null if running off platform. ``` -------------------------------- ### Pay Invoices Approved via Purchase Order Source: https://developer.certinia.com/accounting-payments-plus-api This snippet demonstrates how to select payable invoices that have been approved through a purchase order. It then adds these selected invoices to a payment proposal, creates electronic media data for the payment, and initiates an asynchronous post and match process. ```apex // Build the list of codaTransactionLineItem__c that match our criteria and add them to the proposal. // We assume that c2g__codaTransactionLineItem__c has the custom field purchaseOrderApproved__c Set transLineIDs = new Map([Select Id from c2g__codaTransactionLineItem__c where c2g__LineType__c = 'Account' and c2g__transaction__r.c2g__TransactionType__c = 'Invoice' and purchaseOrderApproved__c = true and c2g__MatchingStatus__c = 'Available']).keySet(); List transLineIDList = new List (transLineIDs); c2g.PaymentsPlusService.addToProposal(pay.Id, transLineIDList); // Create the media data. Payment media is electronic c2g.PaymentsPlusService.createMediaData(pay.Id); // Post and Match c2g.PaymentsPlusService.postAndMatchAsync(pay.Id); ``` -------------------------------- ### Schedule Job via Anonymous Apex Source: https://developer.certinia.com/version-service-api-scheduled-versioning Use the System.schedule method with a CRON expression to trigger the versioning job on a recurring schedule. ```Apex ScheduleVersioning sv = new ScheduleVersioning(); String cronExpr = '0 0 0 1 * ?'; System.schedule('Monthly Versioning', cronExpr, sv); ``` -------------------------------- ### Unit Test Apex Controller Extension Source: https://developer.certinia.com/create-project-from-template-api-global-action-button Tests the functionality of the SimpleCPFTController, including project creation, data binding, and error handling for missing fields. ```Apex @isTest private class TestSimpleCPFTController { @isTest static void testButton() { Case c = new Case(); c.Subject = 'Test Case Project'; c.Status = 'Escalated'; c.Origin = 'Phone'; pse__Proj__c p = new pse__Proj__c(); p.pse__Start_Date__c = Date.Today(); p.Name = 'Test'; p.pse__Is_Template__c = true; insert p; c.Project_Template__c = p.Id; insert c; Test.StartTest(); ApexPages.StandardController sc = new ApexPages.StandardController(c); SimpleCPFTController scc = new SimpleCPFTController(sc); scc.inputProj = p; PageReference returnPage = scc.createProject(); Test.StopTest(); //Check input fields from Visualforce page match System.assertEquals(p.Id, scc.myCase.Project_Template__c, 'Project template does not match'); System.assertEquals('Test Case Project', scc.myCase.Subject, 'Inputted Project name does not match'); System.assertEquals(Date.Today(), scc.inputProj.pse__Start_Date__c, 'inputted start date does not match'); pse__Proj__c proj = [SELECT Id, Name FROM pse__Proj__c WHERE Name='Test Case Project' LIMIT 1]; PageReference testPage = new PageReference('/' + proj.Id); //Check we are redirected correctly System.assertEquals(testPage.getURL(), returnPage.getURL(), 'Not redirected to correct page'); } @isTest static void testGetProject(){ Case c = new Case(); ApexPages.StandardController sc = new ApexPages.StandardController(c); SimpleCPFTController scc = new SimpleCPFTController(sc); Test.StartTest(); pse__Proj__c proj = scc.inputProj; Test.StopTest(); System.assertNotEquals(null, proj, 'dummy project should be created by controller'); } @isTest static void testNoStartDate(){ Case c = new Case(); c.Subject = 'Test Case Project'; c.Status = 'Escalated'; c.Origin = 'Phone'; pse__Proj__c p = new pse__Proj__c(); p.Name = 'Test'; p.pse__Is_Template__c = true; insert p; c.Project_Template__c = p.Id; insert c; Test.StartTest(); ApexPages.StandardController sc = new ApexPages.StandardController(c); SimpleCPFTController scc = new SimpleCPFTController(sc); scc.inputProj = p; PageReference returnPage = scc.createProject(); Test.StopTest(); List msg = ApexPages.getMessages(); String errMsg = 'The request must provide a start date.'; System.debug(msg[0].getDetail()); System.assertEquals(msg[0].getDetail(), errMsg); } } ``` -------------------------------- ### Unit Test for CPFT Implementation Source: https://developer.certinia.com/create-project-from-template-api-custom-objects Basic test class structure for verifying the CPFT implementation. ```Apex @isTest private class testCPFTInclChecklistItemsAndMaterials { @isTest static void test_method_one() { pse__Proj__c p = new pse__Proj__c(); ``` -------------------------------- ### Create and Post Cash Entry Transactions Source: https://developer.certinia.com/accounting-direct-transaction-api Constructs accounting transactions from bank statement lines and posts them to the ledger. Requires pre-defined IDs for bank accounts, periods, and GL accounts. ```apex // Create cash entry transactions from the statement lines List transactions = new List(); for (c2g__codaBankStatementLineItem__c statement : statementLines) { c2g__codaInvoice__c invoice = invoiceByReference.get(statement.c2g__Reference__c); if (invoice != null) { c2g.TransactionService.AccountingTransaction trans = new c2g.TransactionService.AccountingTransaction(); trans.setTransactionDate(statement.c2g__Date__c); trans.setPeriodId(periodId); trans.setTransactionType(c2g.TransactionService.TransactionType.CASHENTRY); trans.setDocumentReference(invoice.c2g__CustomerReference__c); List transLines = new List(); // ACCOUNT c2g.TransactionService.AccountingTransactionLine line = new c2g.TransactionService.AccountingTransactionLine(); line.setLineType(c2g.TransactionService.TransactionLineType.ACCOUNT); line.setAccountId(invoice.c2g__Account__c); line.setGeneralLedgerAccountId(accountGLAId); line.setDueDate(statement.c2g__Date__c); // Document Information line.setDocumentCurrencyId(invoice.c2g__InvoiceCurrency__c); line.setDocumentValue(-1 * invoice.c2g__InvoiceTotal__c); // Override account currency if it's the same of the overridden currency (EUR) if (invoice.c2g__Account__r.c2g__codaAccountTradingCurrency__c == 'EUR') line.setAccountValue(-1 * statement.c2g__Amount__c); // Override home value if home currency is the same of the overridden currency (EUR) line.setHomeValue(-1 * statement.c2g__Amount__c); transLines.add(line); // ANALYSIS - BANK ACCOUNT LINE line = new c2g.TransactionService.AccountingTransactionLine(); line.setLineType(c2g.TransactionService.TransactionLineType.ANALYSIS); line.setBankAccountId(bankAccountId); line.setGeneralLedgerAccountId(bankAccountGLAId); line.setDueDate(statement.c2g__Date__c); // Document Information line.setDocumentCurrencyId(invoice.c2g__InvoiceCurrency__c); line.setDocumentValue(invoice.c2g__InvoiceTotal__c); // Override bank account value line.setBankAccountValue(statement.c2g__Amount__c); // Override bank account GLA value if GLA currency is the same of the overridden currency (EUR) line.setGeneralLedgerAccountValue(statement.c2g__Amount__c); // Override home value if home currency is the same of the overridden currency (EUR) line.setHomeValue(statement.c2g__Amount__c); transLines.add(line); trans.TransactionLineItems = transLines; transactions.add(trans); } } c2g.TransactionService.PostOptions postOptions = new c2g.TransactionService.PostOptions(); postOptions.DestinationCompanyId = companyId; List transIDs = c2g.TransactionService.post(transactions, postOptions); ```