Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions project-ideas/unigate/uniship/CarrierAccountManagement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Unigate Setup Guide: Single Tenant with Multiple Carrier Accounts

**The Previous Approach:** Creating a new "Tenant" in Unigate for every single facility strictly because each facility required its own carrier credentials. This inevitably creates hundreds of tenants, and it was very cumbersome to create config data and manage them.

**The Solution:** Decouple the carrier credentials from the concept of a Tenant.
- A single logical Tenant represents the overarching business.
- A single generic `ShippingGatewayConfig` defines the carrier's API services.
- Numerous distinct `SystemMessageRemote` (SystemMessageRemote) records in Unigate hold the isolated credentials for each specific facility.
- Unigate links these via `ShippingGatewayAuth`.
- OMS natively stores the `systemMessageRemoteId` mapping at the facility level and injects it into the request payload. Unigate then seamlessly queries the precise auth record using the triad of `tenantId`, `shippingGatewayConfigId`, and `systemMessageRemoteId`.

---

## Architecture Breakdown

### 1. In Unigate
* **Tenant Setup:** The Tenant (`Party`, `Organization`, `UserAccount`, `UserLoginKey`) represents the oms-instance and acts as the integration user.
* **Carrier Config:** The `ShippingGatewayConfig` per carrier maps the integration Java services.
* **API Credentials:** Distinct `SystemMessageRemote` records store the unique Carrier API Keys for each facility.
* **Auth Mapping:** `ShippingGatewayAuth` records map the `Tenant` + `ShippingGatewayConfig` perfectly to its corresponding unique `SystemMessageRemote`.

### 2. In OMS
* **Facility Configuration:** The `ShippingCarrierConfig` entity at the facility level natively points to:
1. The `tenantId`
2. The `shippingGatewayConfigId`
3. **The Unigate `systemMessageRemoteId`** (The unique identifier representing this facility's credentials inside Unigate's SystemMessageRemote table).
* **Unigate Connection:** OMS maintains a `SystemMessageRemote` per Tenant. This SystemMessageRemote stores the Unigate instance URL and the single super-secret Login Key to talk to Unigate on behalf of all the stores for that Tenant.

---

## Multi Account Use Case Example

To illustrate this design, we will use a retail ecosystem (encompassing multiple brands across hundreds of facilities) as our primary example. For the purpose of this analogy, we have considered 150 fulfillment facilities/warehouses.

- **Tenant Setup:** A single Tenant, `SMUS`, represents the entire company entity acting as the interface to OMS.
- **Carrier Config:** `FEDEX_CONFIG` maps the generic FedEx rate and label services for all 150 facilities.
- **API Credentials:** 150 different SystemMessageRemote records are created to hold specific credentials for Facility 1, Facility 2, etc. (e.g., `FEDEX_ACCOUNT_01`).
- **Auth Mapping:** 150 records link the `SMUS` tenant and its `FEDEX_CONFIG` to the 150 distinct facility SystemMessageRemote records.
- **Facility Configuration:** The facility configuration for Facility 1 in OMS natively points to `SMUS` (Tenant), `FEDEX_CONFIG` (Gateway), and `FEDEX_ACCOUNT_01` (SystemMessageRemote ID).
- **Unigate Connection:** OMS uses a single `UNIGATE_CONFIG` SystemMessageRemote holding the single super-secret key to connect to Unigate on behalf of all 150 stores.

---

## Execution Flow
1. Facility `FACILITY_01` triggers a FedEx shipping label request in OMS.
2. OMS extracts `tenantId`, `shippingGatewayConfigId`, and `systemMessageRemoteId` from `ShippingCarrierConfig`.
3. OMS authenticates against the Unigate API using its stored Unigate URL and Login Key.
4. The label request payload securely carries the specific `tenantId`, `shippingGatewayConfigId`, and `systemMessageRemoteId`.
5. Unigate receives the payload and queries the `ShippingGatewayAuth` entity matching all three identifiers perfectly.
6. Unigate extracts the specific API credentials natively from the matched SystemMessageRemote, authenticates with the carrier, and returns the result.

---

## Sample Data (XML Implementation)

