HOCO B2B
SRSRequirements

Resellers & prospects (CRM)

Reseller and prospect records, creation paths, unique normalised phone, the ten-status lifecycle, assignment, segments, timeline, follow-ups, validation, documents, list, map and the 360° page.

The CRM centralises the commercial life of every point of sale, from the first prospect record to the active revendeur (reseller) (cahier §4.1, §4.2, §6.5, §6.8, §9, §10, §12, §15.1, §15.5). Records are created in the back-office (BO), in the delegate app (DA, offline-capable), by the reseller's own sign-up in the reseller app or web (RA, RW) or by import; they are worked by delegates in the field and by the commercial team in the back-office. The server's crm module owns Reseller, ResellerAddress, ResellerDocument, ResellerPhoto, ResellerStatusChange, ResellerAssignment, Interaction, CrmTask, Segment and ResellerSegment; files go through the files module. The back-office operations are specified on API › CRM, the delegate ones on API › Delegate resellers, and the statuses on Lifecycles › Reseller.

Every reseller also carries a customer type (Super-gros, Demi-gros, Détail) that selects its prices; how it is declared, reviewed and changed is AUTH-11.

IDRequirementReleasePrioritySourceApps
CRM-01Reseller/prospect record with the minimum data set (store, owner, phone, WhatsApp, email, wilaya, commune, address, GPS, type, potential, delegate, photos, documents, status)V1MUST§4.2, §15.5API · BO · DA
CRM-02Creation from the back-office, the delegate app, reseller self sign-up or import, with the source recordedV1MUST§4.2, §6.5API · BO · DA · RA
CRM-03Duplicate prevention: the main phone number is unique (normalised); duplicate check on every creation pathV1MUST§4.2, §6.5, §12API · BO · DA
CRM-04Reseller lifecycle (10 statuses) with automatic and manual transitions and full historyV1MUST§4.2, §15.1API · BO · DA
CRM-05Assignment to a delegate and a zone; link to the creating delegate; reassignment historyV1MUST§4.2, §6.5API · BO
CRM-06Segmentation by potential, business type, brands sold, level and activityV1SHOULD§4.2API · BO
CRM-07Unified interaction history: calls, visits, notes, orders, payments, claimsV1MUST§4.2API · BO · DA
CRM-08Follow-ups (relances), tasks and next actions with due dates and notificationsV1MUST§4.2, §6.8API · BO · DA
CRM-09Account validation by an authorised user (approve / reject with reason)V1MUST§4.2, §6.5, §12API · BO
CRM-10Store photos and documents (façade, interior, RC, NIF, ID) with protected accessV1MUST§4.2, §10, §15.5API · BO · DA · RA
CRM-11CRM list and map views with filters (status, zone, wilaya, commune, delegate, segment, potential, activity) and exportV1MUST§4.1, §4.2, §9BO
CRM-12Reseller 360° page: orders, invoices, payments, visits, reports, tasks, interactionsV1MUST§4.2BO
CRM-13Merge tool for historical duplicatesV2COULD§4.2BO

Settings used in this area

SettingDefaultMeaning
crm.inactivityDays60Activity window: a reseller with no order confirmed in this window is inactive and gets a follow-up (CRM-04, CRM-06).
crm.duplicateRadiusMeters50Radius within which a near-identical store name is reported as a possible duplicate (CRM-03).
crm.validationReminderHours24A record waiting longer than this in the validation queue triggers one reminder to the validators (CRM-09).

Requirements

CRM-01 — Reseller and prospect record with the minimum data set

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2, §15.5API · BO · DASRV-14 · DSH-03 · WEB-05 · MRA-07 · MDA-04

Statement — The system shall keep one record per point of sale — prospect or reseller — holding at least the data set of cahier §15.5: store, owner, phone, WhatsApp, email, wilaya, commune, address, GPS position, business type, potential, delegate, photos, documents and status.

