Reference → Locations — Reference

Locations — Reference

The Locations surface is the physical-location plane for an SGEN site — the source of truth for a multi-location business's storefronts, branches, clinics, dealerships, restaurants, satellite offices, or service areas. It owns the location records themselves (each one a named place with an address, map coordinates, opening hours, and contact details), the per-location service offerings and staff rosters, the map-rendering configuration that lets a "find us" page draw the locations on a tile map, and the structured data feed that hands the location set to search engines for local-business rich results.

This page is a reference for platform engineers and integrators who need to understand the surface before extending it, scripting against it, or migrating a legacy multi-location Google Business Profile set into SGEN. Customer-facing walkthroughs for adding a location and editing opening hours live in the customer docs set; this page describes the shape of the surface, not the editorial flow.


Overview

Locations live under the Locations module in SG-Admin. The module renders four primary views — the locations list, the location create / edit form, the per-location staff and services panel, and the map view (locations laid out on an interactive tile map) — and exposes a write surface for create, update, duplicate, bulk import, soft delete, restore, permanent delete, regenerate structured-data feed, and CSV export.

The module is paired by convention: one half renders the views and prepares the data (location list, detail view, staff roster, services list, map data, feed payload), the other half handles writes and returns AJAX responses (create, update, hours change, staff assignment, delete). The pairing matches the broader SG-Admin convention used across Pages, Posts, Forms, Media, Users, and Events.

Where it lives in SG-Admin:

  • Sidebar: SG-Admin → Locations
  • URL prefix: /sg-admin/locations/
  • View templates: application/views/Admin/Locations/
A location is three things stored together: the anchor record (name, address, map coordinates, contact phone, contact email, status), the operating schedule (opening hours per day of the week plus holiday overrides), and the per-location attachments (the service offerings available at that location, the staff assigned to it, the photo gallery, the location-specific landing page, the structured-data overrides). The three are edited in the same builder UI but live as distinct shapes on the record.

Locations are paired with the Pages module: every active location optionally generates a location-specific landing page at /locations/ that renders the anchor record, the hours, the staff, the services, and the gallery in a templated layout. The landing page is auto-created on location create but can be edited as a normal page record afterwards.

┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Locations [+ New location]│├──────────────────────────────────────────────────────────────────────┤│ Name City Phone Staff Status ││ ───────────────────── ───────────── ───────────── ────── ─────││ Main store Manila +63 2 8000… 12 active││ Quezon City branch Quezon City +63 2 8500… 9 active││ Makati branch Makati +63 2 8888… 7 active││ Cebu satellite Cebu City +63 32 4100… 4 active││ Pop-up — May launch Various — 3 draft ││ Closed branch (Q1) Pasay — 0 trash ││ ││ [⋯ Edit] [⋯ Hours] [⋯ Staff] [⋯ Services] [⋯ Map] [⋯ Delete] │└──────────────────────────────────────────────────────────────────────┘

Actions

The Locations surface exposes the following operations. Each is described by what it does to the data and to the public-facing rendering, not by its internal method name.

List and search locations

Returns the location records on the site, paginated, with name, city, phone, staff count, and status columns. Supports column sort, free-text filter across name and address, and status filter (active / draft / trash). Map view is a parallel layout of the same records, pinned on a tile map for visual confirmation of geographic spread.

Create location

Opens an empty location form. Required fields at minimum: name, address, status. Optional fields cover map coordinates (auto-geocoded from address on save if left blank), contact phone, contact email, primary service category, cover image, gallery, region grouping, time zone, and structured-data overrides. On submit, the surface validates uniqueness of the slug, geocodes the address if coordinates are blank, persists the record, generates the location landing page at /locations/, and returns the new identifier.

Edit location

Loads an existing location into the same form shape used for create, pre-populated. Submit replaces the stored anchor record. Address edits trigger a re-geocode if coordinates are still derived from the address; manually set coordinates are preserved across address edits.

