HOCO B2B
SRSRequirements

Geography, zones & GPS

The official wilaya and commune reference, sales zones assigned to delegates, the GPS capture standard with distance computation, and optional geocoding.

This area provides the geography every other area relies on (cahier §4.1, §4.5, §6, §6.3, §6.4, §6.5, §8, §11): the official list of wilayas (provinces) and communes used by addresses, filters and reports; the sales zones that group them and are covered by delegates; the single format in which GPS positions are captured and the way distances are computed; and, if HOCO wants it, address geocoding through the maps provider. The server's geo module owns Wilaya, Commune, Zone and ZoneArea and the distance helpers; the delegate app (DA) captures positions, the back-office (BO) edits zones, and every client reads the reference lists.

IDRequirementReleasePrioritySourceApps
GEO-01Official wilaya and commune reference (FR/AR names) used by addresses, zones and filtersV1MUST§4.1, §6.5API · BO · RA · RW · DA
GEO-02Zones grouping wilayas and communes, assigned to delegatesV1MUST§4.5, §6API · BO · DA
GEO-03GPS capture standard (lat/lng, accuracy, timestamp) and distance computationV1MUST§6.3, §6.4, §8API · DA
GEO-04Address geocoding / reverse geocoding through the maps providerV1COULD§11API · DA · BO

Settings used in this area

SettingDefaultMeaning
field.gpsToleranceMeters150Maximum distance between the check-in fix and the point of sale (FLD-07, D-06).
field.gpsMaxAccuracyMeters100A fix less precise than this is not used for a check-in.

The tolerance and accuracy values are owned by field operations; they are listed here because GEO-03 defines how they are applied.

Requirements

GEO-01 — Wilaya and commune reference

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.1, §6.5API · BO · RA · RW · DASRV-02 · SRV-10

Statement — The system shall provide the official list of wilayas and communes, with French and Arabic names, and use it for every address, zone and geographic filter.