Rules

  1. Fields and constraints:

    FieldConstraint
    numberREV-000123, allocated by NumberSequence at creation, never reused
    storeName · ownerName2–120 characters each
    phone (main)Algerian mobile, E.164, unique (CRM-03); it is the owner's sign-in identifier once the account exists
    whatsapp · emailOptional; WhatsApp follows the phone rules (often equal to the main phone); email RFC-valid
    businessTypeCodeItem of the BUSINESS_TYPE reference list (ADM-05)
    customerTypeSUPER_GROS · DEMI_GROS · DETAIL (AUTH-11); may be empty for a field prospect until validation
    potentialLOW · MEDIUM · HIGH
    Store addresswilaya, commune (must belong to the wilaya — GEO-01), address line 5–200, landmark; it is also the default delivery address (ResellerAddress)
    locationGPS position per GEO-03 with its source (field capture, geocoded, manual pin)
    Legal identifiersRC, NIF, NIS, AI (optional text; required later for invoices to professionals per D-21)
    brandsSoldBrands the store declares selling (segmentation, CRM-06)
    Delegate, zoneCRM-05
    Photos, documentsCRM-10
    status · validationStatus · sourceResellerStatus (CRM-04), ValidationStatus (CRM-09), ResellerSource (CRM-02)
  2. Required at creation depends on the path (CRM-02); every record has at least store name, main phone, wilaya and commune. A field-created point of sale also requires GPS position and a façade photo before it can be approved (CRM-09).

  3. Who edits what: the back-office (crm.update) edits every field; a delegate edits records in scope except the main phone of a record that has an account, the assignment, segments, customer type and validation; the reseller edits, through profile.update, owner name, WhatsApp, email, landmark and delivery addresses — the store name, store address, business type and customer type go through support or the back-office (422 FIELD_NOT_EDITABLE for the type).

  4. Main-phone change: by the back-office with the uniqueness check of CRM-03; by the reseller through an OTP of purpose PHONE_CHANGE on the new number. Either way the old number stays in the history.

  5. Every change is historised (AUD-02) with actor, time and before/after values; records are never deleted — a closed shop is SUSPENDED or NOT_INTERESTED.

Acceptance criteria

  • Creating a record without storeName, phone, wilayaCode or communeId returns 400 VALIDATION_FAILED naming the missing fields.
  • A new record gets the next REV- number and a default delivery address equal to its store address.
  • A delegate updating customerType or the assignment of a record in scope gets 403 PERMISSION_DENIED; updating the potential succeeds and appears in the history with the delegate as actor.
  • A reseller calling profile.update with a new email succeeds; with customerType it gets 422 FIELD_NOT_EDITABLE.
  • BO-12, DA-15 and RW-12 show the same values for the same record.

Data — Reseller, ResellerAddress, ResellerDocument, ResellerPhoto, NumberSequence, AuditLog.

Interfaces

CRM-02 — Creation paths with the source recorded

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2, §6.5API · BO · DA · RASRV-14

Statement — The system shall create reseller and prospect records from the back-office, the delegate app, the reseller's self sign-up and imports, recording the source of each record permanently.

Rules

  1. Paths and their defaults:

    PathsourceInitial statusvalidationStatusAssignmentOperation
    Back-office form (BO-12), crm.createBACK_OFFICENEW_PROSPECT, TO_CONTACT or INTERESTED (default NEW_PROSPECT)APPROVEDChosen; proposed: the delegate covering the commune when there is exactly oneadmin.resellers.create
    Delegate app (DA-14), offline-capableDELEGATE_APPNEW_PROSPECT or INTERESTEDPENDING (new point of sale to validate — FLD-14)The creating delegatedelegate.resellers.create
    Self sign-up (RA-05, RW-01)SELF_SIGNUPACCOUNT_CREATEDPENDINGThe inviting delegate, else noneauth.signup.reseller
    Import (ADM-06), settings.administerIMPORTPer row; existing customers ACTIVEAPPROVEDPer rowadmin.imports.create
  2. source and the creating user (createdBy, and createdByDelegateId for delegate creations) are set once and never change — a later claim, assignment or merge keeps them.

  3. Delegate creations carry a client-generated UUIDv7 id and an Idempotency-Key: a replay from the offline queue returns the original result and never creates a second record (SYN-05). The record shows the delegate's capture time (occurredAt) and the server's reception time.

  4. A back-office creation does not create a login: the owner claims the record by signing up with the same phone (AUTH-05).

  5. Every path runs the duplicate checks of CRM-03 and the field checks of CRM-01 before writing.

  6. Side effects: ResellerStatusChange (— → initial status), Interaction on the timeline, AuditLog CREATE; delegate creations and sign-ups raise account.pending to validators (live event); a back-office creation with a delegate notifies that delegate (reseller.assigned).

  7. The new record appears in the CRM list and map with its source, delegate and position within 5 seconds of reception (cahier §6.5 acceptance).

Acceptance criteria

  • A prospect created offline by delegate Karim and synced later appears in BO-09 with source Application délégué, Karim as creator and assigned delegate, its GPS position, and validation En attente.
  • Replaying the same creation (same id and Idempotency-Key) returns REPLAYED and leaves one record.
  • A back-office creation is APPROVED at once and has no login account.
  • An imported existing customer is ACTIVE, APPROVED, source IMPORT, with the customer type of its row.
  • source cannot be changed by admin.resellers.update (the field is ignored or refused).

Data — Reseller, ResellerAddress, ResellerStatusChange, ResellerAssignment, Interaction, ImportJob, IdempotencyRecord, AuditLog, OutboxEvent.

Interfaces

CRM-03 — Unique normalised main phone and duplicate checks

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2, §6.5, §12API · BO · DASRV-14 · DSH-03 · MDA-07

