16. Tenant Setup Wizard — Frontend Integration Guide
معالج إعداد المستأجر — دليل تكامل الواجهة الأمامية
العربية: هذا الفصل يوثّق العقد التقني المتوقع لمعالج إعداد المستأجر (Tenant Setup Wizard) — شاشة تجمع بيانات المنشأة والعنوان وإعدادات المستأجر الافتراضية وإعدادات الضريبة، وتنتهي — عند تفعيل المرحلة الثانية من الفوترة الإلكترونية — بربط المستأجر بمنصة فاتورة (ZATCA/FATOORA).
English: This chapter documents the expected integration contract for the Tenant Setup Wizard — a UI flow that collects organization profile, address, tenant default settings, and tax settings, ending — only when ZATCA Phase 2 is enabled — in connecting the tenant to the ZATCA Fatoora platform.
Implementation status note / ملاحظة حالة التنفيذ: Frontend implementation was reported as committed and pushed in Shumoul.Portal commit
65b1dd1b("Add tenant setup wizard with organization profile, address, defaults, and ZATCA Phase 2 onboarding"). This documentation describes the expected integration contract and user flow against the verified backend source. Shumoul.Portal is not inspected or modified by this documentation task — nothing below should be read as a claim that the Angular implementation was verified against this guide.
Who it's for: Frontend/Angular Developers · Backend Developers · QA Engineers
16.1 Three Flows — Do Not Confuse Them
There are three distinct, unrelated onboarding-style flows on the platform. This wizard combines the first two; it must never be confused with the third's name even though it configures the data the third flow uses.
| # | Flow | What it does | Where it's documented |
|---|---|---|---|
| 1 | Tenant Setup Wizard (this chapter) | Collects organization profile, address, tenant defaults, and tax settings (VAT + ZATCA Phase 2) via AppSettingsController and EInvoicingSettingsController/EInvoicingOnboardingController | This chapter |
| 2 | First-Login Onboarding Questionnaire | Answers → recommendation → apply business-activity questionnaire, shown once after first login | Business Onboarding & Smart Configuration |
| 3 | E-Invoicing (ZATCA/FATOORA) Onboarding | The Compliance CSID → compliance checks → Production CSID device-onboarding sequence this wizard's Step 6 triggers | Chapter 9 — Onboarding, Chapter 5 — Controllers & Endpoints § 5.3 |
The Tenant Setup Wizard is a UI composition over flows the backend already exposes — it introduces no new
backend model, table, or endpoint. Steps 1–3 read/write the existing TenantSettings/TaxSettings AppSetting
groups; Steps 5–7 read/write the existing E-Invoicing AppSetting groups documented in
Chapter 6 — Configuration.
16.2 Flow Table
| Step | Title (AR / EN) | Endpoint(s) | Method | Notes |
|---|---|---|---|---|
| 1 | بيانات المنشأة / Organization Profile | api/v1/AppSettings/GetTenant, api/v1/AppSettings/SetTenant | GET, POST | Full-form load-merge-save; must not null hidden fields |
| 2 | العنوان / Address | same as Step 1 (TenantSettings is one model) | GET, POST | Country saved as Guid, not display text |
| 3 | إعدادات المنشأة / Tenant Defaults | same as Step 1 | GET, POST | Currency fields saved as Guid/Guid? IDs |
| 4.1 | تفعيل الضريبة / Enable VAT | api/v1/AppSettings/GetTax, api/v1/AppSettings/SetTax | GET, POST | Reuses existing TaxSettings.Enable_Sales_Tax — no new field |
| 4.2 | تفعيل المرحلة الثانية / Enable ZATCA Phase 2 | (no endpoint — client-side visibility toggle only) | — | Gates Steps 5–7; not itself a persisted setting |
| 5 | بيانات الوحدة الضريبية / Fiscal Unit Registration Data | api/EInvoicing/Settings/VatRegistration | GET, POST | Existing VatRegistrationSettings model + validator, unchanged |
| 6 | ربط منصة فاتورة / Connect to FATOORA Platform | api/EInvoicing/Onboarding | POST | Headers only, no body; OTP obtained by the tenant admin from ZATCA's portal |
| 7 | حالة الربط / Connection Result | api/EInvoicing/Settings/Integration | GET | Safe reload only — OTP is always null in the response |
On the route prefix: the E-Invoicing endpoints above are documented here as api/EInvoicing/... (no
/Saas/ segment) because that is the variant this platform's Shumoul.Api host actually wires up — see
Chapter 5 § 5.5
for why a /Saas/-prefixed duplicate of the same three controllers exists in the TenancyApi package but is
not the one mounted here. If a task brief or ticket references api/Saas/EInvoicing/Onboarding, that is the
unwired duplicate route, not the live one — use api/EInvoicing/Onboarding.
16.3 Step 1 — Organization Profile / بيانات المنشأة
Purpose: collect and save the tenant organization profile using the existing TenantSettings model — no
new model, no new table.
Fields: CompanyName, F_Company_Name, Activity, F_Activity, PhoneNumber, FaxNumber, WebSite,
Email, Tax_Registration_Name, CRNo, TaxNo, Zakat_Registration_Number, Municipal_License_Number,
Gulf_Council_Identification_Number, SAGIA_License_Number.
Backend storage: the shared AppSetting table, keyed by GroupName = "TenantSettings" — the same
generic mechanism documented in Chapter 6 § 6.3
for E-Invoicing settings.
GET api/v1/AppSettings/GetTenant → Result<TenantSettings> [MustHavePermission: AppSetting.View]
POST api/v1/AppSettings/SetTenant → Result [MustHavePermission: AppSetting.Edit]
Important: the wizard must call GetTenant first, merge only the fields shown in Steps 1–3 into the
loaded object, and submit the entire merged TenantSettings object on SetTenant. TenantSettings also
carries fields the wizard never shows (e.g. LogoUrl) — because SetTenant persists the whole object, any
field the wizard didn't load-and-preserve will be silently wiped. This is the same full-form pattern the
platform's existing settings screen already uses; it is not a new convention invented for this wizard.
16.4 Step 2 — Address / العنوان
Fields: Country, Postal_Code, Province, FProvince, City, FCity, District, FDistrict,
Street, FStreet, Building_No, Floor_No, Office_No, Additional_No — all part of the same
TenantSettings object as Step 1; same endpoints, same load-merge-save rule.
Important: Country is a Guid foreign key on TenantSettings, not free text — the wizard must submit
the selected country's ID, never its display name. Reuse the platform's existing country/city dropdown
endpoints; do not introduce a new lookup source for this wizard.
16.5 Step 3 — Tenant Defaults / إعدادات المنشأة
Fields: LocalCurrencyId, SalesPriceCurrencyId, InventoryCostCurrencyId, PayrollCurrencyId
(optional), InventoryCalculateCostMethod, StockInventoryType, Default_Language, Second_Language,
Print_language, Invoice_Round_Factor, Item_Quantity_Round_Factor — same TenantSettings object, same
endpoints.
Important: currency fields are Guid/Guid? IDs, not display names — LocalCurrencyId,
SalesPriceCurrencyId, and InventoryCostCurrencyId are required; PayrollCurrencyId is optional and must
be left null (never Guid.Empty) when not selected.
16.6 Step 4 — Tax Settings / إعدادات الضرائب
This step is a strict branching flow — each toggle gates what the next sub-step is allowed to do.
GET api/v1/AppSettings/GetTax → Result<TaxSettings> [MustHavePermission: AppSetting.View]
POST api/v1/AppSettings/SetTax → Result [MustHavePermission: AppSetting.Edit]
4.1 VAT toggle — تفعيل الضريبة / Enable VAT
Reuses the existing TaxSettings.Enable_Sales_Tax boolean field — no new backend field was needed, this
field already exists and already carries exactly this meaning on the platform.
| VAT state | Behavior |
|---|---|
| Disabled | Hide ZATCA Phase 2 options. Do not call any E-Invoicing endpoint, including api/EInvoicing/Onboarding. Do not clear previously saved E-Invoicing settings without the user's explicit confirmation. |
| Enabled | Validate TenantSettings.TaxNo per existing business rules before allowing the ZATCA Phase 2 toggle to appear. |
4.2 ZATCA Phase 2 toggle — تفعيل المرحلة الثانية من الفوترة الإلكترونية / Enable ZATCA Phase 2 E-Invoicing Integration
Only shown when VAT is enabled. This toggle has no backend persistence of its own — it is a
client-side gate deciding whether Steps 5–7 render at all; the actual "is this tenant onboarded" state lives
in FatooraSecretSettings.Onboarded (see Chapter 6 § 6.6), which the
wizard never reads or writes directly.
| ZATCA Phase 2 state | Behavior |
|---|---|
| Disabled | Hide Fiscal Unit Registration Data (Step 5) and the OTP connection panel (Step 6). Do not call api/EInvoicing/Onboarding. Do not clear previously saved VatRegistrationSettings without the user's explicit confirmation. |
| Enabled | Continue to Step 5. |
16.7 Step 5 — Fiscal Unit Registration Data / بيانات الوحدة الضريبية
Uses the existing VatRegistrationSettings model — do not duplicate the model or create a duplicate
endpoint.
Fields: CommonName, SerialNumber, OrganizationIdentifier, OrganizationUnitName, OrganizationName,
CountryName, InvoiceType, LocationAddress, IndustryBusinessCategory — see
Chapter 6 § 6.2 for the full field list.
GET api/EInvoicing/Settings/VatRegistration → Result<VatRegistrationSettings>
POST api/EInvoicing/Settings/VatRegistration → Result (validated)
Important:
- Reuse the existing
VatRegistrationSettingsValidator(FluentValidation) server-side; do not re-implement the same rules only in Angular as a substitute for the server check. - The wizard may prefill fields from
TenantSettingsonly where safe (e.g. a company name), but must not silently guess formats the validator enforces —OrganizationIdentifier(15-digit Saudi VAT number pattern),CountryName(must matchCountryISO.GetList()), andInvoiceType(2-digit invoice-subscription-type pattern) all have specific required formats; if the wizard cannot derive a valid value confidently, leave the field blank for the user to fill rather than submitting a guessed value. - Do not allow the user to proceed to Step 6 until this POST succeeds.
16.8 Step 6 — ZATCA OTP / FATOORA Connection — ربط منصة فاتورة / Connect to FATOORA Platform
Only shown after Step 5's VatRegistration POST has succeeded.
Business rule: the OTP is obtained by the tenant admin manually from ZATCA's own portal, out of band. The
platform does not generate it and does not read it back from GET .../Integration — see
Chapter 9 § 9.1.
POST api/EInvoicing/Onboarding
Headers:
OnboardingRequestType: FirstTime | Renewal | StartOver
Otp: <value the user entered>
(no request body)
Controller signature (EInvoicingOnboardingController):
InitializeAsync([FromHeader, BindRequired] OnboardingRequestType type, [FromHeader, BindRequired] string Otp)
which delegates to IOnboardingProcessFlowService.Initialize(type, Otp).
OnboardingRequestType values (Shumoul.Framework.EInvoicing.Onboarding.OnboardingRequestType):
public enum OnboardingRequestType
{
FirstTime,
Renewal,
StartOver
}
Use these exact three values (or their ordinal/serialized form as the API contract defines) — do not hard-code a different value set in the frontend; if the Angular project uses a generated API client, import the enum from that contract instead of re-declaring it by hand.
Important:
- OTP must not be displayed anywhere after submission.
- OTP must not be persisted in frontend state (memory, storage, or otherwise) any longer than the single request needs it — clear it immediately once the call is made, regardless of the result.
16.9 Step 7 — Connection Result / حالة الربط
After the api/EInvoicing/Onboarding call resolves:
- Show success or failure clearly to the user. Do not auto-retry.
- Optionally reload safe integration status:
GET api/EInvoicing/Settings/Integration → Result<IntegrationSettings>
Important: IntegrationSettings.OTP is always null in this response by design (redacted since
Phase 0.2 — see Chapter 5 § 5.2).
Only display ExpiresOn/Invalid from this response. Never expect or display an OTP value from this
endpoint.
16.10 Do / Do Not
Do:
- Load full
TenantSettingsbefore saving any of Steps 1–3, and submit the full merged object each time. - Save
Countryand every currency field as aGuid/Guid?ID, never a display name. - Validate
TaxNobefore allowing VAT to be marked enabled. - Require a successful
VatRegistrationsave before showing the OTP panel. - Clear the OTP value from frontend state immediately after the onboarding call, win or lose.
Do not:
- Mix this wizard's flow or naming with the First-Login Onboarding Questionnaire (§16.1, flow 2).
- Call
api/EInvoicing/Settings/ConfigrationMode(POST — publishesSwitchToProductionModeEvent) or anySwitchToProductionMode-style action automatically from this wizard. - Call
api/EInvoicing/Generate(invoice generation) from the setup wizard. - Erase hidden
TenantSettingsfields (e.g.LogoUrl) by submitting a partial object. - Erase previously saved E-Invoicing settings just because the VAT or ZATCA Phase 2 toggle was switched off — only do so with the user's explicit confirmation.
- Create a duplicate API endpoint or a duplicate settings model for anything this wizard needs — every field
it touches already exists on
TenantSettings,TaxSettings, orVatRegistrationSettings. - Save country/currency display names where a
GuidID is required. - Display or persist the OTP value after it has been submitted.
- Rely on
GET .../Integrationto return the OTP — it is alwaysnull. - Allow the OTP connection step to proceed before
VatRegistrationSettingshas been validated and saved.
16.11 Manual Verification Checklist
- Open the tenant setup wizard.
- Complete Organization Profile (Step 1) and confirm
SetTenantpreserves fields not shown in the wizard. - Complete Address (Step 2) and confirm
Countryis submitted as aGuid. - Complete Tenant Defaults (Step 3) and confirm currency fields are submitted as
Guid/Guid?IDs. - Open Tax Settings (Step 4).
- With VAT disabled: confirm ZATCA Phase 2 options are hidden and no E-Invoicing endpoint is called.
- Enable VAT: confirm the ZATCA Phase 2 toggle appears and
TaxNois validated. - With ZATCA Phase 2 disabled: confirm Fiscal Unit and OTP panels are hidden and
api/EInvoicing/Onboardingis not called. - Enable ZATCA Phase 2: confirm Fiscal Unit Registration Data fields (Step 5) appear.
- Save
VatRegistrationSettingsand confirm the POST succeeds before the OTP panel appears. - Confirm the OTP panel appears only after a successful
VatRegistrationsave. - Enter an OTP obtained from the ZATCA portal.
- Confirm
POST api/EInvoicing/Onboardingis called withOnboardingRequestTypeandOtpheaders, no body. - Confirm the OTP value is cleared from frontend state immediately after submission.
- Confirm
GET api/EInvoicing/Settings/Integrationnever displays an OTP value. - Confirm
ConfigrationModeis never called automatically anywhere in this flow. - Confirm
api/EInvoicing/Generateis never called from this wizard. - Confirm existing tenant settings pages (outside this wizard) remain compatible and unaffected.