Slug changes are reversible inside a 30-day window. The prior slug is held in a redirect-style alias table so the old /locations/ URL resolves to the new page with a 301. After 30 days the prior slug is released.

Edit opening hours

A focused editor scoped to the per-day schedule. Each day of the week carries open/closed flag plus open-time and close-time windows. Multi-window days (open 09:00–13:00 and 17:00–21:00) are supported. Holiday overrides are dated entries that replace the standard schedule for a specific date — used for public holidays, special events, and one-off closures.

Manage staff

Lists the operators assigned to the location, their role at that location (manager, lead, specialist, support), their working schedule (subset of the location's opening hours), and their public-facing display. Add, edit, reorder, and remove operations are available. Staff records reference user IDs from the Users module, so renaming an operator updates every location they are assigned to.

Manage services

Lists the service offerings available at the location — service name, description, duration, price, available-at-this-location flag, and per-service photo. Services can be defined globally (under Settings → Services) and then enabled per-location, or defined location-specific. Used by the "what we offer here" section of the location landing page and by any booking flow that filters services by location.

Bulk import locations

Accepts a CSV of locations and creates the records in one operation. Each row maps to the location form fields. Validation runs per row: slug collisions report as "skip", address geocoding failures report as "warn — coordinates missing", missing required fields report as "skip — missing field". The output is a per-row success / skip / warn summary.

Soft delete

Marks the location as trashed. Trashed locations disappear from the public locations directory, the map view, the structured-data feed, and the staff-assignment surface. The location landing page returns a 410 response. Staff and service attachments remain in place — they re-emerge if the location is restored.

Restore

Returns a trashed location to its prior status. The landing page resumes responding, the location reappears on public surfaces, and attachments rebind without manual reconfiguration.

Permanent delete

Hard-removes the location record. Available only after a soft delete. The landing page is removed from the Pages module entirely (not trashed). Staff assignments at the location dissolve; the user records themselves are untouched. The 301 redirect from the old landing-page URL drops; subsequent requests return a 410.

Regenerate structured-data feed

The Locations module publishes a structured-data feed (LocalBusiness per schema.org) that search engines crawl for local-business rich results. The regenerate action forces a fresh feed build. Under normal operation the feed regenerates on every location write, but a force-regenerate is useful after bulk imports or after a structured-data override is changed site-wide.

Export locations CSV

Produces a CSV of locations for an offline pass — backup, audit, or migration. Columns: name, slug, address, latitude, longitude, phone, email, opening hours (encoded per-day), services, staff count, status. Reciprocal to the bulk-import format — a CSV exported here can be re-imported elsewhere.

Set primary location

A single-write action that flags one location as primary. The primary location surfaces as the default in single-location contexts (single contact phone in the footer, single address in the page metadata, single map pin on a "contact us" page that doesn't itemize all locations).


Data model

A location record carries the following fields. Field names below are the conceptual shape — the on-disk column names match closely but are not contractually stable across releases.

FieldTypeNotes
idintegerPrimary key. Stable across edits.
namestringDisplay name. Unique within the active set.
slugstringURL segment under /locations/. Unique across all locations.
address_line_1stringRequired.
address_line_2stringOptional (suite, unit, floor).
citystringRequired.
regionstringState, province, or admin region.
postal_codestringOptional.
countrystringISO 3166-1 alpha-2 code.
latitudedecimalAuto-geocoded from address on save if blank.
longitudedecimalSame.
contact_phonestringOptional. E.164 format preferred.
contact_emailstringOptional.
timezonestringIANA time zone identifier. Defaults to site primary.
region_groupstringOptional. Groups locations into clusters for filtered map views.
primary_flagbooleanExactly one record carries true at any time.
cover_image_idintegerReferences a Media record.
galleryJSONArray of Media IDs.
landing_page_idintegerForeign key to the auto-generated page.
structured_data_overridesJSONPer-location schema.org overrides.
statusenumdraft, active, trash.
created_attimestampImmutable.
updated_attimestampTouched on save.
An opening-hours record carries:
FieldTypeNotes
idintegerPrimary key.
location_idintegerForeign key to the location.
day_of_weekenummon through sun.
is_openbooleanTrue for open, false for closed.
windowsJSONArray of {open, close} time pairs (multi-window days).
A holiday-override record carries:
FieldTypeNotes
idintegerPrimary key.
location_idintegerForeign key to the location.
effective_datedateThe date the override applies.
is_openbooleanTrue for special open, false for special closed.
windowsJSONReplacement time pairs for that date.
labelstringOptional. Renders on the public hours block ("Christmas day", "Anniversary").
A staff-assignment record carries:
FieldTypeNotes
idintegerPrimary key.
location_idintegerForeign key to the location.
user_idintegerForeign key to the user record.
role_at_locationstringManager, lead, specialist, support.
public_displaybooleanTrue if the staff member appears on the public landing page.
sort_orderintegerDisplay order on the landing page.
Soft-delete semantics. status = trash is the soft-deleted state. Trashed locations retain their hours, holiday overrides, staff assignments, and gallery. Permanent delete cascades to the hours records and the staff assignments (the user records themselves are untouched).

Slug uniqueness: unique across all locations. Collisions with Pages or Blogs slugs surface as warnings at save time — the locations directory at /locations is reserved as a Locations-owned prefix.

Primary flag. Exactly one location carries primary_flag = true at any time. Setting a new primary clears the prior one in the same transaction. If the primary is soft-deleted, the next active location by creation order is promoted automatically; the auto-promotion emits an Activity Log entry.

Structured-data overrides. The base schema.org LocalBusiness payload is generated from the anchor record + hours. Per-location overrides let an operator specialize the payload — for example, marking a specific location as a Restaurant subtype instead of the site-wide default LocalBusiness. Overrides are JSON-merged, not replaced wholesale.

LOCATION RECORD RELATED RECORDS┌─────────────────────────┐│ id │ ──────► OPENING_HOURS (one row per day)│ name, slug │ ──────► HOLIDAY_OVERRIDES (date-specific)│ address, lat/lng │ ──────► STAFF_ASSIGNMENTS (per user)│ contact phone / email │ ──────► SERVICES_AVAILABLE (join)│ region_group │ ──────► PAGE (auto-generated landing)│ primary_flag │ ──────► MEDIA (cover + gallery)│ structured_data_over... ││ status, timestamps │ ◄────── STRUCTURED-DATA FEED└─────────────────────────┘ (regenerated on write)on permanent delete:├─ OPENING_HOURS cascade-deleted├─ HOLIDAY_OVERRIDES cascade-deleted├─ STAFF_ASSIGNMENTS dissolved (users untouched)├─ SERVICES_AVAILABLE dissolved (services untouched)└─ LANDING_PAGE removed from Pages moduleon primary_flag delete:└─ next active location by creation orderauto-promoted to primary

Permissions

Access to the Locations surface is gated at two layers.

Layer 1 — admin gate. Every action under SG-Admin passes through the standard admin access check at request entry. Unauthenticated requests never reach the Locations surface. Public surfaces (the locations directory, the per-location landing page, the structured-data feed) are served without authentication but only render status = active records.

Layer 2 — per-action capability. Within SG-Admin, each Locations action checks a capability on the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and a Location Manager role is a common custom addition on multi-location businesses where local staff manage their own location's hours and services. The capability map is:

CapabilityAdministratorEditorLocation ManagerViewer
List locations
Create location
Edit location (any)
Edit location (own)
Edit opening hours (own)
Manage staff (own)
Manage services (own)
Bulk import
Soft delete
Restore
Permanent delete
Regenerate feed
Export CSV
Set primary
The Location Manager role is location-scoped — the "own" qualifier means the operator can edit only the locations on which they hold the manager assignment. The platform-wide Administrator role implicitly holds every per-location capability on every location.

Self-protection rules. The primary location cannot be soft-deleted without first promoting another location to primary. The last active location cannot be soft-deleted — the surface returns a structured rejection prompting the operator to add another location first. Address edits that would move a location outside its declared country require an explicit confirmation token to prevent accidental cross-border edits.

Audit. Every write — create, edit, hours change, staff assignment, service change, bulk import, soft delete, restore, permanent delete, feed regeneration, set-primary — emits an Activity Log entry. The log records the acting operator, the location, and the change shape (field-level diff for edits, batch summary for bulk imports, transfer pair for primary swaps). Geocoding lookups against the external geocoder are not in the Activity Log — they are infrastructural and logged separately for cost tracking.

REQUEST → /sg-admin/locations/...│▼┌────────────────────────┐│ Admin gate │ unauth → reject└──────────┬─────────────┘│ authed▼┌────────────────────────┐│ Role capability check │ role lacks cap → check per-location│ (platform-wide) │└──────────┬─────────────┘│▼┌────────────────────────┐│ Per-location manager │ no manager grant + no role →│ check (scoped writes) │ reject└──────────┬─────────────┘│ allowed▼┌────────────────────────┐│ Self-protection rules │ primary-without-promote /│ │ last-active / cross-border →│ │ reject└──────────┬─────────────┘│ passes▼write executes│▼Activity Log entry│▼Geocoding lookups bypassActivity Log; they log toa separate infrastructureledger for cost tracking.

Related references

  • Pages — Reference. Each active location auto-generates a landing page at /locations/. Slug collisions are validated at save time.
  • Users — Reference. Staff assignments reference user IDs. Per-action capability checks resolve against Users.
  • Events — Reference. Events can reference a venue, and venues can reference a location for the underlying address record.
  • Forms — Reference. Contact forms on a location landing page can route submissions to that location's contact email by default.
  • Media — Reference. Cover images, gallery photos, and staff portraits reference Media records.
  • Settings — Reference. Default country, default time zone, global services catalogue, structured-data site-wide payload, and geocoder configuration live in Settings.
  • SEO — Reference. Per-location structured-data overrides feed the SEO module's overall structured-data composition.
  • Email — Reference. Per-location contact emails route through the Email module's default deliverability path.
For the corresponding customer-facing walkthrough — adding a second storefront, editing holiday hours, assigning staff to a branch — see the Locations section of the customer docs at /docs/locations.

Source-walk verified (2026-05-28)

List — /sg-admin/locations

  • H1: "Locations" · subtitle: "Manage all locations and details."
  • Columns: Location · Address · Author · Created At
  • Data handler: POST /Admin_Locations_Actions/do_table

Create — /sg-admin/locations/create

  • H1: "Create Location"
  • Form action: POST /Admin_Locations_Actions/do_location (48 fields)
  • Top-level: title
  • _metas[phone_number] · _metas[street_address] · _metas[city] · _metas[state] · _metas[zip_code] · _metas[country] · _metas[place_id] (Google Place ID) · _metas[gbp_link] (Google Business Profile link) · _metas[date_opened]
  • Hours of operation per day: _metas[hours_of_operation][][open_hours|time_from|time_to] — 21 fields (7 days × 3 attrs)

Settings — /sg-admin/locations/settings

  • H1: "Location Settings" · subtitle: "Selector behaviour and GMB attributes."
  • Form action: POST /Admin_Locations_Actions/update_settings (15 fields)
  • Picker config: locations_picker_style · locations_picker_display · locations_picker_card_detail · locations_picker_auto_prompt · locations_picker_auto_prompt_scope · locations_picker_required · locations_picker_remember_days
  • GMB attributes JSON: gmb_attributes_json (free-text JSON for Google Business Profile attribute overrides)
On this page