APIs & services / Common TLD (ctld)

Add inventory products for a farmer.

Endpointtoday's app
Call
POST /ctld/api/inventory/inventoryproduct/custominventoryproduct/v1
Service
Common TLD (ctld)
Features
Crop stock (inventory)
When
Queued sync (INVENTORY_PRODUCT POST)
Auth
X-SESSIONID stamped when queued (CoreAPI dooWithRetry); X-REQUESTID idempotency key
In the App Service

Use case

Queued create of a stock item on the farmer Inventory screen (INVENTORY_PRODUCT POST via InventoryProductWorker); depends on the farmer create. InventoryProductPostRH saves the inventory (if new), the inventory id and supervisor actor id, and the product id. Failures retry.

Headers

HeaderValueNotes
X-SESSIONID<session token>Stamped when the operation is queued (dooWithRetry) and re-set by CoreAPI at send time; refreshed on 401.
X-REQUESTID<uuid v4>Idempotency key generated by OperationManagerImpl.createOperation for POST/PUT/PATCH when the op enters the offline queue.
X-ACK-REQUESTID<X-REQUESTID of an earlier completed op>Added by SyncQueueProcessor for POST/PUT/PATCH: acknowledges a request id the device already saw succeed.
Content-Typeapplication/jsonAdded by CoreAPI.

Request

Example request body (synthetic data)
[
  {
    "usableQuantity": 10,
    "notes": null,
    "aliasId": "ip-alias-0001",
    "quantity": 10,
    "quantityInUOM": {
      "id": 0,
      "value": 10,
      "uom": {
        "symbol": "kg",
        "code": "UOM.KG",
        "unitName": "Kilogram",
        "measurementType": {
          "label": "Weight",
          "dataCode": "MEASUREMENT_TYPE.WEIGHT",
          "globalRefClass": {
            "classCode": "GREF.MEASUREMENT_TYPE",
            "name": "Measurement type"
          },
          "language": {
            "language": "English",
            "languageCode": "ENG"
          }
        }
      }
    },
    "id": 0,
    "isActive": true,
    "productSKU": {
      "id": 7101
    },
    "uom": {
      "symbol": "kg",
      "code": "UOM.KG",
      "unitName": "Kilogram",
      "measurementType": {
        "label": "Weight",
        "dataCode": "MEASUREMENT_TYPE.WEIGHT",
        "globalRefClass": {
          "classCode": "GREF.MEASUREMENT_TYPE",
          "name": "Measurement type"
        },
        "language": {
          "language": "English",
          "languageCode": "ENG"
        }
      }
    },
    "inventory": {
      "owningEnitityId": 40001,
      "notes": null,
      "owningEntityType": "ENTITY_TYPE.IE",
      "name": "Farmer A inventory",
      "id": 0,
      "isActive": true,
      "supervisor": {
        "isActive": true,
        "id": 0,
        "aliasId": "ie-alias-0001",
        "actorId": "40001",
        "actorType": {
          "dataCode": "ACTOR_TYPE.IE",
          "globalRefClass": {
            "classCode": "GREF.ACTOR_TYPE"
          },
          "language": {
            "languageCode": "ENG"
          }
        }
      }
    },
    "appMillMetadata": {
      "ie_appId": "ie-alias-0001"
    },
    "appId": "ip-0000-4000-8000-000000000901",
    "syncStatus": "SYNC_PENDING"
  }
]
FieldTypeRequiredNotes
[0].productSKU.idintegerYesCrop/input SKU chosen on the Inventory screen (product SKU master built from the ctld product list).
[0].quantity / usableQuantity / quantityInUOM / uommixedYesStock quantity and unit.
[0].inventory.idintegerNoServer inventory id (0 on first product; PATCH also sends it as ?inventoryId).
[0].inventory.supervisor.actorId / aliasId / actorTypemixedYesFarmer IE id and alias; actorId filled from IE once synced.
[0].inventory.owningEnitityId / owningEntityType / namemixedNoOwner (sic: 'owningEnitityId' spelling is what is sent).
[0].isActive, notes, aliasIdmixedNoaliasId is the client id of the inventory product row.
[0].appMillMetadata.ie_appId, appId, opId, syncStatusstringNoLocal columns; ignored by server.

