APIs & services / portfolio-service

Edit a crop line item and log the crop activity change.

Endpointtoday's app
Call
PUT /gateway/portfolio-service/api/v1/portfolios/{pid}/portfolioLineItem/v2/crop-activity
Service
portfolio-service
Features
Crop Actual (portfolio), PMDS and A Grade crop details, Activity Tracker
When
Queued sync (IE_PORTFOLIO_LINE_ITEM PUT)
Auth
X-SESSIONID stamped when queued (CoreAPI dooWithRetry); X-REQUESTID idempotency key
In the App Service

Use case

Queued edit of a crop line item (IE_PORTFOLIO_LINE_ITEM PUT via PortfolioLineItemWorker.putWithChangeLog) after the user edits a crop actual. The body wraps the PLI and its change log (crop activity entries such as crop changed, sowing date changed). The returned PLI's ids are mapped back and any sync error cleared. If the server says the PLI is part of a compact block, PortfolioLineItemUpdateWorker re-downloads the server copy and drops the op; asset errors trigger FetchFarmerAssetDataWorker; other errors 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
pidpathYes5001Server portfolio id looked up from the local portfolio.

Request

Example request body (synthetic data)
{
  "portfolioLineItemDTO": {
    "portfolioId": "3a9d0000-0000-4000-8000-000000000101",
    "id": 6001,
    "asset": "ASSET-CODE-0001",
    "uuid": "5f6c1d2e-0000-4000-8000-00000000a001",
    "practiceMasterCodes": [
      "PRACTICE.MULCHING",
      "PRACTICE.SEED_TREATMENT"
    ],
    "portfolioLineItemType": "PORTFOLIO_LINE_ITEM_TYPE.ACTUAL",
    "imageUrl": "/data/user/0/<app>/files/pli_6001.jpg",
    "isActive": true,
    "currentSeason": "KHARIF_2026",
    "methodMaster": {
      "id": 3,
      "uuid": "m-0003",
      "methodCode": "METHOD.PMDS"
    },
    "practiceMaster": {
      "practiceCode": "PRACTICE.MULCHING",
      "id": 4,
      "uuid": "p-0004"
    },
    "appCreatedAt": "1780000000000",
    "appLastModifiedAt": "1780000000000",
    "createdAt": null,
    "lastModifiedAt": null,
    "titleId": "TILE-0001",
    "assetId": 9001,
    "assetCheckSum": "<asset checksum>",
    "hasSyncError": false,
    "syncErrorId": null,
    "compactBlockId": null,
    "isLinkedToCompactBlock": false,
    "createdByApp": 109,
    "lastModifiedByApp": 109,
    "createdFromDevice": "<device id>",
    "lastUpdatedFromDevice": "<device id>",
    "geoFencingFileName": null,
    "geoFencingCreatedOn": null,
    "geoFencingFilePath": null,
    "geoFenceAttachments": null,
    "isEditable": true,
    "imageAttachmentId": null,
    "imageCreatedAt": null,
    "changeLog": [
      {
        "remarks": null,
        "changeCategory": "CROP_CHANGE",
        "activityNotes": "Crop replaced after flood",
        "id": 0,
        "portfolioLineItemId": 6001,
        "oldValue": "PRODUCT.PADDY",
        "portfolioId": 5001,
        "changedField": "genericProduct",
        "newValue": "PRODUCT.MAIZE",
        "tileId": "TILE-0001",
        "assetCode": "ASSET-CODE-0001",
        "assetId": 9001,
        "appCreatedAt": 1780000000000
      }
    ],
    "cropSeason": "KHARIF",
    "isAttachmentFetched": false,
    "portfolioProductLineItemList": [
      {
        "portfolioLineItemId": "5f6c1d2e-0000-4000-8000-00000000a001",
        "id": 7001,
        "uuid": "7b1e0000-0000-4000-8000-000000000201",
        "genericProduct": "PRODUCT.PADDY",
        "plannedStartDate": "2026-06-01",
        "plannedEndDate": "2026-10-31",
        "actualStartDate": "2026-06-05",
        "actualEndDate": null,
        "cropChangeReason": null,
        "assetProportion": {
          "id": 0,
          "uoM": {
            "code": "UOM.ACRE",
            "description": "Acre",
            "measurementType": {
              "code": "MEASUREMENT_TYPE.AREA",
              "description": "Area",
              "globalRefClass": {
                "code": "GREF.MEASUREMENT_TYPE",
                "description": "",
                "name": "Measurement type"
              },
              "name": "Area"
            },
            "name": "Acre",
            "symbol": "ac"
          },
          "value": 0.5
        },
        "isMajorCrop": true,
        "cropType": "CROP_TYPE.MAIN",
        "harvestStatus": "HARVEST_STATUS.PENDING",
        "harvestStartDateTime": null,
        "harvestCompleteDateTime": null,
        "appCreatedAt": "1780000000000",
        "appLastModifiedAt": "1780000000000",
        "createdAt": null,
        "lastModifiedAt": null,
        "ppliCropType": "PPLI_CROP_TYPE.PMDS",
        "expectedHarvestDate": "2026-10-15",
        "seedSource": "SEED_SOURCE.OWN",
        "seedVariety": "Variety A",
        "interestedInCropMarketing": false,
        "sowingDate": "2026-06-05",
        "appId": "7b1e0000-0000-4000-8000-000000000201",
        "syncStatus": "SYNC_PENDING"
      }
    ],
    "portfolioLineItemQuestionResponses": [
      {
        "portfolioLineItemAppId": "5f6c1d2e-0000-4000-8000-00000000a001",
        "id": 0,
        "uuid": "8c2f0000-0000-4000-8000-000000000301",
        "questionId": 12,
        "category": "PMDS",
        "subCategory": "SEED_TREATMENT",
        "questionType": "SINGLE_SELECT",
        "questionCode": "Q_SEED_TREATMENT",
        "questionLabel": "Seed treated?",
        "answerLanguageCode": "en",
        "answerValue": "YES",
        "answerLabel": "Yes",
        "answerText": null,
        "appCreatedAt": "1780000000000",
        "appLastModifiedAt": "1780000000000"
      }
    ],
    "isSelected": false,
    "isPresentInDifferentCB": false,
    "appId": "5f6c1d2e-0000-4000-8000-00000000a001",
    "syncStatus": "SYNC_PENDING"
  },
  "portfolioCropActivityLogDTOs": [
    {
      "remarks": null,
      "changeCategory": "CROP_CHANGE",
      "activityNotes": "Crop replaced after flood",
      "id": 0,
      "portfolioLineItemId": 6001,
      "oldValue": "PRODUCT.PADDY",
      "portfolioId": 5001,
      "changedField": "genericProduct",
      "newValue": "PRODUCT.MAIZE",
      "tileId": "TILE-0001",
      "assetCode": "ASSET-CODE-0001",
      "assetId": 9001,
      "appCreatedAt": 1780000000000
    }
  ]
}
FieldTypeRequiredNotes
portfolioLineItemDTO.idintegerNoServer PLI id (0/null on first create). Column portfolio_portfoliolineitem.id.
portfolioLineItemDTO.uuidstringYesClient UUID; server echoes it so the RH can map ids back.
portfolioLineItemDTO.asset / assetId / assetCheckSumstring / integer / stringYesLand (asset) code, server asset id and checksum from the Land Assets table. Checksum mismatch -> ERR_PLATFORM_ASSET_CHECKSUM_MISMATCH.
portfolioLineItemDTO.titleIdstringNoLand tile id used to match geofence files and compact blocks.
portfolioLineItemDTO.portfolioLineItemTypestringYesPLAN vs ACTUAL (crop plan / crop actual screen).
portfolioLineItemDTO.currentSeason / cropSeasonstringNoFrom app config CURRENT_SEASON.
portfolioLineItemDTO.practiceMasterCodesstring[]NoPractices ticked on the crop actual screen (practicemaster table).
portfolioLineItemDTO.methodMaster / practiceMasterobjectNoMethod (PMDS/RDS) and practice selected.
portfolioLineItemDTO.portfolioProductLineItemList[]arrayYesOne entry per crop on the land: genericProduct, cropType, ppliCropType, assetProportion (area value + UoM), planned/actual dates, sowingDate, seedSource, seedVariety, expectedHarvestDate, harvestStatus, isMajorCrop.
portfolioLineItemDTO.portfolioLineItemQuestionResponses[]arrayNoPMDS A-grade question answers (portfolio_question_response table).
portfolioLineItemDTO.createdByApp / lastModifiedByApp / createdFromDevice / lastUpdatedFromDevicelong / stringNoAudit fields set by the app.
portfolioLineItemDTO.appCreatedAt / appLastModifiedAtstring (epoch ms)NoDevice timestamps.
portfolioLineItemDTO.imageUrl, geoFencing*, hasSyncError, syncErrorId, isEditable, isSelected, isPresentInDifferentCB, practices, appId, opId, syncStatusmixedNoLocal-only columns that Gson also serialises; the server ignores them.
portfolioCropActivityLogDTOs[]arrayYesPLI changeLog entries with id forced 0 and portfolioLineItemId, portfolioId, assetId, assetCode, tileId filled; each has changeCategory, changedField, oldValue, newValue, remarks, activityNotes, appCreatedAt.

