Training Tracker only Admin maintenance Data protection

Admin Data Maintenance Build Pass

This adds two maintenance features: certificate library metadata so PDFs are identifiable in SharePoint, and an admin-only permanent person removal route that deletes that person's training records and certificate files after a strong confirmation.

Read this before building: permanent removal is destructive. Use it for genuine data-protection cleanup only. For ordinary leavers, the existing deactivate pattern is safer because it removes the person from compliance without destroying history.

0. Why The Reminder Flow Guide Changed

You are currently building the email reminder flow. The guide was changed for two concrete reasons, not just tidying:

Problem foundWhat changedWhy it matters
Send an email (V2) was nested at level 9.The separate Reminder due condition was removed. Ready to send now combines "Reminder Type is not blank" and "log row does not already exist".Power Automate has a maximum nesting limit of 8. The old shape could not save.
Power Automate created unwanted For each wrappers.The guide now says to use expressions for nested-loop values instead of picking vague dynamic tokens like Current item.The email and log actions must sit directly inside Ready to send - True / If yes, not inside extra array loops.

If you are halfway through that build, keep following Course Expiry Reminder Emails from the corrected Ready to send section onward. The changes are there because the older branch shape was genuinely invalid.

1. Certificate Library Metadata

The locked certificate library should not be a pile of anonymous PDFs. Add these columns to TT Certificates_LTD / TT Certificates (LTD).

Column internal nameTypeRequired?Purpose
TrainingRecordIDNumberYesLinks the file to TT TrainingRecords.ID.
PersonEmailSingle line textYesThe person the certificate belongs to.
PersonNameSingle line textNoReadable staff name for library maintenance.
CourseIDNumberYesLinks the file to TT Courses.ID.
CourseNameSingle line textYesReadable course title.
DateCompletedDate onlyNoCompletion date stored against the training record.
UploadedByEmailSingle line textNoWho uploaded or replaced the certificate.
UploadedOnDate and timeNoWhen the file was saved.
IsCurrentYes/NoYesCurrent certificate marker. Set to true for the live file.

1.1 Update TT - Save Certificate Library

Open the save-certificate flow. Inside the authorised branch, inside the HasNewFile - True / If yes branch, the shape should be:

Expected save-file branch shape
HasNewFile
└── True / If yes
    ├── Get existing certificate files
    ├── Apply to each existing certificate
    │   └── Delete file
    ├── Create file
    └── Update file properties

If you already have Update file properties, edit it. If not, add it directly under Create file. Do not put it inside the delete loop.

Update file properties fieldUse this value
Site AddressYour Training Tracker SharePoint site.
Library NameTT Certificates_LTD / TT Certificates (LTD).
IdItemId from Create file.
TitleCourseName from When Power Apps calls a flow (V2).
TrainingRecordIDRecordID from the trigger.
PersonEmailPersonEmail from the trigger.
CourseIDCourseID from the trigger.
CourseNameCourseName from the trigger.
DateCompletedDateCompleted from the trigger.
UploadedByEmailActorEmail from the trigger.
UploadedOnExpression: utcNow()
IsCurrenttrue

For PersonName, use section 1.2. Do not guess a dynamic token called Title if there are several of them.

1.2 Optional But Recommended: Fill PersonName

To fill the human-readable name, add this before Update file properties, still inside the same HasNewFile - True / If yes branch.

  1. Add SharePoint - Get items. Name it Get Person For Metadata.
  2. List: TT Personnel.
  3. Filter Query: use the expression below.
  4. Top Count: 1.
Power Automate expression pattern - person metadata filter
concat("Email eq '", PERSON_EMAIL_FROM_TRIGGER, "'")
Do not paste the pattern literally. In the expression editor, type concat("Email eq '", , then insert PersonEmail from When Power Apps calls a flow (V2), then type , "'"). The exact internal trigger name can differ between flows, so the guide deliberately does not guess it here.

Then set PersonName in Update file properties with this expression.

Power Automate expression - PersonName
first(body('Get_Person_For_Metadata')?['value'])?['Title']

2. Build TT - Remove Person

This should be an instant cloud flow called from Power Apps. It performs the destructive cleanup under the flow connection, not by giving every admin direct delete formulas in the app.

2.1 Trigger Inputs

OrderTypeNameNotes
1NumberPersonIDTT Personnel.ID.
2TextPersonEmailThe selected person's email.
3TextConfirmEmailThe email the admin typed into the confirmation box.
4TextActorEmailThe signed-in admin.
5Yes/NoIsAdminMust be true.

2.2 Flow Shape

Expected remove-person flow shape
Power Apps (V2)
├── Initialize varSuccess = false
├── Initialize varMessage = ""
├── Get person
├── Condition - Authorised and confirmed
│   ├── True / If yes
│   │   ├── Get certificate files
│   │   ├── Apply to each certificate file
│   │   │   └── Delete file
│   │   ├── Get training records
│   │   ├── Apply to each training record
│   │   │   └── Delete item
│   │   ├── Get people using this line manager
│   │   ├── Apply to each managed person
│   │   │   └── Update item - clear LineManagerEmail
│   │   ├── Delete item - TT Personnel
│   │   ├── Create item - TT AuditLog
│   │   ├── Set varSuccess = true
│   │   └── Set varMessage = "Person removed."
│   └── False / If no
│       └── Set varMessage = "Not authorised or confirmation did not match."
└── Respond to a PowerApp or flow

2.3 Authorisation Condition

The condition must check both admin status and typed confirmation. Use this as the left side expression, operator is equal to, right side true.

