CRM · Customer create & edit
Tabbed customer record: general data (identity, contact, address, consents, tags, manual campaigns) plus receipts and loyalty tabs once the customer exists.
| Area | CRM |
| Routes | crm.users.new · crm.users.edit |
| Centre modules | crm |
| Staff levels | root · admin · manager · crmmanager · crmoperator · weboperator |
| Models | campaign · channel · extra · user · user-type |
| UI label | crm.new-user / users.general_data |
| Coverage | 5 use cases · 3 happy · 9 failure |
Missing translations
These keys are used on this screen but do not exist in
translations.json, so users see the raw key:
access-to-account-types.clientaccess-to-account-types.employee-outsourceaccess-to-account-types.employee-shop
How it works
Tabs are computed in form-user.js: ‘general’ always, then ‘receipts’ and ‘loyalty’ only when model.id exists AND the centre has the tickets or loyalty-cards module. Required fields are partly centre-driven — auth.center.userRequired injects extra presence validations at runtime, so the same form has different mandatory fields per centre. name and cp are only required on create (if: _this.isNew); for existing records the controller instead shows a soft warning listing the missing recommended fields. Saving is two-stage: saveUser validates, then _hasAcceptedAllNotifications() opens a consent modal when any of mail / partner-mail / app notification consents is unchecked, and only patchUser actually persists. Email is also validated live on change via emailRegexValidator. A Pusher channel keyed on the user id streams in a captured signature URL, and willDestroyElement disconnects it. Existing customers show an identity QR code encoding user_id:<id> (download button hidden) next to the captured signature, so a desk device can scan a customer straight into their record.
Use cases
Why someone opens this (5)
- Registering a walk-in customer at the information desk, including GDPR consent.
- Updating contact details or consents on request.
- Attaching user tags or manual (non-segmented) campaigns to a specific customer.
- Recording single-parent / large-family status for benefit schemes.
- Capturing a handwritten signature from a tablet and seeing it appear live on the record.
Path map
flowchart LR F(["Customer create and edit"]):::feat F --> H1["New customer saved with full consent"]:::ok F --> H2["Existing customer edited with partial consent"]:::ok F --> H3["Signature captured remotely"]:::ok F -.-> X1["Missing mandatory fields"]:::bad F -.-> X2["Invalid email format"]:::bad F -.-> X3["GDPR not accepted"]:::bad F -.-> X4["Centre-specific required field left blank"]:::bad F -.-> X5["Consent modal declined"]:::bad F -.-> X6["API rejection on save"]:::bad F -.-> X7["External sync failure"]:::bad F -.-> X8["Registration email resend fails"]:::bad F -.-> X9["Raw translation key on screen"]:::bad classDef feat fill:#157068,stroke:#157068,color:#ffffff,font-weight:bold classDef ok fill:#e3f1ea,stroke:#0d6b42,color:#0d4a2e classDef bad fill:#f7e6e2,stroke:#9a2d1c,color:#6e1f13
Happy paths
New customer saved with full consent
- Operator opens CRM → new customer and fills registration date, email, name, postal code and the centre’s extra required fields.
- GDPR (
eula) and the notification consents are ticked.saveUservalidates the model successfully and_hasAcceptedAllNotifications()returns true.patchUsersaves, tracksCRM_USER_SAVED, showscrm.users.user-savedand scrolls to the top.
Existing customer edited with partial consent
- Operator edits a record and leaves third-party notifications unchecked.
- The consent modal lists the unchecked statements.
- Operator confirms;
patchUserruns and the record is saved as-is.
Signature captured remotely
- Operator opens an existing customer;
pusher.connectToChannel(user.id)subscribes.- The customer signs on a paired device.
- The pushed URL is written to
model.signatureUrland the signature renders without a reload.
Failure paths
Missing mandatory fields
Cause — Email, registration date or channels blank; name/postal code blank on create.
What happens —
user.validate()fails,parseErrorsmaps the messages to fields (error.user.email.required,error.user.registration-date.required,error.user.name.required,error.user.cp.required,error.channels.required) and the page scrolls to the top.
Invalid email format
Cause — Address fails the email validator.
What happens —
form-user.not-valid.emailis set on change and again by the model’s
GDPR not accepted
Cause —
eulafalse.What happens — Custom validation fails with
error.user.gdpr.
Centre-specific required field left blank
Cause — A field listed in
center.userRequired(e.g.commerce) is empty.What happens — A presence rule is generated at runtime with the generic
requiredmessage; note onlycommercehas a translated attribute label, so other dynamic fields show an untranslated attribute name.
Consent modal declined
Cause — User closes the notifications modal instead of confirming.
What happens —
onCloseNotificationsModalonly closes it — the record stays unsaved, which can read as a silent no-op.
API rejection on save
Cause — Server-side conflict (e.g. duplicate email) or validation.
What happens — The catch block populates
infoErrorsfromparsedErrors.dataorerrors.errors[].detail, setscrm.users.user-save-errorsas the banner and scrolls up.
External sync failure
Cause — Downstream integration (Juguettos Fusion) rejects the create/update.
What happens —
user.fusion-error/user.fusion-error-updateare surfaced; the local record may already be saved, so the two systems can diverge.
Registration email resend fails
Cause — API error on resending the registration email.
What happens —
user.registration-email-error.
Raw translation key on screen
Cause — The code references
access-to-account-types.client,access-to-account-types.employee-outsource,access-to-account-types.employee-shopbut the key does not exist in translations.json.What happens — ember-intl renders the missing key (or its missing-message placeholder) instead of text — visible to every user of this screen until the key is added.
QA checklist
Tick these off while regression-testing this feature. The boxes are plain Markdown, so progress is visible in git diffs.
- ✅ New customer saved with full consent
- ✅ Existing customer edited with partial consent
- ✅ Signature captured remotely
- ⛔ Missing mandatory fields
- ⛔ Invalid email format
- ⛔ GDPR not accepted
- ⛔ Centre-specific required field left blank
- ⛔ Consent modal declined
- ⛔ API rejection on save
- ⛔ External sync failure
- ⛔ Registration email resend fails
- ⛔ Raw translation key on screen
Implementation
app/controllers/crm/users/new.jsapp/routes/crm/users/new.jsapp/routes/crm/users/edit.jsapp/components/form-user.jsapp/models/user.js
Translation keys (15)
Key English crm.new-userNew customer users.general_dataGeneral data crm.users.user-savedThe user has been saved with success! crm.users.user-save-errorsThe following errors have occurred: error.user.email.requiredEmail is required error.user.name.requiredName is required error.user.cp.requiredPostal code is required error.user.gdprThe GPDR should be accepted error.user.registration-date.requiredRegistration date is required error.channels.requiredChannels is required form-user.not-valid.emailInvalid email format user.fusion-errorError when creating user in juguettos fusion user.fusion-error-updateError when updating user in juguettos fusion user.registration-email-errorError resending registration email error.signature.not_existThe user does not have signature
Related
- Same area — Customer list & filtering · Customer deletion with unsubscribe notice · Customer CSV export with column picker · Customer CSV import · Children CSV import · AI natural-language customer filter · Save current filters as a segment · Segment list, refresh & export · Segment builder with automatic tag actions · Segment categories · User tags (user types) · Customer receipts tab & ticket-stamp generation · Customer loyalty — raffle tickets · Customer loyalty — cards & stamps · Customer loyalty — rewards · Customer loyalty — coupon exchanges · Customer children (dependants) · Receipt verification queue · Store list, filters & exports · Store create & edit · Store CSV import · Store labels (store types) · Customer action log (dormant)
- Shares data with — Proposal editor (content, coupon, push, AI images) · Activity list & duplication · Activity editor (loyalty mechanics, rules & targeting) · Activity report & PDF · Activity campaigns (programme grouping) · Push notification editor (scheduling, geofencing, deep links) · App button manager (MOFs) · Centre editor (modules, app config, integrations)
Generated from doc/feature-inventory.json · id crm-customer-form · commit 276788ca6