Statement — The system shall normalise every phone number to E.164, keep the main phone of a point of sale unique across all records and logins, and run a duplicate check on every creation path with a defined answer for each.

Rules

  1. Normalisation (one implementation in @hoco/shared, mirrored in hoco_core with the same test vectors): remove spaces, dots, dashes and parentheses; 00213… → +213…; 213 followed by 9 digits → +213…; 0 followed by 9 digits → +213 + the 9 digits; 9 digits starting with 5, 6 or 7 → +213…. Vectors: 0555 12 34 56, 05-55-12-34-56, 555123456, 00213555123456 and +213 555 12 34 56 all give +213555123456.

  2. Validity. A main phone must be an Algerian mobile — +213 then 9 digits starting with 5, 6 or 7 — because it receives the sign-up code by SMS; anything else is field code INVALID_PHONE. WhatsApp numbers follow the same rule.

  3. Uniqueness. The normalised main phone is unique among all Reseller records whatever their status, and must not be the login of a staff or delegate user. The database enforces it with a unique index, so two concurrent creations cannot both succeed; the loser gets 409 PHONE_ALREADY_EXISTS.

  4. Hints (non-blocking). A creation also looks for possible duplicates: the same WhatsApp number on another record (WHATSAPP), or a store name with trigram similarity of at least 0.80 within crm.duplicateRadiusMeters (50 m) of another record's position (NAME_NEARBY). Names are compared lower-cased, without accents and without the words boutique, magasin, store, shop.

  5. Answers per path:

    PathPre-checkOn creation with a used phoneHints
    Back-office (BO-12)admin.resellers.checkPhone while typing: available, the matching record (RESELLER_PHONE, blocking — with link « Ouvrir la fiche existante ») or USER_LOGIN (blocking, only the user type is revealed); an invalid number answers valid: false409 PHONE_ALREADY_EXISTS with details.existing409 DUPLICATE_SUSPECTED with details.hints; the user re-sends with ignoreDuplicateHints: true to confirm
    Delegate app (DA-14)delegate.resellers.checkPhone when online: for a record in scope, its name and a link to its sheet; outside the scope only « Ce numéro est déjà enregistré — contactez l'administration », no personal dataOnline: 409 PHONE_ALREADY_EXISTS. Offline: the queued creation comes back from sync.batch as REJECTED with PHONE_ALREADY_EXISTS; the sync centre (DA-27) keeps the local record and offers to correct the phone, open the existing record (if in scope) or discard; dependent queued items (photos, invitation) waitRecorded on the new record and shown to the validator — never blocks a field creation
    Self sign-up (RA, RW)—auth.otp.send answers 409 PHONE_ALREADY_EXISTS (canSignIn: true) when the phone has a login; a record without login is claimed, not duplicated (AUTH-05)Listed in the validation queue
    ImportDry run lists each row's normalised phone and statusRow error PHONE_ALREADY_EXISTS (or, with onDuplicate: UPDATE, the existing record is updated)Row warnings
  6. Changing a main phone (back-office edit, PHONE_CHANGE) runs the same uniqueness rule (409 PHONE_ALREADY_EXISTS).

  7. Check operations are read-only and limited to 60 calls per minute per user.

Acceptance criteria

  • The five formats of rule 1 are normalised to +213555123456 by the server and by the Flutter helper.
  • 021 23 45 67 (fixed line) as main phone returns field code INVALID_PHONE.
  • Creating a second record with 0555 12 34 56 in the back-office returns 409 PHONE_ALREADY_EXISTS with the existing REV- number; two simultaneous creations with that phone produce one record and one 409.
  • A back-office creation named Boutique El Wafa 30 m from El Wafa returns 409 DUPLICATE_SUSPECTED (NAME_NEARBY); re-sent with ignoreDuplicateHints: true it succeeds.
  • A delegate checking a phone of a record in another zone sees only the generic message, with no store name.
  • A prospect created offline with a used phone comes back REJECTED / PHONE_ALREADY_EXISTS and stays in the sync centre with the correction options.
  • auth.otp.send (SIGNUP) with the phone of a reseller who has a login returns 409 PHONE_ALREADY_EXISTS.

Data — Reseller (unique normalised phone), User (login phone), ImportJob; pg_trgm index on normalised store names.

Interfaces

CRM-04 — Reseller lifecycle with automatic and manual transitions

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2, §15.1API · BO · DASRV-14 · DSH-03 · MDA-04

Statement — The system shall move every point of sale through the ten statuses of cahier §15.1 — automatically from business events and manually by authorised people — and keep the full history of every change.