Power Automate expression - Authorised and confirmed
and(
    equals(triggerBody()?['boolean'], true),
    equals(toLower(triggerBody()?['text']), toLower(triggerBody()?['text_1']))
)
Check internal trigger names: with the input order above, PersonEmail is usually text, ConfirmEmail is usually text_1, and IsAdmin is usually boolean. If the expression red-lines, open trigger code view and swap in the real names. The logic is still: IsAdmin = true and PersonEmail = ConfirmEmail.

2.4 Delete Certificate Files

Inside Authorised and confirmed - True / If yes, add SharePoint - Get files (properties only).

FieldValue
Library NameTT Certificates_LTD / TT Certificates (LTD).
Filter QueryExpression below.
Power Automate expression - certificate delete filter
concat("PersonEmail eq '", triggerBody()?['text'], "'")

Add Apply to each over the files returned. Inside it add SharePoint - Delete file. Use Identifier from the current certificate file item.

2.5 Delete Training Records

Still inside the True branch, add SharePoint - Get items against TT TrainingRecords.

Power Automate expression - training record delete filter
concat("PersonEmail eq '", triggerBody()?['text'], "'")

Add Apply to each over the returned records. Inside it add SharePoint - Delete item, list TT TrainingRecords, Id from the current training record.

2.6 Sever Line Manager Links

Still inside the True branch, add SharePoint - Get items against TT Personnel to find anyone whose line manager is the removed person.

Power Automate expression - managed people filter
concat("LineManagerEmail eq '", triggerBody()?['text'], "'")

Add Apply to each. Inside it, Update item on TT Personnel, keep required fields unchanged, and set LineManagerEmail blank. Also set LastStatus to Line manager removed, LastUpdatedByEmail to ActorEmail, and LastUpdatedOn to utcNow().

2.7 Delete The Person And Respond

  1. Add SharePoint - Delete item, list TT Personnel, Id = PersonID from the trigger.
  2. Add SharePoint - Create item, list TT AuditLog, event type Person permanently removed.
  3. Set varSuccess to true.
  4. Set varMessage to Person removed..
  5. Put one Respond to a PowerApp or flow after the whole condition, not inside either branch. Return success and message.

3. Add The Power Apps Remove Button

This is a targeted edit to scrAdminManage. Do not replace the whole screen unless you want to discard your manual spacing tweaks.

3.1 Add Confirmation Input

Inside the edit-person area, add a modern text input named txtRemovePersonConfirm.

PropertyFormula
Default""
Placeholder"Type email to confirm permanent removal"
VisiblevarTab = "People" && !IsBlank(varEditPerson) && varShowRemovePersonConfirm
X32
Y410
Width590
Height40

3.2 Add Remove Button

Add a modern button named btnRemovePersonPermanent.

PropertyFormula
TextIf(varShowRemovePersonConfirm, "Confirm permanent removal", "Remove person")
BasePaletteColorRGBA(226,75,74,1)
VisiblevarTab = "People" && !IsBlank(varEditPerson) && varIsAdmin
X32
YIf(varShowRemovePersonConfirm, 462, 410)
Width284
Height44

Set btnRemovePersonPermanent.OnSelect to this. Formula bar paste: no leading equals sign.

Power Fx - btnRemovePersonPermanent.OnSelect
If(
    !varShowRemovePersonConfirm,
    Set(varShowRemovePersonConfirm, true);
    Reset(txtRemovePersonConfirm);
    Notify("Type the person's email address to confirm permanent removal.", NotificationType.Warning),
    If(
        Lower(Trim(txtRemovePersonConfirm.Text)) <> Lower(varEditPerson.Email),
        Notify("Email confirmation does not match. Nothing was removed.", NotificationType.Error),
        Set(varCertBusy, true);
        Set(
            varRemovePersonResult,
            IfError(
                'TT - Remove Person'.Run(
                    varEditPerson.ID,
                    Lower(varEditPerson.Email),
                    Lower(Trim(txtRemovePersonConfirm.Text)),
                    varMyEmail,
                    varIsAdmin
                ),
                {success: false, message: "Remove flow could not be reached."}
            )
        );
        Set(varCertBusy, false);
        If(
            varRemovePersonResult.success,
            Refresh('TT Personnel');
            Refresh('TT TrainingRecords');
            Set(varEditPerson, Blank());
            Set(varShowRemovePersonConfirm, false);
            Notify(varRemovePersonResult.message, NotificationType.Success),
            Notify(varRemovePersonResult.message, NotificationType.Error)
        )
    )
)

3.3 Add Cancel Remove Button

Add a modern button named btnCancelRemovePerson.

PropertyFormula
Text"Cancel removal"
BasePaletteColorRGBA(108,117,125,1)
VisiblevarTab = "People" && !IsBlank(varEditPerson) && varShowRemovePersonConfirm
X326
Y462
Width296
Height44
OnSelectSet(varShowRemovePersonConfirm, false); Reset(txtRemovePersonConfirm)

4. Test Checklist

  1. Upload or replace one certificate.
  2. Open TT Certificates_LTD directly in SharePoint and confirm metadata columns show person email, course, date, uploader, and current marker.
  3. Create a disposable test person with one test training record and one test certificate.
  4. Run TT - Remove Person only against that test person first.
  5. Confirm the person row is gone, training records are gone, certificate files are gone, and any LineManagerEmail links pointing at that email are blank.
  6. Confirm TT AuditLog has a permanent-removal entry.

5. References

← Back to the main Training Tracker guide
Copied