### Unigate Data Model
```xml
<?xml version="1.0" encoding="UTF-8"?>
<entity-facade-xml type="demo">

<!-- The Single Unified Tenant -->
<co.hotwax.unigate.Party partyId="SMUS" partyTypeEnumId="PtyOrganization">
<organization organizationName="org_name"/>
</co.hotwax.unigate.Party>

<moqui.security.UserAccount userId="SMUS_SYS_USER" username="smus.unigate.sys" userFullName="SMUS System Account" partyId="SMUS"/>
<moqui.security.UserLoginKey userId="SMUS_SYS_USER" loginKey="SUPER_SECRET_SMUS_KEY" fromDate="2025-05-07T08:00:00"/>
<moqui.security.UserGroupMember userGroupId="UNIGATE_API" userId="SMUS_SYS_USER" fromDate="2025-05-07T08:00:00"/>

<!-- The Single Gateway Configuration for FedEx -->
<co.hotwax.unigate.ShippingGatewayConfig shippingGatewayConfigId="FEDEX_CONFIG"
description="Fedex Services Config"
getRateServiceName="co.hotwax.shipping.fedex.FedexServices.get#ShippingRate"
requestLabelsServiceName="co.hotwax.shipping.fedex.FedexServices.request#ShippingLabels"
refundLabelsServiceName="co.hotwax.shipping.fedex.FedexServices.refund#ShippingLabels" />

<!-- Distinct SystemMessageRemote records for Multiple Facilities -->
<moqui.service.message.SystemMessageRemote systemMessageRemoteId="FEDEX_ACCOUNT_01" description="FedEx Auth Keys strictly for Store 01" sendUrl="https://apis-sandbox.fedex.com" username="facility01_user" sharedSecret="facility01_secret"/>
<moqui.service.message.SystemMessageRemote systemMessageRemoteId="FEDEX_ACCOUNT_02" description="FedEx Auth Keys strictly for Store 02" sendUrl="https://apis-sandbox.fedex.com" username="facility02_user" sharedSecret="facility02_secret"/>

<!-- Auth Mapping linking the Triad: Config + Tenant + SystemMessageRemote -->
<co.hotwax.unigate.ShippingGatewayAuth shippingGatewayConfigId="FEDEX_CONFIG" tenantPartyId="SMUS" systemMessageRemoteId="FEDEX_ACCOUNT_01"/>
<co.hotwax.unigate.ShippingGatewayAuth shippingGatewayConfigId="FEDEX_CONFIG" tenantPartyId="SMUS" systemMessageRemoteId="FEDEX_ACCOUNT_02"/>

</entity-facade-xml>
```

### OMS Data Model
```xml
<?xml version="1.0" encoding="UTF-8"?>
<entity-facade-xml type="demo">

<!-- OMS connection to Unigate APIs for the specific Tenant -->
<SystemMessageRemote systemMessageRemoteId="UNIGATE_CONFIG"
description="Unigate connection for SMUS Tenant"
sendUrl="https://unigate.hotwax.co"
password="SUPER_SECRET_SMUS_KEY" />

<!-- Facility Configs caching the unigate credential SystemMessageRemote mappings -->
<ShippingCarrierConfig id="STORE_01_FEDEX_01" productStoreId="STORE_01" carrierPartyId="FEDEX"
facilityId="FACILITY_01" tenantId="SMUS" shippingGatewayConfigId="FEDEX_CONFIG"
systemMessageRemoteId="FEDEX_ACCOUNT_01"
weightUomId="WT_lb" packagingType="YOUR_PACKAGING" dropoffType="USE_SCHEDULED_PICKUP"
labelSize="PAPER_4X6" labelImageType="PNG" physicalStoreLabelSize="PAPER_4X6"
distributionCenterLabelSize="PAPER_4X6" carrierAccountId="1111111" customerNumber="2222222"/>

<ShippingCarrierConfig id="STORE_02_FEDEX_02" productStoreId="STORE_02" carrierPartyId="FEDEX"
facilityId="FACILITY_02" tenantId="SMUS" shippingGatewayConfigId="FEDEX_CONFIG"
systemMessageRemoteId="FEDEX_ACCOUNT_02"
weightUomId="WT_lb" packagingType="YOUR_PACKAGING" dropoffType="USE_SCHEDULED_PICKUP"
labelSize="PAPER_4X6" labelImageType="PNG" physicalStoreLabelSize="PAPER_4X6"
distributionCenterLabelSize="PAPER_4X6" carrierAccountId="3333333" customerNumber="4444444"/>

</entity-facade-xml>
```
72 changes: 72 additions & 0 deletions project-ideas/unigate/uniship/entity/ShippingGatewayAuth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Configure `ShippingGatewayAuth`