Rules

  1. The ten statuses form three families (Lifecycles › Reseller): qualification (NEW_PROSPECT, TO_CONTACT, INTERESTED, NOT_INTERESTED, TO_FOLLOW_UP) moved by people and two system rules; activation funnel (APP_INSTALLED, ACCOUNT_CREATED, FIRST_ORDER, ACTIVE) moved only by the system; commercial block (SUSPENDED) moved only by the back-office.
  2. Automatic transitions (table): planning a visit or a call task on a NEW_PROSPECT → TO_CONTACT; an invitation install → APP_INSTALLED; account created or claimed → ACCOUNT_CREATED; first order submitted → FIRST_ORDER; an order delivered → ACTIVE; no order confirmed during crm.inactivityDays → TO_FOLLOW_UP (daily scan at 02:00 Africa/Algiers, with a CALL task « Relance inactivité »); a new order confirmed for a reseller who already had a delivery → back to ACTIVE.
  3. Automatic events only move a reseller forward in the funnel and lift qualification statuses; they never lift SUSPENDED (the event is recorded on the timeline and the status stays). The only backward automatic moves are the first-order revert (the first order ends refused or cancelled) and the inactivity rule. Replayed events are idempotent.
  4. Manual transitions (table): among qualification statuses, by a delegate on a record in scope (DA-15, visit report) or by the back-office (crm.update, also in bulk); NOT_INTERESTED requires a reason (3–300 characters) and cancels the record's open tasks; TO_FOLLOW_UP requires a next action and a date and creates the follow-up task. A delegate may only move a record whose current status is a qualification status.
  5. Setting a funnel status by hand answers 409 RESELLER_TRANSITION_NOT_ALLOWED with details.reason = "AUTOMATIC_STATUS"; a target equal to the current status answers the same code.
  6. SUSPENDED (back-office, reason required: unpaid, fraud, closure…) blocks checkout, assisted orders and return requests (403 RESELLER_SUSPENDED) but not the owner's login; open orders are flagged, not cancelled. restore returns to the status before the suspension, then automation is re-evaluated.
  7. Every change writes a ResellerStatusChange (from, to, time, actor or system, trigger, reason), a timeline Interaction of type STATUS_CHANGE and, for manual changes, AuditLog STATUS_CHANGE.
  8. A delegate's offline status change replayed after an automatic move (the prospect installed the app meanwhile) is answered CONFLICT by sync.batch and shown in the sync centre.
ValueUI label (fr)Meaning
NEW_PROSPECTNouveau prospectJust created; no qualified contact yet.
TO_CONTACTÀ contacterPlanned for a call or a visit.
INTERESTEDIntéresséInterest confirmed during a visit or call.
APP_INSTALLEDApplication installéeInvitation attributed an install (automatic).
ACCOUNT_CREATEDCompte crééReseller account created and phone verified (automatic).
FIRST_ORDERPremière commandeFirst order submitted (automatic).
ACTIVERevendeur actifFirst order delivered, or a validated order within the activity window (automatic).
NOT_INTERESTEDNon intéresséDeclined; kept for later campaigns.
TO_FOLLOW_UPÀ relancerA follow-up is due (from a visit report or inactivity).
SUSPENDEDSuspenduCommercially suspended (unpaid, fraud, closure); ordering blocked.

Acceptance criteria

  • Adding prospect REV-000123 (NEW_PROSPECT) as a stop of a tour sets it to TO_CONTACT with actor Système.
  • A NOT_INTERESTED prospect whose invitation records an install becomes APP_INSTALLED; a SUSPENDED one stays SUSPENDED and the install appears on its timeline.
  • admin.resellers.setStatus to ACTIVE returns 409 RESELLER_TRANSITION_NOT_ALLOWED with AUTOMATIC_STATUS.
  • Setting NOT_INTERESTED without a reason returns 422 REASON_REQUIRED; with a reason, the record's open tasks become CANCELLED.
  • When the reseller's first order is refused and no other order is open, the status returns to its previous value and isFirstOrder moves to the next submitted order.
  • 61 days without a confirmed order turn an ACTIVE reseller into TO_FOLLOW_UP with a CALL task for its delegate; confirming a new order closes the task and restores ACTIVE.
  • A suspended store's owner can open invoices; cart.checkout returns 403 RESELLER_SUSPENDED.

Data — Reseller (status), ResellerStatusChange, Interaction, CrmTask, AuditLog, OutboxEvent.

Interfaces

CRM-05 — Assignment to a delegate and a zone

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2, §6.5API · BOSRV-14 · DSH-03

Statement — The system shall assign each point of sale to at most one delegate and one zone at a time, keep the link to the delegate who created it, and keep the history of every reassignment.

