# organization-charts Specification ## Purpose Maintain customer organization charts locally while enriching people with Oracle contact information and related opportunities. ## Requirements ### Requirement: Charts are scoped and stored locally Charts SHALL persist JSON-compatible records in extension-owned IndexedDB, scoped by user and customer in the browser profile. Records SHALL include revision and update metadata; stale saves SHALL be rejected. Email SHALL not persist in chart nodes. #### Scenario: Concurrent chart change - **WHEN** a user saves an outdated revision after another window has saved the chart - **THEN** the save is rejected with guidance to reopen the current chart #### Scenario: Browser profile is different - **WHEN** the same user accesses the customer in another browser profile - **THEN** this local chart is not automatically synchronized to that profile ### Requirement: Hierarchy editing preserves remaining people The chart SHALL support adding people, changing their manager, notes, branch expansion/collapse, zoom and panning. It SHALL reject invalid hierarchies, support up to 500 people and 60 levels, and promote direct reports to the removed person's manager when a person is deleted. #### Scenario: Manager is deleted - **WHEN** a user deletes a person with direct reports - **THEN** those reports remain in the chart under the deleted person's manager #### Scenario: Unsaved chart is closed - **WHEN** the user closes a chart with unsaved changes - **THEN** the extension requests confirmation before discarding those changes ### Requirement: Contact autocomplete is debounced and customer-scoped Name autocomplete SHALL search contacts after a 350 ms typing pause, filter by the customer's PartyId, display PartyName and JobTitle, and ignore superseded results. Empty queries and opening the chart alone SHALL not issue contact searches. #### Scenario: User keeps typing - **WHEN** another character arrives before the search delay expires - **THEN** the pending search is superseded rather than sending one query per character ### Requirement: Person details refresh contact data on demand Opening a stored person SHALL query contacts using the person's Name as keywords and match by contact PartyId. A matching result SHALL refresh Name and Job Title while preserving notes and reporting relationships; Email SHALL remain transient. Job Title SHALL be eligible for chart persistence. #### Scenario: Contact data changed - **WHEN** a matching contact response has a new name or job title - **THEN** the working chart updates those fields and retains the person's notes and manager ### Requirement: Contact PartyNumber is preserved as an opaque identifier Chart nodes SHALL retain the contact's PartyNumber, including alphanumeric values. Opening person details SHALL persist a returned PartyNumber; opportunity lookup SHALL use an existing stored value and attempt resolution when it is missing. #### Scenario: Alphanumeric number is already saved - **WHEN** opportunities are requested for a person with PartyNumber `A4KCS2P` - **THEN** that exact value is used in `OpportunityContact.ContactPartyNumber`, without numeric conversion ### Requirement: Contact opportunity tables load all pages The person card SHALL provide an opportunities action that opens `Opportunities | `, shows a skeleton while loading and follows `hasMore` until all pages are collected. The resulting table SHALL be sortable and link each Name to its corresponding opportunity in a new tab. #### Scenario: Contact has more than one result page - **WHEN** the opportunities response sets `hasMore=true` - **THEN** the next offset is queried using the same contact PartyNumber until the result is complete ### Requirement: New contacts are created explicitly The contact-creation form SHALL require First Name, Last Name and Email, map email/phone consent to their corresponding Do/DoNot flags and obtain account identifiers from the selected customer. Provided phone components SHALL be combined as country code, hyphen and number. Save SHALL submit directly without a payload-preview step. #### Scenario: Optional phone is omitted - **WHEN** required contact fields are valid but a phone number is not filled - **THEN** that absent phone value is not included as a populated contact field #### Scenario: Phone components are entered - **WHEN** country code `55` and phone number `99999999999` are provided - **THEN** the submitted raw phone value is `55-99999999999`