Response

Example response body (synthetic data)
[
  {
    "usableQuantity": 10,
    "notes": null,
    "aliasId": "ip-alias-0001",
    "quantity": 10,
    "quantityInUOM": {
      "id": 0,
      "value": 10,
      "uom": {
        "symbol": "kg",
        "code": "UOM.KG",
        "unitName": "Kilogram",
        "measurementType": {
          "label": "Weight",
          "dataCode": "MEASUREMENT_TYPE.WEIGHT",
          "globalRefClass": {
            "classCode": "GREF.MEASUREMENT_TYPE",
            "name": "Measurement type"
          },
          "language": {
            "language": "English",
            "languageCode": "ENG"
          }
        }
      }
    },
    "id": 9101,
    "isActive": true,
    "productSKU": {
      "id": 7101
    },
    "uom": {
      "symbol": "kg",
      "code": "UOM.KG",
      "unitName": "Kilogram",
      "measurementType": {
        "label": "Weight",
        "dataCode": "MEASUREMENT_TYPE.WEIGHT",
        "globalRefClass": {
          "classCode": "GREF.MEASUREMENT_TYPE",
          "name": "Measurement type"
        },
        "language": {
          "language": "English",
          "languageCode": "ENG"
        }
      }
    },
    "inventory": {
      "owningEnitityId": 40001,
      "notes": null,
      "owningEntityType": "ENTITY_TYPE.IE",
      "name": "Farmer A inventory",
      "id": 9201,
      "isActive": true,
      "supervisor": {
        "isActive": true,
        "id": 0,
        "aliasId": "ie-alias-0001",
        "actorId": "40001",
        "actorType": {
          "dataCode": "ACTOR_TYPE.IE",
          "globalRefClass": {
            "classCode": "GREF.ACTOR_TYPE"
          },
          "language": {
            "languageCode": "ENG"
          }
        }
      }
    }
  }
]
FieldTypeRequiredNotes
[0].idintegerinventoryproduct.id.
[0].inventory.id / name / supervisor.id / aliasId / actorId / actorType.dataCodemixedSaved to the inventory table if no inventory exists for the farmer alias; inventory id and actorId written to the product row.

Errors

StatusWhenWhat the app does
401Session token expired or invalidCoreAPI invalidates the session id and retries once immediately; if still failing the op stays in the queue (TryAgain.LATER).
500Server error on a /gateway URLBaseResponseHandler fetches a new session id (first 5 attempts) and returns TryAgain.LATER; retried per RetryScheduler policy (retry threshold/delay come from app config).
503Service unavailableCoreAPI retries once immediately, then the op is retried later by the sync queue.
any non-2xxAny other failureHandler onError -> TryAgain.LATER (op stays queued and is visible in Pending Sync); a row is written to the local error_log table (Sync Error Log screen) with URL, body, headers and raw response; every 10th attempt the op is mirrored to Firestore for support.

Source

  • data/src/main/java/org/mindtree/app/agricultor/data/entity/InventoryProduct.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/work/InventoryProductWorker.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/rh/InventoryProductPostRH.kt
  • app/src/main/java/org/platformcommons/urvi/bridge/BridgeSyncHelper.kt
  • app/src/main/java/org/platformcommons/urvi/view/inventory/InventoryNewViewModel.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/manager/CoreAPIManager.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/rh/BaseResponseHandler.kt
  • offline-core/src/main/java/com/mindtree/bridge/coreapi/offline/SyncQueueProcessor.java
  • offline-core/src/main/java/com/mindtree/bridge/coreapi/offline/manager/OperationManagerImpl.java
  • core-api/src/main/java/com/mindtree/bridge/coreapi/impl/CoreAPIDefaultImpl.java