# 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:///taxonomy/ \ -H "Authorization: Bearer " \ -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:///taxonomy/ \ -H "Authorization: Bearer " \ -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:///taxonomy/ \ -H "Authorization: Bearer " \ -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:///taxonomy/ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "brands": ["OldBrand"], "is_delete": true }' ``` ### 4. Add subcategories ```bash curl -X POST https:///taxonomy/ \ -H "Authorization: Bearer " \ -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:///taxonomy/ \ -H "Authorization: Bearer " \ -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`.