Response

Example response body (synthetic data)
{
  "portfolioId": "3a9d0000-0000-4000-8000-000000000101",
  "id": 6001,
  "asset": "ASSET-CODE-0001",
  "uuid": "5f6c1d2e-0000-4000-8000-00000000a001",
  "practiceMasterCodes": [
    "PRACTICE.MULCHING",
    "PRACTICE.SEED_TREATMENT"
  ],
  "portfolioLineItemType": "PORTFOLIO_LINE_ITEM_TYPE.ACTUAL",
  "imageUrl": null,
  "isActive": true,
  "currentSeason": "KHARIF_2026",
  "methodMaster": {
    "id": 3,
    "uuid": "m-0003",
    "methodCode": "METHOD.PMDS"
  },
  "practiceMaster": {
    "practiceCode": "PRACTICE.MULCHING",
    "id": 4,
    "uuid": "p-0004"
  },
  "appCreatedAt": "1780000000000",
  "appLastModifiedAt": "1780000000000",
  "createdAt": null,
  "lastModifiedAt": null,
  "titleId": "TILE-0001",
  "assetId": 9001,
  "assetCheckSum": "<asset checksum>",
  "hasSyncError": false,
  "syncErrorId": null,
  "compactBlockId": null,
  "isLinkedToCompactBlock": false,
  "createdByApp": 109,
  "lastModifiedByApp": 109,
  "createdFromDevice": "<device id>",
  "lastUpdatedFromDevice": "<device id>",
  "geoFencingFileName": null,
  "geoFencingCreatedOn": null,
  "geoFencingFilePath": null,
  "geoFenceAttachments": null,
  "isEditable": true,
  "imageAttachmentId": null,
  "imageCreatedAt": null,
  "changeLog": null,
  "cropSeason": "KHARIF",
  "isAttachmentFetched": false,
  "portfolioProductLineItemList": [
    {
      "portfolioLineItemId": "5f6c1d2e-0000-4000-8000-00000000a001",
      "id": 7001,
      "uuid": "7b1e0000-0000-4000-8000-000000000201",
      "genericProduct": "PRODUCT.PADDY",
      "plannedStartDate": "2026-06-01",
      "plannedEndDate": "2026-10-31",
      "actualStartDate": "2026-06-05",
      "actualEndDate": null,
      "cropChangeReason": null,
      "assetProportion": {
        "id": 0,
        "uoM": {
          "code": "UOM.ACRE",
          "description": "Acre",
          "measurementType": {
            "code": "MEASUREMENT_TYPE.AREA",
            "description": "Area",
            "globalRefClass": {
              "code": "GREF.MEASUREMENT_TYPE",
              "description": "",
              "name": "Measurement type"
            },
            "name": "Area"
          },
          "name": "Acre",
          "symbol": "ac"
        },
        "value": 0.5
      },
      "isMajorCrop": true,
      "cropType": "CROP_TYPE.MAIN",
      "harvestStatus": "HARVEST_STATUS.PENDING",
      "harvestStartDateTime": null,
      "harvestCompleteDateTime": null,
      "appCreatedAt": "1780000000000",
      "appLastModifiedAt": "1780000000000",
      "createdAt": null,
      "lastModifiedAt": null,
      "ppliCropType": "PPLI_CROP_TYPE.PMDS",
      "expectedHarvestDate": "2026-10-15",
      "seedSource": "SEED_SOURCE.OWN",
      "seedVariety": "Variety A",
      "interestedInCropMarketing": false,
      "sowingDate": "2026-06-05",
      "appId": "7b1e0000-0000-4000-8000-000000000201",
      "syncStatus": "SYNC_PENDING"
    }
  ],
  "portfolioLineItemQuestionResponses": [
    {
      "portfolioLineItemAppId": "5f6c1d2e-0000-4000-8000-00000000a001",
      "id": 0,
      "uuid": "8c2f0000-0000-4000-8000-000000000301",
      "questionId": 12,
      "category": "PMDS",
      "subCategory": "SEED_TREATMENT",
      "questionType": "SINGLE_SELECT",
      "questionCode": "Q_SEED_TREATMENT",
      "questionLabel": "Seed treated?",
      "answerLanguageCode": "en",
      "answerValue": "YES",
      "answerLabel": "Yes",
      "answerText": null,
      "appCreatedAt": "1780000000000",
      "appLastModifiedAt": "1780000000000"
    }
  ],
  "isSelected": false,
  "isPresentInDifferentCB": false
}
FieldTypeRequiredNotes
id / uuid / portfolioProductLineItemList[].id / uuidmixedupdateOptionLineItemIds.

Errors

StatusWhenWhat the app does
4xx, errorMessage contains 'is a part of compactBlock. Hence, Editing is not allowed.'PLI locked by a compact blockPortfolioLineItemUpdateWorker overwrites local PLI from server, logs the error and deletes the queued op.
4xx with asset errorCodeChecksum mismatch / quantity limitFetchFarmerAssetDataWorker scheduled; body refreshed; retried.
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/PortfolioLineItem.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/work/PortfolioLineItemWorker.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/rh/PortfolioLineItemPutWithCropChangeRH.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/utils/PortfolioLineItemUpdateWorker.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