## 1. Overview

The `ShippingGatewayAuth` mapping record is the critical bridge connecting a tenant to a shipping carrier gateway and their private authentication credentials.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will drop the idea of immutability.

No from and thru date.

if the carrier account credentials should be changed, update existing systemMessageRemote record.

This entity is **immutable**. To update any credential mapping, a **new record must be created**, and the previous mapping record should be **expired by setting its `thruDate`**. This ensures a complete history of configuration changes for audit and rollback purposes across all tenants.

---

## 2. Purpose

* Maps a specific tenant to a predefined shipping gateway (e.g., FedEx, USPS).
* Links to the raw credentials securely stored in Moqui's native `moqui.service.message.SystemMessageRemote`.
* Ensures traceability and historical accuracy by enforcing immutability natively via `fromDate` scaling.

---

## 3. Entity: `ShippingGatewayAuth`

| Field Name | Type | Required | Description |
| --------------------------- | -------- | -------- | ------------------------------------------------------------------------------------------ |
| systemMessageRemoteId | String | Yes | Primary key (Part 1). Links to the encrypted credentials. |
| shippingGatewayConfigId | String | Yes | Primary key (Part 2). Refers to the predefined shipping gateway type (e.g., FedEx). |
| tenantPartyId | String | Yes | Primary key (Part 3). Party ID of the specific tenant using this configuration. |
| fromDate | DateTime | Yes | Primary key (Part 4). Effective start date, enabling multiple historical records. |
| thruDate | DateTime | No | Expiry date, if any. |

> **Note on Credentials:** Sensitive connection details like the API Key, Shared Secret, Username, and Password, alongside the actual connection URL are **never** stored in `ShippingGatewayAuth`. They are instead securely managed inside the linked `SystemMessageRemote` record.

---

## 4. Setup Workflow

### Step-by-Step Instructions

1. **Tenant Manager Logs In**
A privileged user logs into the UniShip tenant manager interface.

2. **Navigate to Shipping Gateway Setup Page**
Admin chooses to configure shipping gateway access for the tenant.

3. **Provide Secure Credentials to SystemMessageRemote**
The system securely provisions a new `SystemMessageRemote` record, automatically encrypting the provided authentication tokens and URLs.

4. **Choose Gateway Type**
Admin selects from predefined gateway options (sourced from the global `ShippingGatewayConfig` entity).

5. **Create Immutable Mapping**
A new record is generated in the `ShippingGatewayAuth` entity scoped directly to the `tenantPartyId`, `shippingGatewayConfigId`, and the newly created `systemMessageRemoteId`.

6. **Effective Dating**
The mapping is effective immediately via `fromDate`.

7. **Update Handling**
If changes are needed later, a new `SystemMessageRemote` and `ShippingGatewayAuth` mapping is provisioned, and the old mapping's `thruDate` is populated.

---

## 5. Security Considerations

* Credential fields (e.g., `password`, `sharedSecret`) are natively encrypted at rest by Moqui inside `SystemMessageRemote`.
* The `ShippingGatewayAuth` table serves purely as a lookup router without ever exposing the sensitive string values directly.
* All backend API calls should filter entity queries using `conditionDate("fromDate", "thruDate", now)` to return only current/valid mappings and sort them descending by `fromDate`.

---

## 6. Internal Entity Relationship

* `tenantPartyId` → references the tenant Organization (`Party`)
* `shippingGatewayConfigId` → references the global definition for the carrier (`ShippingGatewayConfig`)
* `systemMessageRemoteId` → references the safe vault for credentials (`SystemMessageRemote`)
127 changes: 0 additions & 127 deletions project-ideas/unigate/uniship/entity/ShippingGatewayAuthConfig.md

This file was deleted.

Loading