Rules

  1. The reference contains every wilaya and commune of the official administrative division in force when the seed is built — 58 wilayas and 1 541 communes in the 2019 division, to be re-checked against the latest official list before seeding. A wilaya has a code (two-digit string "01"–"58", the official number) and nameFr/nameAr; a commune has a UUID id, its official code, its wilayaCode, nameFr/nameAr and a centroid (used for maps and to resolve a guest's support region from the device position — SUP-05).
  2. The reference is read-only through the API — reference.wilayas and reference.communes, public, cacheable with ETag for one day — and changes only through a seed migration (SRV-02); it is not editable in the back-office.
  3. Every address or location field (store address, delivery addresses, zones, warehouses) references a commune id and its wilaya; free-text wilaya or commune values are not accepted. A commune that does not belong to the given wilaya returns 400 VALIDATION_FAILED with field code COMMUNE_NOT_IN_WILAYA.
  4. Names follow Accept-Language (French in V1, Arabic prepared for RTL). Wilayas are listed by code (« 16 — Alger »), communes alphabetically; pickers search without accents or case (bejaia finds Béjaïa).
  5. The delegate app stores the whole list locally and refreshes it with If-None-Match, so the new-reseller form works offline (FLD-11).
  6. Filters across CRM, orders, dashboards and reports use the same codes and ids, so a wilaya filter means the same thing everywhere.
  7. Imports (ADM-06) match a wilaya by code or name and a commune by name within that wilaya, ignoring accents and case; an unmatched value is a row error UNKNOWN_COMMUNE.

Acceptance criteria

  • reference.wilayas returns exactly the seeded official list (58 entries in the 2019 division), each with code and both names.
  • A second request with the returned ETag in If-None-Match answers 304 Not Modified.
  • Creating an address with wilaya 16 and a commune of wilaya 31 returns 400 VALIDATION_FAILED with COMMUNE_NOT_IN_WILAYA.
  • With Accept-Language: ar, the names are returned in Arabic.
  • Typing bejaia in the wilaya picker of DA-14 finds 06 — Béjaïa, with the phone in airplane mode.

Data — Wilaya, Commune, ResellerAddress, ZoneArea, Warehouse.

Interfaces

Notes / open points — Algeria's administrative map has changed before (2019); the seed is versioned so a future reform is a data migration, not a code change. The list to use is confirmed with HOCO before go-live (see the decision register).

GEO-02 — Zones grouping wilayas and communes

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.5, §6API · BO · DASRV-10 · DSH-08 · MDA-01

Statement — The system shall let the back-office define sales zones made of whole wilayas or individual communes, assign delegates to them, and derive each point of sale's zone from its commune.

Rules

  1. A zone (Zone: code, name, description, map colour, active flag) is made of areas (ZoneArea), each either a whole wilaya or a single commune. A commune belongs to at most one active zone: saving a zone that would overlap another answers 409 ZONE_OVERLAP with the conflicting communes in details.
  2. A point of sale's zone defaults to the zone containing its store commune; it is recomputed when the address changes and, by a background job, when zones change. An assignment (CRM-05) may set another zone explicitly, with a reason. A commune outside every zone gives « Hors zone », available as a CRM filter.
  3. Delegates cover zones through DelegateZone (several delegates per zone, several zones per delegate). When exactly one delegate covers a commune, the back-office creation form proposes them by default and the validation queue suggests them for unassigned sign-ups (CRM-05); nothing is assigned silently.
  4. Zones are edited in BO-19 (list and map, communes coloured by zone) through admin.zones.crud, which requires field.administer. A zone that still has delegates or points of sale cannot be deleted (409 ZONE_IN_USE); it is deactivated instead.
  5. Changing a zone's communes recomputes the zone of the affected points of sale but never changes their assigned delegate; BO-19 shows how many points of sale now have a delegate who does not cover their zone, with a link to reassign them in bulk.
  6. The delegate app reads its coverage from delegate.profile (zones, wilayas, communes) and delegate.zone (counts of prospects, resellers and active resellers per commune) for DA-25.
  7. Zones feed the delegate scope (ACL-03), the CRM and dashboard filters, and objectives per delegate.

Acceptance criteria

  • Zone Oran Ouest made of wilaya 31 cannot be saved alongside zone Oran Centre containing the commune of Oran: the second save returns 409 ZONE_OVERLAP naming that commune.
  • A point of sale created in Es Sénia gets the zone that contains Es Sénia, with no input from the user.
  • Moving Es Sénia to another zone updates the zone of its points of sale within 5 minutes and leaves their delegate unchanged.
  • With Karim the only delegate covering Es Sénia, BO-12 proposes Karim for a new point of sale there, and a self sign-up from Es Sénia without invitation stays unassigned with Karim suggested in BO-13.
  • Deleting a zone that still has a delegate returns 409 ZONE_IN_USE.
  • DA-25 shows Karim's communes with their counts of prospects and resellers.

Data — Zone, ZoneArea, DelegateZone, DelegateProfile, Reseller, Commune, Wilaya.

Interfaces

GEO-03 — GPS capture standard and distance computation

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §6.3, §6.4, §8API · DASRV-10 · MOB-10 · MDA-03

Statement — The system shall capture every position in one standard format with its accuracy and time, validate it, and compute distances the same way on the server and in the apps.

Rules

  1. Every position is { lat, lng, accuracy, capturedAt }: WGS84 decimal degrees with at least 6 decimals, the horizontal accuracy in metres reported by the device, and the device's UTC time of the fix; Android also sends isMocked (mock-location flag).
  2. Validation: lat between 18.9 and 37.2 and lng between −8.7 and 12.0 (Algeria with a margin), otherwise field code LOCATION_OUT_OF_BOUNDS; accuracy greater than 0; capturedAt at most 5 minutes in the future and, for a check-in, at most 2 minutes older than the action's occurredAt (field code LOCATION_STALE — the app takes a fresh fix).
  3. Positions are captured at tour start, at visit check-in, on visit photos, on a new point of sale, in location pings during a tour (FLD-13, FLD-15) and, optionally, at reseller self sign-up.
  4. Distance between two points is the haversine formula with an Earth radius of 6 371 008.8 m, rounded to the metre. The same function exists in @hoco/shared (TypeScript) and hoco_maps (Dart) and both pass the same test vectors: (35.6969, −0.6331) → (35.6980, −0.6320) = 158 m; (35.6969, −0.6331) → (35.7000, −0.6300) = 444 m; (35.6969, −0.6331) → (36.7538, 3.0588) = 351.4 km.
  5. A check-in is within tolerance when the distance to the point of sale is at most field.gpsToleranceMeters (150 m), the accuracy is at most field.gpsMaxAccuracyMeters (100 m) and the fix is not mocked. Otherwise the API answers 422 GPS_OUT_OF_TOLERANCE (with details.distanceMeters, toleranceMeters, accuracyMeters), 422 GPS_ACCURACY_TOO_LOW (the app asks the delegate to wait for a better fix) or 422 GPS_MOCK_LOCATION.
  6. Path length (tour kilometres, FLD-12) is the sum of haversine distances between consecutive pings whose accuracy is at most 100 m, skipping any ping that implies a speed above 150 km/h from the previous kept ping; it is reported in kilometres with one decimal.
  7. Lists show straight-line distance (« à 1,2 km »); route distance and ETA come from the maps provider when online (INT-01) and are labelled as estimates.
  8. Positions are personal data: pings follow the retention of FLD-15 (90 days by default); check-in positions stay with the visit as proof.

Acceptance criteria

  • The server and the Dart helper both return 158 m and 444 m for the vectors above.
  • A check-in 158 m from the point of sale with accuracy 20 m returns 422 GPS_OUT_OF_TOLERANCE with distanceMeters: 158; at 140 m it is accepted.
  • A check-in with accuracy 180 m returns 422 GPS_ACCURACY_TOO_LOW; a mocked fix returns 422 GPS_MOCK_LOCATION.
  • A position at latitude 48.85 (Paris) returns 400 VALIDATION_FAILED with LOCATION_OUT_OF_BOUNDS.
  • An offline check-in whose fix is 10 minutes older than the action returns field code LOCATION_STALE.
  • A trail with one ping 3 km away from its neighbours 30 seconds apart is measured without that ping.

Data — Visit (check-in position, distance), VisitPhoto, LocationPing, Reseller (position), Tour (start and end positions).

Interfaces

GEO-04 — Address geocoding and reverse geocoding

ReleasePrioritySourceAppsOwner tasks
V1COULDcahier §11API · DA · BO— (not scheduled in the plan)

Statement — The system may suggest addresses from GPS positions and positions from typed addresses through the maps provider, without ever replacing the official wilaya and commune reference.

Rules

  1. Reverse geocoding: after a GPS capture in the new-reseller form (DA-14) or an address form, the app may pre-fill the address line and propose a commune from the provider's answer; the wilaya and commune are always confirmed by the user from the official list (GEO-01).
  2. Forward geocoding in the back-office: an address typed in the reseller form (BO-12) proposes map pins; the user drags the pin to the shop front. The stored position records its source — GPS_FIELD, GEOCODED or MANUAL_PIN — so a geocoded pin is never mistaken for a position captured on site.
  3. Calls go through the server so provider keys are not shipped in the apps (only the map display key is), results are cached 30 days per query or rounded coordinate, and each user is limited to 60 calls per minute; usage stays within the quotas HOCO validated (INT-07).
  4. When the provider is down or the quota is exhausted, the forms work without suggestions.
  5. The V1 API catalogue has no geocoding operation: if HOCO confirms this COULD, the contract gains a reverse-geocode and a geocode operation before any client work starts.

Acceptance criteria

  • With geocoding enabled, capturing a position in DA-14 pre-fills the address line and proposes the commune, which the delegate can change.
  • A pin placed from a typed address is saved with source GEOCODED and appears with a distinct marker on the CRM map.
  • With the provider unreachable, DA-14 and BO-12 still save a point of sale.

Data — Reseller (position and its source), ResellerAddress (position).

Interfaces

  • Screens: none dedicated
  • API: no dedicated operation

Notes / open points — Not scheduled in the plan (COULD). The provider choice (Google Maps or Mapbox) and its costs are open (see the decision register).

Error codes

CodeHTTPRaised when
ZONE_OVERLAP409A commune would belong to two active zones
ZONE_IN_USE409Deleting a zone that still has delegates or points of sale
GPS_OUT_OF_TOLERANCE422Check-in farther than the tolerance
GPS_ACCURACY_TOO_LOW422Fix less precise than field.gpsMaxAccuracyMeters
GPS_MOCK_LOCATION422The device reports a mocked position
Field codes in 400 VALIDATION_FAILED400COMMUNE_NOT_IN_WILAYA, LOCATION_OUT_OF_BOUNDS, LOCATION_STALE, import row code UNKNOWN_COMMUNE

On this page