diff options
| author | Szymon Szukalski <szymon@szymonszukalski.com> | 2026-04-09 10:10:25 +1000 |
|---|---|---|
| committer | Szymon Szukalski <szymon@szymonszukalski.com> | 2026-04-09 10:10:25 +1000 |
| commit | 48dcdc4ed105a8a05f1345c0b00fe8c9bf511670 (patch) | |
| tree | aa898ddf53335e92c2b206238ab98a5728303bde | |
| parent | a9cb41ef7ae6fbd46dc6ff446c86b5df08f324b3 (diff) | |
Extend CRM supplier support
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | config.org | 61 |
2 files changed, 58 insertions, 9 deletions
@@ -101,7 +101,8 @@ The people workflow is a CRM rooted at `~/org/areas/people/people.org`. - Failures must surface visibly. The system must not silently fall back to stale state. - The CRM is individual-focused: `TEAM` captures the current working team and `MANAGER` captures the formal organisational manager. - Team structure, org charts, and matrix relationships belong in separate notes, not in the CRM. -- Person cards use `ROLE`, `TEAM`, `MANAGER`, `ENGAGEMENT`, `LOCATION`, and `CURRENT_FOCUS` in that order. +- Person cards use `ROLE`, `TEAM`, `MANAGER`, `ENGAGEMENT`, `SUPPLIER`, `LOCATION`, and `CURRENT_FOCUS` in that order. +- `ENGAGEMENT` and `SUPPLIER` both use fixed lookup lists in `ss/crm-add` so reports stay consistent. - `CURRENT_FOCUS` must stay short and phrase-like so summaries and completion annotations remain readable. Name entry uses fixed abbrevs plus the people CRM: @@ -116,7 +117,7 @@ Name entry uses fixed abbrevs plus the people CRM: - `M-x ss/crm-insert-name` inserts the canonical name at point. - `M-x ss/crm-insert-summary` inserts a compact single-line summary at point. - `M-x ss/crm-add` adds a new person card directly to `~/org/areas/people/people.org`. -- `M-x ss/crm-report-by-role`, `M-x ss/crm-report-by-team`, `M-x ss/crm-report-by-manager`, `M-x ss/crm-report-by-engagement`, and `M-x ss/crm-report-by-location` render grouped Org reports in a read-only buffer. +- `M-x ss/crm-report-by-role`, `M-x ss/crm-report-by-team`, `M-x ss/crm-report-by-manager`, `M-x ss/crm-report-by-engagement`, `M-x ss/crm-report-by-supplier`, and `M-x ss/crm-report-by-location` render grouped Org reports in a read-only buffer. ### Persistent abbrevs @@ -222,6 +223,7 @@ Denote handles long-lived notes. The main bindings are: - `C-c n p` to open `~/org/areas/people/people.org`. - `C-c n P` to add a new person card. - `C-c n R` to show people grouped by manager. +- `C-c n S` to show people grouped by supplier. - `C-c n T` to show people grouped by team. Keyword prompts and directory placement are part of the workflow, not an afterthought. The config is set up so structure is created first, then capture writes into it, with folder placement carrying most of the durable type information. @@ -297,8 +297,10 @@ overview mode, direct visits reset back to overview mode, and exiting card view means widening the buffer and restoring that overview. Keep =CURRENT_FOCUS= short and phrase-like so summaries and completion annotations stay readable. Person cards use a flat property model in this order: =ROLE=, =TEAM=, -=MANAGER=, =ENGAGEMENT=, =LOCATION=, and =CURRENT_FOCUS=. =ABBREV= and -=ALIASES= remain optional helpers for lookup and insertion. +=MANAGER=, =ENGAGEMENT=, =SUPPLIER=, =LOCATION=, and =CURRENT_FOCUS=. +=ENGAGEMENT= and =SUPPLIER= both use fixed lookup lists in =ss/crm-add= so +reports stay consistent. =ABBREV= and =ALIASES= remain optional helpers for +lookup and insertion. #+begin_src emacs-lisp (require 'seq) @@ -309,9 +311,17 @@ Person cards use a flat property model in this order: =ROLE=, =TEAM=, (expand-file-name "areas/people/people.org" "~/org/") "Single source of truth for the people CRM.") - (defconst ss/crm-engagement-values - '("permanent" "contractor" "other") - "Allowed engagement values for people cards.") + (defconst ss/crm-engagement-options + '("Perm" "SOW" "SOW Fixed Outcome" "NCS India") + "Canonical engagement values for people cards.") + + (defconst ss/crm-supplier-options + '("Accenture Song" + "INFOSYS TECHNOLOGIES LIMITED" + "MAKK Integrations Pty Ltd" + "NCSI Technologies India Private Limited" + "TECH MAHINDRA LTD") + "Canonical supplier values for people cards.") (defvar ss/crm--cache nil "Cached CRM entries loaded from `ss/crm-file'.") @@ -343,6 +353,10 @@ Person cards use a flat property model in this order: =ROLE=, =TEAM=, "Return the engagement in ENTRY." (plist-get entry :engagement)) + (defun ss/crm--entry-supplier (entry) + "Return the supplier in ENTRY." + (plist-get entry :supplier)) + (defun ss/crm--entry-manager (entry) "Return the manager in ENTRY." (plist-get entry :manager)) @@ -429,6 +443,7 @@ Person cards use a flat property model in this order: =ROLE=, =TEAM=, :team (org-entry-get nil "TEAM") :manager (org-entry-get nil "MANAGER") :engagement (org-entry-get nil "ENGAGEMENT") + :supplier (org-entry-get nil "SUPPLIER") :location (org-entry-get nil "LOCATION") :current-focus (org-entry-get nil "CURRENT_FOCUS") ) @@ -620,6 +635,35 @@ Person cards use a flat property model in this order: =ROLE=, =TEAM=, "People by engagement" #'ss/crm--entry-engagement)) + (defun ss/crm-report-by-supplier () + "Show non-empty suppliers grouped by supplier." + (interactive) + (let ((groups + (sort (seq-group-by + #'ss/crm--entry-supplier + (seq-filter + (lambda (entry) + (not (string-empty-p (or (ss/crm--entry-supplier entry) "")))) + (ss/crm-entries))) + (lambda (left right) + (string< (car left) (car right)))))) + (with-current-buffer (get-buffer-create "*People Report*") + (let ((inhibit-read-only t)) + (erase-buffer) + (org-mode) + (insert "#+title: People by supplier\n\n") + (dolist (group groups) + (insert "* " (car group) "\n") + (dolist (entry (sort (copy-sequence (cdr group)) + (lambda (left right) + (string< (ss/crm--entry-name left) + (ss/crm--entry-name right))))) + (insert "- " (ss/crm--display entry) "\n"))) + (goto-char (point-min)) + (read-only-mode 1) + (view-mode 1)) + (pop-to-buffer (current-buffer))))) + (defun ss/crm-report-by-role () "Show people grouped by role." (interactive) @@ -661,8 +705,9 @@ Person cards use a flat property model in this order: =ROLE=, =TEAM=, (team (ss/crm-read-required-string "Team: ")) (manager (ss/crm-read-required-string "Manager: ")) (engagement (completing-read "Engagement: " - ss/crm-engagement-values nil t nil nil - "permanent")) + ss/crm-engagement-options nil t)) + (supplier (completing-read "Supplier: " + ss/crm-supplier-options nil t)) (location (ss/crm-read-required-string "Location: ")) (current-focus (ss/crm-read-required-string "Current focus: ")) ) @@ -685,6 +730,7 @@ Person cards use a flat property model in this order: =ROLE=, =TEAM=, (ss/crm--property-line "TEAM" team) (ss/crm--property-line "MANAGER" manager) (ss/crm--property-line "ENGAGEMENT" engagement) + (ss/crm--property-line "SUPPLIER" supplier) (ss/crm--property-line "LOCATION" location) (ss/crm--property-line "CURRENT_FOCUS" current-focus) ":END:\n\n" @@ -1005,6 +1051,7 @@ When CREATE is non-nil, create the datetree entry when missing." ("C-c n p" . ss/crm-open) ("C-c n P" . ss/crm-add) ("C-c n R" . ss/crm-report-by-manager) + ("C-c n S" . ss/crm-report-by-supplier) ("C-c n T" . ss/crm-report-by-team)) :config (setq org-directory ss/org-directory |