Rules

  1. A record has at most one current assignment (ResellerAssignment: delegate, zone, from, to, actor, reason); the zone defaults to the zone containing the store commune (GEO-02). createdByDelegateId is separate and immutable (attribution in reports, cahier §4.2 « lié au délégué qui l'a créé »).
  2. Defaults: field creation → the creating delegate; self sign-up → the attributed invitation's delegate, else unassigned (the validator assigns it); back-office creation → the delegate chosen, proposed when exactly one covers the commune; import → the row's delegate.
  3. admin.resellers.assign (crm.update) sets or clears the delegate. The delegate must be ACTIVE (422 DELEGATE_INACTIVE) and cover the commune (422 DELEGATE_ZONE_MISMATCH), unless allowOutOfZone: true with a reason — recorded as an OVERRIDE in the activity log.
  4. Effects of a reassignment: the new delegate gains the record in their scope at the next sync.delta; the previous one loses it unless the commune is still in their zones (ACL-03); open tasks of the previous delegate on that record move to the new one; planned tours are not changed (the back-office edits them).
  5. Bulk assignment (admin.resellers.bulk, action ASSIGN): 1–500 ids or a list filter matching at most 5 000 records (422 BULK_TOO_LARGE); each record follows the single-record guards, failures are skipped and reported; notifications are grouped per delegate (« 22 points de vente vous ont été attribués »).
  6. When a delegate is disabled, their records keep the assignment until reassigned and appear under the filter Délégué inactif.
  7. Notifications: reseller.assigned to the new delegate (push, FIELD category). Audit UPDATE per record.

Acceptance criteria

  • Assigning a reseller of Es Sénia to a delegate covering only Alger returns 422 DELEGATE_ZONE_MISMATCH; with allowOutOfZone: true and a reason it succeeds and the activity log shows an OVERRIDE.
  • After reassignment from Karim to Samir, Karim's open call task on that record now belongs to Samir; the 360° history shows both assignments with dates and actors.
  • createdByDelegateId stays Karim after the reassignment, and Karim keeps the first-order credit in reports.
  • A bulk assignment of a filter matching 6 000 records returns 422 BULK_TOO_LARGE; one of 300 records with 3 failing guards updates 297 and reports the 3.

Data — ResellerAssignment, Reseller (createdByDelegateId, zone), DelegateZone, CrmTask, AuditLog, Notification.

Interfaces

CRM-06 — Segmentation

ReleasePrioritySourceAppsOwner tasks
V1SHOULDcahier §4.2API · BOSRV-14 · DSH-03

Statement — The system should let the back-office group resellers into segments by potential, business type, brands sold, customer type, level and activity, and reuse those segments for visibility rules, promotion targeting and reporting.

Rules

  1. A Segment is STATIC (members added by hand or by the bulk action ADD_SEGMENTS) or DYNAMIC (members computed from criteria). Dynamic criteria: potential, business type, brands sold, customer type, wilaya or zone, status, activity and — in V2 — loyalty level.
  2. Activity is derived: ACTIVE = at least one order confirmed (not cancelled) during the last crm.inactivityDays days; INACTIVE = has ordered before, none in the window; NEVER_ORDERED = no confirmed order ever.
  3. Dynamic membership (ResellerSegment rows) is materialised at creation, refreshed nightly at 01:30 and on reseller, order and status events, so the price, visibility and promotion engines read a plain table.
  4. Segments are used by product visibility rules (CAT-05), promotion targeting (PRM-02), CRM filters and reports. In V1 they do not set prices — prices come from the customer type (PRC-02).
  5. Creating, editing and deleting segments needs crm.administer; reading them crm.view. A segment referenced by an active visibility rule or promotion cannot be deleted (409 SEGMENT_IN_USE); dynamic segments cannot receive manual members (400 VALIDATION_FAILED).

Acceptance criteria

  • A dynamic segment Demi-gros actifs Oran (customer type DEMI_GROS, wilaya 31, activity ACTIVE) lists exactly the matching resellers, and a reseller confirmed for an order today enters it without waiting for the night.
  • Adding a reseller by hand to a dynamic segment returns 400 VALIDATION_FAILED.
  • Deleting a segment used by an active promotion returns 409 SEGMENT_IN_USE.
  • BO-09 filters by segment and shows the segment chips on each row.

Data — Segment, ResellerSegment, Reseller, Order (activity).

Interfaces

Notes / open points — Loyalty levels are V2 (LOY-03); the criterion is hidden until then.

CRM-07 — Unified interaction timeline

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2API · BO · DASRV-14 · DSH-03 · MDA-04

Statement — The system shall show, for every point of sale, one timeline of its calls, visits, notes, messages, orders, payments, claims and status changes.

Rules

  1. Entry types (InteractionType): CALL, VISIT, NOTE, WHATSAPP, SMS, EMAIL, ORDER, PAYMENT, CLAIM, STATUS_CHANGE. Each entry has its time (occurredAt), actor (or system), client app, a French one-line summary built by the server, an optional body and a link to the source record (order, payment, visit, ticket, return, task).
  2. Written explicitly: notes and call logs by staff (admin.resellers.notes.add) and delegates (delegate.resellers.addNote, offline-capable with a client id). Written automatically from events: visit completed, order submitted and each order status change, payment confirmed, after-sales claim and ticket opened, return requested, lifecycle change, task completed.
  3. A call started from the point-of-sale sheet (DA-09) logs a CALL entry with the start time; the delegate may add the outcome. Notes are immutable; a correction is a new note.
  4. Visibility: staff with crm.view; delegates for records in scope, where PAYMENT entries show no amount (ACL-04); resellers never see the timeline.
  5. Sorted newest first; filter by type; cursor pagination in the delegate app, page pagination in the back-office.

Acceptance criteria

  • After a visit, an order and a payment, BO-11's timeline lists the three entries with links to the visit, the order CMD-… and the payment PAY-….
  • A note written offline by a delegate appears once after sync, with the delegate's capture time.
  • A delegate opening the timeline sees the payment entry without its amount.
  • Filtering on CALL shows only call logs.

Data — Interaction, with references to Visit, Order, Payment, Ticket, ReturnRequest, CrmTask.

Interfaces

CRM-08 — Follow-ups, tasks and next actions

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2, §6.8API · BO · DASRV-14 · DSH-03 · MDA-06 · DES-13

Statement — The system shall manage follow-ups (relances) as tasks with a type, an assignee, a due date and an outcome, create them from visit reports, inactivity and manual planning, and remind assignees when they are due.

Rules

  1. A CrmTask has a type (TaskType: CALL, VISIT, WHATSAPP, SEND_CATALOG, OTHER), a reseller, an assignee, a due date and time, a status (OPEN, DONE, CANCELLED), a source (MANUAL, VISIT_REPORT, INACTIVITY, SYSTEM) and an outcome. Overdue is derived (OPEN and due date passed), never stored.
  2. A visit report's next action creates or updates one task for that visit (FLD-10): PHONE_FOLLOW_UP → CALL, NEW_VISIT → VISIT, SEND_CATALOG → SEND_CATALOG, WHATSAPP_MESSAGE → WHATSAPP, NONE → no task; the follow-up date is required when the action is not NONE.
  3. The inactivity rule (CRM-04) creates a CALL task for the assigned delegate (commercial administrators when unassigned), due the next working day.
  4. Staff create tasks for any user about a reseller (admin.tasks.create); a delegate assignee must have the record in scope, otherwise the record is added to their scope for this task. A CALL or VISIT task on a NEW_PROSPECT moves it to TO_CONTACT.
  5. Transitions: OPEN → DONE with an outcome (adds a timeline entry of the task's type); OPEN → CANCELLED; DONE → OPEN with a reason; rescheduling and reassignment keep the status and notify the new assignee.
  6. Reminders: task.assigned on creation or reassignment (push for delegates, in-app for staff); task.due at 08:00 Africa/Algiers on the due date; overdue tasks are highlighted in DA-19 and BO-14.
  7. Delegates work their tasks offline (complete, reschedule) through the queue.

Acceptance criteria

  • A visit report with next action Relance téléphonique on 12 Nov creates a CALL task due 12 Nov for the delegate; editing the report to 14 Nov moves the same task instead of creating a second one.
  • On 12 Nov at 08:00 the delegate receives the task.due push.
  • Completing a task without an outcome returns 400 VALIDATION_FAILED; with an outcome a timeline entry appears.
  • A task due yesterday and still OPEN is listed as overdue in DA-19 and BO-14.

Data — CrmTask, VisitReport, Interaction, Notification.

Interfaces

CRM-09 — Account validation by an authorised user

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2, §6.5, §12API · BOSRV-14 · DSH-03 · DES-16

Statement — The system shall put every new self sign-up, claim and field-created point of sale into a validation queue where a user holding crm.validate approves it — confirming its customer type — or rejects it with a reason.

Rules

  1. The queue (BO-13, admin.resellers.validation.list) holds records with validation PENDING, of kind SELF_SIGNUP, FIELD_PROSPECT or CLAIM, oldest first, with what is missing (location, facadePhoto, customerType, businessType, commune), the duplicate hints of CRM-03, the declared and delegate-recorded customer types and any previous rejection. New entries arrive live (account.pending); a record waiting longer than crm.validationReminderHours triggers one reminder.
  2. Approve (admin.resellers.validate): validation APPROVED; account PENDING_VALIDATION → ACTIVE when a login exists (checkout unlocked); the customer type is confirmed or changed (AUTH-11); orders on hold for RESELLER_NOT_VALIDATED are re-checked and return to the confirmation queue (ORD-14).
  3. Approval guards: a field-created point of sale needs a GPS position (422 LOCATION_REQUIRED); the minimum data of §15.5 must be present (422 RESELLER_INCOMPLETE with details.missing); a customer type is needed (422 CUSTOMER_TYPE_REQUIRED).
  4. Reject: reason required (10–500 characters, shown to the reseller on RA-06); the commercial status is unchanged; disableAccount: true (duplicate or fraud) disables the login and revokes sessions; duplicateOfId links the record it duplicates. A rejected owner who corrects the data (profile, documents) sends the record back to PENDING, and the queue shows the previous rejection.
  5. A record not PENDING answers 409 VALIDATION_NOT_PENDING; an approved record is never re-validated.
  6. Notifications: approval → account.approved to the reseller (SMS and push, « Votre compte est validé », with the confirmed customer type) and prospect.approved to the creating delegate; rejection → account.rejected (push and in-app with the reason) and prospect.rejected. Audit VALIDATE or REJECT with the type before and after.

Acceptance criteria

  • A self sign-up appears in BO-13 within 2 seconds of sign-up with kind SELF_SIGNUP and its declared type.
  • Approving it unlocks checkout at once: cart.checkout from that reseller is accepted afterwards.
  • Approving a field prospect without GPS returns 422 LOCATION_REQUIRED.
  • Rejecting with a 5-character reason returns 400 VALIDATION_FAILED; with a proper reason, RA-06 shows it and the delegate receives prospect.rejected.
  • After the owner uploads the missing RC document, the record is back in the queue with the previous rejection visible.
  • Validating an already approved record returns 409 VALIDATION_NOT_PENDING.

Data — Reseller (validationStatus, customer type, validation decision), User (status), Order (pending reasons), AuditLog, Notification.

Interfaces

Notes / open points — Who validates and the target delay are D-30 (default: crm.validate holders, one working day).

CRM-10 — Store photos and documents with protected access

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2, §10, §15.5API · BO · DA · RASRV-14 · DSH-03 · MOB-11 · MRA-07 · MDA-07

Statement — The system shall attach photos (façade, interior, shelf) and documents (RC, NIF, NIS, AI, identity card, contract) to a point of sale, store them privately and serve them only to authorised users through short-lived links.

Rules

  1. Photos (ResellerPhoto, PhotoKind: FACADE, INTERIOR, SHELF, OTHER) and documents (ResellerDocument, DocumentType: COMMERCE_REGISTER, TAX_ID, STAT_ID, ARTICLE_ID, ID_CARD, CONTRACT, OTHER) reference a File uploaded through a pre-signed URL (SEC-04): photos are STORE_PHOTO (at most 5 MB), documents STORE_DOCUMENT (at most 10 MB, PDF, JPEG or PNG); a wrong purpose answers 422 FILE_PURPOSE_MISMATCH, an incomplete upload 422 FILE_NOT_READY.
  2. Who adds: delegates add photos to records in scope (offline queue, capture position and time kept — MOB-11); resellers add their own documents (profile.documents.add); staff with crm.update add both, and may replace a document (the previous version stays in the history).
  3. Files are private and served through signed URLs valid 5 minutes, issued only to: staff with crm.view, the delegate for records in scope (photos and documents they can see), and the reseller for their own files. The URL of an identity card or a legal document is issued only to staff with crm.view, and each issue is written to the activity log (AUD-01).
  4. EXIF metadata is removed from images at upload; the capture position is kept as structured data on the photo instead.
  5. Files are never hard-deleted by users: removing one hides it and keeps it for the retention period (D-11).

Acceptance criteria

  • A façade photo taken offline appears on the record after sync with its capture position and time, and without EXIF data in the stored file.
  • A delegate outside the record's scope requesting a document URL gets 404 NOT_FOUND; a reseller gets URLs for their own documents only.
  • A signed URL older than 5 minutes is refused by storage.
  • Opening an ID card from BO-11 adds an entry to the activity log naming the document and the user.
  • Uploading a 12 MB PDF as a document is refused with the file-size error.

Data — ResellerPhoto, ResellerDocument, File, AuditLog.

Interfaces

CRM-11 — CRM list and map with filters and export

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.1, §4.2, §9BOSRV-14 · DSH-03 · DES-16

Statement — The system shall list and map resellers and prospects in the back-office with combinable filters, search, sorting and export of the filtered list.

Rules

  1. Filters (BO-09, BO-10): status, validation status, account status (including sans compte), customer type (including none), source, zone, wilaya, commune, assigned delegate (including non attribué and délégué inactif), segment, potential, business type, activity, has GPS position, creation date range. Values of one filter are OR-ed, different filters AND-ed.
  2. Search q (at least 2 characters) on store name, owner name, REV- number and phone (normalised, partial digits accepted).
  3. Sort by store name, creation date, last order date, revenue over 12 months or status; page pagination (default 25, at most 200).
  4. Each row carries everything BO-09 shows without a second call (number, store, owner, phone, customer type, status pills, zone, delegate, segments, activity, last order, revenue, next task); balanceDue only with finance.view.
  5. The map shows the same filtered set within the visible bounding box; pins are coloured by status and clustered server-side above a threshold; clicking a pin opens the record's summary card.
  6. Export (admin.resellers.export, crm.export): an asynchronous job over the current filters in CSV or XLSX through the export centre (ANA-05); financial columns only with finance.view; journaled as EXPORT.
  7. Performance: the first page answers in under 1 second for 20 000 records.

Acceptance criteria

  • Filtering wilaya 31 + statut Intéressé + délégué Karim returns the matching records with the right total, and the map shows the same records.
  • Searching 555 12 34 finds the reseller whose phone is +213555123456.
  • A commercial administrator without finance.view sees no balance column and gets no balance column in the export.
  • The export of a filtered list of 1 200 records appears in the export centre and in the activity log.

Data — Reseller, ResellerAssignment, ResellerSegment, ExportJob.

Interfaces

CRM-12 — Reseller 360° page

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.2BOSRV-14 · DSH-03 · DES-16

Statement — The system shall give the back-office one page per point of sale that gathers its identity, status, assignment, customer type and every related record: orders, invoices, payments, visits, reports, tasks, interactions, documents, tickets and returns.

Rules

  1. Header (admin.resellers.get): number, store, owner, phone and WhatsApp with call/WhatsApp actions, status, validation and account status, customer type (declared and confirmed, with its history), source, creating and assigned delegates, zone, segments, potential, legal identifiers, map position, creation date, KPIs (first order date, orders and revenue over 12 months, average basket) and — with finance.view — balance and credit.
  2. Tabs, each loaded from the module's own list filtered by the reseller and shown only with the right permission: orders and returns (orders.view), invoices and payments (finance.view), visits and reports (field.view), tickets and after-sales claims (support.view), tasks, timeline, documents and photos, addresses, history (status, assignment, type changes). Each tab label carries its counter.
  3. allowedActions drives the buttons: edit, set status, assign, validate, add note, add document, create task, create an order on behalf (ORD-11).
  4. The header and counters load in under 1 second; tab contents load on demand.

Acceptance criteria

  • A commercial administrator without finance.view sees no invoice or payment tab and no balance; a finance manager sees them.
  • The orders tab counter equals the number of the reseller's non-draft orders.
  • « Créer une commande » opens BO-40 with the reseller pre-selected.

Data — Reseller and every model linked to it; counters computed per permission.

Interfaces

CRM-13 — Merge tool for historical duplicates

ReleasePrioritySourceAppsOwner tasks
V2COULDcahier §4.2BO— (not scheduled in the plan)

Statement — The system could, in V2, merge two records of the same point of sale into one, moving every related record to the survivor.

Rules

  1. The user (crm.administer) chooses a survivor and a duplicate; the survivor keeps its REV- number, main phone, customer type and login; the duplicate's orders, invoices, payments, visits, interactions, tasks, documents, photos, invitations and returns move to the survivor.
  2. A merge is refused when both records have a login with orders (manual review), or when one is SUSPENDED for fraud.
  3. The duplicate is closed with a link to the survivor and keeps its history; the merge is journaled with both ids and is not reversible.

Acceptance criteria

  • Merging REV-000245 into REV-000123 moves its 2 orders and 3 visits; searching REV-000245 opens a page pointing to REV-000123.
  • Merging two records that both have a login with orders is refused.

Data — Reseller and every linked model; AuditLog.

Interfaces

  • Screens: none dedicated
  • API: no dedicated operation

Notes / open points — V2. In V1 duplicates are prevented (CRM-03) and handled at validation (reject as duplicate with duplicateOfId).

Error codes

CodeHTTPRaised when
PHONE_ALREADY_EXISTS409The main phone belongs to another record or a login
DUPLICATE_SUSPECTED409WhatsApp or near-identical name nearby, not confirmed with ignoreDuplicateHints
RESELLER_TRANSITION_NOT_ALLOWED409Funnel status set by hand, or a move outside the lifecycle tables
VALIDATION_NOT_PENDING409Validating a record that is not PENDING
SEGMENT_IN_USE409Deleting a segment used by a visibility rule or promotion
REASON_REQUIRED422NOT_INTERESTED, suspension, out-of-zone assignment or type change without reason
DELEGATE_INACTIVE · DELEGATE_ZONE_MISMATCH422Assignment to a non-active delegate or outside their zones
LOCATION_REQUIRED · RESELLER_INCOMPLETE · CUSTOMER_TYPE_REQUIRED422Approval guards
BULK_TOO_LARGE422A bulk filter matches more than 5 000 records
FILE_NOT_READY · FILE_PURPOSE_MISMATCH422Attaching an incomplete upload or a file of the wrong purpose
RESELLER_SUSPENDED403Checkout, assisted order or return request for a commercially suspended store
Field codes in 400 VALIDATION_FAILED400INVALID_PHONE, COMMUNE_NOT_IN_WILAYA, import row codes PHONE_ALREADY_EXISTS, UNKNOWN_COMMUNE

On this page