Compare commits
2 Commits
63b1bfd758
...
e058dedb82
| Author | SHA1 | Date |
|---|---|---|
|
|
e058dedb82 | |
|
|
1e532029fa |
|
|
@ -0,0 +1 @@
|
||||||
|
<EFBFBD>覆骒陡<EFBFBD>'<27>噏
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,71 @@
|
||||||
|
# DocuSign CLM Template Mapping Snippet
|
||||||
|
|
||||||
|
This project assumes Salesforce is the system of record and DocuSign CLM doc gen receives a payload built from `Appraiser_Case__c` and its related `Appraiser_Deficiency__c` records.
|
||||||
|
|
||||||
|
## Suggested Merge Payload
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"templateKey": "APPRAISER_REVIEW_LETTER",
|
||||||
|
"recordId": "a01XXXXXXXXXXXX",
|
||||||
|
"sourceObject": "Appraiser_Case__c",
|
||||||
|
"mergeData": {
|
||||||
|
"appraiserCaseNumber": "AC-000123",
|
||||||
|
"appraiserFieldReviewDate": "2026-04-01",
|
||||||
|
"propertyStreet": "123 Main St",
|
||||||
|
"propertyCity": "Ottawa",
|
||||||
|
"propertyStateProvince": "ON",
|
||||||
|
"propertyPostalCode": "K1A 0A1",
|
||||||
|
"propertyCountry": "Canada",
|
||||||
|
"propertyAddressSingleLine": "123 Main St | Ottawa, ON, K1A 0A1 | Canada",
|
||||||
|
"deficiencies": [
|
||||||
|
{
|
||||||
|
"deficiencyNumber": "1",
|
||||||
|
"description": "Missing comparable sale analysis",
|
||||||
|
"resolution": "Provide updated comparable sales section",
|
||||||
|
"sortOrder": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Suggested Template Tokens
|
||||||
|
|
||||||
|
Use the actual DocuSign CLM token syntax used in your tenant, but the field model should be equivalent to:
|
||||||
|
|
||||||
|
- `{{mergeData.appraiserCaseNumber}}`
|
||||||
|
- `{{mergeData.appraiserFieldReviewDate}}`
|
||||||
|
- `{{mergeData.propertyStreet}}`
|
||||||
|
- `{{mergeData.propertyCity}}`
|
||||||
|
- `{{mergeData.propertyStateProvince}}`
|
||||||
|
- `{{mergeData.propertyPostalCode}}`
|
||||||
|
- `{{mergeData.propertyCountry}}`
|
||||||
|
- `{{mergeData.propertyAddressSingleLine}}`
|
||||||
|
|
||||||
|
### Deficiency Repeat Block
|
||||||
|
|
||||||
|
```handlebars
|
||||||
|
{{#each mergeData.deficiencies}}
|
||||||
|
| {{deficiencyNumber}} | {{description}} | {{resolution}} |
|
||||||
|
{{/each}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Recommended Letter Sections
|
||||||
|
|
||||||
|
1. Header / logo block
|
||||||
|
2. Review metadata
|
||||||
|
- Case number
|
||||||
|
- Field review date
|
||||||
|
- Property address
|
||||||
|
3. Deficiency table
|
||||||
|
- Deficiency number
|
||||||
|
- Description
|
||||||
|
- Resolution
|
||||||
|
4. Closing / signature block
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Keep `deficiencies` as a repeatable child collection, not a flattened text blob.
|
||||||
|
- If DocuSign CLM requires a REST callout payload, `AppraiserCaseDocGenService.buildDocGenRequestJson()` is a good source payload to hand to your integration layer.
|
||||||
|
- If your CLM tenant uses a different collection token syntax, map the same logical field names there.
|
||||||
|
|
@ -1,39 +1,11 @@
|
||||||
# Deployment & Testing — Appraiser Review Letter
|
# Deployment & Testing — Appraiser Review Letter
|
||||||
|
|
||||||
## Implemented Salesforce Metadata
|
|
||||||
- Parent object: Appraiser_Case__c (label: Appraiser Case)
|
|
||||||
- Name field (auto number): Appraiser Case Number (AC-{00000})
|
|
||||||
- Fields on Appraiser Case:
|
|
||||||
- Appraiser_Field_Review_Date__c (Date)
|
|
||||||
- Property_Address__c (Text 255)
|
|
||||||
- Child object for repeatable deficiencies: Appraiser_Case_Deficiency__c
|
|
||||||
- Fields on Appraiser Case Deficiency:
|
|
||||||
- Appraiser_Case__c (Master-Detail to Appraiser_Case__c)
|
|
||||||
- Deficiency_Number__c (Number)
|
|
||||||
- Description__c (Long Text Area)
|
|
||||||
- Resolution__c (Long Text Area)
|
|
||||||
- Permission set: Appraiser_Case_Access
|
|
||||||
- Apex Classes:
|
|
||||||
- AppraiserCasePayloadBuilder: Transforms Salesforce data to CLM merge payload
|
|
||||||
- AppraiserCasePayloadBuilderTest: Unit tests for payload builder
|
|
||||||
- CLMDocGenCallout: HTTP integration with DocuSign CLM API
|
|
||||||
|
|
||||||
## Deployment Steps
|
## Deployment Steps
|
||||||
1. Deploy custom objects & fields
|
1. Deploy custom objects & fields
|
||||||
2. Deploy Apex classes (included in manifest)
|
2. Copy updated template files into Salesforce docs directory
|
||||||
3. Configure Named Credentials for CLM API access
|
3. Map merge fields to object schema
|
||||||
4. Configure Remote Site Settings for CLM instance
|
4. Configure CLM template and connect API
|
||||||
5. Map merge fields to object schema
|
5. Test with sample Appraisal and Deficiency objects
|
||||||
6. Configure CLM template and connect API
|
|
||||||
7. Test with sample Appraiser Case records
|
|
||||||
|
|
||||||
### Suggested CLI Deploy Commands
|
|
||||||
1. Authenticate to your org:
|
|
||||||
- sf org login web --alias appraiser-dev
|
|
||||||
2. Validate source deploy:
|
|
||||||
- sf project deploy validate --target-org appraiser-dev --manifest manifest/package.xml
|
|
||||||
3. Deploy metadata:
|
|
||||||
- sf project deploy start --target-org appraiser-dev --manifest manifest/package.xml
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -43,39 +15,6 @@
|
||||||
- Test table rendering for arrays (DeficiencyList, ReviewerComments)
|
- Test table rendering for arrays (DeficiencyList, ReviewerComments)
|
||||||
- Document any failed merges or formatting gaps
|
- Document any failed merges or formatting gaps
|
||||||
|
|
||||||
## CLM Data Mapping Starter
|
|
||||||
- AppraiserCaseNumber -> Appraiser_Case__c.Name
|
|
||||||
- AppraiserFieldReviewDate -> Appraiser_Case__c.Appraiser_Field_Review_Date__c
|
|
||||||
- PropertyAddress -> Appraiser_Case__c.Property_Address__c
|
|
||||||
- DeficiencyList[] -> Appraiser_Case__c.Deficiencies__r
|
|
||||||
- deficiencyNumber -> Deficiency_Number__c
|
|
||||||
- description -> Description__c
|
|
||||||
- resolution -> Resolution__c
|
|
||||||
|
|
||||||
## Smoke Test Execution
|
|
||||||
After deployment, sample test data was created:
|
|
||||||
- Appraiser Case: AC-00001 (a0wKW000007OIiCYAW)
|
|
||||||
- 3 related deficiency records verified
|
|
||||||
|
|
||||||
### Test Payload Query (run in Apex Execute or Query Editor)
|
|
||||||
```soql
|
|
||||||
SELECT Id, Name, Appraiser_Field_Review_Date__c, Property_Address__c,
|
|
||||||
(SELECT Id, Deficiency_Number__c, Description__c, Resolution__c
|
|
||||||
FROM Deficiencies__r ORDER BY Deficiency_Number__c)
|
|
||||||
FROM Appraiser_Case__c
|
|
||||||
WHERE Id='a0wKW000007OIiCYAW'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Payload Generation (Apex)
|
|
||||||
```apex
|
|
||||||
String caseId = 'a0wKW000007OIiCYAW';
|
|
||||||
Map<String, Object> payload = AppraiserCasePayloadBuilder.buildPayload(caseId);
|
|
||||||
System.debug(JSON.serializePretty(payload));
|
|
||||||
```
|
|
||||||
|
|
||||||
## CLM Doc Gen Integration
|
|
||||||
See [CLM_INTEGRATION.md](CLM_INTEGRATION.md) for complete setup and integration patterns.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
_Last updated: 2026-02-26 13:26 PM_
|
_Last updated: 2026-02-26 13:26 PM_
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Next Steps — DocuSign CLM Launch Path
|
||||||
|
|
||||||
|
I added a placeholder Quick Action metadata file so there is an explicit place in the project for the document generation launch pattern.
|
||||||
|
|
||||||
|
## Reality check
|
||||||
|
|
||||||
|
DocuSign CLM launch configuration varies by package and org setup. Because of that, the action in this repo is a scaffold/placeholder, not a guaranteed final production action.
|
||||||
|
|
||||||
|
## Recommended implementation path
|
||||||
|
|
||||||
|
### Option A — Screen Flow (recommended first)
|
||||||
|
- Create a Screen Flow or autolaunched Flow that accepts `recordId`
|
||||||
|
- Call an Apex invocable or Apex action that builds the payload
|
||||||
|
- Hand that payload to your DocuSign CLM mechanism
|
||||||
|
- Redirect user to resulting document or status page
|
||||||
|
|
||||||
|
### Option B — LWC / Aura quick action
|
||||||
|
- Use a Lightning Web Component quick action on `Appraiser_Case__c`
|
||||||
|
- Call `AppraiserCaseDocGenService.buildDocGenRequestJson(recordId, templateKey)`
|
||||||
|
- Send the payload to the installed DocuSign CLM endpoint or orchestration layer
|
||||||
|
|
||||||
|
### Option C — Button / URL hack
|
||||||
|
- Usually fast, usually brittle. I don’t recommend it unless your CLM package explicitly documents it.
|
||||||
|
|
||||||
|
## What to confirm in your org
|
||||||
|
|
||||||
|
1. Exact DocuSign CLM package/API available in Salesforce
|
||||||
|
2. Whether generation is initiated by package component, Flow action, Apex callout, or named credential call
|
||||||
|
3. Template identifier format (`templateKey`, template Id, or external document key)
|
||||||
|
4. Returned artifact behavior (attach to record, email, save to CLM repository, etc.)
|
||||||
|
|
||||||
|
## Good next move
|
||||||
|
|
||||||
|
Once you know the exact DocuSign package artifact available in the org, I can wire the placeholder into a real Flow/LWC/Apex launch path.
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
# Salesforce Setup — Appraiser Case + DocuSign CLM
|
||||||
|
|
||||||
|
## What was added
|
||||||
|
|
||||||
|
### Custom object: `Appraiser_Case__c`
|
||||||
|
- Auto-number name field labeled **Appraiser Case Number**
|
||||||
|
- `Appraiser_Field_Review_Date__c` (Date)
|
||||||
|
- `Property_Street__c` (Text 255)
|
||||||
|
- `Property_City__c` (Text 80)
|
||||||
|
- `Property_State_Province__c` (Text 80)
|
||||||
|
- `Property_Postal_Code__c` (Text 20)
|
||||||
|
- `Property_Country__c` (Text 80)
|
||||||
|
|
||||||
|
### Child custom object: `Appraiser_Deficiency__c`
|
||||||
|
- Master-detail to `Appraiser_Case__c`
|
||||||
|
- `Deficiency_Number__c` (Text 50)
|
||||||
|
- `Description__c` (Long Text Area)
|
||||||
|
- `Resolution__c` (Long Text Area)
|
||||||
|
- `Sort_Order__c` (Number)
|
||||||
|
|
||||||
|
### Layouts
|
||||||
|
- Basic page layout for Appraiser Case
|
||||||
|
- Basic page layout for Appraiser Deficiency
|
||||||
|
- Related list on Appraiser Case for deficiencies
|
||||||
|
- Basic list view on Appraiser Case
|
||||||
|
|
||||||
|
### Tabs and permissions
|
||||||
|
- Custom tabs for both objects
|
||||||
|
- Permission set: `Appraiser_Case_Admin`
|
||||||
|
|
||||||
|
### Apex
|
||||||
|
- `AppraiserCaseDocGenService.cls`
|
||||||
|
- `AppraiserCaseDocGenServiceTest.cls`
|
||||||
|
|
||||||
|
### Sample data
|
||||||
|
- Anonymous Apex script: `scripts/apex/createSampleAppraiserCase.apex`
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
From the project root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sf project deploy start --source-dir force-app
|
||||||
|
```
|
||||||
|
|
||||||
|
Or from elsewhere:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sf project deploy start --source-dir /home/paulh/.openclaw/workspace/projects/salesforce-appraiser-review-letter/force-app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Apex
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sf apex run test --tests AppraiserCaseDocGenServiceTest --result-format human
|
||||||
|
```
|
||||||
|
|
||||||
|
## Load sample data
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sf apex run --file scripts/apex/createSampleAppraiserCase.apex
|
||||||
|
```
|
||||||
|
|
||||||
|
## Assign permission set
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sf org assign permset --name Appraiser_Case_Admin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Suggested next steps in Salesforce
|
||||||
|
|
||||||
|
1. Deploy metadata.
|
||||||
|
2. Assign permission set.
|
||||||
|
3. Run the sample Apex script.
|
||||||
|
4. Open the `Appraiser Case` tab and verify the record + related deficiencies.
|
||||||
|
5. Validate the JSON payload in debug logs or by running the Apex methods directly.
|
||||||
|
6. Wire the DocuSign CLM launch path based on the exact package capability in your org.
|
||||||
|
|
||||||
|
## About the quick action
|
||||||
|
|
||||||
|
A placeholder quick action metadata file was added to mark the launch point, but DocuSign CLM launch mechanics vary by package/org. See `docs/NEXT_STEPS_DOCGEN.md` for the practical wiring options.
|
||||||
|
|
||||||
|
## About “page layout” and “default setup”
|
||||||
|
|
||||||
|
You said “just do default.” In Salesforce terms that means I created the baseline metadata so records can be created and edited in a normal Lightning UI without custom polish yet.
|
||||||
|
|
@ -42,39 +42,6 @@ Outline technical and functional requirements for Appraiser Review Letter templa
|
||||||
- What is the authoritative object and field schema for merge?
|
- What is the authoritative object and field schema for merge?
|
||||||
- Are custom field mappings needed for relationships (e.g. lookup fields)?
|
- Are custom field mappings needed for relationships (e.g. lookup fields)?
|
||||||
|
|
||||||
## Initial Authoritative Salesforce Schema
|
---
|
||||||
- Appraiser_Case__c (Appraiser Case)
|
_Last updated: 2026-02-26 10:35 AM_
|
||||||
- Name (label: Appraiser Case Number, AutoNumber)
|
_Work in progress: Integration and schema expansion next._
|
||||||
- Appraiser_Field_Review_Date__c (Date)
|
|
||||||
- Property_Address__c (Text)
|
|
||||||
- Appraiser_Case_Deficiency__c (Appraiser Case Deficiency)
|
|
||||||
- Appraiser_Case__c (Master-Detail -> Appraiser_Case__c)
|
|
||||||
- Deficiency_Number__c (Number)
|
|
||||||
- Description__c (Long Text Area)
|
|
||||||
- Resolution__c (Long Text Area)
|
|
||||||
|
|
||||||
This schema supports CLM array merges by iterating child deficiency records tied to one appraiser case.
|
|
||||||
|
|
||||||
## CLM Integration
|
|
||||||
|
|
||||||
### Payload Structure (from AppraiserCasePayloadBuilder)
|
|
||||||
The Apex class `AppraiserCasePayloadBuilder` transforms Salesforce records into CLM-ready JSON:
|
|
||||||
- AppraiserCaseNumber (string) -> Appraiser_Case__c.Name
|
|
||||||
- AppraiserFieldReviewDate (ISO date) -> Appraiser_Case__c.Appraiser_Field_Review_Date__c
|
|
||||||
- PropertyAddress (string) -> Appraiser_Case__c.Property_Address__c
|
|
||||||
- DeficiencyList (array of objects):
|
|
||||||
- deficiencyNumber (number) -> Appraiser_Case_Deficiency__c.Deficiency_Number__c
|
|
||||||
- description (string) -> Appraiser_Case_Deficiency__c.Description__c
|
|
||||||
- resolution (string) -> Appraiser_Case_Deficiency__c.Resolution__c
|
|
||||||
|
|
||||||
### CLM API Integration (CLMDocGenCallout)
|
|
||||||
- HTTP POST to DocuSign CLM API with merge payload
|
|
||||||
- Named Credentials: Securely store CLM endpoint + OAuth token
|
|
||||||
- Remote Site Settings: Whitelist CLM instance domain
|
|
||||||
- Response includes document URL and ID for tracking
|
|
||||||
|
|
||||||
See [CLM_INTEGRATION.md](CLM_INTEGRATION.md) for setup and usage patterns.
|
|
||||||
|
|
||||||
---
|
|
||||||
_Last updated: 2026-02-26 10:35 AM_
|
|
||||||
_Work in progress: Integration and schema expansion next._
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
public with sharing class AppraiserCaseDocGenService {
|
||||||
|
public class DeficiencyDTO {
|
||||||
|
@AuraEnabled public String deficiencyNumber;
|
||||||
|
@AuraEnabled public String description;
|
||||||
|
@AuraEnabled public String resolution;
|
||||||
|
@AuraEnabled public Decimal sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AppraiserCasePayload {
|
||||||
|
@AuraEnabled public Id caseId;
|
||||||
|
@AuraEnabled public String appraiserCaseNumber;
|
||||||
|
@AuraEnabled public Date appraiserFieldReviewDate;
|
||||||
|
@AuraEnabled public String propertyStreet;
|
||||||
|
@AuraEnabled public String propertyCity;
|
||||||
|
@AuraEnabled public String propertyStateProvince;
|
||||||
|
@AuraEnabled public String propertyPostalCode;
|
||||||
|
@AuraEnabled public String propertyCountry;
|
||||||
|
@AuraEnabled public String propertyAddressSingleLine;
|
||||||
|
@AuraEnabled public List<DeficiencyDTO> deficiencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AuraEnabled(cacheable=false)
|
||||||
|
public static String buildPayloadJson(Id appraiserCaseId) {
|
||||||
|
return JSON.serialize(buildPayload(appraiserCaseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AppraiserCasePayload buildPayload(Id appraiserCaseId) {
|
||||||
|
Appraiser_Case__c appraiserCase = [
|
||||||
|
SELECT Id,
|
||||||
|
Name,
|
||||||
|
Appraiser_Field_Review_Date__c,
|
||||||
|
Property_Street__c,
|
||||||
|
Property_City__c,
|
||||||
|
Property_State_Province__c,
|
||||||
|
Property_Postal_Code__c,
|
||||||
|
Property_Country__c,
|
||||||
|
(SELECT Id,
|
||||||
|
Name,
|
||||||
|
Deficiency_Number__c,
|
||||||
|
Description__c,
|
||||||
|
Resolution__c,
|
||||||
|
Sort_Order__c
|
||||||
|
FROM Appraiser_Deficiencies__r
|
||||||
|
ORDER BY Sort_Order__c ASC, CreatedDate ASC)
|
||||||
|
FROM Appraiser_Case__c
|
||||||
|
WHERE Id = :appraiserCaseId
|
||||||
|
LIMIT 1
|
||||||
|
];
|
||||||
|
|
||||||
|
AppraiserCasePayload payload = new AppraiserCasePayload();
|
||||||
|
payload.caseId = appraiserCase.Id;
|
||||||
|
payload.appraiserCaseNumber = appraiserCase.Name;
|
||||||
|
payload.appraiserFieldReviewDate = appraiserCase.Appraiser_Field_Review_Date__c;
|
||||||
|
payload.propertyStreet = appraiserCase.Property_Street__c;
|
||||||
|
payload.propertyCity = appraiserCase.Property_City__c;
|
||||||
|
payload.propertyStateProvince = appraiserCase.Property_State_Province__c;
|
||||||
|
payload.propertyPostalCode = appraiserCase.Property_Postal_Code__c;
|
||||||
|
payload.propertyCountry = appraiserCase.Property_Country__c;
|
||||||
|
payload.propertyAddressSingleLine = buildAddress(appraiserCase);
|
||||||
|
payload.deficiencies = new List<DeficiencyDTO>();
|
||||||
|
|
||||||
|
for (Appraiser_Deficiency__c deficiency : appraiserCase.Appraiser_Deficiencies__r) {
|
||||||
|
DeficiencyDTO dto = new DeficiencyDTO();
|
||||||
|
dto.deficiencyNumber = deficiency.Deficiency_Number__c;
|
||||||
|
dto.description = deficiency.Description__c;
|
||||||
|
dto.resolution = deficiency.Resolution__c;
|
||||||
|
dto.sortOrder = deficiency.Sort_Order__c;
|
||||||
|
payload.deficiencies.add(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildAddress(Appraiser_Case__c appraiserCase) {
|
||||||
|
List<String> parts = new List<String>();
|
||||||
|
if (String.isNotBlank(appraiserCase.Property_Street__c)) parts.add(appraiserCase.Property_Street__c);
|
||||||
|
|
||||||
|
List<String> cityLine = new List<String>();
|
||||||
|
if (String.isNotBlank(appraiserCase.Property_City__c)) cityLine.add(appraiserCase.Property_City__c);
|
||||||
|
if (String.isNotBlank(appraiserCase.Property_State_Province__c)) cityLine.add(appraiserCase.Property_State_Province__c);
|
||||||
|
if (String.isNotBlank(appraiserCase.Property_Postal_Code__c)) cityLine.add(appraiserCase.Property_Postal_Code__c);
|
||||||
|
if (!cityLine.isEmpty()) parts.add(String.join(cityLine, ', '));
|
||||||
|
|
||||||
|
if (String.isNotBlank(appraiserCase.Property_Country__c)) parts.add(appraiserCase.Property_Country__c);
|
||||||
|
return String.join(parts, ' | ');
|
||||||
|
}
|
||||||
|
|
||||||
|
@AuraEnabled(cacheable=false)
|
||||||
|
public static Map<String, Object> buildDocGenRequest(Id appraiserCaseId, String templateKey) {
|
||||||
|
AppraiserCasePayload payload = buildPayload(appraiserCaseId);
|
||||||
|
Map<String, Object> requestBody = new Map<String, Object>();
|
||||||
|
requestBody.put('templateKey', templateKey);
|
||||||
|
requestBody.put('recordId', appraiserCaseId);
|
||||||
|
requestBody.put('sourceObject', 'Appraiser_Case__c');
|
||||||
|
requestBody.put('mergeData', payload);
|
||||||
|
return requestBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AuraEnabled(cacheable=false)
|
||||||
|
public static String buildDocGenRequestJson(Id appraiserCaseId, String templateKey) {
|
||||||
|
return JSON.serialize(buildDocGenRequest(appraiserCaseId, templateKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<apiVersion>63.0</apiVersion>
|
||||||
|
<status>Active</status>
|
||||||
|
</ApexClass>
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
@IsTest
|
||||||
|
private class AppraiserCaseDocGenServiceTest {
|
||||||
|
@IsTest
|
||||||
|
static void buildsPayloadAndRequestJson() {
|
||||||
|
Appraiser_Case__c appraiserCase = new Appraiser_Case__c(
|
||||||
|
Appraiser_Field_Review_Date__c = Date.newInstance(2026, 4, 1),
|
||||||
|
Property_Street__c = '123 Main St',
|
||||||
|
Property_City__c = 'Ottawa',
|
||||||
|
Property_State_Province__c = 'ON',
|
||||||
|
Property_Postal_Code__c = 'K1A 0A1',
|
||||||
|
Property_Country__c = 'Canada'
|
||||||
|
);
|
||||||
|
insert appraiserCase;
|
||||||
|
|
||||||
|
insert new Appraiser_Deficiency__c(
|
||||||
|
Name = 'Deficiency 1',
|
||||||
|
Appraiser_Case__c = appraiserCase.Id,
|
||||||
|
Deficiency_Number__c = '1',
|
||||||
|
Description__c = 'Missing comparable sale analysis',
|
||||||
|
Resolution__c = 'Provide updated comparable sales section',
|
||||||
|
Sort_Order__c = 1
|
||||||
|
);
|
||||||
|
|
||||||
|
Test.startTest();
|
||||||
|
AppraiserCaseDocGenService.AppraiserCasePayload payload = AppraiserCaseDocGenService.buildPayload(appraiserCase.Id);
|
||||||
|
String json = AppraiserCaseDocGenService.buildDocGenRequestJson(appraiserCase.Id, 'APPRAISER_REVIEW_LETTER');
|
||||||
|
Test.stopTest();
|
||||||
|
|
||||||
|
System.assertEquals(appraiserCase.Id, payload.caseId);
|
||||||
|
System.assertEquals('Ottawa', payload.propertyCity);
|
||||||
|
System.assertEquals(1, payload.deficiencies.size());
|
||||||
|
System.assertEquals('1', payload.deficiencies[0].deficiencyNumber);
|
||||||
|
System.assert(json.contains('APPRAISER_REVIEW_LETTER'));
|
||||||
|
System.assert(json.contains('Missing comparable sale analysis'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<apiVersion>63.0</apiVersion>
|
||||||
|
<status>Active</status>
|
||||||
|
</ApexClass>
|
||||||
|
|
@ -112,20 +112,25 @@ public class CLMDocGenCallout {
|
||||||
private static String buildDataXml(Map<String, Object> payload) {
|
private static String buildDataXml(Map<String, Object> payload) {
|
||||||
String xml = '<TemplateFieldData>';
|
String xml = '<TemplateFieldData>';
|
||||||
|
|
||||||
|
// Emit flat fields first
|
||||||
for (String key : payload.keySet()) {
|
for (String key : payload.keySet()) {
|
||||||
if (key == 'DeficiencyList') continue;
|
if (key == 'DeficiencyList') continue;
|
||||||
xml += '<' + key + '>' + escapeXml(String.valueOf(payload.get(key))) + '</' + key + '>';
|
xml += '<' + key + '>' + escapeXml(String.valueOf(payload.get(key))) + '</' + key + '>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit DeficiencyList as a nested list so templates can iterate dynamically
|
||||||
List<Object> deficiencies = (List<Object>) payload.get('DeficiencyList');
|
List<Object> deficiencies = (List<Object>) payload.get('DeficiencyList');
|
||||||
if (deficiencies != null) {
|
if (deficiencies != null && !deficiencies.isEmpty()) {
|
||||||
|
xml += '<DeficiencyList>';
|
||||||
for (Integer i = 0; i < deficiencies.size(); i++) {
|
for (Integer i = 0; i < deficiencies.size(); i++) {
|
||||||
Map<String, Object> d = (Map<String, Object>) deficiencies[i];
|
Map<String, Object> d = (Map<String, Object>) deficiencies[i];
|
||||||
String p = 'Deficiency_' + (i + 1) + '_';
|
xml += '<Deficiency>';
|
||||||
xml += '<' + p + 'Number>' + escapeXml(String.valueOf(d.get('deficiencyNumber'))) + '</' + p + 'Number>';
|
xml += '<Number>' + escapeXml(String.valueOf(d.get('deficiencyNumber'))) + '</Number>';
|
||||||
xml += '<' + p + 'Description>' + escapeXml(String.valueOf(d.get('description'))) + '</' + p + 'Description>';
|
xml += '<Description>' + escapeXml(String.valueOf(d.get('description'))) + '</Description>';
|
||||||
xml += '<' + p + 'Resolution>' + escapeXml(String.valueOf(d.get('resolution'))) + '</' + p + 'Resolution>';
|
xml += '<Resolution>' + escapeXml(String.valueOf(d.get('resolution'))) + '</Resolution>';
|
||||||
|
xml += '</Deficiency>';
|
||||||
}
|
}
|
||||||
|
xml += '</DeficiencyList>';
|
||||||
xml += '<DeficiencyCount>' + deficiencies.size() + '</DeficiencyCount>';
|
xml += '<DeficiencyCount>' + deficiencies.size() + '</DeficiencyCount>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Layout xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<layoutSections>
|
||||||
|
<customLabel>false</customLabel>
|
||||||
|
<detailHeading>true</detailHeading>
|
||||||
|
<editHeading>true</editHeading>
|
||||||
|
<label>Information</label>
|
||||||
|
<layoutColumns>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Required</behavior>
|
||||||
|
<field>Name</field>
|
||||||
|
</layoutItems>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Appraiser_Field_Review_Date__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>CreatedDate</field>
|
||||||
|
</layoutItems>
|
||||||
|
</layoutColumns>
|
||||||
|
<layoutColumns>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>LastModifiedDate</field>
|
||||||
|
</layoutItems>
|
||||||
|
</layoutColumns>
|
||||||
|
<style>TwoColumnsTopToBottom</style>
|
||||||
|
</layoutSections>
|
||||||
|
<layoutSections>
|
||||||
|
<customLabel>false</customLabel>
|
||||||
|
<detailHeading>true</detailHeading>
|
||||||
|
<editHeading>true</editHeading>
|
||||||
|
<label>Property Address</label>
|
||||||
|
<layoutColumns>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Property_Street__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Property_City__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Property_State_Province__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
</layoutColumns>
|
||||||
|
<layoutColumns>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Property_Postal_Code__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Property_Country__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
</layoutColumns>
|
||||||
|
<style>TwoColumnsTopToBottom</style>
|
||||||
|
</layoutSections>
|
||||||
|
<relatedLists>
|
||||||
|
<fields>NAME</fields>
|
||||||
|
<fields>Deficiency_Number__c</fields>
|
||||||
|
<fields>Description__c</fields>
|
||||||
|
<fields>Resolution__c</fields>
|
||||||
|
<fields>Sort_Order__c</fields>
|
||||||
|
<relatedList>Appraiser_Deficiencies__r</relatedList>
|
||||||
|
</relatedLists>
|
||||||
|
<relatedLists>
|
||||||
|
<fields>SUBJECT</fields>
|
||||||
|
<fields>STATUS</fields>
|
||||||
|
<fields>DUE_DATE</fields>
|
||||||
|
<relatedList>OpenActivities</relatedList>
|
||||||
|
</relatedLists>
|
||||||
|
<showEmailCheckbox>false</showEmailCheckbox>
|
||||||
|
<showHighlightsPanel>true</showHighlightsPanel>
|
||||||
|
<showInteractionLogPanel>false</showInteractionLogPanel>
|
||||||
|
<showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox>
|
||||||
|
<showSubmitAndAttachButton>false</showSubmitAndAttachButton>
|
||||||
|
</Layout>
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Layout xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<layoutSections>
|
||||||
|
<customLabel>false</customLabel>
|
||||||
|
<detailHeading>true</detailHeading>
|
||||||
|
<editHeading>true</editHeading>
|
||||||
|
<label>Information</label>
|
||||||
|
<layoutColumns>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Required</behavior>
|
||||||
|
<field>Name</field>
|
||||||
|
</layoutItems>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Required</behavior>
|
||||||
|
<field>Appraiser_Case__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Deficiency_Number__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
</layoutColumns>
|
||||||
|
<layoutColumns>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Sort_Order__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
</layoutColumns>
|
||||||
|
<style>TwoColumnsTopToBottom</style>
|
||||||
|
</layoutSections>
|
||||||
|
<layoutSections>
|
||||||
|
<customLabel>false</customLabel>
|
||||||
|
<detailHeading>true</detailHeading>
|
||||||
|
<editHeading>true</editHeading>
|
||||||
|
<label>Details</label>
|
||||||
|
<layoutColumns>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Description__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
</layoutColumns>
|
||||||
|
<layoutColumns>
|
||||||
|
<layoutItems>
|
||||||
|
<behavior>Edit</behavior>
|
||||||
|
<field>Resolution__c</field>
|
||||||
|
</layoutItems>
|
||||||
|
</layoutColumns>
|
||||||
|
<style>TwoColumnsTopToBottom</style>
|
||||||
|
</layoutSections>
|
||||||
|
<showEmailCheckbox>false</showEmailCheckbox>
|
||||||
|
<showHighlightsPanel>true</showHighlightsPanel>
|
||||||
|
<showInteractionLogPanel>false</showInteractionLogPanel>
|
||||||
|
<showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox>
|
||||||
|
<showSubmitAndAttachButton>false</showSubmitAndAttachButton>
|
||||||
|
</Layout>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<NamedCredential xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<allowMergeFieldsInBody>false</allowMergeFieldsInBody>
|
||||||
|
<allowMergeFieldsInHeader>false</allowMergeFieldsInHeader>
|
||||||
|
<calloutStatus>Enabled</calloutStatus>
|
||||||
|
<generateAuthorizationHeader>true</generateAuthorizationHeader>
|
||||||
|
<label>CLMuatDownload</label>
|
||||||
|
<namedCredentialParameters>
|
||||||
|
<parameterName>Url</parameterName>
|
||||||
|
<parameterType>Url</parameterType>
|
||||||
|
<parameterValue>https://apidownloaduatna11.springcm.com</parameterValue>
|
||||||
|
</namedCredentialParameters>
|
||||||
|
<namedCredentialParameters>
|
||||||
|
<externalCredential>DocusignJWT</externalCredential>
|
||||||
|
<parameterName>ExternalCredential</parameterName>
|
||||||
|
<parameterType>Authentication</parameterType>
|
||||||
|
</namedCredentialParameters>
|
||||||
|
<namedCredentialType>SecuredEndpoint</namedCredentialType>
|
||||||
|
</NamedCredential>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<NamedCredential xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<allowMergeFieldsInBody>false</allowMergeFieldsInBody>
|
||||||
|
<allowMergeFieldsInHeader>false</allowMergeFieldsInHeader>
|
||||||
|
<calloutStatus>Enabled</calloutStatus>
|
||||||
|
<generateAuthorizationHeader>true</generateAuthorizationHeader>
|
||||||
|
<label>CLMuatDownloadNamedCreds</label>
|
||||||
|
<namedCredentialParameters>
|
||||||
|
<parameterName>Url</parameterName>
|
||||||
|
<parameterType>Url</parameterType>
|
||||||
|
<parameterValue>https://apidownloaduatna11.springcm.com</parameterValue>
|
||||||
|
</namedCredentialParameters>
|
||||||
|
<namedCredentialParameters>
|
||||||
|
<externalCredential>DocusignJWT</externalCredential>
|
||||||
|
<parameterName>ExternalCredential</parameterName>
|
||||||
|
<parameterType>Authentication</parameterType>
|
||||||
|
</namedCredentialParameters>
|
||||||
|
<namedCredentialType>SecuredEndpoint</namedCredentialType>
|
||||||
|
</NamedCredential>
|
||||||
|
|
@ -1,16 +1,38 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<actionOverrides>
|
||||||
|
<actionName>New</actionName>
|
||||||
|
<type>Default</type>
|
||||||
|
</actionOverrides>
|
||||||
|
<actionOverrides>
|
||||||
|
<actionName>Edit</actionName>
|
||||||
|
<type>Default</type>
|
||||||
|
</actionOverrides>
|
||||||
|
<actionOverrides>
|
||||||
|
<actionName>View</actionName>
|
||||||
|
<type>Default</type>
|
||||||
|
</actionOverrides>
|
||||||
|
<allowInChatterGroups>false</allowInChatterGroups>
|
||||||
|
<compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
|
||||||
<deploymentStatus>Deployed</deploymentStatus>
|
<deploymentStatus>Deployed</deploymentStatus>
|
||||||
<description>Main record for appraiser review letter generation in CLM.</description>
|
|
||||||
<enableActivities>true</enableActivities>
|
<enableActivities>true</enableActivities>
|
||||||
|
<enableBulkApi>true</enableBulkApi>
|
||||||
|
<enableFeeds>false</enableFeeds>
|
||||||
|
<enableHistory>true</enableHistory>
|
||||||
|
<enableLicensing>false</enableLicensing>
|
||||||
<enableReports>true</enableReports>
|
<enableReports>true</enableReports>
|
||||||
<enableSearch>true</enableSearch>
|
<enableSearch>true</enableSearch>
|
||||||
|
<enableSharing>true</enableSharing>
|
||||||
|
<enableStreamingApi>true</enableStreamingApi>
|
||||||
|
<externalSharingModel>Private</externalSharingModel>
|
||||||
<label>Appraiser Case</label>
|
<label>Appraiser Case</label>
|
||||||
<nameField>
|
<nameField>
|
||||||
<displayFormat>AC-{00000}</displayFormat>
|
<displayFormat>AC-{000000}</displayFormat>
|
||||||
<label>Appraiser Case Number</label>
|
<label>Appraiser Case Number</label>
|
||||||
<type>AutoNumber</type>
|
<type>AutoNumber</type>
|
||||||
</nameField>
|
</nameField>
|
||||||
<pluralLabel>Appraiser Cases</pluralLabel>
|
<pluralLabel>Appraiser Cases</pluralLabel>
|
||||||
|
<searchLayouts></searchLayouts>
|
||||||
<sharingModel>ReadWrite</sharingModel>
|
<sharingModel>ReadWrite</sharingModel>
|
||||||
</CustomObject>
|
<visibility>Public</visibility>
|
||||||
|
</CustomObject>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
<fullName>Appraiser_Field_Review_Date__c</fullName>
|
<fullName>Appraiser_Field_Review_Date__c</fullName>
|
||||||
<description>Date the appraiser field review was completed.</description>
|
|
||||||
<label>Appraiser Field Review Date</label>
|
<label>Appraiser Field Review Date</label>
|
||||||
<required>false</required>
|
<required>false</required>
|
||||||
<trackHistory>false</trackHistory>
|
<trackHistory>true</trackHistory>
|
||||||
<type>Date</type>
|
<type>Date</type>
|
||||||
</CustomField>
|
</CustomField>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Property_City__c</fullName>
|
||||||
|
<label>Property City</label>
|
||||||
|
<length>80</length>
|
||||||
|
<required>false</required>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>Text</type>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Property_Country__c</fullName>
|
||||||
|
<label>Property Country</label>
|
||||||
|
<length>80</length>
|
||||||
|
<required>false</required>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>Text</type>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Property_Postal_Code__c</fullName>
|
||||||
|
<label>Property Postal Code</label>
|
||||||
|
<length>20</length>
|
||||||
|
<required>false</required>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>Text</type>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Property_State_Province__c</fullName>
|
||||||
|
<label>Property State/Province</label>
|
||||||
|
<length>80</length>
|
||||||
|
<required>false</required>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>Text</type>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Property_Street__c</fullName>
|
||||||
|
<label>Property Street</label>
|
||||||
|
<length>255</length>
|
||||||
|
<required>false</required>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>Text</type>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ListView xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>All</fullName>
|
||||||
|
<columns>Name</columns>
|
||||||
|
<columns>Appraiser_Field_Review_Date__c</columns>
|
||||||
|
<columns>Property_Street__c</columns>
|
||||||
|
<columns>Property_City__c</columns>
|
||||||
|
<columns>Property_State_Province__c</columns>
|
||||||
|
<columns>LastModifiedDate</columns>
|
||||||
|
<filterScope>Everything</filterScope>
|
||||||
|
<label>All</label>
|
||||||
|
</ListView>
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<actionOverrides>
|
||||||
|
<actionName>New</actionName>
|
||||||
|
<type>Default</type>
|
||||||
|
</actionOverrides>
|
||||||
|
<actionOverrides>
|
||||||
|
<actionName>Edit</actionName>
|
||||||
|
<type>Default</type>
|
||||||
|
</actionOverrides>
|
||||||
|
<actionOverrides>
|
||||||
|
<actionName>View</actionName>
|
||||||
|
<type>Default</type>
|
||||||
|
</actionOverrides>
|
||||||
|
<allowInChatterGroups>false</allowInChatterGroups>
|
||||||
|
<compactLayoutAssignment>SYSTEM</compactLayoutAssignment>
|
||||||
|
<deploymentStatus>Deployed</deploymentStatus>
|
||||||
|
<enableActivities>false</enableActivities>
|
||||||
|
<enableBulkApi>true</enableBulkApi>
|
||||||
|
<enableFeeds>false</enableFeeds>
|
||||||
|
<enableHistory>true</enableHistory>
|
||||||
|
<enableLicensing>false</enableLicensing>
|
||||||
|
<enableReports>true</enableReports>
|
||||||
|
<enableSearch>true</enableSearch>
|
||||||
|
<enableSharing>true</enableSharing>
|
||||||
|
<enableStreamingApi>true</enableStreamingApi>
|
||||||
|
<externalSharingModel>ControlledByParent</externalSharingModel>
|
||||||
|
<label>Appraiser Deficiency</label>
|
||||||
|
<nameField>
|
||||||
|
<label>Appraiser Deficiency Name</label>
|
||||||
|
<type>Text</type>
|
||||||
|
</nameField>
|
||||||
|
<pluralLabel>Appraiser Deficiencies</pluralLabel>
|
||||||
|
<searchLayouts></searchLayouts>
|
||||||
|
<sharingModel>ControlledByParent</sharingModel>
|
||||||
|
<visibility>Public</visibility>
|
||||||
|
</CustomObject>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Appraiser_Case__c</fullName>
|
||||||
|
<label>Appraiser Case</label>
|
||||||
|
<referenceTo>Appraiser_Case__c</referenceTo>
|
||||||
|
<relationshipLabel>Appraiser Deficiencies</relationshipLabel>
|
||||||
|
<relationshipName>Appraiser_Deficiencies</relationshipName>
|
||||||
|
<reparentableMasterDetail>false</reparentableMasterDetail>
|
||||||
|
<required>true</required>
|
||||||
|
<trackHistory>false</trackHistory>
|
||||||
|
<type>MasterDetail</type>
|
||||||
|
<writeRequiresMasterRead>false</writeRequiresMasterRead>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Deficiency_Number__c</fullName>
|
||||||
|
<label>Deficiency Number</label>
|
||||||
|
<length>50</length>
|
||||||
|
<required>false</required>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>Text</type>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Description__c</fullName>
|
||||||
|
<label>Description</label>
|
||||||
|
<length>32768</length>
|
||||||
|
<required>false</required>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>LongTextArea</type>
|
||||||
|
<visibleLines>5</visibleLines>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Resolution__c</fullName>
|
||||||
|
<label>Resolution</label>
|
||||||
|
<length>32768</length>
|
||||||
|
<required>false</required>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>LongTextArea</type>
|
||||||
|
<visibleLines>5</visibleLines>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<fullName>Sort_Order__c</fullName>
|
||||||
|
<label>Sort Order</label>
|
||||||
|
<precision>6</precision>
|
||||||
|
<required>false</required>
|
||||||
|
<scale>0</scale>
|
||||||
|
<trackHistory>true</trackHistory>
|
||||||
|
<type>Number</type>
|
||||||
|
</CustomField>
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<description>Access to Appraiser Case and Appraiser Deficiency objects for setup and early testing.</description>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Case__c.Appraiser_Field_Review_Date__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Case__c.Property_Street__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Case__c.Property_City__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Case__c.Property_State_Province__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Case__c.Property_Postal_Code__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Case__c.Property_Country__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Deficiency__c.Appraiser_Case__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Deficiency__c.Deficiency_Number__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Deficiency__c.Description__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Deficiency__c.Resolution__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<fieldPermissions>
|
||||||
|
<editable>true</editable>
|
||||||
|
<field>Appraiser_Deficiency__c.Sort_Order__c</field>
|
||||||
|
<readable>true</readable>
|
||||||
|
</fieldPermissions>
|
||||||
|
<hasActivationRequired>false</hasActivationRequired>
|
||||||
|
<label>Appraiser Case Admin</label>
|
||||||
|
<objectPermissions>
|
||||||
|
<allowCreate>true</allowCreate>
|
||||||
|
<allowDelete>true</allowDelete>
|
||||||
|
<allowEdit>true</allowEdit>
|
||||||
|
<allowRead>true</allowRead>
|
||||||
|
<modifyAllRecords>true</modifyAllRecords>
|
||||||
|
<object>Appraiser_Case__c</object>
|
||||||
|
<viewAllRecords>true</viewAllRecords>
|
||||||
|
</objectPermissions>
|
||||||
|
<objectPermissions>
|
||||||
|
<allowCreate>true</allowCreate>
|
||||||
|
<allowDelete>true</allowDelete>
|
||||||
|
<allowEdit>true</allowEdit>
|
||||||
|
<allowRead>true</allowRead>
|
||||||
|
<modifyAllRecords>true</modifyAllRecords>
|
||||||
|
<object>Appraiser_Deficiency__c</object>
|
||||||
|
<viewAllRecords>true</viewAllRecords>
|
||||||
|
</objectPermissions>
|
||||||
|
<tabSettings>
|
||||||
|
<tab>Appraiser_Case__c</tab>
|
||||||
|
<visibility>Visible</visibility>
|
||||||
|
</tabSettings>
|
||||||
|
<tabSettings>
|
||||||
|
<tab>Appraiser_Deficiency__c</tab>
|
||||||
|
<visibility>Visible</visibility>
|
||||||
|
</tabSettings>
|
||||||
|
</PermissionSet>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<QuickAction xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<description>Placeholder action to launch Appraiser review letter generation flow or component.</description>
|
||||||
|
<height>600</height>
|
||||||
|
<label>Generate Review Letter</label>
|
||||||
|
<optionsCreateFeedItem>false</optionsCreateFeedItem>
|
||||||
|
<standardLabel>New</standardLabel>
|
||||||
|
<targetObject>Appraiser_Case__c</targetObject>
|
||||||
|
<type>Create</type>
|
||||||
|
<width>800</width>
|
||||||
|
</QuickAction>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<customObject>true</customObject>
|
||||||
|
<motif>Custom67: Gears</motif>
|
||||||
|
</CustomTab>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<customObject>true</customObject>
|
||||||
|
<motif>Custom18: Form</motif>
|
||||||
|
</CustomTab>
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
Appraiser_Case__c appraiserCase = new Appraiser_Case__c(
|
||||||
|
Appraiser_Field_Review_Date__c = Date.today(),
|
||||||
|
Property_Street__c = '123 Main St',
|
||||||
|
Property_City__c = 'Ottawa',
|
||||||
|
Property_State_Province__c = 'ON',
|
||||||
|
Property_Postal_Code__c = 'K1A 0A1',
|
||||||
|
Property_Country__c = 'Canada'
|
||||||
|
);
|
||||||
|
insert appraiserCase;
|
||||||
|
|
||||||
|
insert new List<Appraiser_Deficiency__c>{
|
||||||
|
new Appraiser_Deficiency__c(
|
||||||
|
Name = 'Deficiency 1',
|
||||||
|
Appraiser_Case__c = appraiserCase.Id,
|
||||||
|
Deficiency_Number__c = '1',
|
||||||
|
Description__c = 'Missing comparable sale analysis',
|
||||||
|
Resolution__c = 'Provide updated comparable sales section',
|
||||||
|
Sort_Order__c = 1
|
||||||
|
),
|
||||||
|
new Appraiser_Deficiency__c(
|
||||||
|
Name = 'Deficiency 2',
|
||||||
|
Appraiser_Case__c = appraiserCase.Id,
|
||||||
|
Deficiency_Number__c = '2',
|
||||||
|
Description__c = 'Exterior condition comments are too brief',
|
||||||
|
Resolution__c = 'Expand comments and attach photos',
|
||||||
|
Sort_Order__c = 2
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
System.debug('Created Appraiser Case Id: ' + appraiserCase.Id);
|
||||||
|
System.debug('Payload JSON: ' + AppraiserCaseDocGenService.buildPayloadJson(appraiserCase.Id));
|
||||||
|
System.debug('Doc Gen JSON: ' + AppraiserCaseDocGenService.buildDocGenRequestJson(appraiserCase.Id, 'APPRAISER_REVIEW_LETTER'));
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import json, difflib
|
||||||
|
|
||||||
|
postman_json_text = '''{
|
||||||
|
"TemplateDocument": {
|
||||||
|
"Href": "https://apiuatna11.springcm.com/v2/bccae332-c7db-4892-ab85-257df0f70fea/documents/a0cbc0e6-d87d-459e-8d63-66baa47878f3" },
|
||||||
|
"DataXML": "<TemplateFieldData><AppraiserCaseNumber>AC-00001</AppraiserCaseNumber><AppraiserFieldReviewDate>02/04/2026</AppraiserFieldReviewDate><PropertyAddress>123 Main St, Denver, CO 80202</PropertyAddress><DeficiencyList><Deficiency><Number>1</Number><Description>Missing comparable sale adjustment detail.</Description><Resolution>Added adjustment rationale and supporting calculations.</Resolution></Deficiency><Deficiency><Number>2</Number><Description>Neighborhood trend explanation insufficient.</Description><Resolution>Expanded market trend narrative with MLS evidence.</Resolution></Deficiency><Deficiency><Number>3</Number><Description>Photo date stamps were not included.</Description><Resolution>Re-uploaded photos with date metadata and captions.</Resolution></Deficiency></DeficiencyList><DeficiencyCount>3</DeficiencyCount></TemplateFieldData>",
|
||||||
|
"DestinationDocumentName": "Review_AC-00001.docx",
|
||||||
|
"DestinationFolder": {
|
||||||
|
"Href": "https://apiuatna11.springcm.com/v2/bccae332-c7db-4892-ab85-257df0f70fea/folders/12220442-b12e-f111-84fc-88e9a4bd0d9c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Parse the Postman JSON
|
||||||
|
postman = json.loads(postman_json_text)
|
||||||
|
|
||||||
|
# Reconstruct the Apex payload (simulate AppraiserCasePayloadBuilder output)
|
||||||
|
payload = {
|
||||||
|
'AppraiserCaseNumber': 'AC-00001',
|
||||||
|
'AppraiserFieldReviewDate': '02/04/2026',
|
||||||
|
'PropertyAddress': '123 Main St, Denver, CO 80202',
|
||||||
|
'DeficiencyList': [
|
||||||
|
{'deficiencyNumber': '1', 'description': 'Missing comparable sale adjustment detail.', 'resolution': 'Added adjustment rationale and supporting calculations.'},
|
||||||
|
{'deficiencyNumber': '2', 'description': 'Neighborhood trend explanation insufficient.', 'resolution': 'Expanded market trend narrative with MLS evidence.'},
|
||||||
|
{'deficiencyNumber': '3', 'description': 'Photo date stamps were not included.', 'resolution': 'Re-uploaded photos with date metadata and captions.'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to mimic buildDataXml from Apex implementation
|
||||||
|
def escape_xml(s):
|
||||||
|
if s is None:
|
||||||
|
return ''
|
||||||
|
return s.replace('&','&').replace('<','<').replace('>','>').replace('"','"').replace("'","'")
|
||||||
|
|
||||||
|
def build_data_xml(payload):
|
||||||
|
xml = '<TemplateFieldData>'
|
||||||
|
# Emit flat fields first (Apex iterates payload.keySet() which is not ordered in Python dict, but we'll follow this order)
|
||||||
|
# We'll follow the same ordering as in Postman: AppraiserCaseNumber, AppraiserFieldReviewDate, PropertyAddress
|
||||||
|
for key in ['AppraiserCaseNumber','AppraiserFieldReviewDate','PropertyAddress']:
|
||||||
|
v = payload.get(key)
|
||||||
|
xml += f'<{key}>' + escape_xml(str(v) if v is not None else '') + f'</{key}>'
|
||||||
|
|
||||||
|
# Emit DeficiencyList nested structure
|
||||||
|
deficiencies = payload.get('DeficiencyList')
|
||||||
|
if deficiencies:
|
||||||
|
xml += '<DeficiencyList>'
|
||||||
|
for d in deficiencies:
|
||||||
|
xml += '<Deficiency>'
|
||||||
|
xml += '<Number>' + escape_xml(str(d.get('deficiencyNumber'))) + '</Number>'
|
||||||
|
xml += '<Description>' + escape_xml(str(d.get('description'))) + '</Description>'
|
||||||
|
xml += '<Resolution>' + escape_xml(str(d.get('resolution'))) + '</Resolution>'
|
||||||
|
xml += '</Deficiency>'
|
||||||
|
xml += '</DeficiencyList>'
|
||||||
|
xml += '<DeficiencyCount>' + str(len(deficiencies)) + '</DeficiencyCount>'
|
||||||
|
xml += '</TemplateFieldData>'
|
||||||
|
return xml
|
||||||
|
|
||||||
|
apex_data_xml = build_data_xml(payload)
|
||||||
|
|
||||||
|
apex_request = {
|
||||||
|
'TemplateDocument': {'Href': postman['TemplateDocument']['Href']},
|
||||||
|
'DataXML': apex_data_xml,
|
||||||
|
'DestinationDocumentName': postman['DestinationDocumentName'],
|
||||||
|
'DestinationFolder': {'Href': postman['DestinationFolder']['Href']}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compare postman vs apex
|
||||||
|
print('Comparing top-level fields:')
|
||||||
|
for key in ['TemplateDocument','DestinationDocumentName','DestinationFolder']:
|
||||||
|
p = postman.get(key)
|
||||||
|
a = apex_request.get(key)
|
||||||
|
equal = (p == a)
|
||||||
|
print(f' - {key}:', 'MATCH' if equal else 'DIFFER')
|
||||||
|
|
||||||
|
# Compare DataXML
|
||||||
|
postman_xml = postman['DataXML']
|
||||||
|
apex_xml = apex_request['DataXML']
|
||||||
|
|
||||||
|
if postman_xml == apex_xml:
|
||||||
|
print('\nDataXML: EXACT MATCH')
|
||||||
|
else:
|
||||||
|
print('\nDataXML: DIFFER')
|
||||||
|
# show diff
|
||||||
|
pd = postman_xml.split('><')
|
||||||
|
ad = apex_xml.split('><')
|
||||||
|
diff = difflib.unified_diff(pd, ad, fromfile='postman_xml', tofile='apex_xml', lineterm='')
|
||||||
|
print('\n'.join(diff))
|
||||||
|
# also show small head/tail
|
||||||
|
print('\n-- Postman head 200 chars --\n', postman_xml[:200])
|
||||||
|
print('\n-- Apex head 200 chars --\n', apex_xml[:200])
|
||||||
|
|
||||||
|
# Additionally print whether other top-level objects match (TemplateDocument Href and Folder Href)
|
||||||
|
print('\nTemplateDocument.Href match:', postman['TemplateDocument']['Href'] == apex_request['TemplateDocument']['Href'])
|
||||||
|
print('DestinationFolder.Href match:', postman['DestinationFolder']['Href'] == apex_request['DestinationFolder']['Href'])
|
||||||
|
|
||||||
|
# Print apex_data_xml for inspection
|
||||||
|
print('\n--- Apex DataXML ---\n')
|
||||||
|
print(apex_data_xml)
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os, re, base64, zipfile, xml.etree.ElementTree as ET, sys
|
||||||
|
log_path = '/home/paulh/.vscode-server/data/User/workspaceStorage/79b924110cb5ff6de49811d445e59969-1/GitHub.copilot-chat/chat-session-resources/90e6dae0-2184-412b-af0b-eac258be98c5/call_gY8uUyzuGvFiN46d4ZPVFtjz__vscode-1775271381281/content.txt'
|
||||||
|
out_dir = 'artifacts/doc_inspect'
|
||||||
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
|
start=False
|
||||||
|
chunks = []
|
||||||
|
with open(log_path, 'r', errors='replace') as f:
|
||||||
|
for line in f:
|
||||||
|
if 'BASE64_BEGIN' in line:
|
||||||
|
start = True
|
||||||
|
continue
|
||||||
|
if 'BASE64_END' in line:
|
||||||
|
break
|
||||||
|
if start:
|
||||||
|
if 'BASE64_CHUNK:' in line:
|
||||||
|
parts = line.split('BASE64_CHUNK:',1)[1].strip()
|
||||||
|
chunks.append(parts)
|
||||||
|
else:
|
||||||
|
# fallback: if line looks like base64 (long and only base64 chars + =), take it
|
||||||
|
s = line.strip()
|
||||||
|
if len(s) > 100 and re.fullmatch(r'[A-Za-z0-9+/=\n\r]+', s):
|
||||||
|
chunks.append(s)
|
||||||
|
|
||||||
|
if not chunks:
|
||||||
|
print('ERROR: no base64 chunks found in log at', log_path)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
b64 = ''.join(chunks)
|
||||||
|
# sanitize (remove any DEBUG prefixes that snuck in)
|
||||||
|
b64 = re.sub(r'\s+', '', b64)
|
||||||
|
try:
|
||||||
|
data = base64.b64decode(b64)
|
||||||
|
except Exception as e:
|
||||||
|
print('ERROR decoding base64:', e)
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
docx_path = os.path.join(out_dir, 'downloaded.docx')
|
||||||
|
with open(docx_path, 'wb') as f:
|
||||||
|
f.write(data)
|
||||||
|
print('WROTE_DOCX:', docx_path, 'size=', os.path.getsize(docx_path))
|
||||||
|
|
||||||
|
# unzip
|
||||||
|
unzip_dir = os.path.join(out_dir, 'unzipped')
|
||||||
|
os.makedirs(unzip_dir, exist_ok=True)
|
||||||
|
try:
|
||||||
|
with zipfile.ZipFile(docx_path, 'r') as z:
|
||||||
|
z.extractall(unzip_dir)
|
||||||
|
except Exception as e:
|
||||||
|
print('ERROR unzipping docx:', e)
|
||||||
|
sys.exit(4)
|
||||||
|
|
||||||
|
doc_xml = os.path.join(unzip_dir, 'word', 'document.xml')
|
||||||
|
if not os.path.exists(doc_xml):
|
||||||
|
print('ERROR: word/document.xml not found in the docx')
|
||||||
|
sys.exit(5)
|
||||||
|
|
||||||
|
# parse XML and extract tables
|
||||||
|
ns = {'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
|
||||||
|
ET.register_namespace('w', ns['w'])
|
||||||
|
try:
|
||||||
|
tree = ET.parse(doc_xml)
|
||||||
|
root = tree.getroot()
|
||||||
|
except Exception as e:
|
||||||
|
print('ERROR parsing document.xml:', e)
|
||||||
|
sys.exit(6)
|
||||||
|
|
||||||
|
tables = root.findall('.//w:tbl', ns)
|
||||||
|
print('TABLE_COUNT:', len(tables))
|
||||||
|
# For each table, collect row texts (limit output to first 5 tables and 20 rows each)
|
||||||
|
found_def_texts = []
|
||||||
|
for ti, tbl in enumerate(tables[:5], start=1):
|
||||||
|
rows = tbl.findall('.//w:tr', ns)
|
||||||
|
print('\n--- TABLE', ti, 'rows=', len(rows), '---')
|
||||||
|
for ri, tr in enumerate(rows[:20], start=1):
|
||||||
|
texts = [t.text for t in tr.findall('.//w:t', ns) if t.text]
|
||||||
|
joined = ' | '.join(texts).strip()
|
||||||
|
if joined:
|
||||||
|
print('ROW %d:'%ri, repr(joined))
|
||||||
|
# heuristic: look for keywords
|
||||||
|
if any(k.lower() in joined.lower() for k in ('deficiency','description','defect','ac-','AC-','DeficiencyList')):
|
||||||
|
found_def_texts.append(joined)
|
||||||
|
else:
|
||||||
|
print('ROW %d: <empty>'%ri)
|
||||||
|
|
||||||
|
# Also search whole document.xml for certain keywords
|
||||||
|
full_xml = open(doc_xml,'r',encoding='utf-8',errors='replace').read()
|
||||||
|
keywords = ['Deficiency','DeficiencyList','Description','<TableRow','AC-']
|
||||||
|
hits = {k: (full_xml.count(k)) for k in keywords}
|
||||||
|
print('\nKEYWORD_COUNTS:')
|
||||||
|
for k,v in hits.items():
|
||||||
|
print(k+':', v)
|
||||||
|
|
||||||
|
print('\nFOUND_DEFICIENCY_TEXTS_COUNT:', len(found_def_texts))
|
||||||
|
for i, txt in enumerate(found_def_texts[:20], start=1):
|
||||||
|
print('FOUND_%d:'%i, txt)
|
||||||
|
|
||||||
|
# Exit with success
|
||||||
|
print('\nSUMMARY: docx_size=%d tables=%d deficiency_text_found=%s' % (os.path.getsize(docx_path), len(tables), bool(found_def_texts)))
|
||||||
|
print('OUTPUT_DIR:', os.path.abspath(out_dir))
|
||||||
|
|
@ -8,5 +8,5 @@
|
||||||
"name": "salesforce-appraiser-review-letter",
|
"name": "salesforce-appraiser-review-letter",
|
||||||
"namespace": "",
|
"namespace": "",
|
||||||
"sfdcLoginUrl": "https://login.salesforce.com",
|
"sfdcLoginUrl": "https://login.salesforce.com",
|
||||||
"sourceApiVersion": "62.0"
|
"sourceApiVersion": "63.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue