Spaces:
Runtime error
Runtime error
| # Taxonomy API | |
| ## Endpoints | |
| | Method | URL | Description | | |
| | ------ | ------------ | ----------------------------------------- | | |
| | `POST` | `/taxonomy/` | Create or append taxonomy values | | |
| | `PUT` | `/taxonomy/` | Replace (overwrite) taxonomy field values | | |
| Both endpoints require a Bearer token (JWT). `merchant_id` is always extracted from the JWT — do **not** pass it in the request body. | |
| --- | |
| ## POST /taxonomy/ — Create or Append | |
| ### Overview | |
| Handles both **create** and **append** of taxonomy data. The operation is determined automatically based on whether a taxonomy document already exists for the merchant. | |
| ### How Create vs Append Works | |
| | Scenario | Behaviour | | |
| | -------------------------------------------- | ------------------------------------------------------------------------------------- | | |
| | No taxonomy document exists for the merchant | Creates a new document (`operation: "created"`) | | |
| | Document already exists | Appends new values using MongoDB `$addToSet` — no duplicates (`operation: "updated"`) | | |
| | `is_delete: true` is passed | Removes the specified values from the arrays | | |
| The key behaviour for updates: **values are appended, not replaced**. If you send `brands: ["Nike"]` and the document already has `["Adidas"]`, the result will be `["Adidas", "Nike"]`. To replace entirely, use `PUT /taxonomy/`. | |
| --- | |
| ## PUT /taxonomy/ — Replace Field Values | |
| ### Overview | |
| Replaces the value of each provided field entirely. Only fields included in the request body are affected — omitted fields remain unchanged. | |
| - **Method**: `PUT` | |
| - **URL**: `/taxonomy/` | |
| - **Auth**: Bearer token (JWT) required | |
| - **Prerequisite**: A taxonomy document must already exist for the merchant (use `POST` to create it first) | |
| ### How Replace Works | |
| | Scenario | Behaviour | | |
| | ---------------------------------- | ----------------------------------------------------- | | |
| | Field is included in request body | Existing array is **overwritten** with the new values | | |
| | Field is omitted from request body | Field is left unchanged | | |
| | Document does not exist | Returns `422` — use `POST /taxonomy/` to create first | | |
| Example: if the document has `brands: ["Adidas", "Nike"]` and you `PUT` with `brands: ["Puma"]`, the result will be `brands: ["Puma"]`. | |
| ### Response — `200 OK` | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Taxonomy fields replaced successfully", | |
| "data": { | |
| "taxonomy_id": "64f1a2b3c4d5e6f7a8b9c0d1", | |
| "operation": "replaced", | |
| "merchant_id": "af59ab73-4ef4-43a0-aac3-6909f3ba87e4", | |
| "modified_count": 1 | |
| }, | |
| "timestamp": "2026-03-14T10:30:00.000Z" | |
| } | |
| ``` | |
| ### Usage Example | |
| ```bash | |
| curl -X PUT https://<host>/taxonomy/ \ | |
| -H "Authorization: Bearer <token>" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "brands": ["Puma"], | |
| "categories": ["Hair", "Skin"] | |
| }' | |
| ``` | |
| --- | |
| --- | |
| ## Request Body | |
| All fields are optional. Only send the fields you want to add/update. | |
| ```json | |
| { | |
| "brands": ["L'Oréal Professional", "Schwarzkopf"], | |
| "categories": ["Hair", "Skin", "Nails"], | |
| "lines": ["Hair Care", "Hair Styling"], | |
| "classes": ["Premium", "Luxury", "Basic"], | |
| "subcategories": { | |
| "Hair": ["Hair Cut", "Hair Color", "Hair Treatment"], | |
| "Skin": ["Facial", "Cleanup"] | |
| }, | |
| "job_role": ["Senior Stylist", "Director", "Massage Therapist"], | |
| "specializations": ["Hairdresser", "Nail Technician", "Esthetician"], | |
| "languages": ["English", "Hindi", "Tamil"], | |
| "designation": ["CTO", "CEO", "RSM", "ASM"], | |
| "regions": ["East", "West", "North", "South"], | |
| "customer_group": ["VIP", "Regular", "Premium", "Gold"], | |
| "pos_tender_modes": ["Cash", "Credit Card", "Debit Card", "Mobile Payment"], | |
| "payment_types": ["Cash and Carry", "Credit"], | |
| "payment_methods": ["Bank Transfer", "Cash", "Cheque"], | |
| "pos_modes": ["Walk-in", "Appointment", "Online"], | |
| "pricing_model": ["Weight_Based", "Fixed_Rate", "Zone_Based"], | |
| "asset_location": ["Store Floor", "Warehouse", "Back Office"], | |
| "asset_category": ["Electronics", "Furniture", "Equipment"], | |
| "stock_bin_location": ["A1-01", "B2-15", "C3-07"], | |
| "branch_types": ["Flagship", "Outlet", "Franchise"], | |
| "store_types": ["Retail", "Wholesale", "Online"], | |
| "expense_types": ["Travel", "Office Supplies"], | |
| "region_codes": ["IN-KA-BLR", "IN-TN-CHN"], | |
| "states": ["Maharashtra", "Karnataka", "Tamil Nadu"], | |
| "relations": ["Parent", "Subsidiary", "Partner", "Vendor"], | |
| "document_types": ["Invoice", "Receipt", "Purchase Order"], | |
| "merchant_types": [ | |
| { "code": "cnf", "label": "CNF", "order": 1 }, | |
| { "code": "distributor", "label": "Distributor", "order": 2 }, | |
| { "code": "retail", "label": "Retail", "order": 3 } | |
| ], | |
| "is_delete": false | |
| } | |
| ``` | |
| ### Field Reference | |
| #### Simple String Arrays | |
| All of these follow the same pattern — send an array of strings to add values: | |
| | Field | Description | Example | | |
| | -------------------- | ------------------------ | -------------------------------- | | |
| | `brands` | Product brands | `["L'Oréal", "Schwarzkopf"]` | | |
| | `categories` | Product categories | `["Hair", "Skin", "Nails"]` | | |
| | `lines` | Product lines | `["Hair Care", "Hair Styling"]` | | |
| | `classes` | Product classes | `["Premium", "Luxury"]` | | |
| | `job_role` | Employee job roles | `["Senior Stylist", "Director"]` | | |
| | `specializations` | Employee specializations | `["Hairdresser", "Esthetician"]` | | |
| | `languages` | Languages | `["English", "Hindi"]` | | |
| | `designation` | Employee designations | `["CTO", "RSM", "ASM"]` | | |
| | `regions` | Region groups | `["East", "West", "North"]` | | |
| | `customer_group` | Customer groups | `["VIP", "Regular", "Gold"]` | | |
| | `pos_tender_modes` | POS tender modes | `["Cash", "Credit Card"]` | | |
| | `payment_types` | Payment types | `["Cash and Carry", "Credit"]` | | |
| | `payment_methods` | Payment methods | `["Bank Transfer", "Cash"]` | | |
| | `pos_modes` | POS modes | `["Walk-in", "Appointment"]` | | |
| | `pricing_model` | Pricing models | `["Fixed_Rate", "Weight_Based"]` | | |
| | `asset_location` | Asset locations | `["Store Floor", "Warehouse"]` | | |
| | `asset_category` | Asset categories | `["Electronics", "Furniture"]` | | |
| | `stock_bin_location` | Stock bin locations | `["A1-01", "B2-15"]` | | |
| | `branch_types` | Branch types | `["Flagship", "Outlet"]` | | |
| | `store_types` | Store types | `["Retail", "Wholesale"]` | | |
| | `expense_types` | Expense types | `["Travel", "Office Supplies"]` | | |
| | `region_codes` | Region codes | `["IN-KA-BLR", "IN-TN-CHN"]` | | |
| | `states` | States | `["Maharashtra", "Karnataka"]` | | |
| | `relations` | Merchant relations | `["Parent", "Subsidiary"]` | | |
| | `document_types` | Document types | `["Invoice", "Receipt"]` | | |
| #### `subcategories` — Object (category → values) | |
| Maps a parent category to its subcategory values. | |
| ```json | |
| { | |
| "subcategories": { | |
| "Hair": ["Hair Cut", "Hair Color", "Hair Treatment"], | |
| "Skin": ["Facial", "Cleanup", "Peel"] | |
| } | |
| } | |
| ``` | |
| #### `merchant_types` — Structured Array | |
| Each item must have all three fields: | |
| | Field | Type | Required | Description | | |
| | ------- | ----------- | -------- | -------------------------------------------------- | | |
| | `code` | string | ✅ | Unique identifier (e.g., `"cnf"`, `"distributor"`) | | |
| | `label` | string | ✅ | Display name (e.g., `"CNF"`, `"Distributor"`) | | |
| | `order` | integer ≥ 1 | ✅ | Sort order — must be positive, no duplicates | | |
| ```json | |
| { | |
| "merchant_types": [ | |
| { "code": "cnf", "label": "CNF", "order": 1 }, | |
| { "code": "distributor", "label": "Distributor", "order": 2 } | |
| ] | |
| } | |
| ``` | |
| #### `is_delete` — Boolean (default: `false`) | |
| When `true`, the values in the payload are **removed** from the existing arrays instead of being added. | |
| ```json | |
| { | |
| "brands": ["OldBrand"], | |
| "is_delete": true | |
| } | |
| ``` | |
| --- | |
| ## Response | |
| ### Success — `201 Created` | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Taxonomy operation completed successfully", | |
| "data": { | |
| "taxonomy_id": "64f1a2b3c4d5e6f7a8b9c0d1", | |
| "operation": "created", | |
| "merchant_id": "af59ab73-4ef4-43a0-aac3-6909f3ba87e4", | |
| "modified_count": 1 | |
| }, | |
| "timestamp": "2026-03-14T10:30:00.000Z" | |
| } | |
| ``` | |
| | Field | Description | | |
| | --------------------- | ------------------------------------------------------- | | |
| | `data.taxonomy_id` | MongoDB document ID of the taxonomy record | | |
| | `data.operation` | `"created"` for new documents, `"updated"` for existing | | |
| | `data.modified_count` | Number of documents modified (typically `1`) | | |
| --- | |
| ## Usage Examples | |
| ### 1. Initial Setup — Create taxonomy for a new merchant | |
| Send all the values you want to seed: | |
| ```bash | |
| curl -X POST https://<host>/taxonomy/ \ | |
| -H "Authorization: Bearer <token>" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "brands": ["L'\''Oréal Professional", "Schwarzkopf"], | |
| "categories": ["Hair", "Skin", "Nails"], | |
| "job_role": ["Senior Stylist", "Junior Stylist"], | |
| "customer_group": ["VIP", "Regular"] | |
| }' | |
| ``` | |
| ### 2. Add new values to existing taxonomy | |
| Only send the new values — existing ones are preserved: | |
| ```bash | |
| curl -X POST https://<host>/taxonomy/ \ | |
| -H "Authorization: Bearer <token>" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "brands": ["Kerastase"], | |
| "categories": ["Makeup"] | |
| }' | |
| ``` | |
| Result: `brands` will now contain `["L'Oréal Professional", "Schwarzkopf", "Kerastase"]` | |
| ### 3. Delete specific values | |
| ```bash | |
| curl -X POST https://<host>/taxonomy/ \ | |
| -H "Authorization: Bearer <token>" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "brands": ["OldBrand"], | |
| "is_delete": true | |
| }' | |
| ``` | |
| ### 4. Add subcategories | |
| ```bash | |
| curl -X POST https://<host>/taxonomy/ \ | |
| -H "Authorization: Bearer <token>" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "subcategories": { | |
| "Hair": ["Hair Cut", "Hair Color"], | |
| "Skin": ["Facial", "Cleanup"] | |
| } | |
| }' | |
| ``` | |
| ### 5. Set up merchant types | |
| ```bash | |
| curl -X POST https://<host>/taxonomy/ \ | |
| -H "Authorization: Bearer <token>" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "merchant_types": [ | |
| { "code": "cnf", "label": "CNF", "order": 1 }, | |
| { "code": "distributor", "label": "Distributor", "order": 2 }, | |
| { "code": "retail", "label": "Retail", "order": 3 } | |
| ] | |
| }' | |
| ``` | |
| --- | |
| ## Error Responses | |
| | Status | Scenario | | |
| | --------------------------- | ----------------------------------------------------- | | |
| | `401 Unauthorized` | Missing or invalid JWT token | | |
| | `403 Forbidden` | User has no associated merchant | | |
| | `422 Unprocessable Entity` | Validation failure (e.g., `order <= 0`, empty `code`) | | |
| | `500 Internal Server Error` | Database operation failed | | |
| ### Validation Error Example (`422`) | |
| ```json | |
| { | |
| "success": false, | |
| "error": { | |
| "code": "VALIDATION_ERROR", | |
| "message": "Order must be a positive integer", | |
| "details": {} | |
| } | |
| } | |
| ``` | |
| --- | |
| ## Important Notes | |
| 1. **merchant_id is never sent in the body** — it is always extracted from the JWT token. | |
| 2. **Append-only by default** — sending existing values again has no effect (idempotent for duplicates). | |
| 3. **`merchant_types` deduplication** — duplicate `code` or `order` values within the same request are silently skipped; the first occurrence wins. | |
| 4. **String sanitization** — all string values are trimmed of whitespace and empty strings are ignored automatically. | |
| 5. **`subcategories` merge** — new keys are added, existing keys get new values appended. Existing subcategory values are not removed unless `is_delete: true`. | |