APIs & services / portfolio-service

Record a crop harvest. Sent through the queue or online.

Endpointtoday's app
Call
POST /gateway/portfolio-service/api/v1/portfolios/harvest/{id}/ENTITY_TYPE.IE
Service
portfolio-service
Features
Crop Actual harvest
When
Queued sync (HARVEST POST) or user action online
Auth
X-SESSIONID stamped when queued (CoreAPI dooWithRetry); X-REQUESTID idempotency key; online path: X-SESSIONID header (AuthInterceptor adds it; re-auth on 401/400)
In the App Service

Use case

Queued create of a crop harvest (HARVEST POST via HarvestWorker) after the user saves a harvest in SelectHarvestBottomSheet; IePortfolioRepositoryImpl.saveHarvestLog stores the harvest_logs row, updates the crop's harvest status and triggers sync. The Retrofit saveHarvestLog (online) method still exists but its only call is commented out, so in practice this is queue-only. The response body is the new harvest id, saved to harvest_logs; 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.

Parameters

ParamInRequiredExampleNotes
idpathYes40001Farmer IE id (HarvestLogs.ownerId); op is not built if ownerId is null.

Request

Example request body (synthetic data)
{
  "id": 0,
  "surplusOutput": {
    "id": null,
    "uoM": {
      "symbol": "q",
      "code": "UOM.QUINTAL",
      "name": "Quintal",
      "measurementType": {
        "name": "Weight",
        "code": "MEASUREMENT_TYPE.WEIGHT",
        "globalRefClass": {
          "classCode": "GREF.MEASUREMENT_TYPE",
          "name": "Measurement type"
        },
        "language": {
          "language": "English",
          "languageCode": "ENG"
        }
      }
    },
    "value": 2
  },
  "actualOutput": {
    "id": null,
    "uoM": {
      "symbol": "q",
      "code": "UOM.QUINTAL",
      "name": "Quintal"
    },
    "value": 12
  },
  "earlyHarvestReason": null,
  "harvestDate": "2026-10-15",
  "harvestCompleteDate": "2026-10-20",
  "portfolioLineItemId": "6001",
  "productLineItemId": "7001",
  "harvestStatus": "HARVEST_STATUS.COMPLETED",
  "harvestType": "HARVEST_TYPE.WHOLE_CROP",
  "portfolioProductLineItemId": "7001",
  "genericProduct": "PRODUCT.PADDY",
  "ownerId": "40001",
  "ownerEntityType": "ENTITY_TYPE.IE",
  "uuid": "hv-0000-4000-8000-000000001001",
  "interestedInCropMarketing": true
}
FieldTypeRequiredNotes
portfolioLineItemId / portfolioProductLineItemId / productLineItemIdstringYesCrop being harvested.
genericProductstringYesCrop product code.
harvestDate / harvestCompleteDatestringYesHarvest form dates.
harvestType / harvestStatusstringYesContinuous vs whole crop.
actualOutput (local column quantity_*) / surplusOutputobjectNoQuantity + UoM (uom 'name' and measurementType 'name'/'code' are Gson serialised names).
earlyHarvestReason / interestedInCropMarketingmixedNoForm answers.
ownerId / ownerEntityType / uuid / idmixedYesOwner farmer, client uuid (ledger entity id), local id.

Response

Example response body (synthetic data)
9301
FieldTypeRequiredNotes
(body)integerNew harvest id -> harvest_logs.id (updateHarvestId). If the body is not an integer the ledger is not closed.

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/HarvestLogs.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/work/HarvestWorker.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/rh/HarvestPostRH.kt
  • app/src/main/java/org/platformcommons/urvi/datasource/repository/impl/IePortfolioRepositoryImpl.kt
  • app/src/main/java/org/platformcommons/urvi/view/harvest/SelectHarvestBottomSheet.kt
  • app/src/main/java/org/platformcommons/urvi/view/portfolio/IePortfolioViewModel.kt
  • app/src/main/java/org/platformcommons/urvi/datasource/network/retrofit/APIInterface.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